Compare commits

..

No commits in common. "master" and "5.0.18" have entirely different histories.

485 changed files with 68932 additions and 24637 deletions

View File

@ -5,6 +5,7 @@
"ignore": [ "ignore": [
"resource/require.js", "resource/require.js",
"chrome/content/zotero/include.js", "chrome/content/zotero/include.js",
"resource/tinymce/tinymce.js",
"chrome/content/zotero/xpcom/citeproc.js", "chrome/content/zotero/xpcom/citeproc.js",
"resource/csl-validator.js", "resource/csl-validator.js",
"resource/react.js", "resource/react.js",
@ -14,9 +15,25 @@
"test/resource/*.js" "test/resource/*.js"
], ],
"plugins": [ "plugins": [
"syntax-flow",
"syntax-jsx", "syntax-jsx",
"syntax-async-generators",
"syntax-class-properties",
"syntax-decorators",
"syntax-do-expressions",
"syntax-export-extensions",
"syntax-flow",
"syntax-jsx",
"syntax-object-rest-spread",
"transform-react-jsx", "transform-react-jsx",
"transform-react-display-name", "transform-react-display-name",
[
"transform-async-to-module-method",
{
"module": "resource://zotero/bluebird.js",
"method": "coroutine"
}
],
[ [
"transform-es2015-modules-commonjs", "transform-es2015-modules-commonjs",
{ {

1
.gitignore vendored
View File

@ -2,4 +2,3 @@
node_modules node_modules
build build
.signatures.json .signatures.json
tmp

6
.gitmodules vendored
View File

@ -1,24 +1,18 @@
[submodule "translators"] [submodule "translators"]
path = translators path = translators
url = git://github.com/zotero/translators.git url = git://github.com/zotero/translators.git
branch = master
[submodule "chrome/content/zotero/locale/csl"] [submodule "chrome/content/zotero/locale/csl"]
path = chrome/content/zotero/locale/csl path = chrome/content/zotero/locale/csl
url = git://github.com/citation-style-language/locales.git url = git://github.com/citation-style-language/locales.git
branch = master
[submodule "styles"] [submodule "styles"]
path = styles path = styles
url = git://github.com/zotero/bundled-styles.git url = git://github.com/zotero/bundled-styles.git
branch = master
[submodule "test/resource/chai"] [submodule "test/resource/chai"]
path = test/resource/chai path = test/resource/chai
url = https://github.com/chaijs/chai.git url = https://github.com/chaijs/chai.git
branch = master
[submodule "test/resource/mocha"] [submodule "test/resource/mocha"]
path = test/resource/mocha path = test/resource/mocha
url = https://github.com/mochajs/mocha.git url = https://github.com/mochajs/mocha.git
branch = master
[submodule "test/resource/chai-as-promised"] [submodule "test/resource/chai-as-promised"]
path = test/resource/chai-as-promised path = test/resource/chai-as-promised
url = https://github.com/domenic/chai-as-promised.git url = https://github.com/domenic/chai-as-promised.git
branch = master

View File

@ -1,7 +1,7 @@
sudo: false sudo: false
language: node_js language: node_js
node_js: node_js:
- "8" - "node"
cache: cache:
directories: directories:
- "build" - "build"
@ -10,6 +10,7 @@ env:
global: global:
secure: "NxvkbZ7/Op7BTGQRR3C4q8lLoO29f8WtyNN27NSH7AO3H0vBr1Vp5xO8gn+H2qHEug5HvM+YrZ/xAkNXaZVbOInmBmKVMxqVvdpKp9JM1Amf+gzsXWQphfySvs6iqzyP6cwU/jspdvX/WSakgU5v7PWXxtUIaKxdANt6Rw7W+Pc=" secure: "NxvkbZ7/Op7BTGQRR3C4q8lLoO29f8WtyNN27NSH7AO3H0vBr1Vp5xO8gn+H2qHEug5HvM+YrZ/xAkNXaZVbOInmBmKVMxqVvdpKp9JM1Amf+gzsXWQphfySvs6iqzyP6cwU/jspdvX/WSakgU5v7PWXxtUIaKxdANt6Rw7W+Pc="
matrix: matrix:
- FX_VERSION="54.0"
- FX_VERSION="52.0.3" - FX_VERSION="52.0.3"
matrix: matrix:
fast_finish: true fast_finish: true
@ -25,15 +26,19 @@ install:
- if [ $FX_VERSION = "52.0.3" ]; then - 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"; 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 fi
- if [ $FX_VERSION = "54.0" ]; then
wget -O tarball "https://archive.mozilla.org/pub/firefox/tinderbox-builds/mozilla-release-linux64-add-on-devel/1496944705/firefox-54.0.en-US.linux-x86_64-add-on-devel.tar.bz2";
fi
- tar xf tarball - tar xf tarball
before_script: before_script:
- export DISPLAY=:99.0 - export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start - sh -e /etc/init.d/xvfb start
- npm i - npm i
- npm run build - npm run build
- if [[ $TRAVIS_REPO_SLUG = "zotero/zotero" && - if [ $FX_VERSION = "54.0" ] &&
($TRAVIS_BRANCH = "master" || $TRAVIS_BRANCH = *-hotfix) && [ $TRAVIS_REPO_SLUG = "zotero/zotero" ] &&
$TRAVIS_PULL_REQUEST = "false" ]]; then [ $TRAVIS_BRANCH = "master" ] &&
[ $TRAVIS_PULL_REQUEST = "false" ]; then
mkdir build-zip; mkdir build-zip;
cd build; cd build;
zip -r ../build-zip/$TRAVIS_COMMIT.zip *; zip -r ../build-zip/$TRAVIS_COMMIT.zip *;

View File

@ -2,9 +2,9 @@
## Bug Reports and Feature Requests ## Bug Reports and Feature Requests
Zotero does not use GitHub Issues for bug reports or feature requests. Please post all such requests to the [Zotero Forums](https://forums.zotero.org), where Zotero developers and many others can help. Keeping product discussions in the Zotero Forums allows the entire Zotero community to participate, including domain experts that can address many questions better than Zotero developers. In order to keep product discussions open to as many people as possible, Zotero does not use GitHub Issues for bug reports or feature requests. Please use the [Zotero Forums](https://forums.zotero.org) to report problems and suggest changes.
For confirmed bugs or agreed-upon changes, Zotero developers will create new issues in the relevant repositories. For confirmed bugs or agreed-upon changes, new issues will be created in the relevant repositories on GitHub by Zotero developers.
## Working with Zotero Code ## Working with Zotero Code

11
COPYING
View File

@ -1,15 +1,12 @@
Zotero is Copyright © 2018 Corporation for Digital Scholarship, Zotero is Copyright © 2006, 2007, 2008, 2009, 2010, 2011
Vienna, Virginia, USA http://digitalscholar.org Center for History and New Media, George Mason University,
Copyright © 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017
Roy Rosenzweig Center for History and New Media, George Mason University,
Fairfax, Virginia, USA http://zotero.org Fairfax, Virginia, USA http://zotero.org
The Corporation for Digital Scholarship distributes the Zotero source code The Center for History and New Media distributes the Zotero source code
under the GNU Affero General Public License, version 3 (AGPLv3). The full text under the GNU Affero General Public License, version 3 (AGPLv3). The full text
of this license is given below. of this license is given below.
The Zotero name is a registered trademark of the Corporation for Digital Scholarship. The Zotero name is a registered trademark of George Mason University.
See http://zotero.org/trademark for more information. See http://zotero.org/trademark for more information.
Third-party copyright in this distribution is noted where applicable. Third-party copyright in this distribution is noted where applicable.

View File

@ -1,11 +1,5 @@
content zotero chrome/content/zotero/ content zotero chrome/content/zotero/
content zotero-platform chrome/content/zotero-platform/mac/ os=Darwin content zotero-platform chrome/content/zotero-platform/ platform
content zotero-platform chrome/content/zotero-platform/win/ os=WINNT
content zotero-platform chrome/content/zotero-platform/unix/ os=Linux
content zotero-platform chrome/content/zotero-platform/unix/ os=SunOS
content zotero-platform chrome/content/zotero-platform/unix/ os=FreeBSD
content zotero-platform chrome/content/zotero-platform/unix/ os=OpenBSD
resource zotero resource/ resource zotero resource/
locale zotero en-US chrome/locale/en-US/zotero/ locale zotero en-US chrome/locale/en-US/zotero/

View File

@ -14,10 +14,6 @@ body[multiline="true"] {
width: 800px; width: 800px;
} }
#quick-format-dialog.progress-bar #quick-format-deck {
height: 37px;
}
#quick-format-search { #quick-format-search {
background: white; background: white;
-moz-appearance: searchfield; -moz-appearance: searchfield;

View File

@ -30,9 +30,3 @@ textbox
margin: -1px 5px -1px 0; margin: -1px 5px -1px 0;
padding: 0; padding: 0;
} }
/* BEGIN 2X BLOCK -- DO NOT EDIT MANUALLY -- USE 2XIZE */
@media (min-resolution: 1.25dppx) {
.creator-type-label > image { list-style-image: url('chrome://zotero/skin/mac/arrow-down@2x.png'); }
}

View File

@ -107,37 +107,22 @@
margin-left: 7px; margin-left: 7px;
} }
#zotero-tb-sync-stop .toolbarbutton-icon, @media (min-resolution: 1.5dppx) {
#zotero-tb-sync-error .toolbarbutton-icon { #zotero-pane .toolbarbutton-icon {
max-width: 28px;
}
#zotero-tb-sync-stop .toolbarbutton-icon,
#zotero-tb-sync-error .toolbarbutton-icon {
width: 16px; width: 16px;
}
} }
.zotero-tb-button > .toolbarbutton-icon { .zotero-tb-button, .zotero-tb-button:first-child, .zotero-tb-button:last-child {
max-width: 31px;
}
.zotero-tb-button,
.zotero-tb-button:first-child,
.zotero-tb-button:last-child {
-moz-margin-start: 0 !important; -moz-margin-start: 0 !important;
-moz-margin-end: 3px !important; -moz-margin-end: 3px !important;
-moz-padding-end: 10px !important; -moz-padding-end: 10px !important;
background: url("chrome://zotero/skin/mac/menubutton-end.png") right center/auto 24px no-repeat; background: url("chrome://zotero/skin/mac/menubutton-end.png") right center no-repeat;
}
.zotero-tb-button[type=menu] {
-moz-padding-end: 8px !important;
}
.zotero-tb-button > .toolbarbutton-icon {
background: url("chrome://zotero/skin/mac/menubutton-start.png") left center/auto 24px no-repeat;
padding: 4px 4px 4px 11px;
}
/* For menu buttons, decrease left padding by 1px */
.zotero-tb-button[type=menu] > .toolbarbutton-icon {
-moz-padding-start: 9px;
max-width: 29px;
} }
#zotero-collections-toolbar { #zotero-collections-toolbar {
@ -145,23 +130,12 @@
} }
.zotero-tb-button:-moz-window-inactive { .zotero-tb-button:-moz-window-inactive {
opacity: 0.5; opacity: 0.7;
}
.zotero-tb-button:-moz-window-inactive > .toolbarbutton-icon {
background: url("chrome://zotero/skin/mac/menubutton-start-inactive-window.png") left center/auto 24px no-repeat;
}
/* Use a darker background when inactive so the button itself doesn't get too dark at 50% */
.zotero-tb-button:-moz-window-inactive,
.zotero-tb-button:-moz-window-inactive:first-child,
.zotero-tb-button:-moz-window-inactive:last-child {
background: url("chrome://zotero/skin/mac/menubutton-end-inactive-window.png") right center/auto 24px no-repeat;
} }
.zotero-tb-button[open="true"], .zotero-tb-button[open="true"],
.zotero-tb-button:not([disabled="true"]):hover:active { .zotero-tb-button:not([disabled="true"]):hover:active {
background: url("chrome://zotero/skin/mac/menubutton-end-pressed.png") right center/auto 24px no-repeat; background: url("chrome://zotero/skin/mac/menubutton-end-pressed.png") right center no-repeat;
} }
.zotero-tb-button > menupopup { .zotero-tb-button > menupopup {
@ -169,9 +143,17 @@
margin-top: -4px; margin-top: -4px;
} }
#zotero-tb-search-menu-button { .zotero-tb-button > .toolbarbutton-icon {
margin: -6px 0 -6px -16px; /*-moz-binding: url('chrome://zotero-platform/content/zoterotbbutton.xml#zotero-tb-button');*/
padding: 6px 0 6px 15px; background: url("chrome://zotero/skin/mac/menubutton-start.png") left center no-repeat;
padding: 5px 1px 5px 11px;
}
#zotero-tb-search-menu-button
{
margin: -6px -2px -6px -16px;
padding: 6px 2px 6px 14px;
list-style-image: url("chrome://zotero/skin/mac/searchbar-dropmarker.png");
} }
#zotero-tb-sync > .toolbarbutton-icon { #zotero-tb-sync > .toolbarbutton-icon {
@ -186,7 +168,7 @@
.zotero-tb-button[open="true"] > .toolbarbutton-icon, .zotero-tb-button[open="true"] > .toolbarbutton-icon,
.zotero-tb-button:not([disabled="true"]):hover:active > .toolbarbutton-icon { .zotero-tb-button:not([disabled="true"]):hover:active > .toolbarbutton-icon {
background: url("chrome://zotero/skin/mac/menubutton-start-pressed.png") left center/auto 24px no-repeat; background: url("chrome://zotero/skin/mac/menubutton-start-pressed.png") left center no-repeat;
} }
.zotero-tb-button > .toolbarbutton-text { .zotero-tb-button > .toolbarbutton-text {
@ -378,7 +360,9 @@
#zotero-collections-splitter[state=collapsed] > grippy, #zotero-items-splitter[state=collapsed] > grippy #zotero-collections-splitter[state=collapsed] > grippy, #zotero-items-splitter[state=collapsed] > grippy
{ {
-moz-appearance: none; -moz-appearance: none;
background: url(chrome://zotero/skin/mac/vgrippy.png) center/auto 8px no-repeat; background-image: url("chrome://zotero/skin/mac/vgrippy.png");
background-position: center;
background-repeat: no-repeat;
width: 8px; width: 8px;
} }
@ -397,7 +381,9 @@
#zotero-tags-splitter > grippy #zotero-tags-splitter > grippy
{ {
-moz-appearance: none; -moz-appearance: none;
background: url(chrome://zotero/skin/mac/hgrippy.png) center/auto 8px no-repeat; background-image: url("chrome://zotero/skin/mac/hgrippy.png");
background-position: center;
background-repeat: no-repeat;
height: 8px; height: 8px;
} }
@ -474,6 +460,16 @@ treechildren::-moz-tree-image {
list-style-image: url('chrome://zotero/skin/mac/toolbar-note-add.png'); list-style-image: url('chrome://zotero/skin/mac/toolbar-note-add.png');
} }
@media (min-resolution: 1.5dppx) {
#zotero-tb-advanced-search {
list-style-image: url('chrome://zotero/skin/mac/toolbar-advanced-search@2x.png');
}
#zotero-tb-note-add {
list-style-image: url('chrome://zotero/skin/mac/toolbar-note-add@2x.png');
}
}
#zotero-tb-actions-menu #zotero-tb-actions-menu
{ {
list-style-image: url('chrome://zotero/skin/mac/cog.png'); list-style-image: url('chrome://zotero/skin/mac/cog.png');
@ -488,18 +484,3 @@ treechildren::-moz-tree-image {
#zotero-collectionmenu > .menuitem-iconic > .menu-iconic-left, #zotero-itemmenu > .menuitem-iconic > .menu-iconic-left, #zotero-collectionmenu > .menu-iconic > .menu-iconic-left, #zotero-itemmenu > .menu-iconic > .menu-iconic-left { #zotero-collectionmenu > .menuitem-iconic > .menu-iconic-left, #zotero-itemmenu > .menuitem-iconic > .menu-iconic-left, #zotero-collectionmenu > .menu-iconic > .menu-iconic-left, #zotero-itemmenu > .menu-iconic > .menu-iconic-left {
display: none; display: none;
} }
/* BEGIN 2X BLOCK -- DO NOT EDIT MANUALLY -- USE 2XIZE */
@media (min-resolution: 1.25dppx) {
.zotero-tb-button,.zotero-tb-button:first-child,.zotero-tb-button:last-child { background: url("chrome://zotero/skin/mac/menubutton-end@2x.png") right center/auto 24px no-repeat; }
.zotero-tb-button > .toolbarbutton-icon { background: url("chrome://zotero/skin/mac/menubutton-start@2x.png") left center/auto 24px no-repeat; }
.zotero-tb-button:-moz-window-inactive > .toolbarbutton-icon { background: url("chrome://zotero/skin/mac/menubutton-start-inactive-window@2x.png") left center/auto 24px no-repeat; }
.zotero-tb-button:-moz-window-inactive,.zotero-tb-button:-moz-window-inactive:first-child,.zotero-tb-button:-moz-window-inactive:last-child { background: url("chrome://zotero/skin/mac/menubutton-end-inactive-window@2x.png") right center/auto 24px no-repeat; }
.zotero-tb-button[open="true"],.zotero-tb-button:not([disabled="true"]):hover:active { background: url("chrome://zotero/skin/mac/menubutton-end-pressed@2x.png") right center/auto 24px no-repeat; }
.zotero-tb-button[open="true"] > .toolbarbutton-icon,.zotero-tb-button:not([disabled="true"]):hover:active > .toolbarbutton-icon { background: url("chrome://zotero/skin/mac/menubutton-start-pressed@2x.png") left center/auto 24px no-repeat; }
#zotero-collections-splitter[state=collapsed] > grippy, #zotero-items-splitter[state=collapsed] > grippy { background: url(chrome://zotero/skin/mac/vgrippy@2x.png) center/auto 8px no-repeat; }
#zotero-tags-splitter > grippy { background: url(chrome://zotero/skin/mac/hgrippy@2x.png) center/auto 8px no-repeat; }
#zotero-tb-advanced-search { list-style-image: url('chrome://zotero/skin/mac/toolbar-advanced-search@2x.png'); }
#zotero-tb-note-add { list-style-image: url('chrome://zotero/skin/mac/toolbar-note-add@2x.png'); }
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -168,12 +168,8 @@ toolbar:not([id="nav-bar"]) #zotero-toolbar-buttons separator {
background-color: Highlight; background-color: Highlight;
} }
#zotero-collections-tree treechildren::-moz-tree-row {
height: 1.3em;
}
@media (min-resolution: 1.5dppx) {
@media (min-resolution: 1.25dppx) {
#zotero-pane .toolbarbutton-icon { #zotero-pane .toolbarbutton-icon {
width: 16px; width: 16px;
} }
@ -183,11 +179,6 @@ toolbar:not([id="nav-bar"]) #zotero-toolbar-buttons separator {
} }
} }
/* Dropmarker added automatically on Linux */
.toolbarbutton-menu-dropmarker, #zotero-tb-search-menu-button {
list-style-image: none;
}
.zotero-tb-button:not([type=menu]) { .zotero-tb-button:not([type=menu]) {
margin-right: 4px; margin-right: 4px;
} }
@ -207,12 +198,12 @@ toolbar:not([id="nav-bar"]) #zotero-toolbar-buttons separator {
} }
/* Fixes tabs missing styling on (GTK 3.20) Ubuntu 16.10. See https://bugzilla.mozilla.org/show_bug.cgi?id=1306425 */ /* Fixes tabs missing styling on (GTK 3.20) Ubuntu 16.10. See https://bugzilla.mozilla.org/show_bug.cgi?id=1306425 */
tabpanels { #zotero-pane tabpanels, #zotero-prefs tabpanels {
-moz-appearance: none; -moz-appearance: none;
border: 1px solid hsla(0, 0%, 0%, 0.2); border: 1px solid hsla(0, 0%, 0%, 0.2);
} }
tab { #zotero-pane tab, #zotero-prefs tab {
-moz-appearance: none; -moz-appearance: none;
border-top: 1px solid hsla(0, 0%, 0%, 0.2); border-top: 1px solid hsla(0, 0%, 0%, 0.2);
border-right: 1px solid hsla(0, 0%, 0%, 0.2); border-right: 1px solid hsla(0, 0%, 0%, 0.2);
@ -228,14 +219,8 @@ tab {
#zotero-prefs .numberbox-input-box{ #zotero-prefs .numberbox-input-box{
-moz-appearance: textfield; -moz-appearance: textfield;
} }
/* Grippy icon missing anyway */
#zotero-prefs #noteFontSize { #zotero-pane splitter{
min-width: 3.8em;
}
#zotero-pane splitter {
border: 0;
width: 6px; width: 6px;
background-color: transparent;
background-image: none;
} }

View File

@ -1,3 +0,0 @@
#view-settings-menu .toolbarbutton-icon {
margin-right: 0; /* dropmarker spacing handled automatically on Linux */
}

View File

@ -1,18 +1,3 @@
:root {
--theme-border-color: #cecece;
}
/* Hide horrible blue effect for menu bar and toolbar */
#navigator-toolbox {
-moz-appearance: none;
}
#zotero-pane #zotero-toolbar {
-moz-appearance: none !important;
margin-top: -3px;
border-bottom-color: var(--theme-border-color);
}
/* /*
As of Fx36, the built-in styles don't properly handle a menu-button within combined buttons. As of Fx36, the built-in styles don't properly handle a menu-button within combined buttons.
@ -191,7 +176,7 @@ toolbar:not([id="nav-bar"]) #zotero-toolbar-buttons separator {
/* End toolbar buttons */ /* End toolbar buttons */
@media (min-resolution: 1.25dppx) { @media (min-resolution: 1.5dppx) {
#zotero-toolbar .toolbarbutton-icon { #zotero-toolbar .toolbarbutton-icon {
width: 16px; width: 16px;
} }
@ -203,15 +188,23 @@ toolbar:not([id="nav-bar"]) #zotero-toolbar-buttons separator {
#zotero-tb-search-menu-button { #zotero-tb-search-menu-button {
margin: 0 -1px 0 -4px; margin: 0 -1px 0 -4px;
padding: 5px 0 5px 5px;
border: 0; border: 0;
background: transparent; background: transparent;
/* Mozilla file copied from chrome://browser/skin/mainwindow-dropdown-arrow.png
for availability in Standalone */
list-style-image: url("chrome://zotero/skin/win/mainwindow-dropdown-arrow.png");
-moz-image-region: rect(0, 13px, 11px, 0);
} }
#zotero-tb-search-menu-button .button-menu-dropmarker { #zotero-tb-search-menu-button .button-menu-dropmarker {
display: none; display: none;
} }
#zotero-tb-search-menu-button:hover:active,
#zotero-tb-search-menu-button[open="true"] {
-moz-image-region: rect(0, 26px, 11px, 13px);
}
#zotero-tb-search .textbox-search-icon { #zotero-tb-search .textbox-search-icon {
visibility: hidden; visibility: hidden;
} }
@ -236,44 +229,13 @@ toolbar:not([id="nav-bar"]) #zotero-toolbar-buttons separator {
padding-left: 2px; padding-left: 2px;
} }
#zotero-collections-splitter:not([state=collapsed]), #zotero-toolbar:-moz-system-metric(windows-compositor) {
#zotero-items-splitter:not([state=collapsed]), -moz-appearance: none !important;
#zotero-tags-splitter:not([state=collapsed]) { background-image: -moz-linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
border: 0; background-color: rgb(207, 219, 236) !important;
background-color: transparent; border-width: 0 0 1px 0;
position: relative; border-style: solid;
/* Positive z-index positions the splitter on top of its siblings and makes border-color: #818790;
it clickable on both sides. */
z-index: 1;
}
#zotero-collections-splitter:not([state=collapsed]),
#zotero-items-splitter:not([state=collapsed]):not([orient=vertical]),
#zotero-tags-splitter:not([state=collapsed]) {
border-inline-end: 1px solid var(--theme-border-color);
min-width: 0;
width: 3px;
margin-inline-start: -3px;
}
#zotero-tags-splitter:not([state=collapsed]),
#zotero-items-splitter:not([state=collapsed])[orient=vertical] {
border-block-end: 1px solid var(--theme-border-color);
min-height: 0;
height: 3px;
margin-block-start: -3px;
}
#zotero-collections-splitter > grippy,
#zotero-items-splitter > grippy,
#zotero-tags-splitter > grippy {
border: 0;
}
#zotero-collections-splitter:not([state=collapsed]) > grippy,
#zotero-items-splitter:not([state=collapsed]) > grippy,
#zotero-tags-splitter:not([state=collapsed]) > grippy {
display: none;
} }
#zotero-collections-tree, #zotero-items-tree, #zotero-view-item { #zotero-collections-tree, #zotero-items-tree, #zotero-view-item {
@ -288,10 +250,6 @@ toolbar:not([id="nav-bar"]) #zotero-toolbar-buttons separator {
-moz-border-left-colors: none; -moz-border-left-colors: none;
} }
treechildren::-moz-tree-twisty {
padding: 0 4px;
}
/* Undo tree row spacing change in Fx25 on Windows */ /* Undo tree row spacing change in Fx25 on Windows */
#zotero-collections-tree treechildren::-moz-tree-row, #zotero-collections-tree treechildren::-moz-tree-row,
#zotero-items-tree treechildren::-moz-tree-row, #zotero-items-tree treechildren::-moz-tree-row,
@ -299,8 +257,8 @@ treechildren::-moz-tree-twisty {
height: 1.6em; height: 1.6em;
} }
tree { #zotero-collections-tree {
border-width: 0; border-width: 0 1px 1px 0;
} }
/* Restore row highlighting on drag over, though I'm not sure how we're losing it to begin with. */ /* Restore row highlighting on drag over, though I'm not sure how we're losing it to begin with. */
@ -308,31 +266,15 @@ tree {
background-color: Highlight; background-color: Highlight;
} }
#zotero-tag-selector groupbox { #zotero-items-tree {
-moz-appearance: none; border-width: 0 1px;
padding: 0;
border: 0;
}
#tags-box {
padding-top: 0.1em;
padding-left: 0.05em;
}
#tags-box button {
margin: .04em 0 0 .15em !important;
}
#zotero-editpane-tabs spacer {
border: 0;
} }
#zotero-view-item { #zotero-view-item {
padding: 0 !important; padding: 0 !important;
-moz-appearance: none; -moz-appearance: none;
background-color: -moz-field; background-color: -moz-field;
border-width: 1px 0 0 0; border-width: 1px 0 0 1px;
border-color: var(--theme-border-color);
} }
#zotero-view-tabbox > tabs { #zotero-view-tabbox > tabs {
@ -341,7 +283,10 @@ tree {
#zotero-item-pane-groupbox { #zotero-item-pane-groupbox {
-moz-appearance: none !important; -moz-appearance: none !important;
border-width: 0; border-radius: 0;
border-width: 0 0 0 1px;
border-color: #818790;
border-style: solid;
} }
#zotero-editpane-item-box > scrollbox, #zotero-view-item > tabpanel > vbox, #zotero-editpane-item-box > scrollbox, #zotero-view-item > tabpanel > vbox,

View File

@ -1,3 +1,4 @@
button { button {
font-family: Segoe UI, sans-serif; font-family: Segoe UI, sans-serif;
font-size: 1em;
} }

View File

@ -59,7 +59,6 @@ var ZoteroAdvancedSearch = new function() {
// A minimal implementation of Zotero.CollectionTreeRow // A minimal implementation of Zotero.CollectionTreeRow
var collectionTreeRow = { var collectionTreeRow = {
view: {},
ref: _searchBox.search, ref: _searchBox.search,
isSearchMode: function() { return true; }, isSearchMode: function() { return true; },
getItems: Zotero.Promise.coroutine(function* () { getItems: Zotero.Promise.coroutine(function* () {

View File

@ -146,9 +146,8 @@ var Zotero_File_Interface_Bibliography = new function() {
if(_io.useEndnotes && _io.useEndnotes == 1) document.getElementById("displayAs").selectedIndex = 1; if(_io.useEndnotes && _io.useEndnotes == 1) document.getElementById("displayAs").selectedIndex = 1;
let dialog = document.getElementById("zotero-doc-prefs-dialog"); let dialog = document.getElementById("zotero-doc-prefs-dialog");
dialog.setAttribute('title', `${Zotero.clientName} - ${dialog.getAttribute('title')}`); dialog.setAttribute('title', `${Zotero.clientName} - ${dialog.getAttribute('title')}`);
}
if (document.getElementById("formatUsing-groupbox")) { if(document.getElementById("formatUsing")) {
if (["Field", "ReferenceMark"].includes(_io.primaryFieldType)) {
if(_io.fieldType == "Bookmark") document.getElementById("formatUsing").selectedIndex = 1; if(_io.fieldType == "Bookmark") document.getElementById("formatUsing").selectedIndex = 1;
var formatOption = (_io.primaryFieldType == "ReferenceMark" ? "referenceMarks" : "fields"); var formatOption = (_io.primaryFieldType == "ReferenceMark" ? "referenceMarks" : "fields");
document.getElementById("fields").label = document.getElementById("fields").label =
@ -159,10 +158,6 @@ var Zotero_File_Interface_Bibliography = new function() {
Zotero.getString("integration."+formatOption+".fileFormatNotice"); Zotero.getString("integration."+formatOption+".fileFormatNotice");
document.getElementById("bookmarks-file-format-notice").textContent = document.getElementById("bookmarks-file-format-notice").textContent =
Zotero.getString("integration.fields.fileFormatNotice"); Zotero.getString("integration.fields.fileFormatNotice");
} else {
document.getElementById("formatUsing-groupbox").style.display = "none";
_io.fieldType = _io.primaryFieldType;
}
} }
if(document.getElementById("automaticJournalAbbreviations-checkbox")) { if(document.getElementById("automaticJournalAbbreviations-checkbox")) {
if(_io.automaticJournalAbbreviations === undefined) { if(_io.automaticJournalAbbreviations === undefined) {
@ -171,23 +166,12 @@ var Zotero_File_Interface_Bibliography = new function() {
if(_io.automaticJournalAbbreviations) { if(_io.automaticJournalAbbreviations) {
document.getElementById("automaticJournalAbbreviations-checkbox").checked = true; document.getElementById("automaticJournalAbbreviations-checkbox").checked = true;
} }
document.getElementById("automaticCitationUpdates-checkbox").checked = !_io.delayCitationUpdates;
}
} }
// set style to false, in case this is cancelled // set style to false, in case this is cancelled
_io.style = false; _io.style = false;
}); });
this.openHelpLink = function() {
var url = "https://www.zotero.org/support/word_processor_plugin_usage";
var win = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
Zotero.launchURL(url);
};
/* /*
* Called when locale is changed * Called when locale is changed
*/ */
@ -208,18 +192,22 @@ var Zotero_File_Interface_Bibliography = new function() {
// //
// For integrationDocPrefs.xul // For integrationDocPrefs.xul
// //
if (isDocPrefs) {
// update status of displayAs box based on style class // update status of displayAs box based on style class
if(document.getElementById("displayAs-groupbox")) {
var isNote = selectedStyleObj.class == "note"; var isNote = selectedStyleObj.class == "note";
var multipleNotesSupported = _io.supportedNotes.length > 1; document.getElementById("displayAs-groupbox").hidden = !isNote;
document.getElementById("displayAs-groupbox").hidden = !isNote || !multipleNotesSupported;
// update status of formatUsing box based on style class // update status of formatUsing box based on style class
if(document.getElementById("formatUsing")) {
if(isNote) document.getElementById("formatUsing").selectedIndex = 0; if(isNote) document.getElementById("formatUsing").selectedIndex = 0;
document.getElementById("bookmarks").disabled = isNote; document.getElementById("bookmarks").disabled = isNote;
document.getElementById("bookmarks-caption").disabled = isNote; document.getElementById("bookmarks-caption").disabled = isNote;
}
}
// update status of displayAs box based on style class // update status of displayAs box based on style class
if(document.getElementById("automaticJournalAbbreviations-vbox")) {
document.getElementById("automaticJournalAbbreviations-vbox").hidden = document.getElementById("automaticJournalAbbreviations-vbox").hidden =
!selectedStyleObj.usesAbbreviation; !selectedStyleObj.usesAbbreviation;
} }
@ -278,7 +266,6 @@ var Zotero_File_Interface_Bibliography = new function() {
} }
_io.useEndnotes = document.getElementById("displayAs").selectedIndex; _io.useEndnotes = document.getElementById("displayAs").selectedIndex;
_io.fieldType = (document.getElementById("formatUsing").selectedIndex == 0 ? _io.primaryFieldType : _io.secondaryFieldType); _io.fieldType = (document.getElementById("formatUsing").selectedIndex == 0 ? _io.primaryFieldType : _io.secondaryFieldType);
_io.delayCitationUpdates = !document.getElementById("automaticCitationUpdates-checkbox").checked;
} }
// remember style and locale if user selected these explicitly // remember style and locale if user selected these explicitly
@ -296,7 +283,8 @@ var Zotero_File_Interface_Bibliography = new function() {
document.documentElement.getButton('cancel').click(); document.documentElement.getButton('cancel').click();
var win = Zotero.Utilities.Internal.openPreferences('zotero-prefpane-cite', { tab: 'styles-tab' }); var win = Zotero.Utilities.Internal.openPreferences('zotero-prefpane-cite', { tab: 'styles-tab' });
if (isDocPrefs) { if (isDocPrefs) {
Zotero.Utilities.Internal.activate(win); // TODO: Move activate() code elsewhere
Zotero.Integration.activate(win);
} }
}; };
} }

View File

@ -160,21 +160,12 @@
<parameter name="event"/> <parameter name="event"/>
<parameter name="type"/> <parameter name="type"/>
<parameter name="ids"/> <parameter name="ids"/>
<parameter name="extraData"/>
<body><![CDATA[ <body><![CDATA[
if (event != 'modify' || !this.item || !this.item.id) return; if (event != 'modify' || !this.item || !this.item.id) return;
for (let id of ids) { for (let i = 0; i < ids.length; i++) {
if (id != this.item.id) { if (ids[i] != this.item.id) {
continue; continue;
} }
var noteEditor = this._id('attachment-note-editor')
if (extraData && extraData[id]
&& extraData[id].noteEditorID == noteEditor.instanceID) {
//Zotero.debug("Skipping notification from current attachment note field");
continue;
}
this.refresh(); this.refresh();
break; break;
} }
@ -239,7 +230,6 @@
if (this.displayURL) { if (this.displayURL) {
var urlSpec = this.item.getField('url'); var urlSpec = this.item.getField('url');
urlField.setAttribute('value', urlSpec); urlField.setAttribute('value', urlSpec);
urlField.setAttribute('tooltiptext', urlSpec);
urlField.setAttribute('hidden', false); urlField.setAttribute('hidden', false);
if (this.clickableLink) { if (this.clickableLink) {
urlField.onclick = function (event) { urlField.onclick = function (event) {
@ -260,22 +250,15 @@
if (this.displayAccessed) { if (this.displayAccessed) {
this._id("accessed-label").value = Zotero.getString('itemFields.accessDate') this._id("accessed-label").value = Zotero.getString('itemFields.accessDate')
+ Zotero.getString('punctuation.colon'); + Zotero.getString('punctuation.colon');
let val = this.item.getField('accessDate'); this._id("accessed").value = Zotero.Date.sqlToDate(
if (val) { this.item.getField('accessDate'), true
val = Zotero.Date.sqlToDate(val, true); ).toLocaleString();
}
if (val) {
this._id("accessed").value = val.toLocaleString();
accessed.hidden = false; accessed.hidden = false;
} }
else { else {
accessed.hidden = true; accessed.hidden = true;
} }
} }
else {
accessed.hidden = true;
}
}
// Metadata for files // Metadata for files
else { else {
urlField.hidden = true; urlField.hidden = true;

View File

@ -313,6 +313,7 @@
this.itemTypeMenu.parentNode.hidden = true; this.itemTypeMenu.parentNode.hidden = true;
} }
// //
// Clear and rebuild metadata fields // Clear and rebuild metadata fields
// //
@ -426,7 +427,7 @@
// Pull out DOI, in case there's a prefix // Pull out DOI, in case there's a prefix
var doi = Zotero.Utilities.cleanDOI(val); var doi = Zotero.Utilities.cleanDOI(val);
if (doi) { if (doi) {
doi = "https://doi.org/" + encodeURIComponent(doi); doi = "http://dx.doi.org/" + encodeURIComponent(doi);
label.classList.add("pointer"); label.classList.add("pointer");
label.setAttribute("onclick", "ZoteroPane_Local.loadURI('" + doi + "', event)"); label.setAttribute("onclick", "ZoteroPane_Local.loadURI('" + doi + "', event)");
label.setAttribute("tooltiptext", Zotero.getString('locate.online.tooltip')); label.setAttribute("tooltiptext", Zotero.getString('locate.online.tooltip'));
@ -601,11 +602,10 @@
} }
// Move to next or previous field if (shift-)tab was pressed // Move to next or previous field if (shift-)tab was pressed
if (this._lastTabIndex && this._lastTabIndex != -1) { if (this._lastTabIndex && this._tabDirection)
this._focusNextField(this._lastTabIndex); {
this._focusNextField(this._dynamicFields, this._lastTabIndex, this._tabDirection == -1);
} }
this._refreshed = true;
]]> ]]>
</body> </body>
</method> </method>
@ -933,7 +933,7 @@
// Switch to single-field mode // Switch to single-field mode
if (fieldMode == 1) { if (fieldMode == 1) {
button.style.background = `url("chrome://zotero/skin/textfield-dual${Zotero.hiDPISuffix}.png") center/21px auto no-repeat`; button.style.backgroundImage = 'url("chrome://zotero/skin/textfield-dual.png")';
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.two')); button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.two'));
lastName.setAttribute('fieldMode', '1'); lastName.setAttribute('fieldMode', '1');
button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 0, false, true)"); button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 0, false, true)");
@ -966,7 +966,7 @@
} }
// Switch to two-field mode // Switch to two-field mode
else { else {
button.style.background = `url("chrome://zotero/skin/textfield-single${Zotero.hiDPISuffix}.png") center/21px auto no-repeat`; button.style.backgroundImage = 'url("chrome://zotero/skin/textfield-single.png")';
button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.one')); button.setAttribute('tooltiptext', Zotero.getString('pane.item.switchFieldMode.one'));
lastName.setAttribute('fieldMode', '0'); lastName.setAttribute('fieldMode', '0');
button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 1, false, true)"); button.setAttribute('onclick', "document.getBindingParent(this).switchCreatorMode(Zotero.getAncestorByTagName(this, 'row'), 1, false, true)");
@ -1046,10 +1046,6 @@
var fields = this.getCreatorFields(row); var fields = this.getCreatorFields(row);
fields.fieldMode = fieldMode; fields.fieldMode = fieldMode;
this.modifyCreator(index, fields); this.modifyCreator(index, fields);
if (this.saveOnEdit) {
// See note in transformText()
this.blurOpenField().then(() => this.item.saveTx());
}
} }
]]> ]]>
</body> </body>
@ -1087,17 +1083,12 @@
<method name="changeTypeTo"> <method name="changeTypeTo">
<parameter name="itemTypeID"/> <parameter name="itemTypeID"/>
<parameter name="menu"/> <parameter name="menu"/>
<body><![CDATA[ <body>
return (async function () { <![CDATA[
if (itemTypeID == this.item.itemTypeID) { if (itemTypeID == this.item.itemTypeID) {
return true; return true;
} }
if (this.saveOnEdit) {
await this.blurOpenField();
await this.item.saveTx();
}
var fieldsToDelete = this.item.getFieldsNotInType(itemTypeID, true); var fieldsToDelete = this.item.getFieldsNotInType(itemTypeID, true);
// Special cases handled below // Special cases handled below
@ -1153,16 +1144,15 @@
this.item.setType(itemTypeID); this.item.setType(itemTypeID);
if (this.saveOnEdit) { if (this.saveOnEdit) {
// See note in transformText() this.item.saveTx();
await this.blurOpenField();
await this.item.saveTx();
} }
else { else {
this.refresh(); this.refresh();
} }
if (this.eventHandlers['itemtypechange'] && this.eventHandlers['itemtypechange'].length) { if (this.eventHandlers['itemtypechange'] && this.eventHandlers['itemtypechange'].length) {
this.eventHandlers['itemtypechange'].forEach(f => f.bind(this)()); var self = this;
this.eventHandlers['itemtypechange'].forEach(function (f) f.bind(self)());
} }
return true; return true;
@ -1174,8 +1164,8 @@
} }
return false; return false;
}.bind(this))(); ]]>
]]></body> </body>
</method> </method>
@ -1274,7 +1264,6 @@
var valueElement = document.createElement("label"); var valueElement = document.createElement("label");
} }
valueElement.setAttribute('id', `itembox-field-value-${fieldName}`);
valueElement.setAttribute('fieldname', fieldName); valueElement.setAttribute('fieldname', fieldName);
valueElement.setAttribute('flex', 1); valueElement.setAttribute('flex', 1);
@ -1314,10 +1303,7 @@
if (date) { if (date) {
// If no time, interpret as local, not UTC // If no time, interpret as local, not UTC
if (Zotero.Date.isSQLDate(valueText)) { if (Zotero.Date.isSQLDate(valueText)) {
// Add time to avoid showing previous day if date is in date = Zotero.Date.sqlToDate(valueText);
// DST (including the current date at 00:00:00) and we're
// in standard time
date = Zotero.Date.sqlToDate(valueText + ' 12:00:00');
valueText = date.toLocaleDateString(); valueText = date.toLocaleDateString();
} }
else { else {
@ -1426,26 +1412,11 @@
<method name="showEditor"> <method name="showEditor">
<parameter name="elem"/> <parameter name="elem"/>
<body><![CDATA[ <body>
return (async function () { <![CDATA[
Zotero.debug(`Showing editor for ${elem.getAttribute('fieldname')}`); // Blur any active fields
if (this._dynamicFields) {
var label = Zotero.getAncestorByTagName(elem, 'row').querySelector('label'); this._dynamicFields.focus();
var lastTabIndex = this._lastTabIndex = parseInt(elem.getAttribute('ztabindex'));
// If a field is open, hide it before selecting the new field, which might
// trigger a refresh
var activeField = this._dynamicFields.querySelector('textbox');
if (activeField) {
this._refreshed = false;
await this.blurOpenField();
this._lastTabIndex = lastTabIndex;
// If the box was refreshed, the clicked element is no longer valid,
// so just focus by tab index
if (this._refreshed) {
this._focusNextField(this._lastTabIndex);
return;
}
} }
// In Firefox 45, when clicking a multiline field such as Extra, the event is // In Firefox 45, when clicking a multiline field such as Extra, the event is
@ -1454,6 +1425,8 @@
elem = elem.parentNode; elem = elem.parentNode;
} }
Zotero.debug('Showing editor');
var fieldName = elem.getAttribute('fieldname'); var fieldName = elem.getAttribute('fieldname');
var tabindex = elem.getAttribute('ztabindex'); var tabindex = elem.getAttribute('ztabindex');
@ -1495,7 +1468,6 @@
} }
var t = document.createElement("textbox"); var t = document.createElement("textbox");
t.setAttribute('id', `itembox-field-textbox-${fieldName}`);
t.setAttribute('value', value); t.setAttribute('value', value);
t.setAttribute('fieldname', fieldName); t.setAttribute('fieldname', fieldName);
t.setAttribute('ztabindex', tabindex); t.setAttribute('ztabindex', tabindex);
@ -1552,9 +1524,6 @@
var box = elem.parentNode; var box = elem.parentNode;
box.replaceChild(t, elem); box.replaceChild(t, elem);
// Associate textbox with label
label.setAttribute('control', t.getAttribute('id'));
// Prevent error when clicking between a changed field // Prevent error when clicking between a changed field
// and another -- there's probably a better way // and another -- there's probably a better way
if (!t.select) { if (!t.select) {
@ -1583,9 +1552,12 @@
}); });
t.setAttribute('onkeypress', "return document.getBindingParent(this).handleKeyPress(event)"); t.setAttribute('onkeypress', "return document.getBindingParent(this).handleKeyPress(event)");
this._tabDirection = false;
this._lastTabIndex = tabindex;
return t; return t;
}.bind(this))(); ]]>
]]></body> </body>
</method> </method>
@ -1628,8 +1600,8 @@
textbox.getAttribute('fieldname').split('-'); textbox.getAttribute('fieldname').split('-');
if (stayFocused) { if (stayFocused) {
this._lastTabIndex = parseInt(textbox.getAttribute('ztabindex')); this._lastTabIndex = parseInt(textbox.getAttribute('ztabindex')) - 1;
this._tabDirection = false; this._tabDirection = 1;
} }
var creator = Zotero.Creators.get(creatorID); var creator = Zotero.Creators.get(creatorID);
@ -1662,13 +1634,11 @@
fields[creatorField] = creator[creatorField]; fields[creatorField] = creator[creatorField];
fields[otherField] = creator[otherField]; fields[otherField] = creator[otherField];
this.modifyCreator(creatorIndex, fields);
if (this.saveOnEdit) {
this.ignoreBlur = true; this.ignoreBlur = true;
this.item.saveTx().then(() => { this.modifyCreator(creatorIndex, fields)
.then(function () {
this.ignoreBlur = false; this.ignoreBlur = false;
}); }.bind(this));
}
} }
// Otherwise let the autocomplete popup handle matters // Otherwise let the autocomplete popup handle matters
@ -1692,6 +1662,7 @@
break; break;
} }
// Prevent blur on containing textbox // Prevent blur on containing textbox
// DEBUG: what happens if this isn't present? // DEBUG: what happens if this isn't present?
event.preventDefault(); event.preventDefault();
@ -1703,7 +1674,7 @@
Zotero.debug("Value hasn't changed"); Zotero.debug("Value hasn't changed");
// If + button is disabled, just focus next creator row // If + button is disabled, just focus next creator row
if (Zotero.getAncestorByTagName(target, 'row').lastChild.lastChild.disabled) { if (Zotero.getAncestorByTagName(target, 'row').lastChild.lastChild.disabled) {
this._focusNextField(this._lastTabIndex); this._focusNextField(this._dynamicFields, this._lastTabIndex, false);
} }
else { else {
var creatorFields = this.getCreatorFields(Zotero.getAncestorByTagName(target, 'row')); var creatorFields = this.getCreatorFields(Zotero.getAncestorByTagName(target, 'row'));
@ -1743,12 +1714,10 @@
return false; return false;
case event.DOM_VK_TAB: case event.DOM_VK_TAB:
if (event.shiftKey) { this._tabDirection = event.shiftKey ? -1 : 1;
this._focusNextField(this._lastTabIndex, true); // Blur the old manually -- not sure why this is necessary,
} // but it prevents an immediate blur() on the next tag
else { focused.blur();
this._focusNextField(++this._lastTabIndex);
}
return false; return false;
} }
@ -1778,11 +1747,8 @@
<method name="hideEditor"> <method name="hideEditor">
<parameter name="textbox"/> <parameter name="textbox"/>
<body><![CDATA[ <body><![CDATA[
return (async function () { return Zotero.spawn(function* () {
Zotero.debug(`Hiding editor for ${textbox.getAttribute('fieldname')}`); Zotero.debug('Hiding editor');
var label = Zotero.getAncestorByTagName(textbox, 'row').querySelector('label');
this._lastTabIndex = -1;
// Prevent autocomplete breakage in Firefox 3 // Prevent autocomplete breakage in Firefox 3
if (textbox.mController) { if (textbox.mController) {
@ -1797,7 +1763,6 @@
var elem; var elem;
var [field, creatorIndex, creatorField] = fieldName.split('-'); var [field, creatorIndex, creatorField] = fieldName.split('-');
var newVal;
// Creator fields // Creator fields
if (field == 'creator') { if (field == 'creator') {
@ -1828,7 +1793,7 @@
if (creatorsToShift > 0) { if (creatorsToShift > 0) {
//Add extra creators //Add extra creators
for (var i=0;i<nameArray.length;i++) { for (var i=0;i<nameArray.length;i++) {
this.modifyCreator(i + initNumCreators, otherFields); yield this.modifyCreator(i + initNumCreators, otherFields, true);
} }
//Shift existing creators //Shift existing creators
@ -1850,7 +1815,7 @@
otherFields.lastName=tempName; otherFields.lastName=tempName;
otherFields.firstName=''; otherFields.firstName='';
} }
this.modifyCreator(creatorIndex, otherFields); yield this.modifyCreator(creatorIndex, otherFields, true);
creatorIndex++; creatorIndex++;
} }
this._tabDirection = tabDirectionBuffer; this._tabDirection = tabDirectionBuffer;
@ -1863,7 +1828,11 @@
} }
} }
else { else {
this.modifyCreator(creatorIndex, otherFields); yield this.modifyCreator(creatorIndex, otherFields);
}
if (this.saveOnEdit) {
yield this.item.saveTx();
} }
var val = this.item.getCreator(creatorIndex); var val = this.item.getCreator(creatorIndex);
@ -1880,17 +1849,7 @@
} }
} }
newVal = val; var newVal = val;
// Reset creator mode settings here so that flex attribute gets reset
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
if (Zotero.ItemTypes.getName(this.item.itemTypeID) === "bookSection") {
var creatorTypeLabels = document.getAnonymousNodes(this)[0].getElementsByClassName("creator-type-label");
Zotero.debug(creatorTypeLabels[creatorTypeLabels.length-1] + "");
document.getElementById("zotero-author-guidance").show({
forEl: creatorTypeLabels[creatorTypeLabels.length-1]
});
}
} }
// Fields // Fields
@ -1899,12 +1858,8 @@
if (value != '') { if (value != '') {
switch (fieldName) { switch (fieldName) {
case 'accessDate': case 'accessDate':
// Allow "now" to use current time
if (value == 'now') {
value = Zotero.Date.dateToSQL(new Date(), true);
}
// If just date, don't convert to UTC // If just date, don't convert to UTC
else if (Zotero.Date.isSQLDate(value)) { if (Zotero.Date.isSQLDate(value)) {
var localDate = Zotero.Date.sqlToDate(value); var localDate = Zotero.Date.sqlToDate(value);
value = Zotero.Date.dateToSQL(localDate).replace(' 00:00:00', ''); value = Zotero.Date.dateToSQL(localDate).replace(' 00:00:00', '');
} }
@ -1959,26 +1914,40 @@
} }
} }
this._modifyField(fieldName, value); yield this._modifyField(fieldName, value, this.saveOnEdit);
newVal = this.item.getField(fieldName);
var newVal = this.item.getField(fieldName);
} }
// Close box // If box is still open (due to field not being modified and there not being
// a refresh), close it manually
if (textbox && textbox.parentNode) {
elem = this.createValueElement( elem = this.createValueElement(
newVal, newVal,
fieldName, fieldName,
tabindex tabindex
); );
var box = textbox.parentNode; var box = textbox.parentNode;
box.replaceChild(elem, textbox); box.replaceChild(elem,textbox);
// Disassociate textbox from label
label.setAttribute('control', elem.getAttribute('id'));
if (this.saveOnEdit) {
await this.item.saveTx();
} }
}.bind(this))();
if(field === 'creator') {
// Reset creator mode settings here so that flex attribute gets reset
this.switchCreatorMode(row, (otherFields.fieldMode ? 1 : 0), true);
if(Zotero.ItemTypes.getName(this.item.itemTypeID) === "bookSection") {
var creatorTypeLabels = document.getAnonymousNodes(this)[0].getElementsByClassName("creator-type-label");
Zotero.debug(creatorTypeLabels[creatorTypeLabels.length-1] + "");
document.getElementById("zotero-author-guidance").show({
forEl: creatorTypeLabels[creatorTypeLabels.length-1]
});
}
}
if (this._tabDirection) {
var focusBox = this._dynamicFields;
this._focusNextField(focusBox, this._lastTabIndex, this._tabDirection == -1);
}
}, this);
]]></body> ]]></body>
</method> </method>
@ -2009,8 +1978,14 @@
<method name="_modifyField"> <method name="_modifyField">
<parameter name="field"/> <parameter name="field"/>
<parameter name="value"/> <parameter name="value"/>
<parameter name="save"/>
<body><![CDATA[ <body><![CDATA[
return Zotero.spawn(function* () {
this.item.setField(field, value); this.item.setField(field, value);
if (save) {
yield this.item.saveTx();
}
}, this);
]]></body> ]]></body>
</method> </method>
@ -2047,7 +2022,7 @@
<parameter name="label"/> <parameter name="label"/>
<parameter name="mode"/> <parameter name="mode"/>
<body><![CDATA[ <body><![CDATA[
return (async function () { return Zotero.spawn(function* () {
var val = this._getFieldValue(label); var val = this._getFieldValue(label);
switch (mode) { switch (mode) {
case 'title': case 'title':
@ -2065,14 +2040,12 @@
throw ("Invalid transform mode '" + mode + "' in zoteroitembox.textTransform()"); throw ("Invalid transform mode '" + mode + "' in zoteroitembox.textTransform()");
} }
this._setFieldValue(label, newVal); this._setFieldValue(label, newVal);
this._modifyField(label.getAttribute('fieldname'), newVal);
if (this.saveOnEdit) { if (this.saveOnEdit) {
// If a field is open, blur it, which will trigger a save and cause // See note in modifyCreator()
// the saveTx() to be a no-op yield this.blurOpenField();
await this.blurOpenField();
await this.item.saveTx();
} }
}.bind(this))(); return this._modifyField(label.getAttribute('fieldname'), newVal, this.saveOnEdit);
}, this);
]]></body> ]]></body>
</method> </method>
@ -2112,7 +2085,9 @@
<method name="modifyCreator"> <method name="modifyCreator">
<parameter name="index"/> <parameter name="index"/>
<parameter name="fields"/> <parameter name="fields"/>
<parameter name="skipSave"/>
<body><![CDATA[ <body><![CDATA[
return Zotero.spawn(function* () {
var libraryID = this.item.libraryID; var libraryID = this.item.libraryID;
var firstName = fields.firstName; var firstName = fields.firstName;
var lastName = fields.lastName; var lastName = fields.lastName;
@ -2124,12 +2099,28 @@
// Don't save empty creators // Don't save empty creators
if (!firstName && !lastName){ if (!firstName && !lastName){
if (!oldCreator) { if (!oldCreator) {
return false; return;
} }
return this.item.removeCreator(index); this.item.removeCreator(index);
if (this.saveOnEdit && !skipSave) {
// Make sure any open field is saved, since a blur() isn't otherwise
// triggered clicking directly to a popup menu. (If a field is open, the
// saveTx() below will become a no-op.)
yield this.blurOpenField();
return this.item.saveTx();
}
return;
} }
return this.item.setCreator(index, fields); var changed = this.item.setCreator(index, fields);
if (changed && this.saveOnEdit && !skipSave) {
// See note above
yield this.blurOpenField();
return this.item.saveTx();
}
}, this);
]]></body> ]]></body>
</method> </method>
@ -2140,7 +2131,7 @@
<method name="swapNames"> <method name="swapNames">
<parameter name="event"/> <parameter name="event"/>
<body><![CDATA[ <body><![CDATA[
return (async function () { return Zotero.Promise.try(function () {
var row = Zotero.getAncestorByTagName(document.popupNode, 'row'); var row = Zotero.getAncestorByTagName(document.popupNode, 'row');
var typeBox = row.getElementsByAttribute('popup', 'creator-type-menu')[0]; var typeBox = row.getElementsByAttribute('popup', 'creator-type-menu')[0];
var creatorIndex = parseInt(typeBox.getAttribute('fieldname').split('-')[1]); var creatorIndex = parseInt(typeBox.getAttribute('fieldname').split('-')[1]);
@ -2149,13 +2140,8 @@
var firstName = fields.firstName; var firstName = fields.firstName;
fields.lastName = firstName; fields.lastName = firstName;
fields.firstName = lastName; fields.firstName = lastName;
this.modifyCreator(creatorIndex, fields); return this.modifyCreator(creatorIndex, fields);
if (this.saveOnEdit) { }.bind(this));
// See note in transformText()
await this.blurOpenField();
await this.item.saveTx();
}
}.bind(this))();
]]></body> ]]></body>
</method> </method>
@ -2182,8 +2168,9 @@
this.item.setCreator(newIndex, a); this.item.setCreator(newIndex, a);
this.item.setCreator(index, b); this.item.setCreator(index, b);
if (this.saveOnEdit) { if (this.saveOnEdit) {
// See note in transformText() // See note in modifyCreator()
yield this.blurOpenField(); yield this.blurOpenField();
return this.item.saveTx(); return this.item.saveTx();
} }
}, this); }, this);
@ -2213,7 +2200,7 @@
<method name="focusFirstField"> <method name="focusFirstField">
<body> <body>
<![CDATA[ <![CDATA[
this._focusNextField(1); this._focusNextField(this._dynamicFields, 0, false);
]]> ]]>
</body> </body>
</method> </method>
@ -2228,11 +2215,11 @@
completes, so it doesn't know where it's supposed to go next.) completes, so it doesn't know where it's supposed to go next.)
--> -->
<method name="_focusNextField"> <method name="_focusNextField">
<parameter name="box"/>
<parameter name="tabindex"/> <parameter name="tabindex"/>
<parameter name="back"/> <parameter name="back"/>
<body> <body>
<![CDATA[ <![CDATA[
var box = this._dynamicFields;
tabindex = parseInt(tabindex); tabindex = parseInt(tabindex);
// Get all fields with ztabindex attributes // Get all fields with ztabindex attributes
@ -2254,9 +2241,9 @@
} }
} }
else { else {
Zotero.debug('Looking for tabindex ' + tabindex, 4); Zotero.debug('Looking for next tabindex after ' + tabindex, 4);
for (var pos = 0; pos < tabbableFields.length; pos++) { for (var pos = 0; pos < tabbableFields.length; pos++) {
if (parseInt(tabbableFields[pos].getAttribute('ztabindex')) >= tabindex) { if (parseInt(tabbableFields[pos].getAttribute('ztabindex')) > tabindex) {
next = tabbableFields[pos]; next = tabbableFields[pos];
break; break;
} }
@ -2291,13 +2278,12 @@
<method name="blurOpenField"> <method name="blurOpenField">
<body><![CDATA[ <body><![CDATA[
return (async function () { return Zotero.spawn(function* () {
var activeField = this._dynamicFields.querySelector('textbox'); var textboxes = document.getAnonymousNodes(this)[0].getElementsByTagName('textbox');
if (!activeField) { if (textboxes && textboxes.length) {
return false; yield this.blurHandler(textboxes[0]);
} }
return this.blurHandler(activeField); }, this);
}.bind(this))();
]]></body> ]]></body>
</method> </method>
@ -2399,11 +2385,7 @@
}; };
itemBox._updateAutoCompleteParams(row, changedParams); itemBox._updateAutoCompleteParams(row, changedParams);
itemBox.modifyCreator(index, fields); itemBox.modifyCreator(index, fields);"/>
if (itemBox.saveOnEdit) {
itemBox.item.saveTx();
}
"/>
<menupopup id="zotero-field-transform-menu"> <menupopup id="zotero-field-transform-menu">
<menu label="&zotero.item.textTransform;"> <menu label="&zotero.item.textTransform;">
<menupopup> <menupopup>

View File

@ -380,15 +380,10 @@
// Store JSON // Store JSON
this._data = val; this._data = val;
// Create a copy of the JSON that we can clean for display, since the remote object
// might reference things that don't exist locally
var displayJSON = Object.assign({}, val);
displayJSON.collections = [];
// Create item from JSON for metadata box // Create item from JSON for metadata box
var item = new Zotero.Item(val.itemType); var item = new Zotero.Item(val.itemType);
item.libraryID = this.libraryID; item.libraryID = this.libraryID;
item.fromJSON(displayJSON); item.fromJSON(val);
objbox.item = item; objbox.item = item;
]]> ]]>
</setter> </setter>

View File

@ -65,11 +65,6 @@
switch (val) { switch (val) {
case 'view': case 'view':
case 'merge': case 'merge':
// If there's an existing editor, mark it as read-only. This allows for
// disabling an existing editable note (e.g., if there's a save error).
if (this.noteField) {
this.noteField.onInit(ed => ed.setMode('readonly'));
}
break; break;
case 'edit': case 'edit':
@ -149,7 +144,7 @@
<constructor> <constructor>
<![CDATA[ <![CDATA[
this.instanceID = Zotero.Utilities.randomString(); this._instanceID = Zotero.Utilities.randomString();
this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'noteeditor'); this._notifierID = Zotero.Notifier.registerObserver(this, ['item'], 'noteeditor');
]]> ]]>
</constructor> </constructor>
@ -172,7 +167,7 @@
if (id != this.item.id) { if (id != this.item.id) {
continue; continue;
} }
if (extraData && extraData[id] && extraData[id].noteEditorID == this.instanceID) { if (extraData && extraData[id] && extraData[id].noteEditorID == this._instanceID) {
//Zotero.debug("Skipping notification from current note field"); //Zotero.debug("Skipping notification from current note field");
continue; continue;
} }
@ -248,9 +243,9 @@
</method> </method>
<method name="save"> <method name="save">
<body><![CDATA[ <body>
<![CDATA[
return Zotero.spawn(function* () { return Zotero.spawn(function* () {
try {
if (this._mode == 'view') { if (this._mode == 'view') {
Zotero.debug("Not saving read-only note"); Zotero.debug("Not saving read-only note");
return; return;
@ -267,6 +262,10 @@
if (this.item) { if (this.item) {
// If note field doesn't match item, abort save and run error handler // If note field doesn't match item, abort save and run error handler
if (noteField.getAttribute('itemID') != this.item.id) { if (noteField.getAttribute('itemID') != this.item.id) {
if (this.hasAttribute('onerror')) {
let fn = new Function("", this.getAttribute('onerror'));
fn.call(this)
}
throw new Error("Note field doesn't match current item"); throw new Error("Note field doesn't match current item");
} }
@ -275,7 +274,7 @@
this.noteField.changed = false; this.noteField.changed = false;
yield this.item.saveTx({ yield this.item.saveTx({
notifierData: { notifierData: {
noteEditorID: this.instanceID noteEditorID: this._instanceID
} }
}); });
} }
@ -299,21 +298,10 @@
} }
} }
this.item = item; this.item = yield Zotero.Items.getAsync(id);
}
catch (e) {
Zotero.logError(e);
if (this.hasAttribute('onerror')) {
let fn = new Function("", this.getAttribute('onerror'));
fn.call(this)
}
if (this.onError) {
this.onError(e);
}
}
}.bind(this)); }.bind(this));
]]></body> ]]>
</body>
</method> </method>
<!-- Used to insert a tab manually --> <!-- Used to insert a tab manually -->

View File

@ -152,7 +152,28 @@
let id = relatedItem.id; let id = relatedItem.id;
let icon = document.createElement("image"); let icon = document.createElement("image");
icon.className = "zotero-box-icon"; icon.className = "zotero-box-icon";
icon.setAttribute('src', relatedItem.getImageSrc()); let type = Zotero.ItemTypes.getName(relatedItem.itemTypeID);
if (type=='attachment')
{
switch (relatedItem.attaachmentLinkMode) {
case Zotero.Attachments.LINK_MODE_LINKED_URL:
type += '-web-link';
break;
case Zotero.Attachments.LINK_MODE_IMPORTED_URL:
type += '-snapshot';
break;
case Zotero.Attachments.LINK_MODE_LINKED_FILE:
type += '-link';
break;
case Zotero.Attachments.LINK_MODE_IMPORTED_FILE:
type += '-file';
break;
}
}
icon.setAttribute('src','chrome://zotero/skin/treeitem-' + type + '.png');
var label = document.createElement("label"); var label = document.createElement("label");
label.className = "zotero-box-label"; label.className = "zotero-box-label";
@ -162,7 +183,7 @@
var box = document.createElement('box'); var box = document.createElement('box');
box.setAttribute('onclick', box.setAttribute('onclick',
"document.getBindingParent(this).showItem(" + id + ")"); "document.getBindingParent(this).showItem('" + id + "')");
box.setAttribute('class','zotero-clicky'); box.setAttribute('class','zotero-clicky');
box.setAttribute('flex','1'); box.setAttribute('flex','1');
box.appendChild(icon); box.appendChild(icon);

View File

@ -48,6 +48,7 @@
<constructor><![CDATA[ <constructor><![CDATA[
this.mode = this.getAttribute('mode'); this.mode = this.getAttribute('mode');
this._onInitCallbacks = [];
this._iframe = document.getAnonymousElementByAttribute(this, "anonid", "rt-view"); this._iframe = document.getAnonymousElementByAttribute(this, "anonid", "rt-view");
// Atomic units, HTML -> RTF (cleanup) // Atomic units, HTML -> RTF (cleanup)
@ -404,17 +405,17 @@
var commandEvent = false; var commandEvent = false;
if (Zotero.Prefs.get('debugNoteEvents')) { //Zotero.debug(event.type);
Zotero.debug(event.type);
Zotero.debug(event.which);
}
switch (event.type) { switch (event.type) {
case 'keydown': case 'keydown':
// Handle forward-delete, which doesn't register as a keypress // Intercept and manually trigger redo for Cmd-Shift-Z,
// when a selection is cleared // which keeps it from toggling the Zotero pane instead
if (event.which == event.DOM_VK_DELETE) { if (Zotero.isMac && event.metaKey && event.shiftKey && !event.ctrlKey
this._changed = true; && !event.altKey && event.keyCode == 90) {
commandEvent = true; event.stopPropagation();
event.preventDefault();
this.redo();
return;
} }
break; break;
@ -600,13 +601,18 @@
</body> </body>
</method> </method>
<method name="redo">
<body>
<![CDATA[
this._editor.undoManager.redo();
]]>
</body>
</method>
<method name="clearUndo"> <method name="clearUndo">
<body> <body>
<![CDATA[ <![CDATA[
if (this._editor) {
this._editor.undoManager.clear(); this._editor.undoManager.clear();
this._editor.undoManager.add();
}
]]> ]]>
</body> </body>
</method> </method>
@ -618,9 +624,6 @@
callback(this._editor); callback(this._editor);
} }
else { else {
if (!this._onInitCallbacks) {
this._onInitCallbacks = [];
}
this._onInitCallbacks.push(callback); this._onInitCallbacks.push(callback);
} }
]]></body> ]]></body>
@ -691,16 +694,10 @@
} }
if (self._value) { if (self._value) {
self.value = self._value; self.value = self._value;
// Prevent undoing to empty note after initialization
self._editor.undoManager.clear();
self._editor.undoManager.add();
} }
if (self._focus) { if (self._focus) {
setTimeout(function () {
self._iframe.focus(); self._iframe.focus();
self._editor.focus(); self._editor.focus();
});
self._focus = false; self._focus = false;
} }
@ -733,11 +730,9 @@
} }
let cb; let cb;
if (this._onInitCallbacks) {
while (cb = this._onInitCallbacks.shift()) { while (cb = this._onInitCallbacks.shift()) {
cb(this._editor); cb(this._editor);
} }
}
}.bind(this); }.bind(this);
} }

View File

@ -321,7 +321,7 @@
iconFile += '-automatic'; iconFile += '-automatic';
icon.setAttribute('tooltiptext', Zotero.getString('pane.item.tags.icon.automatic')); icon.setAttribute('tooltiptext', Zotero.getString('pane.item.tags.icon.automatic'));
} }
icon.setAttribute('src', `chrome://zotero/skin/${iconFile}${Zotero.hiDPISuffix}.png`); icon.setAttribute('src', 'chrome://zotero/skin/' + iconFile + '.png');
// "-" button // "-" button
if (this.editable) { if (this.editable) {

View File

@ -1169,10 +1169,8 @@
oncommand="document.getBindingParent(this).handleKeyPress(); event.stopPropagation()" oncommand="document.getBindingParent(this).handleKeyPress(); event.stopPropagation()"
onkeypress="if (event.keyCode == event.DOM_VK_ESCAPE) { document.getBindingParent(this).handleKeyPress(true); }" onkeypress="if (event.keyCode == event.DOM_VK_ESCAPE) { document.getBindingParent(this).handleKeyPress(true); }"
tabindex="-1"/> tabindex="-1"/>
<toolbarbutton <toolbarbutton id="view-settings-menu" tooltiptext="&zotero.toolbar.actions.label;"
id="view-settings-menu" image="chrome://zotero/skin/tag-selector-menu.png" type="menu">
tooltiptext="&zotero.toolbar.actions.label;"
type="menu">
<menupopup id="view-settings-popup" <menupopup id="view-settings-popup"
onpopupshowing="document.getBindingParent(this)._updateDeleteAutomaticMenuOption()" onpopupshowing="document.getBindingParent(this)._updateDeleteAutomaticMenuOption()"
onpopupshown="/* onpopupshown="/*

View File

@ -49,16 +49,7 @@
<![CDATA[ <![CDATA[
this.searchRef = val; this.searchRef = val;
var libraryMenu = this.id('libraryMenu'); this.buildLibraryMenu();
var libraries = Zotero.Libraries.getAll();
Zotero.Utilities.Internal.buildLibraryMenu(
libraryMenu, libraries, this.searchRef.libraryID
);
if (this.searchRef.id) {
libraryMenu.disabled = true;
}
this.updateLibrary();
var conditionsBox = this.id('conditions'); var conditionsBox = this.id('conditions');
while(conditionsBox.hasChildNodes()) while(conditionsBox.hasChildNodes())
@ -90,6 +81,43 @@
</setter> </setter>
</property> </property>
<method name="buildLibraryMenu">
<body><![CDATA[
var menulist = this.id('libraryMenu');
var menupopup = menulist.firstChild;
while (menupopup.hasChildNodes()) {
menupopup.removeChild(menupopup.firstChild);
}
var libraryID = this.searchRef.libraryID;
var libraryIndex = 0;
var libraries = Zotero.Libraries.getAll();
var selectedIndex = 0;
var i = 0;
for (let library of libraries) {
let menuitem = document.createElement('menuitem');
menuitem.setAttribute('label', library.name);
menuitem.setAttribute('libraryID', library.libraryID);
menupopup.appendChild(menuitem);
if (library.libraryID == libraryID) {
selectedIndex = i;
}
i++
}
menulist.appendChild(menupopup);
menulist.selectedIndex = selectedIndex;
if (this.searchRef.id) {
this.id('libraryMenu').disabled = true;
}
this.updateLibrary();
]]></body>
</method>
<method name="addCondition"> <method name="addCondition">
<parameter name="ref"/> <parameter name="ref"/>
<body> <body>
@ -142,7 +170,7 @@
<method name="updateLibrary"> <method name="updateLibrary">
<body><![CDATA[ <body><![CDATA[
var menu = this.id('libraryMenu'); var menu = this.id('libraryMenu');
var libraryID = parseInt(menu.selectedItem.value); var libraryID = parseInt(menu.selectedItem.getAttribute('libraryID'));
if (this.onLibraryChange) { if (this.onLibraryChange) {
this.onLibraryChange(libraryID); this.onLibraryChange(libraryID);
@ -409,7 +437,6 @@
case 'dateModified': case 'dateModified':
case 'itemType': case 'itemType':
case 'fileTypeID': case 'fileTypeID':
case 'publicationTitle':
case 'tag': case 'tag':
case 'note': case 'note':
case 'childNote': case 'childNote':

View File

@ -125,7 +125,8 @@ var Zotero_DownloadOverlay = new function() {
try { try {
if (item && item.getFile()) { if (item && item.getFile()) {
timer.cancel(); timer.cancel();
Zotero.RecognizePDF.recognizeItems([item]); var recognizer = new win.Zotero_RecognizePDF.ItemRecognizer();
recognizer.recognizeItems([item]);
} }
} catch(e) { dump(e.toSource()) }; } catch(e) { dump(e.toSource()) };
}, 1000, Components.interfaces.nsITimer.TYPE_REPEATING_SLACK); }, 1000, Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
@ -144,7 +145,15 @@ var Zotero_DownloadOverlay = new function() {
// to happen automatically // to happen automatically
if(zoteroSelected) document.getElementById('rememberChoice').selected = false; if(zoteroSelected) document.getElementById('rememberChoice').selected = false;
document.getElementById('rememberChoice').disabled = zoteroSelected; document.getElementById('rememberChoice').disabled = zoteroSelected;
// disable recognizePDF checkbox as necessary
if(!Zotero.Fulltext.pdfConverterIsRegistered()) {
document.getElementById('zotero-noPDFTools-description').hidden = !zoteroSelected;
document.getElementById('zotero-recognizePDF').disabled = true;
window.sizeToContent();
} else {
document.getElementById('zotero-recognizePDF').disabled = !zoteroSelected; document.getElementById('zotero-recognizePDF').disabled = !zoteroSelected;
}
Zotero_DownloadOverlay.updateLibraryNote(); Zotero_DownloadOverlay.updateLibraryNote();
}; };
@ -203,6 +212,9 @@ var Zotero_DownloadOverlay = new function() {
recognizePDF.label = Zotero.getString("pane.items.menu.recognizePDF"); recognizePDF.label = Zotero.getString("pane.items.menu.recognizePDF");
recognizePDF.hidden = false; recognizePDF.hidden = false;
recognizePDF.disabled = true; recognizePDF.disabled = true;
if(!Zotero.Fulltext.pdfConverterIsRegistered()) {
recognizePDF.checked = false;
}
} }
}; };
} }

View File

@ -43,6 +43,7 @@
<vbox style="margin-left: 15px"> <vbox style="margin-left: 15px">
<description id="zotero-saveToLibrary-description" style="font: small-caption; font-weight: normal" hidden="true">&zotero.downloadManager.saveToLibrary.description;</description> <description id="zotero-saveToLibrary-description" style="font: small-caption; font-weight: normal" hidden="true">&zotero.downloadManager.saveToLibrary.description;</description>
<checkbox id="zotero-recognizePDF" hidden="true" persist="checked" disabled="true"/> <checkbox id="zotero-recognizePDF" hidden="true" persist="checked" disabled="true"/>
<description style="margin-left: 20px; font: small-caption; font-weight: normal" id="zotero-noPDFTools-description" hidden="true">&zotero.downloadManager.noPDFTools.description;</description>
</vbox> </vbox>
</vbox> </vbox>
</radiogroup> </radiogroup>

View File

@ -23,8 +23,6 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
*/ */
Components.utils.import("resource://gre/modules/osfile.jsm")
/****Zotero_File_Exporter**** /****Zotero_File_Exporter****
** **
* A class to handle exporting of items, collections, or the entire library * A class to handle exporting of items, collections, or the entire library
@ -208,119 +206,13 @@ var Zotero_File_Interface = new function() {
} }
} }
this.getMendeleyDirectory = function () {
Components.classes["@mozilla.org/net/osfileconstantsservice;1"]
.getService(Components.interfaces.nsIOSFileConstantsService)
.init();
var path = OS.Constants.Path.homeDir;
if (Zotero.isMac) {
path = OS.Path.join(path, 'Library', 'Application Support', 'Mendeley Desktop');
}
else if (Zotero.isWin) {
path = OS.Path.join(path, 'AppData', 'Local', 'Mendeley Ltd', 'Mendeley Desktop');
}
else if (Zotero.isLinux) {
path = OS.Path.join(path, '.local', 'share', 'data', 'Mendeley Ltd.', 'Mendeley Desktop');
}
else {
throw new Error("Invalid platform");
}
return path;
};
this.findMendeleyDatabases = async function () {
var dbs = [];
try {
var dir = this.getMendeleyDirectory();
if (!await OS.File.exists(dir)) {
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
});
}
}
});
for (let i = 0; i < dbs.length; i++) {
let dbPath = OS.Path.join(dir, dbs[i].name);
let info = await OS.File.stat(dbPath);
dbs[i].size = info.size;
dbs[i].lastModified = info.lastModificationDate;
}
dbs.sort((a, b) => {
return b.lastModified - a.lastModified;
});
}
catch (e) {
Zotero.logError(e);
}
return dbs;
};
this.showImportWizard = function () {
var libraryID = Zotero.Libraries.userLibraryID;
try {
let zp = Zotero.getActiveZoteroPane();
libraryID = zp.getSelectedLibraryID();
}
catch (e) {
Zotero.logError(e);
}
var args = {
libraryID
};
args.wrappedJSObject = args;
Services.ww.openWindow(null, "chrome://zotero/content/import/importWizard.xul",
"importFile", "chrome,dialog=yes,centerscreen,width=600,height=400", args);
};
/** /**
* Creates Zotero.Translate instance and shows file picker for file import * Creates Zotero.Translate instance and shows file picker for file import
*
* @param {Object} options
* @param {nsIFile|string|null} [options.file=null] - File to import, or none to show a filepicker
* @param {Boolean} [options.addToLibraryRoot=false]
* @param {Boolean} [options.createNewCollection=true] - Put items in a new collection
* @param {Function} [options.onBeforeImport] - Callback to receive translation object, useful
* for displaying progress in a different way. This also causes an error to be throw
* instead of shown in the main window.
*/ */
this.importFile = Zotero.Promise.coroutine(function* (options = {}) { this.importFile = Zotero.Promise.coroutine(function* (file, createNewCollection) {
if (!options) { if(createNewCollection === undefined) {
options = {};
}
if (typeof options == 'string' || options instanceof Components.interfaces.nsIFile) {
Zotero.debug("WARNING: importFile() now takes a single options object -- update your code");
options = {
file: options,
createNewCollection: arguments[1]
};
}
var file = options.file ? Zotero.File.pathToFile(options.file) : null;
var createNewCollection = options.createNewCollection;
var addToLibraryRoot = options.addToLibraryRoot;
var onBeforeImport = options.onBeforeImport;
if (createNewCollection === undefined && !addToLibraryRoot) {
createNewCollection = true; createNewCollection = true;
} } else if(!createNewCollection) {
else if (!createNewCollection) {
try { try {
if (!ZoteroPane.collectionsView.editable) { if (!ZoteroPane.collectionsView.editable) {
ZoteroPane.collectionsView.selectLibrary(null); ZoteroPane.collectionsView.selectLibrary(null);
@ -328,40 +220,32 @@ var Zotero_File_Interface = new function() {
} catch(e) {} } catch(e) {}
} }
var defaultNewCollectionPrefix = Zotero.getString("fileInterface.imported"); var translation = new Zotero.Translate.Import();
if (!file) {
let translators = yield 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 translation; fp.appendFilters(nsIFilePicker.filterAll);
// Check if the file is an SQLite database
var sample = yield Zotero.File.getSample(file.path); var collation = Zotero.getLocaleCollation();
if (file.path == Zotero.DataDirectory.getDatabase()) { translators.sort((a, b) => collation.compareString(1, a.label, b.label))
// Blacklist the current Zotero database, which would cause a hang for (let translator of translators) {
} fp.appendFilter(translator.label, "*." + translator.target);
else if (Zotero.MIME.sniffForMIMEType(sample) == 'application/x-sqlite3') {
// Mendeley import doesn't use the real translation architecture, but we create a
// translation object with the same interface
translation = yield _getMendeleyTranslation();
translation.createNewCollection = createNewCollection;
defaultNewCollectionPrefix = Zotero.getString(
'fileInterface.appImportCollection', 'Mendeley'
);
}
else if (file.path.endsWith('@www.mendeley.com.sqlite')
|| file.path.endsWith('online.sqlite')) {
// Keep in sync with importWizard.js
throw new Error('Encrypted Mendeley database');
} }
if (!translation) { var rv = fp.show();
translation = new Zotero.Translate.Import(); if (rv !== nsIFilePicker.returnOK && rv !== nsIFilePicker.returnReplace) {
return false;
} }
file = fp.file;
}
translation.setLocation(file); translation.setLocation(file);
return _finishImport({ yield _finishImport(translation, createNewCollection);
translation,
createNewCollection,
addToLibraryRoot,
defaultNewCollectionPrefix,
onBeforeImport
});
}); });
@ -389,10 +273,7 @@ var Zotero_File_Interface = new function() {
} }
} catch(e) {} } catch(e) {}
yield _finishImport({ yield _finishImport(translation, false);
translation,
createNewCollection: false
});
// Select imported items // Select imported items
try { try {
@ -406,36 +287,17 @@ var Zotero_File_Interface = new function() {
}); });
var _finishImport = Zotero.Promise.coroutine(function* (options) { var _finishImport = Zotero.Promise.coroutine(function* (translation, createNewCollection) {
var t = performance.now();
var translation = options.translation;
var addToLibraryRoot = options.addToLibraryRoot;
var createNewCollection = options.createNewCollection;
var defaultNewCollectionPrefix = options.defaultNewCollectionPrefix;
var onBeforeImport = options.onBeforeImport;
if (addToLibraryRoot && createNewCollection) {
throw new Error("Can't add to library root and create new collection");
}
var showProgressWindow = !onBeforeImport;
let translators = yield translation.getTranslators(); let translators = yield translation.getTranslators();
// Unrecognized file if(!translators.length) {
if (!translators.length) { var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
if (onBeforeImport) {
yield onBeforeImport(false);
}
let ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService); .getService(Components.interfaces.nsIPromptService);
let buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_OK)
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING; + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING);
let index = ps.confirmEx( var index = ps.confirmEx(
null, null,
Zotero.getString('general.error'), "",
Zotero.getString("fileInterface.unsupportedFormat"), Zotero.getString("fileInterface.unsupportedFormat"),
buttonFlags, buttonFlags,
null, null,
@ -443,26 +305,16 @@ var Zotero_File_Interface = new function() {
null, null, {} null, null, {}
); );
if (index == 1) { if (index == 1) {
Zotero.launchURL("https://www.zotero.org/support/kb/importing"); ZoteroPane_Local.loadURI("http://zotero.org/support/kb/importing");
} }
return false; return;
} }
var libraryID = Zotero.Libraries.userLibraryID; let importCollection = null, libraryID = Zotero.Libraries.userLibraryID;
var importCollection = null;
try { try {
let zp = Zotero.getActiveZoteroPane(); libraryID = ZoteroPane.getSelectedLibraryID();
libraryID = zp.getSelectedLibraryID(); importCollection = ZoteroPane.getSelectedCollection();
if (addToLibraryRoot) { } catch(e) {}
yield zp.collectionsView.selectLibrary(libraryID);
}
else if (!createNewCollection) {
importCollection = zp.getSelectedCollection();
}
}
catch (e) {
Zotero.logError(e);
}
if(createNewCollection) { if(createNewCollection) {
// Create a new collection to take imported items // Create a new collection to take imported items
@ -478,9 +330,8 @@ var Zotero_File_Interface = new function() {
break; break;
} }
} }
} } else {
else { collectionName = Zotero.getString("fileInterface.imported")+" "+(new Date()).toLocaleString();
collectionName = defaultNewCollectionPrefix + " " + (new Date()).toLocaleString();
} }
importCollection = new Zotero.Collection; importCollection = new Zotero.Collection;
importCollection.libraryID = libraryID; importCollection.libraryID = libraryID;
@ -491,15 +342,12 @@ var Zotero_File_Interface = new function() {
translation.setTranslator(translators[0]); translation.setTranslator(translators[0]);
// Show progress popup // Show progress popup
var progressWin; var progressWin = new Zotero.ProgressWindow({
var progress;
if (showProgressWindow) {
progressWin = new Zotero.ProgressWindow({
closeOnClick: false closeOnClick: false
}); });
progressWin.changeHeadline(Zotero.getString('fileInterface.importing')); progressWin.changeHeadline(Zotero.getString('fileInterface.importing'));
let icon = 'chrome://zotero/skin/treesource-unfiled' + (Zotero.hiDPI ? "@2x" : "") + '.png'; var icon = 'chrome://zotero/skin/treesource-unfiled' + (Zotero.hiDPI ? "@2x" : "") + '.png';
progress = new progressWin.ItemProgress( let progress = new progressWin.ItemProgress(
icon, translation.path ? OS.Path.basename(translation.path) : translators[0].label icon, translation.path ? OS.Path.basename(translation.path) : translators[0].label
); );
progressWin.show(); progressWin.show();
@ -509,10 +357,6 @@ var Zotero_File_Interface = new function() {
}); });
yield Zotero.Promise.delay(0); yield Zotero.Promise.delay(0);
}
else {
yield onBeforeImport(translation);
}
let failed = false; let failed = false;
try { try {
@ -521,73 +365,32 @@ var Zotero_File_Interface = new function() {
collections: importCollection ? [importCollection.id] : null collections: importCollection ? [importCollection.id] : null
}); });
} catch(e) { } catch(e) {
if (!showProgressWindow) {
throw e;
}
progressWin.close();
Zotero.logError(e); Zotero.logError(e);
Zotero.alert( Zotero.alert(
null, null,
Zotero.getString('general.error'), Zotero.getString('general.error'),
Zotero.getString("fileInterface.importError") Zotero.getString("fileInterface.importError")
); );
return false; return;
} }
var numItems = translation.newItems.length;
// Show popup on completion // Show popup on completion
if (showProgressWindow) { var numItems = translation.newItems.length;
progressWin.changeHeadline(Zotero.getString('fileInterface.importComplete')); progressWin.changeHeadline(Zotero.getString('fileInterface.importComplete'));
let icon;
if (numItems == 1) { if (numItems == 1) {
icon = translation.newItems[0].getImageSrc(); var icon = translation.newItems[0].getImageSrc();
} }
else { else {
icon = 'chrome://zotero/skin/treesource-unfiled' + (Zotero.hiDPI ? "@2x" : "") + '.png'; var icon = 'chrome://zotero/skin/treesource-unfiled' + (Zotero.hiDPI ? "@2x" : "") + '.png';
} }
let text = Zotero.getString(`fileInterface.itemsWereImported`, numItems, numItems); var text = Zotero.getString(`fileInterface.itemsWereImported`, numItems, numItems);
progress.setIcon(icon); progress.setIcon(icon);
progress.setText(text); progress.setText(text);
// For synchronous translators, which don't update progress // For synchronous translators, which don't update progress
progress.setProgress(100); progress.setProgress(100);
progressWin.startCloseTimer(5000); progressWin.startCloseTimer(5000);
}
Zotero.debug(`Imported ${numItems} item(s) in ${performance.now() - t} ms`);
return true;
}); });
var _getMendeleyTranslation = async function () {
if (true) {
Components.utils.import("chrome://zotero/content/import/mendeley/mendeleyImport.js");
}
// TEMP: Load uncached from ~/zotero-client for development
else {
Components.utils.import("resource://gre/modules/FileUtils.jsm");
let file = FileUtils.getDir("Home", []);
file = OS.Path.join(
file.path,
'zotero-client', 'chrome', 'content', 'zotero', 'import', 'mendeley', 'mendeleyImport.js'
);
let fileURI = OS.Path.toFileURI(file);
let xmlhttp = await Zotero.HTTP.request(
'GET',
fileURI,
{
dontCache: true,
responseType: 'text'
}
);
eval(xmlhttp.response);
}
return new Zotero_Import_Mendeley();
}
/** /**
* Creates a bibliography from a collection or saved search * Creates a bibliography from a collection or saved search
*/ */

View File

@ -1,332 +0,0 @@
var Zotero_Import_Wizard = {
_wizard: null,
_dbs: null,
_file: null,
_translation: null,
init: async function () {
this._wizard = document.getElementById('import-wizard');
var dbs = await Zotero_File_Interface.findMendeleyDatabases();
if (dbs.length) {
document.getElementById('radio-import-source-mendeley').hidden = false;
}
// If no existing collections or non-trash items in the library, don't create a new
// collection by default
var args = window.arguments[0].wrappedJSObject;
if (args && args.libraryID) {
let sql = "SELECT ROWID FROM collections WHERE libraryID=?1 "
+ "UNION "
+ "SELECT ROWID FROM items WHERE libraryID=?1 "
// Not in trash
+ "AND itemID NOT IN (SELECT itemID FROM deletedItems) "
// And not a child item (which doesn't necessarily show up in the trash)
+ "AND itemID NOT IN (SELECT itemID FROM itemNotes WHERE parentItemID IS NOT NULL) "
+ "AND itemID NOT IN (SELECT itemID FROM itemAttachments WHERE parentItemID IS NOT NULL) "
+ "LIMIT 1";
if (!await Zotero.DB.valueQueryAsync(sql, args.libraryID)) {
document.getElementById('create-collection-checkbox').removeAttribute('checked');
}
}
Zotero.Translators.init(); // async
},
onModeChosen: async function () {
var wizard = this._wizard;
var mode = document.getElementById('import-source').selectedItem.id;
try {
switch (mode) {
case 'radio-import-source-file':
await this.chooseFile();
break;
case 'radio-import-source-mendeley':
this._dbs = await Zotero_File_Interface.findMendeleyDatabases();
// This shouldn't happen, because we only show the wizard if there are databases
if (!this._dbs.length) {
throw new Error("No databases found");
}
this._populateFileList(this._dbs);
document.getElementById('file-options-header').textContent
= Zotero.getString('fileInterface.chooseAppDatabaseToImport', 'Mendeley')
wizard.goTo('page-file-list');
wizard.canRewind = true;
this._enableCancel();
break;
default:
throw new Error(`Unknown mode ${mode}`);
}
}
catch (e) {
this._onDone(
Zotero.getString('general.error'),
Zotero.getString('fileInterface.importError'),
true
);
throw e;
}
},
goToStart: function () {
this._wizard.goTo('page-start');
this._wizard.canAdvance = true;
return false;
},
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);
fp.appendFilters(nsIFilePicker.filterAll);
var collation = Zotero.getLocaleCollation();
// Add Mendeley DB, which isn't a translator
var mendeleyFilter = {
label: "Mendeley Database", // TODO: Localize
target: "*.sqlite"
};
var filters = [...translators];
filters.push(mendeleyFilter);
filters.sort((a, b) => collation.compareString(1, a.label, b.label));
for (let filter of filters) {
fp.appendFilter(filter.label, "*." + filter.target);
}
var rv = fp.show();
if (rv !== nsIFilePicker.returnOK && rv !== nsIFilePicker.returnReplace) {
return false;
}
Zotero.debug(`File is ${fp.file.path}`);
this._file = fp.file.path;
this._wizard.canAdvance = true;
this._wizard.goTo('page-options');
},
/**
* When a file is clicked on in the file list
*/
onFileSelected: async function () {
var index = document.getElementById('file-list').selectedIndex;
if (index != -1) {
this._file = this._dbs[index].path;
this._wizard.canAdvance = true;
}
else {
this._file = null;
this._wizard.canAdvance = false;
}
},
/**
* When the user clicks "Other…" to choose a file not in the list
*/
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);
fp.appendFilter("Mendeley Database", "*.sqlite"); // TODO: Localize
var rv = fp.show();
if (rv != nsIFilePicker.returnOK) {
return false;
}
this._file = fp.file.path;
this._wizard.canAdvance = true;
this._wizard.advance();
},
onOptionsShown: function () {
},
onImportStart: async function () {
if (!this._file) {
let index = document.getElementById('file-list').selectedIndex;
this._file = this._dbs[index].path;
}
this._disableCancel();
this._wizard.canRewind = false;
this._wizard.canAdvance = false;
await this.doImport({
createNewCollection: document.getElementById('create-collection-checkbox').hasAttribute('checked')
});
},
onBeforeImport: async function (translation) {
// Unrecognized translator
if (!translation) {
// Allow error dialog to be displayed, and then close window
setTimeout(function () {
window.close();
});
return;
}
this._translation = translation;
// Switch to progress pane
this._wizard.goTo('page-progress');
var pm = document.getElementById('import-progressmeter');
translation.setHandler('itemDone', function () {
pm.value = translation.getProgress();
});
},
doImport: async function (options) {
try {
let result = await Zotero_File_Interface.importFile({
file: this._file,
onBeforeImport: this.onBeforeImport.bind(this),
addToLibraryRoot: !options.createNewCollection
});
// Cancelled by user or due to error
if (!result) {
window.close();
return;
}
let numItems = this._translation.newItems.length;
this._onDone(
Zotero.getString('fileInterface.importComplete'),
Zotero.getString(`fileInterface.itemsWereImported`, numItems, numItems)
);
}
catch (e) {
if (e.message == 'Encrypted Mendeley database') {
let url = 'https://www.zotero.org/support/kb/mendeley_import';
this._onDone(
Zotero.getString('general.error'),
// TODO: Localize
`The selected Mendeley database cannot be read, likely because it is encrypted. `
+ `See <a href="${url}" class="text-link">How do I import a Mendeley library `
+ `into Zotero?</a> for more information.`
);
}
else {
this._onDone(
Zotero.getString('general.error'),
Zotero.getString('fileInterface.importError'),
true
);
}
throw e;
}
},
reportError: function () {
Zotero.getActiveZoteroPane().reportErrors();
window.close();
},
_populateFileList: async function (files) {
var listbox = document.getElementById('file-list');
// Remove existing entries
var items = listbox.getElementsByTagName('listitem');
for (let item of items) {
listbox.removeChild(item);
}
for (let file of files) {
let li = document.createElement('listitem');
let name = document.createElement('listcell');
// Simply filenames
let nameStr = file.name
.replace(/\.sqlite$/, '')
.replace(/@www\.mendeley\.com$/, '');
if (nameStr == 'online') {
nameStr = Zotero.getString('dataDir.default', 'online.sqlite');
}
name.setAttribute('label', nameStr + ' ');
li.appendChild(name);
let lastModified = document.createElement('listcell');
lastModified.setAttribute('label', file.lastModified.toLocaleString() + ' ');
li.appendChild(lastModified);
let size = document.createElement('listcell');
size.setAttribute(
'label',
Zotero.getString('general.nMegabytes', (file.size / 1024 / 1024).toFixed(1)) + ' '
);
li.appendChild(size);
listbox.appendChild(li);
}
if (files.length == 1) {
listbox.selectedIndex = 0;
}
},
_enableCancel: function () {
this._wizard.getButton('cancel').disabled = false;
},
_disableCancel: function () {
this._wizard.getButton('cancel').disabled = true;
},
_onDone: function (label, description, showReportErrorButton) {
var wizard = this._wizard;
wizard.getPageById('page-done').setAttribute('label', label);
var xulElem = document.getElementById('result-description');
var htmlElem = document.getElementById('result-description-html');
if (description.includes('href')) {
htmlElem.innerHTML = description;
Zotero.Utilities.Internal.updateHTMLInXUL(htmlElem);
xulElem.hidden = true;
htmlElem.setAttribute('display', 'block');
}
else {
xulElem.textContent = description;
xulElem.hidden = false;
htmlElem.setAttribute('display', 'none');
}
document.getElementById('result-description')
if (showReportErrorButton) {
let button = document.getElementById('result-report-error');
button.setAttribute('label', Zotero.getString('errorReport.reportError'));
button.hidden = false;
}
// When done, move to last page and allow closing
wizard.canAdvance = true;
wizard.goTo('page-done');
wizard.canRewind = false;
}
};

View File

@ -1,75 +0,0 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/importWizard.css" type="text/css"?>
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
<wizard id="import-wizard"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="&zotero.import;"
onload="Zotero_Import_Wizard.init()">
<script src="../include.js"/>
<script src="../fileInterface.js"/>
<script src="importWizard.js"/>
<wizardpage pageid="page-start"
label="&zotero.import.whereToImportFrom;"
next="page-options"
onpageadvanced="Zotero_Import_Wizard.onModeChosen(); return false;">
<radiogroup id="import-source">
<radio id="radio-import-source-file" label="&zotero.import.source.file;"/>
<radio id="radio-import-source-mendeley" label="Mendeley" hidden="true"/>
</radiogroup>
</wizardpage>
<wizardpage pageid="page-file-list"
next="page-options"
onpagerewound="return Zotero_Import_Wizard.goToStart()">
<description id="file-options-header"/>
<listbox id="file-list" onselect="Zotero_Import_Wizard.onFileSelected()">
<listhead>
<listheader label="&zotero.import.database;"/>
<listheader label="&zotero.import.lastModified;"/>
<listheader label="&zotero.import.size;"/>
</listhead>
<listcols>
<listcol flex="1"/>
<listcol/>
<listcol/>
</listcols>
</listbox>
<hbox>
<button label="&zotero.general.other;" oncommand="Zotero_Import_Wizard.chooseMendeleyDB()"/>
</hbox>
</wizardpage>
<wizardpage pageid="page-options"
label="&zotero.general.options;"
next="page-progress"
onpageshow="Zotero_Import_Wizard.onOptionsShown()"
onpagerewound="return Zotero_Import_Wizard.goToStart()"
onpageadvanced="Zotero_Import_Wizard.onImportStart()">
<checkbox id="create-collection-checkbox" label="&zotero.import.createCollection;" checked="true" />
</wizardpage>
<wizardpage pageid="page-progress"
label="&zotero.import.importing;"
onpageshow="document.getElementById('import-wizard').canRewind = false;"
next="page-done">
<progressmeter id="import-progressmeter" mode="determined"/>
</wizardpage>
<wizardpage pageid="page-done">
<description id="result-description"/>
<html:div id="result-description-html"/>
<hbox>
<button id="result-report-error"
oncommand="Zotero_Import_Wizard.reportError()"
hidden="true"/>
</hbox>
</wizardpage>
</wizard>

File diff suppressed because it is too large Load Diff

View File

@ -1,102 +0,0 @@
var map = {
83: {
itemTypes: {
Bill: "bill",
Book: "book",
BookSection: "bookSection",
Case: "case",
ComputerProgram: "computerProgram",
ConferenceProceedings: "conferencePaper",
EncyclopediaArticle: "encyclopediaArticle",
Film: "film",
Generic: "document",
JournalArticle: "journalArticle",
MagazineArticle: "magazineArticle",
NewspaperArticle: "newspaperArticle",
Patent: "patent",
Report: "report",
Statute: "statute",
TelevisionBroadcast: "tvBroadcast",
Thesis: "thesis",
WebPage: "webpage",
WorkingPaper: "report"
},
fields: {
id: "",
uuid: "",
reviewedArticle: "",
revisionNumber: "",
publisher: "publisher",
reprintEdition: "",
series: "seriesTitle",
seriesNumber: "seriesNumber",
sections: "section",
seriesEditor: "creator[seriesEditor]", // falls back to editor if necessary
owner: "",
pages: "func[pages]",
month: "", // handled explicitly
originalPublication: "",
publication: "publicationTitle",
publicLawNumber: "publicLawNumber",
pmid: "extra[PMID]",
sourceType: "",
session: "session",
shortTitle: "shortTitle",
volume: "volume",
year: "", // handled explicitly
userType: "type",
country: "place[country]",
dateAccessed: "accessDate",
committee: "committee",
counsel: "creator[counsel]",
doi: "DOI",
edition: "edition",
day: "", // handled explicitly
department: "",
citationKey: "citationKey", // put in Extra
city: "place[city]",
chapter: "",
codeSection: "section",
codeVolume: "codeVolume",
code: "code",
codeNumber: "codeNumber",
issue: "issue",
language: "language",
isbn: "ISBN",
issn: "ISSN",
length: "",
medium: "medium",
lastUpdate: "",
legalStatus: "legalStatus",
hideFromMendeleyWebIndex: "",
institution: "publisher",
genre: "genre",
internationalTitle: "",
internationalUserType: "",
internationalAuthor: "",
internationalNumber: "",
deletionPending: "",
favourite: "", // tag?
confirmed: "", // tag?
deduplicated: "",
read: "", // tag?
type: "", // item type handled separately
title: "title",
privacy: "",
applicationNumber: "applicationNumber",
arxivId: "extra[arXiv]",
advisor: "",
articleColumn: "",
modified: "func[fromUnixtime:dateModified]",
abstract: "abstractNote",
added: "func[fromUnixtime:dateAdded]",
note: "func[note]",
importer: ""
},
creatorTypes: {
DocumentAuthor: "author",
DocumentEditor: "editor",
DocumentTranslator: "translator"
}
}
};

View File

@ -59,8 +59,10 @@ var Zotero_Citation_Dialog = new function () {
this.listItemSelected = listItemSelected; this.listItemSelected = listItemSelected;
this.up = up; this.up = up;
this.down = down; this.down = down;
this.add = add;
this.remove = remove; this.remove = remove;
this.setSortToggle = setSortToggle; this.setSortToggle = setSortToggle;
this.citationSortUnsort = citationSortUnsort;
this.confirmRegenerate = confirmRegenerate; this.confirmRegenerate = confirmRegenerate;
this.accept = accept; this.accept = accept;
this.cancel = cancel; this.cancel = cancel;
@ -371,13 +373,13 @@ var Zotero_Citation_Dialog = new function () {
/* /*
* Adds an item to the multipleSources list * Adds an item to the multipleSources list
*/ */
this.add = Zotero.Promise.coroutine(function* (first_item) { function add(first_item) {
var pos, len; var pos, len;
var item = itemsView.getSelectedItems()[0]; // treeview from xpcom/itemTreeView.js var item = itemsView.getSelectedItems()[0]; // treeview from xpcom/itemTreeView.js
if (!item) { if (!item) {
yield sortCitation(); sortCitation();
_updateAccept(); _updateAccept();
_updatePreview(); _updatePreview();
return; return;
@ -410,11 +412,11 @@ var Zotero_Citation_Dialog = new function () {
_citationList.ensureElementIsVisible(selectionNode); _citationList.ensureElementIsVisible(selectionNode);
// allow user to press OK // allow user to press OK
selectionNode = yield sortCitation(selectionNode); selectionNode = sortCitation(selectionNode);
_citationList.selectItem(selectionNode); _citationList.selectItem(selectionNode);
_updateAccept(); _updateAccept();
_updatePreview(); _updatePreview();
}); }
/* /*
* Deletes a citation from the multipleSources list * Deletes a citation from the multipleSources list
@ -444,11 +446,11 @@ var Zotero_Citation_Dialog = new function () {
/* /*
* Sorts preview citations, if preview is open. * Sorts preview citations, if preview is open.
*/ */
this.citationSortUnsort = Zotero.Promise.coroutine(function* () { function citationSortUnsort() {
setSortToggle(); setSortToggle();
yield sortCitation(); sortCitation();
_updatePreview(); _updatePreview();
}); }
/* /*
* Sets the current sort toggle state persistently on the citation. * Sets the current sort toggle state persistently on the citation.
@ -466,7 +468,7 @@ var Zotero_Citation_Dialog = new function () {
/* /*
* Sorts the list of citations * Sorts the list of citations
*/ */
var sortCitation = Zotero.Promise.coroutine(function* (scrollToItem) { function sortCitation(scrollToItem) {
if(!_sortCheckbox) return scrollToItem; if(!_sortCheckbox) return scrollToItem;
if(!_sortCheckbox.checked) { if(!_sortCheckbox.checked) {
io.citation.properties.unsorted = true; io.citation.properties.unsorted = true;
@ -483,7 +485,7 @@ var Zotero_Citation_Dialog = new function () {
// run preview function to re-sort, if it hasn't already been // run preview function to re-sort, if it hasn't already been
// run // run
yield io.sort(); io.sort();
// add items back to list // add items back to list
scrollToItem = null; scrollToItem = null;
@ -500,7 +502,7 @@ var Zotero_Citation_Dialog = new function () {
if(scrollToItem) _citationList.ensureElementIsVisible(scrollToItem); if(scrollToItem) _citationList.ensureElementIsVisible(scrollToItem);
return scrollToItem; return scrollToItem;
}); }
/* /*
* Ask whether to modifiy the preview * Ask whether to modifiy the preview

View File

@ -74,8 +74,8 @@ var Zotero_Bibliography_Dialog = new function () {
if(selectedItemIDs.length) { if(selectedItemIDs.length) {
for (let itemID of selectedItemIDs) { for (let itemID of selectedItemIDs) {
var itemIndexToSelect = false; var itemIndexToSelect = false;
for(var i in bibEditInterface.bib[0].entry_ids) { for(var i in bibEditInterface.bibliography[0].entry_ids) {
if(bibEditInterface.bib[0].entry_ids[i].indexOf(itemID) !== -1) { if(bibEditInterface.bibliography[0].entry_ids[i].indexOf(itemID) !== -1) {
itemIndexToSelect = i; itemIndexToSelect = i;
continue; continue;
} }
@ -254,7 +254,7 @@ var Zotero_Bibliography_Dialog = new function () {
*/ */
function _getSelectedListItemIDs() { function _getSelectedListItemIDs() {
return Array.from(_itemList.selectedItems) return Array.from(_itemList.selectedItems)
.map(item => bibEditInterface.bib[0].entry_ids[item.value][0]); .map(item => bibEditInterface.bibliography[0].entry_ids[item.value][0]);
} }
/** /**
@ -287,8 +287,8 @@ var Zotero_Bibliography_Dialog = new function () {
editor.readonly = index === undefined; editor.readonly = index === undefined;
if(index !== undefined) { if(index !== undefined) {
var itemID = bibEditInterface.bib[0].entry_ids[index]; var itemID = bibEditInterface.bibliography[0].entry_ids[index];
editor.value = bibEditInterface.bib[1][index]; editor.value = bibEditInterface.bibliography[1][index];
_lastSelectedIndex = index; _lastSelectedIndex = index;
_lastSelectedItemID = itemID; _lastSelectedItemID = itemID;
_lastSelectedValue = editor.value; _lastSelectedValue = editor.value;
@ -304,7 +304,7 @@ var Zotero_Bibliography_Dialog = new function () {
* loads items from itemSet * loads items from itemSet
*/ */
function _loadItems() { function _loadItems() {
var itemIDs = bibEditInterface.bib[0].entry_ids; var itemIDs = bibEditInterface.bibliography[0].entry_ids;
var items = itemIDs.map(itemID => Zotero.Cite.getItem(itemID[0])); var items = itemIDs.map(itemID => Zotero.Cite.getItem(itemID[0]));
// delete all existing items from list // delete all existing items from list

View File

@ -31,11 +31,10 @@
<dialog <dialog
id="zotero-doc-prefs-dialog" id="zotero-doc-prefs-dialog"
orient="vertical" orient="vertical"
buttons="accept,cancel,help" buttons="accept,cancel"
title="&zotero.integration.docPrefs.title;" title="&zotero.integration.docPrefs.title;"
onload="Zotero_File_Interface_Bibliography.init();" onload="Zotero_File_Interface_Bibliography.init();"
ondialogaccept="Zotero_File_Interface_Bibliography.acceptSelection();" ondialogaccept="Zotero_File_Interface_Bibliography.acceptSelection();"
ondialoghelp="Zotero_File_Interface_Bibliography.openHelpLink();"
onclose="document.documentElement.cancelDialog(); event.preventDefault(); event.stopPropagation();" onclose="document.documentElement.cancelDialog(); event.preventDefault(); event.stopPropagation();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
persist="screenX screenY" persist="screenX screenY"
@ -53,7 +52,7 @@
</hbox> </hbox>
</groupbox> </groupbox>
<groupbox id="locale-box"> <groupbox>
<hbox align="center"> <hbox align="center">
<caption label="&zotero.bibliography.locale.label;"/> <caption label="&zotero.bibliography.locale.label;"/>
<menulist id="locale-menu" oncommand="Zotero_File_Interface_Bibliography.localeChanged(this.selectedItem.value)"/> <menulist id="locale-menu" oncommand="Zotero_File_Interface_Bibliography.localeChanged(this.selectedItem.value)"/>
@ -68,7 +67,7 @@
</radiogroup> </radiogroup>
</groupbox> </groupbox>
<groupbox id="formatUsing-groupbox"> <groupbox>
<caption label="&zotero.integration.prefs.formatUsing.label;"/> <caption label="&zotero.integration.prefs.formatUsing.label;"/>
<radiogroup id="formatUsing" orient="vertical"> <radiogroup id="formatUsing" orient="vertical">
@ -85,10 +84,5 @@
<checkbox id="automaticJournalAbbreviations-checkbox" label="&zotero.integration.prefs.automaticJournalAbbeviations.label;"/> <checkbox id="automaticJournalAbbreviations-checkbox" label="&zotero.integration.prefs.automaticJournalAbbeviations.label;"/>
<description class="radioDescription">&zotero.integration.prefs.automaticJournalAbbeviations.caption;</description> <description class="radioDescription">&zotero.integration.prefs.automaticJournalAbbeviations.caption;</description>
</vbox> </vbox>
<vbox id="automaticCitationUpdates-vbox">
<checkbox id="automaticCitationUpdates-checkbox" label="&zotero.integration.prefs.automaticCitationUpdates.label;" tooltiptext="&zotero.integration.prefs.automaticCitationUpdates.tooltip;"/>
<description class="radioDescription">&zotero.integration.prefs.automaticCitationUpdates.description;</description>
</vbox>
</vbox> </vbox>
</dialog> </dialog>

View File

@ -1,121 +0,0 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2018 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
Components.utils.import("resource://gre/modules/Services.jsm");
var Zotero_ProgressBar = new function () {
var initialized, io;
/**
* Pre-initialization, when the dialog has loaded but has not yet appeared
*/
this.onDOMContentLoaded = function(event) {
if(event.target === document) {
initialized = true;
io = window.arguments[0].wrappedJSObject;
if (io.onLoad) {
io.onLoad(_onProgress);
}
// Only hide chrome on Windows or Mac
if(Zotero.isMac) {
document.documentElement.setAttribute("drawintitlebar", true);
} else if(Zotero.isWin) {
document.documentElement.setAttribute("hidechrome", true);
}
new WindowDraggingElement(document.getElementById("quick-format-dialog"), window);
}
};
/**
* Center the window
*/
this.onLoad = function(event) {
if(event.target !== document) return;
// make sure we are visible
window.focus();
window.setTimeout(function() {
var targetX = Math.floor(-window.outerWidth/2 + (window.screen.width / 2));
var targetY = Math.floor(-window.outerHeight/2 + (window.screen.height / 2));
Zotero.debug("Moving window to "+targetX+", "+targetY);
window.moveTo(targetX, targetY);
}, 0);
};
/**
* Called when progress changes
*/
function _onProgress(percent) {
var meter = document.getElementById("quick-format-progress-meter");
if(percent === null) {
meter.mode = "undetermined";
} else {
meter.mode = "determined";
meter.value = Math.round(percent);
}
}
/**
* Resizes windows
* @constructor
*/
var Resizer = function(panel, targetWidth, targetHeight, pixelsPerStep, stepsPerSecond) {
this.panel = panel;
this.curWidth = panel.clientWidth;
this.curHeight = panel.clientHeight;
this.difX = (targetWidth ? targetWidth - this.curWidth : 0);
this.difY = (targetHeight ? targetHeight - this.curHeight : 0);
this.step = 0;
this.steps = Math.ceil(Math.max(Math.abs(this.difX), Math.abs(this.difY))/pixelsPerStep);
this.timeout = (1000/stepsPerSecond);
var me = this;
this._animateCallback = function() { me.animate() };
};
/**
* Performs a step of the animation
*/
Resizer.prototype.animate = function() {
if(this.stopped) return;
this.step++;
this.panel.sizeTo(this.curWidth+Math.round(this.step*this.difX/this.steps),
this.curHeight+Math.round(this.step*this.difY/this.steps));
if(this.step !== this.steps) {
window.setTimeout(this._animateCallback, this.timeout);
}
};
/**
* Halts resizing
*/
Resizer.prototype.stop = function() {
this.stopped = true;
};
}
window.addEventListener("DOMContentLoaded", Zotero_ProgressBar.onDOMContentLoaded, false);
window.addEventListener("load", Zotero_ProgressBar.onLoad, false);

View File

@ -1,51 +0,0 @@
<?xml version="1.0"?>
<!--
***** BEGIN LICENSE BLOCK *****
Copyright © 2018 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
-->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://global/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/integration.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/integration.css" type="text/css"?>
<!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd">
<window
id="quick-format-dialog"
class="progress-bar"
orient="vertical"
title="&zotero.progress.title;"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
persist="screenX screenY">
<script src="../include.js"/>
<script src="windowDraggingUtils.js" type="text/javascript"/>
<script src="progressBar.js" type="text/javascript"/>
<box orient="horizontal" id="quick-format-entry">
<deck id="quick-format-deck" selectedIndex="0" flex="1">
<progressmeter id="quick-format-progress-meter" mode="undetermined" value="0" flex="1"/>
</deck>
</box>
</window>

View File

@ -37,10 +37,9 @@ var Zotero_QuickFormat = new function () {
keepSorted, showEditor, referencePanel, referenceBox, referenceHeight = 0, keepSorted, showEditor, referencePanel, referenceBox, referenceHeight = 0,
separatorHeight = 0, currentLocator, currentLocatorLabel, currentSearchTime, dragging, separatorHeight = 0, currentLocator, currentLocatorLabel, currentSearchTime, dragging,
panel, panelPrefix, panelSuffix, panelSuppressAuthor, panelLocatorLabel, panelLocator, panel, panelPrefix, panelSuffix, panelSuppressAuthor, panelLocatorLabel, panelLocator,
panelLibraryLink, panelInfo, panelRefersToBubble, panelFrameHeight = 0, accepted = false; panelLibraryLink, panelInfo, panelRefersToBubble, panelFrameHeight = 0, accepted = false,
var _searchPromise; searchTimeout;
const SEARCH_TIMEOUT = 250;
const SHOWN_REFERENCES = 7; const SHOWN_REFERENCES = 7;
/** /**
@ -179,7 +178,6 @@ var Zotero_QuickFormat = new function () {
*/ */
function _getCurrentEditorTextNode() { function _getCurrentEditorTextNode() {
var selection = qfiWindow.getSelection(); var selection = qfiWindow.getSelection();
if (!selection) return false;
var range = selection.getRangeAt(0); var range = selection.getRangeAt(0);
var node = range.startContainer; var node = range.startContainer;
@ -712,7 +710,7 @@ var Zotero_QuickFormat = new function () {
/** /**
* Converts the selected item to a bubble * Converts the selected item to a bubble
*/ */
var _bubbleizeSelected = Zotero.Promise.coroutine(function* () { function _bubbleizeSelected() {
if(!referenceBox.hasChildNodes() || !referenceBox.selectedItem) return false; if(!referenceBox.hasChildNodes() || !referenceBox.selectedItem) return false;
var citationItem = {"id":referenceBox.selectedItem.getAttribute("zotero-item")}; var citationItem = {"id":referenceBox.selectedItem.getAttribute("zotero-item")};
@ -735,11 +733,11 @@ var Zotero_QuickFormat = new function () {
node.nodeValue = ""; node.nodeValue = "";
var bubble = _insertBubble(citationItem, node); var bubble = _insertBubble(citationItem, node);
_clearEntryList(); _clearEntryList();
yield _previewAndSort(); _previewAndSort();
_refocusQfe(); _refocusQfe();
return true; return true;
}); }
/** /**
* Ignores clicks (for use on separators in the rich list box) * Ignores clicks (for use on separators in the rich list box)
@ -903,13 +901,13 @@ var Zotero_QuickFormat = new function () {
/** /**
* Generates the preview and sorts citations * Generates the preview and sorts citations
*/ */
var _previewAndSort = Zotero.Promise.coroutine(function* () { function _previewAndSort() {
var shouldKeepSorted = keepSorted.hasAttribute("checked"), var shouldKeepSorted = keepSorted.hasAttribute("checked"),
editorShowing = showEditor.hasAttribute("checked"); editorShowing = showEditor.hasAttribute("checked");
if(!shouldKeepSorted && !editorShowing) return; if(!shouldKeepSorted && !editorShowing) return;
_updateCitationObject(); _updateCitationObject();
yield io.sort(); io.sort();
if(shouldKeepSorted) { if(shouldKeepSorted) {
// means we need to resort citations // means we need to resort citations
_clearCitation(); _clearCitation();
@ -921,7 +919,7 @@ var Zotero_QuickFormat = new function () {
_moveCursorToEnd(); _moveCursorToEnd();
} }
}); }
/** /**
* Shows the citation properties panel for a given bubble * Shows the citation properties panel for a given bubble
@ -1053,26 +1051,14 @@ var Zotero_QuickFormat = new function () {
* keypress, since searches can be slow. * keypress, since searches can be slow.
*/ */
function _resetSearchTimer() { function _resetSearchTimer() {
// Show spinner if(searchTimeout) clearTimeout(searchTimeout);
var spinner = document.getElementById('quick-format-spinner'); searchTimeout = setTimeout(_quickFormat, 250);
spinner.style.visibility = '';
// Cancel current search if active
if (_searchPromise && _searchPromise.isPending()) {
_searchPromise.cancel();
}
// Start new search
_searchPromise = Zotero.Promise.delay(SEARCH_TIMEOUT)
.then(() => _quickFormat())
.then(() => {
_searchPromise = null;
spinner.style.visibility = 'hidden';
});
} }
/** /**
* Handle return or escape * Handle return or escape
*/ */
var _onQuickSearchKeyPress = Zotero.Promise.coroutine(function* (event) { function _onQuickSearchKeyPress(event) {
// Prevent hang if another key is pressed after Enter // Prevent hang if another key is pressed after Enter
// https://forums.zotero.org/discussion/59157/ // https://forums.zotero.org/discussion/59157/
if (accepted) { if (accepted) {
@ -1084,7 +1070,7 @@ var Zotero_QuickFormat = new function () {
var keyCode = event.keyCode; var keyCode = event.keyCode;
if (keyCode === event.DOM_VK_RETURN) { if (keyCode === event.DOM_VK_RETURN) {
event.preventDefault(); event.preventDefault();
if(!(yield _bubbleizeSelected()) && !_getEditorContent()) { if(!_bubbleizeSelected() && !_getEditorContent()) {
_accept(); _accept();
} }
} else if(keyCode === event.DOM_VK_TAB || event.charCode === 59 /* ; */) { } else if(keyCode === event.DOM_VK_TAB || event.charCode === 59 /* ; */) {
@ -1163,7 +1149,7 @@ var Zotero_QuickFormat = new function () {
} else { } else {
_resetSearchTimer(); _resetSearchTimer();
} }
}); }
/** /**
* Adds a dummy element to make dragging work * Adds a dummy element to make dragging work
@ -1191,7 +1177,7 @@ var Zotero_QuickFormat = new function () {
/** /**
* Replaces the dummy element with a node to make dropping work * Replaces the dummy element with a node to make dropping work
*/ */
var _onBubbleDrop = Zotero.Promise.coroutine(function* (event) { function _onBubbleDrop(event) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -1209,9 +1195,9 @@ var Zotero_QuickFormat = new function () {
keepSorted.removeAttribute("checked"); keepSorted.removeAttribute("checked");
} }
yield _previewAndSort(); _previewAndSort();
_moveCursorToEnd(); _moveCursorToEnd();
}); }
/** /**
* Handle a click on a bubble * Handle a click on a bubble
@ -1334,7 +1320,7 @@ var Zotero_QuickFormat = new function () {
pane.selectItem(id); pane.selectItem(id);
// Pull window to foreground // Pull window to foreground
Zotero.Utilities.Internal.activate(pane.document.defaultView); Zotero.Integration.activate(pane.document.defaultView);
} }
/** /**

View File

@ -62,9 +62,6 @@
</toolbarbutton> </toolbarbutton>
<iframe id="quick-format-iframe" ondragstart="event.stopPropagation()" src="data:application/xhtml+xml,%3C!DOCTYPE%20html%20PUBLIC%20%22-//W3C//DTD%20XHTML%201.0%20Strict//EN%22%20%22http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd%22%3E%3Chtml%20xmlns=%22http://www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Clink%20rel=%22stylesheet%22%20type=%22text/css%22%20href=%22chrome://zotero/skin/integration.css%22/%3E%3Clink%20rel=%22stylesheet%22%20type=%22text/css%22%20href=%22chrome://zotero-platform/content/integration.css%22/%3E%3C/head%3E%3Cbody%20contenteditable=%22true%22%20spellcheck=%22false%22%20id=%22quick-format-editor%22/%3E%3C/html%3E" <iframe id="quick-format-iframe" ondragstart="event.stopPropagation()" src="data:application/xhtml+xml,%3C!DOCTYPE%20html%20PUBLIC%20%22-//W3C//DTD%20XHTML%201.0%20Strict//EN%22%20%22http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd%22%3E%3Chtml%20xmlns=%22http://www.w3.org/1999/xhtml%22%3E%3Chead%3E%3Clink%20rel=%22stylesheet%22%20type=%22text/css%22%20href=%22chrome://zotero/skin/integration.css%22/%3E%3Clink%20rel=%22stylesheet%22%20type=%22text/css%22%20href=%22chrome://zotero-platform/content/integration.css%22/%3E%3C/head%3E%3Cbody%20contenteditable=%22true%22%20spellcheck=%22false%22%20id=%22quick-format-editor%22/%3E%3C/html%3E"
tabindex="1" flex="1"/> tabindex="1" flex="1"/>
<vbox id="quick-format-spinner" style="visibility: hidden">
<image class="zotero-spinner-16"/>
</vbox>
</hbox> </hbox>
</hbox> </hbox>
<progressmeter id="quick-format-progress-meter" mode="undetermined" value="0" flex="1"/> <progressmeter id="quick-format-progress-meter" mode="undetermined" value="0" flex="1"/>

View File

@ -25,7 +25,6 @@
var ZoteroItemPane = new function() { var ZoteroItemPane = new function() {
var _lastItem, _itemBox, _notesLabel, _notesButton, _notesList, _tagsBox, _relatedBox; var _lastItem, _itemBox, _notesLabel, _notesButton, _notesList, _tagsBox, _relatedBox;
var _selectedNoteID;
var _translationTarget; var _translationTarget;
var _noteIDs; var _noteIDs;
@ -138,7 +137,7 @@ var ZoteroItemPane = new function() {
var icon = document.createElement('image'); var icon = document.createElement('image');
icon.className = "zotero-box-icon"; icon.className = "zotero-box-icon";
icon.setAttribute('src', `chrome://zotero/skin/treeitem-note${Zotero.hiDPISuffix}.png`); icon.setAttribute('src','chrome://zotero/skin/treeitem-note.png');
var label = document.createElement('label'); var label = document.createElement('label');
label.className = "zotero-box-label"; label.className = "zotero-box-label";
@ -223,61 +222,6 @@ var ZoteroItemPane = new function() {
}); });
this.onNoteSelected = function (item, editable) {
_selectedNoteID = item.id;
// If an external note window is open for this item, don't show the editor
if (ZoteroPane.findNoteWindow(item.id)) {
this.showNoteWindowMessage();
return;
}
var noteEditor = document.getElementById('zotero-note-editor');
// If loading new or different note, disable undo while we repopulate the text field
// so Undo doesn't end up clearing the field. This also ensures that Undo doesn't
// undo content from another note into the current one.
var clearUndo = noteEditor.item ? noteEditor.item.id != item.id : false;
noteEditor.mode = editable ? 'edit' : 'view';
noteEditor.parent = null;
noteEditor.item = item;
if (clearUndo) {
noteEditor.clearUndo();
}
document.getElementById('zotero-view-note-button').hidden = !editable;
document.getElementById('zotero-item-pane-content').selectedIndex = 2;
};
this.showNoteWindowMessage = function () {
ZoteroPane.setItemPaneMessage(Zotero.getString('pane.item.notes.editingInWindow'));
};
/**
* Select the parent item and open the note editor
*/
this.openNoteWindow = async function () {
var selectedNote = Zotero.Items.get(_selectedNoteID);
// We don't want to show the note in two places, since it causes unnecessary UI updates
// and can result in weird bugs where note content gets lost.
//
// If this is a child note, select the parent
if (selectedNote.parentID) {
await ZoteroPane.selectItem(selectedNote.parentID);
}
// Otherwise, hide note and replace with a message that we're editing externally
else {
this.showNoteWindowMessage();
}
ZoteroPane.openNoteWindow(selectedNote.id);
};
this.addNote = function (popup) { this.addNote = function (popup) {
ZoteroPane_Local.newNote(popup, _lastItem.key); ZoteroPane_Local.newNote(popup, _lastItem.key);
} }
@ -378,9 +322,21 @@ var ZoteroItemPane = new function() {
}; };
this.setReadLabel = function (isRead) { this.setToggleReadLabel = function() {
var markRead = false;
var items = ZoteroPane_Local.itemsView.getSelectedItems();
for (let item of items) {
if (!item.isRead) {
markRead = true;
break;
}
}
var elem = document.getElementById('zotero-feed-item-toggleRead-button'); var elem = document.getElementById('zotero-feed-item-toggleRead-button');
var label = Zotero.getString('pane.item.' + (isRead ? 'markAsUnread' : 'markAsRead')); if (markRead) {
var label = Zotero.getString('pane.item.markAsRead');
} else {
label = Zotero.getString('pane.item.markAsUnread');
}
elem.setAttribute('label', label); elem.setAttribute('label', label);
var key = Zotero.Keys.getKeyForCommand('toggleRead'); var key = Zotero.Keys.getKeyForCommand('toggleRead');

View File

@ -113,10 +113,8 @@
--> -->
<zoteronoteeditor id="zotero-note-editor" flex="1" notitle="1" <zoteronoteeditor id="zotero-note-editor" flex="1" notitle="1"
previousfocus="zotero-items-tree" previousfocus="zotero-items-tree"
onerror="ZoteroPane.displayErrorMessage(); this.mode = 'view'"/> onerror="ZoteroPane.displayErrorMessage()"/>
<button id="zotero-view-note-button" <button id="zotero-view-note-button" label="&zotero.notes.separate;" oncommand="ZoteroPane_Local.openNoteWindow(this.getAttribute('noteID')); if(this.hasAttribute('sourceID')) ZoteroPane_Local.selectItem(this.getAttribute('sourceID'));"/>
label="&zotero.notes.separate;"
oncommand="ZoteroItemPane.openNoteWindow()"/>
</groupbox> </groupbox>
<!-- Attachment item --> <!-- Attachment item -->

@ -1 +1 @@
Subproject commit b8c370c8a978790d2aeefa302f05f3bfb1478e75 Subproject commit ed11f0764934f8bda3b5097feaaf74d43adfd07d

View File

@ -32,8 +32,73 @@ var Zotero_Lookup = new function () {
* Performs a lookup by DOI, PMID, or ISBN * Performs a lookup by DOI, PMID, or ISBN
*/ */
this.accept = Zotero.Promise.coroutine(function* (textBox) { this.accept = Zotero.Promise.coroutine(function* (textBox) {
var identifiers = Zotero.Utilities.Internal.extractIdentifiers(textBox.value); var foundIDs = []; //keep track of identifiers to avoid duplicates
if (!identifiers.length) { var identifier = textBox.value;
//first look for DOIs
var ids = identifier.split(/[\s\u00A0]+/); //whitespace + non-breaking space
var searches = [], doi;
for(var i=0, n=ids.length; i<n; i++) {
if((doi = Zotero.Utilities.cleanDOI(ids[i])) && foundIDs.indexOf(doi) == -1) {
searches.push({
itemType: "journalArticle",
DOI: doi
});
foundIDs.push(doi);
}
}
//then try ISBNs
if (!searches.length) {
//first try replacing dashes
ids = identifier.replace(/[\u002D\u00AD\u2010-\u2015\u2212]+/g, "") //hyphens and dashes
.toUpperCase();
var ISBN_RE = /(?:\D|^)(97[89]\d{10}|\d{9}[\dX])(?!\d)/g;
var isbn;
while(isbn = ISBN_RE.exec(ids)) {
isbn = Zotero.Utilities.cleanISBN(isbn[1]);
if(isbn && foundIDs.indexOf(isbn) == -1) {
searches.push({
itemType: "book",
ISBN: isbn
});
foundIDs.push(isbn);
}
}
//now try spaces
if (!searches.length) {
ids = ids.replace(/[ \u00A0]+/g, ""); //space + non-breaking space
while(isbn = ISBN_RE.exec(ids)) {
isbn = Zotero.Utilities.cleanISBN(isbn[1]);
if(isbn && foundIDs.indexOf(isbn) == -1) {
searches.push({
itemType: "book",
ISBN: isbn
});
foundIDs.push(isbn);
}
}
}
}
//finally try for PMID
if (!searches.length) {
// PMID; right now, the longest PMIDs are 8 digits, so it doesn't
// seem like we will need to discriminate for a fairly long time
var PMID_RE = /(?:\D|^)(\d{1,9})(?!\d)/g;
var pmid;
while((pmid = PMID_RE.exec(identifier)) && foundIDs.indexOf(pmid) == -1) {
searches.push({
itemType: "journalArticle",
contextObject: "rft_id=info:pmid/" + pmid[1]
});
foundIDs.push(pmid);
}
}
if (!searches.length) {
Zotero.alert( Zotero.alert(
window, window,
Zotero.getString("lookup.failure.title"), Zotero.getString("lookup.failure.title"),
@ -55,9 +120,9 @@ var Zotero_Lookup = new function () {
Zotero_Lookup.toggleProgress(true); Zotero_Lookup.toggleProgress(true);
for (let identifier of identifiers) { for (let search of searches) {
var translate = new Zotero.Translate.Search(); var translate = new Zotero.Translate.Search();
translate.setIdentifier(identifier); translate.setSearch(search);
// be lenient about translators // be lenient about translators
let translators = yield translate.getTranslators(); let translators = yield translate.getTranslators();
@ -92,45 +157,6 @@ var Zotero_Lookup = new function () {
return false; return false;
}); });
this.showPanel = function (button) {
var panel = document.getElementById('zotero-lookup-panel');
panel.openPopup(button, "after_start", 16, -2, false, false);
}
/**
* Focuses the field
*/
this.onShowing = function (event) {
// Ignore context menu
if (event.originalTarget.id != 'zotero-lookup-panel') return;
document.getElementById("zotero-lookup-panel").style.padding = "10px";
this.getActivePanel().getElementsByTagName('textbox')[0].focus();
}
/**
* Cancels the popup and resets fields
*/
this.onHidden = function (event) {
// Ignore context menu
if (event.originalTarget.id != 'zotero-lookup-panel') return;
document.getElementById("zotero-lookup-textbox").value = "";
document.getElementById("zotero-lookup-multiline-textbox").value = "";
Zotero_Lookup.toggleProgress(false);
}
this.getActivePanel = function() {
var mlPanel = document.getElementById("zotero-lookup-multiline");
if (mlPanel.collapsed) return document.getElementById("zotero-lookup-singleLine");
return mlPanel;
}
/** /**
* Handles a key press * Handles a key press
*/ */
@ -153,27 +179,44 @@ var Zotero_Lookup = new function () {
return true; return true;
} }
/**
* Focuses the field
*/
this.onShowing = function() {
document.getElementById("zotero-lookup-panel").style.padding = "10px";
this.onInput = function (event, textbox) { // Workaround for field being truncated in middle
this.adjustTextbox(textbox); // https://github.com/zotero/zotero/issues/343
}; this.toggleMultiline(true);
var identifierElement = Zotero_Lookup.toggleMultiline(false);
Zotero_Lookup.toggleProgress(false);
identifierElement.focus();
}
/**
* Cancels the popup and resets fields
*/
this.onHidden = function() {
var txtBox = Zotero_Lookup.toggleMultiline(false);
var mlTextbox = document.getElementById("zotero-lookup-multiline-textbox");
txtBox.value = "";
mlTextbox.value = "";
}
/** /**
* Converts the textbox to multiline if newlines are detected * Converts the textbox to multiline if newlines are detected
*/ */
this.adjustTextbox = function (textbox) { this.adjustTextbox = function(txtBox) {
if (textbox.value.trim().match(/[\r\n]/)) { if(txtBox.value.trim().match(/[\r\n]/)) {
Zotero_Lookup.toggleMultiline(true); Zotero_Lookup.toggleMultiline(true);
} } else {
// Since we ignore trailing and leading newlines, we should also trim them for display //since we ignore trailing and leading newlines, we should also trim them for display
// can't use trim, because then we cannot add leading/trailing spaces to the single line textbox //can't use trim, because then we cannot add leading/trailing spaces to the single line textbox
else { txtBox.value = txtBox.value.replace(/^([ \t]*[\r\n]+[ \t]*)+|([ \t]*[\r\n]+[ \t]*)+$/g,"");
textbox.value = textbox.value.replace(/^([ \t]*[\r\n]+[ \t]*)+|([ \t]*[\r\n]+[ \t]*)+$/g,"");
} }
} }
/** /**
* Performs the switch to multiline textbox and returns that textbox * Performs the switch to multiline textbox and returns that textbox
*/ */
@ -206,20 +249,21 @@ var Zotero_Lookup = new function () {
} }
this.toggleProgress = function(on) { this.toggleProgress = function(on) {
// In Firefox 52.6.0, progressmeters burn CPU at idle on Linux when undetermined, even
// if they're hidden. (Being hidden is enough on macOS.)
var mode = on ? 'undetermined' : 'determined';
//single line //single line
var txtBox = document.getElementById("zotero-lookup-textbox"); var txtBox = document.getElementById("zotero-lookup-textbox");
txtBox.style.opacity = on ? 0.5 : 1; txtBox.style.opacity = on ? 0.5 : 1;
txtBox.disabled = !!on; txtBox.disabled = !!on;
var p1 = document.getElementById("zotero-lookup-progress"); document.getElementById("zotero-lookup-progress").setAttribute("collapsed", !on);
p1.mode = mode;
//multiline //multiline
document.getElementById("zotero-lookup-multiline-textbox").disabled = !!on; document.getElementById("zotero-lookup-multiline-textbox").disabled = !!on;
var p2 = document.getElementById("zotero-lookup-multiline-progress"); document.getElementById("zotero-lookup-multiline-progress").setAttribute("collapsed", !on);
p2.mode = mode; }
this.getActivePanel = function() {
var mlPanel = document.getElementById("zotero-lookup-multiline");
if(mlPanel.collapsed) return document.getElementById("zotero-lookup-singleLine");
return mlPanel;
} }
} }

View File

@ -168,6 +168,7 @@ var Zotero_Merge_Window = new function () {
if (x.data) { if (x.data) {
x.data.version = _conflicts[i][x.selected].version; x.data.version = _conflicts[i][x.selected].version;
} }
a[i] = x.data;
}) })
_io.dataOut = _merged; _io.dataOut = _merged;

View File

@ -26,9 +26,10 @@
var noteEditor; var noteEditor;
var notifierUnregisterID; var notifierUnregisterID;
async function onLoad() { function onLoad() {
noteEditor = document.getElementById('zotero-note-editor'); noteEditor = document.getElementById('zotero-note-editor');
noteEditor.mode = 'edit'; noteEditor.mode = 'edit';
noteEditor.focus();
// Set font size from pref // Set font size from pref
Zotero.setFontSize(noteEditor); Zotero.setFontSize(noteEditor);
@ -36,14 +37,25 @@ async function onLoad() {
if (window.arguments) { if (window.arguments) {
var io = window.arguments[0]; var io = window.arguments[0];
} }
var itemID = parseInt(io.itemID); var itemID = parseInt(io.itemID);
var collectionID = parseInt(io.collectionID); var collectionID = parseInt(io.collectionID);
var parentItemKey = io.parentItemKey; var parentItemKey = io.parentItemKey;
return Zotero.spawn(function* () {
if (itemID) { if (itemID) {
var ref = await Zotero.Items.getAsync(itemID); var ref = yield Zotero.Items.getAsync(itemID);
var clearUndo = noteEditor.item ? noteEditor.item.id != ref.id : false;
noteEditor.item = ref; noteEditor.item = ref;
// If loading new or different note, disable undo while we repopulate the text field
// so Undo doesn't end up clearing the field. This also ensures that Undo doesn't
// undo content from another note into the current one.
if (clearUndo) {
noteEditor.clearUndo();
}
document.title = ref.getNoteTitle(); document.title = ref.getNoteTitle();
} }
else { else {
@ -59,35 +71,26 @@ async function onLoad() {
noteEditor.refresh(); noteEditor.refresh();
} }
noteEditor.focus(); notifierUnregisterID = Zotero.Notifier.registerObserver(NotifyCallback, 'item');
notifierUnregisterID = Zotero.Notifier.registerObserver(NotifyCallback, 'item', 'noteWindow'); });
} }
// If there's an error saving a note, close the window and crash the app function onUnload()
function onError() { {
try { if(noteEditor && noteEditor.value)
window.opener.ZoteroPane.displayErrorMessage(); noteEditor.save();
}
catch (e) {
Zotero.logError(e);
}
window.close();
}
function onUnload() {
Zotero.Notifier.unregisterObserver(notifierUnregisterID); Zotero.Notifier.unregisterObserver(notifierUnregisterID);
if (noteEditor.item) {
window.opener.ZoteroPane.onNoteWindowClosed(noteEditor.item.id, noteEditor.value);
}
} }
var NotifyCallback = { var NotifyCallback = {
notify: function(action, type, ids){ notify: function(action, type, ids){
if (noteEditor.item && ids.includes(noteEditor.item.id)) { if (noteEditor.item && ids.indexOf(noteEditor.item.id) != -1) {
var noteTitle = noteEditor.item.getNoteTitle(); // If the document title hasn't yet been set, reset undo so
// undoing to empty isn't possible
var noteTitle = noteEditor.note.getNoteTitle();
if (!document.title && noteTitle != '') { if (!document.title && noteTitle != '') {
noteEditor.clearUndo();
document.title = noteTitle; document.title = noteTitle;
} }

View File

@ -15,6 +15,7 @@
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="include.js"/> <script src="include.js"/>
<script src="itemPane.js"/>
<script src="note.js"/> <script src="note.js"/>
<keyset> <keyset>
@ -22,5 +23,5 @@
</keyset> </keyset>
<command id="cmd_close" oncommand="window.close();"/> <command id="cmd_close" oncommand="window.close();"/>
<zoteronoteeditor id="zotero-note-editor" flex="1" onerror="onError()"/> <zoteronoteeditor id="zotero-note-editor" flex="1"/>
</window> </window>

View File

@ -6,12 +6,10 @@
title="&zotero.progress.title;" width="550" height="230" title="&zotero.progress.title;" width="550" height="230"
id="zotero-progress"> id="zotero-progress">
<vbox style="padding:10px" flex="1"> <vbox style="padding:10px" flex="1">
<label id="label" control="progress-indicator" value=""/> <label id="label" control="progress-indicator" value="&zotero.recognizePDF.recognizing.label;"/>
<hbox align="center"> <hbox align="center">
<progressmeter id="progress-indicator" mode="determined" flex="1"/> <progressmeter id="progress-indicator" mode="determined" flex="1"/>
<button id="cancel-button" label="&zotero.general.cancel;"/> <button id="cancel-button" label="&zotero.recognizePDF.cancel.label;"/>
<button id="minimize-button" label="&zotero.general.minimize;"/>
<button id="close-button" label="&zotero.general.close;"/>
</hbox> </hbox>
<tree flex="1" id="tree" hidecolumnpicker="true"> <tree flex="1" id="tree" hidecolumnpicker="true">
<treecols> <treecols>

View File

@ -42,28 +42,6 @@ Zotero_Preferences.Advanced = {
}, },
updateTranslators: Zotero.Promise.coroutine(function* () {
var updated = yield Zotero.Schema.updateFromRepository(Zotero.Schema.REPO_UPDATE_MANUAL);
var button = document.getElementById('updateButton');
if (button) {
if (updated===-1) {
var label = Zotero.getString('zotero.preferences.update.upToDate');
}
else if (updated) {
var label = Zotero.getString('zotero.preferences.update.updated');
}
else {
var label = Zotero.getString('zotero.preferences.update.error');
}
button.setAttribute('label', label);
if (updated && Zotero_Preferences.Cite) {
yield Zotero_Preferences.Cite.refreshStylesList();
}
}
}),
migrateDataDirectory: Zotero.Promise.coroutine(function* () { migrateDataDirectory: Zotero.Promise.coroutine(function* () {
var currentDir = Zotero.DataDirectory.dir; var currentDir = Zotero.DataDirectory.dir;
var defaultDir = Zotero.DataDirectory.defaultDir; var defaultDir = Zotero.DataDirectory.defaultDir;
@ -155,15 +133,13 @@ Zotero_Preferences.Advanced = {
}), }),
runIntegrityCheck: async function (button) { runIntegrityCheck: Zotero.Promise.coroutine(function* () {
button.disabled = true; var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
try { var ok = yield Zotero.DB.integrityCheck();
let ps = Services.prompt;
var ok = await Zotero.DB.integrityCheck();
if (ok) { if (ok) {
ok = await Zotero.Schema.integrityCheck(); ok = yield Zotero.Schema.integrityCheck();
if (!ok) { if (!ok) {
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL); + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
@ -179,13 +155,13 @@ Zotero_Preferences.Advanced = {
if (index == 0) { if (index == 0) {
// Safety first // Safety first
await Zotero.DB.backupDatabase(); yield Zotero.DB.backupDatabase();
// Fix the errors // Fix the errors
await Zotero.Schema.integrityCheck(true); yield Zotero.Schema.integrityCheck(true);
// And run the check again // And run the check again
ok = await Zotero.Schema.integrityCheck(); ok = yield Zotero.Schema.integrityCheck();
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING); var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING);
if (ok) { if (ok) {
var str = 'success'; var str = 'success';
@ -215,7 +191,7 @@ Zotero_Preferences.Advanced = {
} }
try { try {
await Zotero.DB.vacuum(); yield Zotero.DB.vacuum();
} }
catch (e) { catch (e) {
Zotero.logError(e); Zotero.logError(e);
@ -228,11 +204,7 @@ Zotero_Preferences.Advanced = {
Zotero.getString('general.' + str), Zotero.getString('general.' + str),
Zotero.getString('db.integrityCheck.' + str) Zotero.getString('db.integrityCheck.' + str)
+ (!ok ? "\n\n" + Zotero.getString('db.integrityCheck.dbRepairTool') : '')); + (!ok ? "\n\n" + Zotero.getString('db.integrityCheck.dbRepairTool') : ''));
} }),
finally {
button.disabled = false;
}
},
resetTranslatorsAndStyles: function () { resetTranslatorsAndStyles: function () {

View File

@ -37,16 +37,12 @@
onpaneload="Zotero_Preferences.Advanced.init()" onpaneload="Zotero_Preferences.Advanced.init()"
helpTopic="advanced"> helpTopic="advanced">
<preferences> <preferences>
<preference id="pref-automaticScraperUpdates" name="extensions.zotero.automaticScraperUpdates" type="bool"/>
<preference id="pref-reportTranslationFailure" name="extensions.zotero.reportTranslationFailure" type="bool"/>
<preference id="pref-baseAttachmentPath" name="extensions.zotero.baseAttachmentPath" type="string"/> <preference id="pref-baseAttachmentPath" name="extensions.zotero.baseAttachmentPath" type="string"/>
<preference id="pref-useDataDir" name="extensions.zotero.useDataDir" type="bool"/> <preference id="pref-useDataDir" name="extensions.zotero.useDataDir" type="bool"/>
<preference id="pref-dataDir" name="extensions.zotero.dataDir" type="string"/> <preference id="pref-dataDir" name="extensions.zotero.dataDir" type="string"/>
<preference id="pref-debug-output-enableAfterRestart" name="extensions.zotero.debug.store" type="bool"/> <preference id="pref-debug-output-enableAfterRestart" name="extensions.zotero.debug.store" type="bool"/>
<preference id="pref-openURL-resolver" name="extensions.zotero.openURL.resolver" type="string"/> <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-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-openZotero" name="extensions.zotero.keys.openZotero" type="string"/>
<preference id="pref-keys-saveToZotero" name="extensions.zotero.keys.saveToZotero" 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-library" name="extensions.zotero.keys.library" type="string"/>
@ -78,14 +74,6 @@
<groupbox id="zotero-prefpane-advanced-miscellaneous"> <groupbox id="zotero-prefpane-advanced-miscellaneous">
<caption label="&zotero.preferences.miscellaneous;"/> <caption label="&zotero.preferences.miscellaneous;"/>
<hbox align="center">
<checkbox label="&zotero.preferences.autoUpdate;" preference="pref-automaticScraperUpdates"/>
<button id="updateButton" style="margin-top:0" label="&zotero.preferences.updateNow;"
oncommand="Zotero_Preferences.Advanced.updateTranslators()"/>
</hbox>
<checkbox label="&zotero.preferences.reportTranslationFailure;" preference="pref-reportTranslationFailure"/>
<hbox align="center"> <hbox align="center">
<label value="&zotero.bibliography.locale.label;"/> <label value="&zotero.bibliography.locale.label;"/>
<menulist id="locale-menu" <menulist id="locale-menu"
@ -214,7 +202,7 @@
<hbox style="display: block"> <hbox style="display: block">
<button label="&zotero.preferences.dbMaintenance.integrityCheck;" <button label="&zotero.preferences.dbMaintenance.integrityCheck;"
oncommand="Zotero_Preferences.Advanced.runIntegrityCheck(this)"/> oncommand="Zotero_Preferences.Advanced.runIntegrityCheck()"/>
<button id="reset-translators-button" <button id="reset-translators-button"
label="&zotero.preferences.dbMaintenance.resetTranslators;" label="&zotero.preferences.dbMaintenance.resetTranslators;"
oncommand="Zotero_Preferences.Advanced.resetTranslators()"/> oncommand="Zotero_Preferences.Advanced.resetTranslators()"/>

View File

@ -26,48 +26,22 @@
"use strict"; "use strict";
Zotero_Preferences.Cite = { Zotero_Preferences.Cite = {
wordPluginIDs: new Set([
'zoteroOpenOfficeIntegration@zotero.org',
'zoteroMacWordIntegration@zotero.org',
'zoteroWinWordIntegration@zotero.org'
]),
init: Zotero.Promise.coroutine(function* () { init: Zotero.Promise.coroutine(function* () {
Components.utils.import("resource://gre/modules/AddonManager.jsm");
this.updateWordProcessorInstructions(); this.updateWordProcessorInstructions();
yield this.refreshStylesList(); yield this.refreshStylesList();
}), }),
/** /**
* Determines if any word processors are disabled and if so, shows a message in the pref pane * Determines if there are word processors, and if not, enables no word processor message
*/ */
updateWordProcessorInstructions: async function () { updateWordProcessorInstructions: function () {
var someDisabled = false; if(document.getElementById("wordProcessors").childNodes.length == 2) {
await new Promise(function(resolve) { document.getElementById("wordProcessors-noWordProcessorPluginsInstalled").hidden = undefined;
AddonManager.getAllAddons(function(addons) {
for (let addon of addons) {
if (Zotero_Preferences.Cite.wordPluginIDs.has(addon.id) && addon.userDisabled) {
someDisabled = true;
} }
if(Zotero.isStandalone) {
document.getElementById("wordProcessors-getWordProcessorPlugins").hidden = true;
} }
resolve();
});
});
if (someDisabled) {
document.getElementById("wordProcessors-somePluginsDisabled").hidden = undefined;
}
},
enableWordPlugins: function () {
AddonManager.getAllAddons(function(addons) {
for (let addon of addons) {
if (Zotero_Preferences.Cite.wordPluginIDs.has(addon.id) && addon.userDisabled) {
addon.userDisabled = false;
}
}
return Zotero.Utilities.Internal.quit(true);
});
}, },

View File

@ -96,15 +96,11 @@
</groupbox> </groupbox>
</tabpanel> </tabpanel>
<tabpanel orient="vertical" id="wordProcessors"> <tabpanel orient="vertical" id="wordProcessors">
<vbox id="wordProcessors-somePluginsDisabled" hidden="true"> <label id="wordProcessors-noWordProcessorPluginsInstalled" width="45em" hidden="true">
<label style="font-weight: bold; margin-top: 1em; text-align: center">Some word processor plugins are disabled.</label> &zotero.preferences.cite.wordProcessors.noWordProcessorPluginsInstalled;
<hbox pack="center" style="margin-bottom: 2em"> </label>
<button id="wordProcessors-enablePlugins"
label="Enable Plugins and Restart Zotero"
oncommand="Zotero_Preferences.Cite.enableWordPlugins()"/>
</hbox>
</vbox>
<checkbox label="&zotero.preferences.cite.wordProcessors.useClassicAddCitationDialog;" preference="pref-cite-useClassicAddCitationDialog"/> <checkbox label="&zotero.preferences.cite.wordProcessors.useClassicAddCitationDialog;" preference="pref-cite-useClassicAddCitationDialog"/>
<label id="wordProcessors-getWordProcessorPlugins" class="zotero-text-link" href="&zotero.preferences.cite.wordProcessors.getPlugins.url;" value="&zotero.preferences.cite.wordProcessors.getPlugins;"/>
</tabpanel> </tabpanel>
</tabpanels> </tabpanels>
</tabbox> </tabbox>

View File

@ -25,9 +25,6 @@
"use strict"; "use strict";
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm");
Zotero_Preferences.General = { Zotero_Preferences.General = {
init: function () { init: function () {
// JS-based strings // JS-based strings
@ -39,89 +36,27 @@ Zotero_Preferences.General = {
} }
document.getElementById('noteFontSize').value = Zotero.Prefs.get('note.fontSize'); document.getElementById('noteFontSize').value = Zotero.Prefs.get('note.fontSize');
this._updateFileHandlerUI();
}, },
//
// File handlers
//
chooseFileHandler: function (type) {
var pref = this._getFileHandlerPref(type);
var currentPath = Zotero.Prefs.get(pref);
var nsIFilePicker = Components.interfaces.nsIFilePicker; updateTranslators: Zotero.Promise.coroutine(function* () {
var fp = Components.classes["@mozilla.org/filepicker;1"] var updated = yield Zotero.Schema.updateFromRepository(Zotero.Schema.REPO_UPDATE_MANUAL);
.createInstance(nsIFilePicker); var button = document.getElementById('updateButton');
if (currentPath) { if (button) {
fp.displayDirectory = Zotero.File.pathToFile(OS.Path.dirname(currentPath)); if (updated===-1) {
var label = Zotero.getString('zotero.preferences.update.upToDate');
} }
fp.init( else if (updated) {
window, var label = Zotero.getString('zotero.preferences.update.updated');
Zotero.getString('zotero.preferences.chooseApplication'),
nsIFilePicker.modeOpen
);
fp.appendFilters(nsIFilePicker.filterApps);
if (fp.show() != nsIFilePicker.returnOK) {
this._updateFileHandlerUI();
return false;
}
var newPath = OS.Path.normalize(fp.file.path);
this.setFileHandler(type, newPath);
},
setFileHandler: function (type, handler) {
var pref = this._getFileHandlerPref(type);
if (handler) {
Zotero.Prefs.set(pref, handler);
} }
else { else {
Zotero.Prefs.clear(pref); var label = Zotero.getString('zotero.preferences.update.error');
} }
this._updateFileHandlerUI(); button.setAttribute('label', label);
},
_updateFileHandlerUI: function () { if (updated && Zotero_Preferences.Cite) {
var handler = Zotero.Prefs.get('fileHandler.pdf'); yield Zotero_Preferences.Cite.refreshStylesList();
var menulist = document.getElementById('fileHandler-pdf');
var customMenuItem = document.getElementById('fileHandler-custom');
if (handler) {
let icon;
try {
let fph = Services.io.getProtocolHandler("file")
.QueryInterface(Components.interfaces.nsIFileProtocolHandler);
let urlspec = fph.getURLSpecFromFile(Zotero.File.pathToFile(handler));
icon = "moz-icon://" + urlspec + "?size=16";
} }
catch (e) {
Zotero.logError(e);
}
let handlerFilename = OS.Path.basename(handler);
if (Zotero.isMac) {
handlerFilename = handlerFilename.replace(/\.app$/, '');
}
customMenuItem.setAttribute('label', handlerFilename);
if (icon) {
customMenuItem.className = 'menuitem-iconic';
customMenuItem.setAttribute('image', icon);
}
else {
customMenuItem.className = '';
}
customMenuItem.hidden = false;
menulist.selectedIndex = 0;
}
else {
customMenuItem.hidden = true;
menulist.selectedIndex = 1;
}
},
_getFileHandlerPref: function (type) {
if (type != 'pdf') {
throw new Error(`Unknown file type ${type}`);
}
return 'fileHandler.pdf';
} }
})
} }

View File

@ -33,12 +33,10 @@
<preferences id="zotero-prefpane-general-preferences"> <preferences id="zotero-prefpane-general-preferences">
<preference id="pref-fontSize" name="extensions.zotero.fontSize" type="string"/> <preference id="pref-fontSize" name="extensions.zotero.fontSize" type="string"/>
<preference id="pref-layout" name="extensions.zotero.layout" type="string"/> <preference id="pref-layout" name="extensions.zotero.layout" type="string"/>
<preference id="pref-automaticScraperUpdates" name="extensions.zotero.automaticScraperUpdates" type="bool"/>
<preference id="pref-reportTranslationFailure" name="extensions.zotero.reportTranslationFailure" type="bool"/>
<preference id="pref-automaticSnapshots" name="extensions.zotero.automaticSnapshots" type="bool"/> <preference id="pref-automaticSnapshots" name="extensions.zotero.automaticSnapshots" type="bool"/>
<preference id="pref-downloadAssociatedFiles" name="extensions.zotero.downloadAssociatedFiles" type="bool"/> <preference id="pref-downloadAssociatedFiles" name="extensions.zotero.downloadAssociatedFiles" type="bool"/>
<preference id="pref-autoRecognizeFiles" name="extensions.zotero.autoRecognizeFiles" type="bool"/>
<preference id="pref-autoRenameFiles" name="extensions.zotero.autoRenameFiles" type="bool"/>
<preference id="pref-automaticTags" name="extensions.zotero.automaticTags" type="bool"/> <preference id="pref-automaticTags" name="extensions.zotero.automaticTags" type="bool"/>
<preference id="pref-trashAutoEmptyDays" name="extensions.zotero.trashAutoEmptyDays" type="int"/> <preference id="pref-trashAutoEmptyDays" name="extensions.zotero.trashAutoEmptyDays" type="int"/>
@ -110,33 +108,20 @@
</grid> </grid>
</groupbox> </groupbox>
<groupbox id="zotero-prefpane-file-handling-groupbox"> <groupbox id="zotero-prefpane-miscellaneous-groupbox">
<caption label="&zotero.preferences.fileHandling;"/> <caption label="&zotero.preferences.miscellaneous;"/>
<hbox align="center">
<checkbox label="&zotero.preferences.autoUpdate;" preference="pref-automaticScraperUpdates"/>
<button id="updateButton" style="margin-top:0" label="&zotero.preferences.updateNow;"
oncommand="Zotero_Preferences.General.updateTranslators()"/>
</hbox>
<checkbox label="&zotero.preferences.reportTranslationFailure;" preference="pref-reportTranslationFailure"/>
<checkbox id="automaticSnapshots-checkbox" <checkbox id="automaticSnapshots-checkbox"
label="&zotero.preferences.automaticSnapshots;" label="&zotero.preferences.automaticSnapshots;"
preference="pref-automaticSnapshots"/> preference="pref-automaticSnapshots"/>
<checkbox label="&zotero.preferences.downloadAssociatedFiles;" preference="pref-downloadAssociatedFiles"/> <checkbox label="&zotero.preferences.downloadAssociatedFiles;" preference="pref-downloadAssociatedFiles"/>
<checkbox label="&zotero.preferences.autoRecognizeFiles;" preference="pref-autoRecognizeFiles"/>
<checkbox label="&zotero.preferences.autoRenameFiles;" preference="pref-autoRenameFiles"/>
<hbox align="center">
<label value="&zotero.preferences.fileHandler.openPDFsUsing;" control="file-handler-pdf"/>
<menulist id="fileHandler-pdf" class="fileHandler-menu">
<menupopup>
<menuitem id="fileHandler-custom"/>
<menuitem label="&zotero.preferences.fileHandler.systemDefault;"
oncommand="Zotero_Preferences.General.setFileHandler('pdf', false)"/>
<menuitem label="&zotero.preferences.custom;"
oncommand="Zotero_Preferences.General.chooseFileHandler('pdf')"/>
</menupopup>
</menulist>
</hbox>
</groupbox>
<groupbox id="zotero-prefpane-miscellaneous-groupbox">
<caption label="&zotero.preferences.miscellaneous;"/>
<checkbox label="&zotero.preferences.automaticTags;" preference="pref-automaticTags"/> <checkbox label="&zotero.preferences.automaticTags;" preference="pref-automaticTags"/>
<hbox align="center"> <hbox align="center">
<label value="&zotero.preferences.trashAutoEmptyDaysPre;"/> <label value="&zotero.preferences.trashAutoEmptyDaysPre;"/>

View File

@ -33,10 +33,264 @@ Zotero_Preferences.Search = {
document.getElementById('fulltext-clearIndex').setAttribute('label', document.getElementById('fulltext-clearIndex').setAttribute('label',
Zotero.getString('zotero.preferences.search.clearIndex') Zotero.getString('zotero.preferences.search.clearIndex')
+ Zotero.getString('punctuation.ellipsis')); + Zotero.getString('punctuation.ellipsis'));
this.updatePDFToolsStatus();
this.updateIndexStats(); this.updateIndexStats();
// Quick hack to support install prompt from PDF recognize option
var io = window.arguments[0];
if (io.action && io.action == 'pdftools-install') {
this.checkPDFToolsDownloadVersion();
}
}, },
/*
* Update window according to installation status for PDF tools
* (e.g. status line, install/update button, etc.)
*/
updatePDFToolsStatus: function () {
var converterIsRegistered = Zotero.Fulltext.pdfConverterIsRegistered();
var infoIsRegistered = Zotero.Fulltext.pdfInfoIsRegistered();
var converterStatusLabel = document.getElementById('pdfconverter-status');
var infoStatusLabel = document.getElementById('pdfinfo-status');
var requiredLabel = document.getElementById('pdftools-required');
var updateButton = document.getElementById('pdftools-update-button');
var documentationLink = document.getElementById('pdftools-documentation-link');
var settingsBox = document.getElementById('pdftools-settings');
// If we haven't already generated the required and documentation messages
if (!converterIsRegistered && !requiredLabel.hasChildNodes()) {
// Xpdf link
var str = Zotero.getString('zotero.preferences.search.pdf.toolsRequired',
[Zotero.Fulltext.pdfConverterName, Zotero.Fulltext.pdfInfoName,
'<a href="' + Zotero.Fulltext.pdfToolsURL + '">'
+ Zotero.Fulltext.pdfToolsName + '</a>']);
var parts = Zotero.Utilities.parseMarkup(str);
for (var i=0; i<parts.length; i++) {
var part = parts[i];
if (part.type == 'text') {
var elem = document.createTextNode(part.text);
}
else if (part.type == 'link') {
var elem = document.createElement('label');
elem.setAttribute('value', part.text);
elem.setAttribute('class', 'zotero-text-link');
for (var key in part.attributes) {
elem.setAttribute(key, part.attributes[key]);
if (key == 'href') {
elem.setAttribute('tooltiptext', part.attributes[key]);
}
}
}
requiredLabel.appendChild(elem);
}
requiredLabel.appendChild(document.createTextNode(' '
+ Zotero.getString('zotero.preferences.search.pdf.automaticInstall')));
// Documentation link
var link = '<a href="http://www.zotero.org/documentation/pdf_fulltext_indexing">'
+ Zotero.getString('zotero.preferences.search.pdf.documentationLink')
+ '</a>';
var str = Zotero.getString('zotero.preferences.search.pdf.advancedUsers', link);
var parts = Zotero.Utilities.parseMarkup(str);
for (var i=0; i<parts.length; i++) {
var part = parts[i];
if (part.type == 'text') {
var elem = document.createTextNode(part.text);
}
else if (part.type == 'link') {
var elem = document.createElement('label');
elem.setAttribute('value', part.text);
elem.setAttribute('class', 'zotero-text-link');
for (var key in part.attributes) {
elem.setAttribute(key, part.attributes[key]);
if (key == 'href') {
elem.setAttribute('tooltiptext', part.attributes[key]);
}
}
}
documentationLink.appendChild(elem);
}
}
// converter status line
var prefix = 'zotero.preferences.search.pdf.tool';
if (converterIsRegistered) {
var version = Zotero.Fulltext.pdfConverterVersion;
str = Zotero.getString(prefix + 'Registered',
Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
[Zotero.Fulltext.pdfConverterName, version]));
}
else {
str = Zotero.getString(prefix + 'NotRegistered',
[Zotero.Fulltext.pdfConverterFileName]);
}
converterStatusLabel.setAttribute('value', str);
// pdfinfo status line
if (infoIsRegistered) {
var version = Zotero.Fulltext.pdfInfoVersion;
str = Zotero.getString(prefix + 'Registered',
Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
[Zotero.Fulltext.pdfInfoName, version]));
}
else {
str = Zotero.getString(prefix + 'NotRegistered',
[Zotero.Fulltext.pdfInfoFileName]);
}
infoStatusLabel.setAttribute('value', str);
str = converterIsRegistered ?
Zotero.getString('general.checkForUpdate') :
Zotero.getString('zotero.preferences.search.pdf.checkForInstaller');
updateButton.setAttribute('label', str);
requiredLabel.setAttribute('hidden', converterIsRegistered);
documentationLink.setAttribute('hidden', converterIsRegistered);
settingsBox.setAttribute('hidden', !converterIsRegistered);
},
/*
* Check available versions of PDF tools from server and prompt for installation
* if a newer version is available
*/
checkPDFToolsDownloadVersion: Zotero.Promise.coroutine(function* () {
try {
var latestVersion = yield Zotero.Fulltext.getLatestPDFToolsVersion();
var converterIsRegistered = Zotero.Fulltext.pdfConverterIsRegistered();
var infoIsRegistered = Zotero.Fulltext.pdfInfoIsRegistered();
var bothRegistered = converterIsRegistered && infoIsRegistered;
// On Windows, install if not installed or anything other than 3.02a
if (Zotero.isWin) {
var converterVersionAvailable = !converterIsRegistered
|| Zotero.Fulltext.pdfConverterVersion != '3.02a';
var infoVersionAvailable = !infoIsRegistered
|| Zotero.Fulltext.pdfInfoVersion != '3.02a';
var bothAvailable = converterVersionAvailable && infoVersionAvailable;
}
// Install if not installed, version unknown, outdated, or
// Xpdf 3.02/3.04 (to upgrade to Poppler),
else {
var converterVersionAvailable = (!converterIsRegistered ||
Zotero.Fulltext.pdfConverterVersion == 'UNKNOWN'
|| latestVersion > Zotero.Fulltext.pdfConverterVersion
|| (!latestVersion.startsWith('3.02')
&& Zotero.Fulltext.pdfConverterVersion.startsWith('3.02'))
|| (!latestVersion.startsWith('3.02') && latestVersion != '3.04'
&& Zotero.Fulltext.pdfConverterVersion == '3.04'));
var infoVersionAvailable = (!infoIsRegistered ||
Zotero.Fulltext.pdfInfoVersion == 'UNKNOWN'
|| latestVersion > Zotero.Fulltext.pdfInfoVersion
|| (!latestVersion.startsWith('3.02')
&& Zotero.Fulltext.pdfInfoVersion.startsWith('3.02'))
|| (!latestVersion.startsWith('3.02') && latestVersion != '3.04'
&& Zotero.Fulltext.pdfInfoVersion == '3.04'));
var bothAvailable = converterVersionAvailable && infoVersionAvailable;
}
// Up to date -- disable update button
if (!converterVersionAvailable && !infoVersionAvailable) {
var button = document.getElementById('pdftools-update-button');
button.setAttribute('label', Zotero.getString('zotero.preferences.update.upToDate'));
button.setAttribute('disabled', true);
return;
}
// New version available -- display update prompt
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
createInstance(Components.interfaces.nsIPromptService);
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
var msg = Zotero.getString('zotero.preferences.search.pdf.available'
+ ((converterIsRegistered || infoIsRegistered) ? 'Updates' : 'Downloads'),
[Zotero.platform, 'zotero.org']) + '\n\n';
if (converterVersionAvailable) {
let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
[Zotero.Fulltext.pdfConverterName, latestVersion]);
msg += '- ' + tvp + '\n';
}
if (infoVersionAvailable) {
let tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
[Zotero.Fulltext.pdfInfoName, latestVersion]);
msg += '- ' + tvp + '\n';
}
msg += '\n';
msg += Zotero.getString('zotero.preferences.search.pdf.zoteroCanInstallVersion'
+ (bothAvailable ? 's' : ''));
var index = ps.confirmEx(null,
converterIsRegistered ?
Zotero.getString('general.updateAvailable') : '',
msg,
buttonFlags,
converterIsRegistered ?
Zotero.getString('general.upgrade') :
Zotero.getString('general.install'),
null, null, null, {});
if (index != 0) {
return;
}
document.getElementById('pdftools-update-button').disabled = true;
var str = Zotero.getString('zotero.preferences.search.pdf.downloading');
document.getElementById('pdftools-update-button').setAttribute('label', str);
if (converterVersionAvailable) {
yield Zotero.Fulltext.downloadPDFTool('converter', latestVersion)
.catch(function (e) {
Zotero.logError(e);
throw new Error("Error downloading pdftotext");
});
}
if (infoVersionAvailable) {
yield Zotero.Fulltext.downloadPDFTool('info', latestVersion)
.catch(function (e) {
Zotero.logError(e);
throw new Error("Error downloading pdfinfo");
});
}
this.updatePDFToolsStatus();
}
catch (e) {
this.onPDFToolsDownloadError(e);
}
}),
onPDFToolsDownloadError: function (e) {
if (e == 404) {
var str = Zotero.getString('zotero.preferences.search.pdf.toolDownloadsNotAvailable',
Zotero.Fulltext.pdfToolsName) + ' '
+ Zotero.getString('zotero.preferences.search.pdf.viewManualInstructions');
}
else {
Components.utils.reportError(e);
var str = Zotero.getString('zotero.preferences.search.pdf.toolsDownloadError', Zotero.Fulltext.pdfToolsName)
+ ' ' + Zotero.getString('zotero.preferences.search.pdf.tryAgainOrViewManualInstructions');
}
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.createInstance(Components.interfaces.nsIPromptService);
ps.alert(
null,
Zotero.getString('pane.item.attachments.PDF.installTools.title'),
str
);
},
updateIndexStats: Zotero.Promise.coroutine(function* () { updateIndexStats: Zotero.Promise.coroutine(function* () {
var stats = yield Zotero.Fulltext.getIndexStats(); var stats = yield Zotero.Fulltext.getIndexStats();
document.getElementById('fulltext-stats-indexed'). document.getElementById('fulltext-stats-indexed').

View File

@ -49,10 +49,34 @@
<textbox size="10" preference="pref-fulltext-textMaxLength"/> <textbox size="10" preference="pref-fulltext-textMaxLength"/>
<label value="(&zotero.preferences.default; 500000)"/> <label value="(&zotero.preferences.default; 500000)"/>
</hbox> </hbox>
</groupbox>
<groupbox id="pdftools-box">
<caption label="&zotero.preferences.search.pdfIndexing;"/>
<label id="pdfconverter-status"/>
<separator class="thin"/>
<label id="pdfinfo-status"/>
<separator class="thin"/> <separator class="thin"/>
<hbox align="center"> <!-- This doesn't wrap without an explicit width -->
<label id="pdftools-required" width="45em" hidden="true"/>
<separator class="thin"/>
<hbox>
<button id="pdftools-update-button" flex="1" oncommand="Zotero_Preferences.Search.checkPDFToolsDownloadVersion()"/>
</hbox>
<separator class="thin"/>
<!-- This doesn't wrap without an explicit width -->
<label id="pdftools-documentation-link" width="45em" hidden="true"/>
<separator class="thin"/>
<hbox id="pdftools-settings" align="center" hidden="true">
<label value="&zotero.preferences.fulltext.pdfMaxPages;"/> <label value="&zotero.preferences.fulltext.pdfMaxPages;"/>
<textbox size="5" preference="pref-fulltext-pdfmaxpages"/> <textbox size="5" preference="pref-fulltext-pdfmaxpages"/>
<label value="(&zotero.preferences.default; 100)"/> <label value="(&zotero.preferences.default; 100)"/>

View File

@ -26,12 +26,10 @@
"use strict"; "use strict";
Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/osfile.jsm"); Components.utils.import("resource://gre/modules/osfile.jsm");
Components.utils.import("resource://zotero/config.js");
Zotero_Preferences.Sync = { Zotero_Preferences.Sync = {
init: Zotero.Promise.coroutine(function* () { init: Zotero.Promise.coroutine(function* () {
this.updateStorageSettingsUI(); this.updateStorageSettingsUI();
this.updateStorageSettingsGroupsUI();
var username = Zotero.Users.getCurrentUsername() || Zotero.Prefs.get('sync.server.username') || " "; var username = Zotero.Users.getCurrentUsername() || Zotero.Prefs.get('sync.server.username') || " ";
var apiKey = yield Zotero.Sync.Data.Local.getAPIKey(); var apiKey = yield Zotero.Sync.Data.Local.getAPIKey();
@ -49,6 +47,7 @@ Zotero_Preferences.Sync = {
{timeout: 5000} {timeout: 5000}
); );
this.displayFields(keyInfo.username); this.displayFields(keyInfo.username);
Zotero.Users.setCurrentUsername(keyInfo.username);
} }
catch (e) { catch (e) {
// API key wrong/invalid // API key wrong/invalid
@ -65,8 +64,6 @@ Zotero_Preferences.Sync = {
} }
} }
} }
this.initResetPane();
}), }),
displayFields: function (username) { displayFields: function (username) {
@ -85,7 +82,7 @@ Zotero_Preferences.Sync = {
}, },
credentialsChange: function (event) { credentialsKeyPress: function (event) {
var username = document.getElementById('sync-username-textbox'); var username = document.getElementById('sync-username-textbox');
var password = document.getElementById('sync-password'); var password = document.getElementById('sync-password');
@ -99,12 +96,9 @@ Zotero_Preferences.Sync = {
syncAuthButton.setAttribute('disabled', 'false'); syncAuthButton.setAttribute('disabled', 'false');
} }
}); });
},
credentialsKeyPress: function (event) {
if (event.keyCode == 13) { if (event.keyCode == 13) {
this.linkAccount(event); Zotero_Preferences.Sync.linkAccount(event);
event.preventDefault(); event.preventDefault();
} }
}, },
@ -229,8 +223,7 @@ Zotero_Preferences.Sync = {
var row = {}, col = {}, child = {}; var row = {}, col = {}, child = {};
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child); tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child);
// Below the list or on checkmark column if (col.value.element.id == 'libraries-to-sync-checked') {
if (!col.value || col.value.element.id == 'libraries-to-sync-checked') {
return; return;
} }
// if dblclicked anywhere but the checkbox update pref // if dblclicked anywhere but the checkbox update pref
@ -243,8 +236,7 @@ Zotero_Preferences.Sync = {
var row = {}, col = {}, child = {}; var row = {}, col = {}, child = {};
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child); tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child);
// Below the list or not on checkmark column if (col.value.element.id != 'libraries-to-sync-checked') {
if (!col.value || col.value.element.id != 'libraries-to-sync-checked') {
return; return;
} }
// if clicked on checkbox update pref // if clicked on checkbox update pref
@ -369,19 +361,30 @@ Zotero_Preferences.Sync = {
sep.hidden = true; sep.hidden = true;
} }
document.getElementById('storage-user-download-mode').disabled = !enabled; var menulists = document.querySelectorAll('#storage-settings menulist.storage-mode');
for (let menulist of menulists) {
menulist.disabled = !enabled;
}
this.updateStorageTerms(); this.updateStorageTerms();
window.sizeToContent(); window.sizeToContent();
}), }),
updateStorageSettingsGroupsUI: function () { updateStorageSettingsGroups: function (enabled) {
setTimeout(() => { var storageSettings = document.getElementById('storage-settings');
var enabled = document.getElementById('pref-storage-groups-enabled').value; var menulists = storageSettings.getElementsByTagName('menulist');
document.getElementById('storage-groups-download-mode').disabled = !enabled; for (let menulist of menulists) {
this.updateStorageTerms(); if (menulist.className == 'storage-groups') {
}); menulist.disabled = !enabled;
}
}
var self = this;
setTimeout(function () {
self.updateStorageTerms();
}, 1)
}, },
@ -390,7 +393,7 @@ Zotero_Preferences.Sync = {
var libraryEnabled = document.getElementById('pref-storage-enabled').value; var libraryEnabled = document.getElementById('pref-storage-enabled').value;
var storageProtocol = document.getElementById('pref-storage-protocol').value; var storageProtocol = document.getElementById('pref-storage-protocol').value;
var groupsEnabled = document.getElementById('pref-storage-groups-enabled').value; var groupsEnabled = document.getElementById('pref-group-storage-enabled').value;
terms.hidden = !((libraryEnabled && storageProtocol == 'zotero') || groupsEnabled); terms.hidden = !((libraryEnabled && storageProtocol == 'zotero') || groupsEnabled);
}, },
@ -398,6 +401,7 @@ Zotero_Preferences.Sync = {
onStorageSettingsKeyPress: Zotero.Promise.coroutine(function* (event) { onStorageSettingsKeyPress: Zotero.Promise.coroutine(function* (event) {
if (event.keyCode == 13) { if (event.keyCode == 13) {
yield this.onStorageSettingsChange();
yield this.verifyStorageServer(); yield this.verifyStorageServer();
} }
}), }),
@ -417,7 +421,7 @@ Zotero_Preferences.Sync = {
var newEnabled = document.getElementById('pref-storage-enabled').value; var newEnabled = document.getElementById('pref-storage-enabled').value;
if (oldProtocol != newProtocol) { if (oldProtocol != newProtocol) {
yield Zotero.Sync.Storage.Local.resetAllSyncStates(Zotero.Libraries.userLibraryID); yield Zotero.Sync.Storage.Local.resetAllSyncStates();
} }
if (oldProtocol == 'webdav') { if (oldProtocol == 'webdav') {
@ -479,11 +483,6 @@ Zotero_Preferences.Sync = {
verifyStorageServer: Zotero.Promise.coroutine(function* () { verifyStorageServer: Zotero.Promise.coroutine(function* () {
// onchange weirdly isn't triggered when clicking straight from a field to the button,
// so we have to trigger this here (and we don't trigger it for Enter in
// onStorageSettingsKeyPress()).
yield this.onStorageSettingsChange();
Zotero.debug("Verifying storage"); Zotero.debug("Verifying storage");
var verifyButton = document.getElementById("storage-verify"); var verifyButton = document.getElementById("storage-verify");
@ -567,87 +566,38 @@ Zotero_Preferences.Sync = {
}, },
// handleSyncResetSelect: function (obj) {
// Reset pane var index = obj.selectedIndex;
// var rows = obj.getElementsByTagName('row');
initResetPane: function () {
//
// Build library selector
//
var libraryMenu = document.getElementById('sync-reset-library-menu');
// Some options need to be disabled when certain libraries are selected
libraryMenu.onchange = (event) => {
this.onResetLibraryChange(parseInt(event.target.value));
}
this.onResetLibraryChange(Zotero.Libraries.userLibraryID);
var libraries = Zotero.Libraries.getAll()
.filter(x => x.libraryType == 'user' || x.libraryType == 'group');
Zotero.Utilities.Internal.buildLibraryMenuHTML(libraryMenu, libraries);
// Disable read-only libraries, at least until there are options that make sense for those
Array.from(libraryMenu.querySelectorAll('option'))
.filter(x => x.getAttribute('data-editable') == 'false')
.forEach(x => x.disabled = true);
var list = document.getElementById('sync-reset-list'); for (var i=0; i<rows.length; i++) {
for (let li of document.querySelectorAll('#sync-reset-list li')) { if (i == index) {
li.addEventListener('click', function (event) { rows[i].setAttribute('selected', 'true');
// Ignore clicks if disabled
if (this.hasAttribute('disabled')) {
event.stopPropagation();
return;
}
document.getElementById('sync-reset-button').disabled = false;
});
}
},
onResetLibraryChange: function (libraryID) {
var library = Zotero.Libraries.get(libraryID);
var section = document.getElementById('reset-file-sync-history');
var input = section.querySelector('input');
if (library.filesEditable) {
section.removeAttribute('disabled');
input.disabled = false;
} }
else { else {
section.setAttribute('disabled', ''); rows[i].removeAttribute('selected');
// If radio we're disabling is already selected, select the first one in the list
// instead
if (input.checked) {
document.querySelector('#sync-reset-list li:first-child input').checked = true;
} }
input.disabled = true;
} }
}, },
reset: async function () { handleSyncReset: Zotero.Promise.coroutine(function* (action) {
var ps = Services.prompt; var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
if (Zotero.Sync.Runner.syncInProgress) { if (!Zotero.Sync.Runner.enabled) {
Zotero.alert( ps.alert(
null, null,
Zotero.getString('general.error'), Zotero.getString('general.error'),
Zotero.getString('sync.error.syncInProgress') Zotero.getString('zotero.preferences.sync.reset.userInfoMissing',
+ "\n\n" document.getElementById('zotero-prefpane-sync')
+ Zotero.getString('general.operationInProgress.waitUntilFinishedAndTryAgain') .getElementsByTagName('tab')[0].label)
); );
return; return;
} }
var libraryID = parseInt(
Array.from(document.querySelectorAll('#sync-reset-library-menu option'))
.filter(x => x.selected)[0]
.value
);
var library = Zotero.Libraries.get(libraryID);
var action = Array.from(document.querySelectorAll('#sync-reset-list input[name=sync-reset-radiogroup]'))
.filter(x => x.checked)[0]
.getAttribute('value');
switch (action) { switch (action) {
/*case 'full-sync': case 'full-sync':
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL) + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL)
+ ps.BUTTON_POS_1_DEFAULT; + ps.BUTTON_POS_1_DEFAULT;
@ -668,7 +618,7 @@ Zotero_Preferences.Sync = {
switch (index) { switch (index) {
case 0: case 0:
let libraries = Zotero.Libraries.getAll().filter(library => library.syncable); let libraries = Zotero.Libraries.getAll().filter(library => library.syncable);
await Zotero.DB.executeTransaction(function* () { yield Zotero.DB.executeTransaction(function* () {
for (let library of libraries) { for (let library of libraries) {
library.libraryVersion = -1; library.libraryVersion = -1;
yield library.save(); yield library.save();
@ -701,13 +651,13 @@ Zotero_Preferences.Sync = {
// TODO: better error handling // TODO: better error handling
// Verify username and password // Verify username and password
var callback = async function () { var callback = Zotero.Promise.coroutine(function* () {
Zotero.Schema.stopRepositoryTimer(); Zotero.Schema.stopRepositoryTimer();
Zotero.Sync.Runner.clearSyncTimeout(); Zotero.Sync.Runner.clearSyncTimeout();
Zotero.DB.skipBackup = true; Zotero.DB.skipBackup = true;
await Zotero.File.putContentsAsync( yield Zotero.File.putContentsAsync(
OS.Path.join(Zotero.DataDirectory.dir, 'restore-from-server'), OS.Path.join(Zotero.DataDirectory.dir, 'restore-from-server'),
'' ''
); );
@ -725,7 +675,7 @@ Zotero_Preferences.Sync = {
var appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"] var appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]
.getService(Components.interfaces.nsIAppStartup); .getService(Components.interfaces.nsIAppStartup);
appStartup.quit(Components.interfaces.nsIAppStartup.eRestart | Components.interfaces.nsIAppStartup.eAttemptQuit); appStartup.quit(Components.interfaces.nsIAppStartup.eRestart | Components.interfaces.nsIAppStartup.eAttemptQuit);
}; });
// TODO: better way of checking for an active session? // TODO: better way of checking for an active session?
if (Zotero.Sync.Server.sessionIDComponent == 'sessionid=') { if (Zotero.Sync.Server.sessionIDComponent == 'sessionid=') {
@ -742,7 +692,7 @@ Zotero_Preferences.Sync = {
case 1: case 1:
return; return;
} }
break;*/ break;
case 'restore-to-server': case 'restore-to-server':
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING) var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
@ -751,28 +701,43 @@ Zotero_Preferences.Sync = {
var index = ps.confirmEx( var index = ps.confirmEx(
null, null,
Zotero.getString('general.warning'), Zotero.getString('general.warning'),
Zotero.getString( Zotero.getString('zotero.preferences.sync.reset.restoreToServer', account),
'zotero.preferences.sync.reset.restoreToServer',
[Zotero.clientName, library.name, ZOTERO_CONFIG.DOMAIN_NAME]
),
buttonFlags, buttonFlags,
Zotero.getString('zotero.preferences.sync.reset.restoreToServer.button'), Zotero.getString('zotero.preferences.sync.reset.replaceServerData'),
null, null, null, {} null, null, null, {}
); );
switch (index) { switch (index) {
case 0: case 0:
var resetButton = document.getElementById('sync-reset-button'); // TODO: better error handling
resetButton.disabled = true; Zotero.Sync.Server.clear(function () {
try { Zotero.Sync.Server.sync(/*{
await Zotero.Sync.Runner.sync({
libraries: [libraryID], // TODO: this doesn't work if the pref window is
resetMode: Zotero.Sync.Runner.RESET_MODE_TO_SERVER closed. fix, perhaps by making original callbacks
available to the custom callbacks
onSuccess: function () {
Zotero.Sync.Runner.updateIcons();
ps.alert(
null,
"Restore Completed",
"Data on the Zotero server has been successfully restored."
);
},
onError: function (msg) {
// TODO: combine with error dialog for regular syncs
ps.alert(
null,
"Restore Failed",
"An error occurred uploading your data to the server.\n\n"
+ "Click the sync error icon in the Zotero toolbar "
+ "for further information."
);
Zotero.Sync.Runner.error(msg);
}
}*/);
}); });
}
finally {
resetButton.disabled = false;
}
break; break;
// Cancel // Cancel
@ -783,17 +748,14 @@ Zotero_Preferences.Sync = {
break; break;
case 'reset-file-sync-history': case 'reset-storage-history':
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL)
+ ps.BUTTON_POS_1_DEFAULT; + ps.BUTTON_POS_1_DEFAULT;
var index = ps.confirmEx( var index = ps.confirmEx(
null, null,
Zotero.getString('general.warning'), Zotero.getString('general.warning'),
Zotero.getString( Zotero.getString('zotero.preferences.sync.reset.fileSyncHistory', Zotero.clientName),
'zotero.preferences.sync.reset.fileSyncHistory',
[Zotero.clientName, library.name]
),
buttonFlags, buttonFlags,
Zotero.getString('general.reset'), Zotero.getString('general.reset'),
null, null, null, {} null, null, null, {}
@ -801,14 +763,11 @@ Zotero_Preferences.Sync = {
switch (index) { switch (index) {
case 0: case 0:
await Zotero.Sync.Storage.Local.resetAllSyncStates(libraryID); yield Zotero.Sync.Storage.Local.resetAllSyncStates();
ps.alert( ps.alert(
null, null,
Zotero.getString('general.success'), "File Sync History Cleared",
Zotero.getString( "The file sync history has been cleared."
'zotero.preferences.sync.reset.fileSyncHistory.cleared',
library.name
)
); );
break; break;
@ -820,7 +779,7 @@ Zotero_Preferences.Sync = {
break; break;
default: default:
throw new Error(`Invalid action '${action}' in handleSyncReset()`); throw ("Invalid action '" + action + "' in handleSyncReset()");
}
} }
})
}; };

View File

@ -40,7 +40,7 @@
<preference id="pref-storage-username" name="extensions.zotero.sync.storage.username" type="string"/> <preference id="pref-storage-username" name="extensions.zotero.sync.storage.username" type="string"/>
<preference id="pref-storage-downloadMode-personal" name="extensions.zotero.sync.storage.downloadMode.personal" type="string"/> <preference id="pref-storage-downloadMode-personal" name="extensions.zotero.sync.storage.downloadMode.personal" type="string"/>
<preference id="pref-storage-downloadMode-groups" name="extensions.zotero.sync.storage.downloadMode.groups" type="string"/> <preference id="pref-storage-downloadMode-groups" name="extensions.zotero.sync.storage.downloadMode.groups" type="string"/>
<preference id="pref-storage-groups-enabled" name="extensions.zotero.sync.storage.groups.enabled" type="bool"/> <preference id="pref-group-storage-enabled" name="extensions.zotero.sync.storage.groups.enabled" type="bool"/>
</preferences> </preferences>
<tabbox> <tabbox>
@ -68,16 +68,12 @@
<textbox id="sync-username-textbox" <textbox id="sync-username-textbox"
preference="pref-sync-username" preference="pref-sync-username"
onblur="Zotero_Preferences.Sync.trimUsername()" onblur="Zotero_Preferences.Sync.trimUsername()"
oninput="Zotero_Preferences.Sync.credentialsChange(event)" onkeypress="Zotero_Preferences.Sync.credentialsKeyPress(event);"/>
onchange="Zotero_Preferences.Sync.credentialsChange(event)"
onkeypress="Zotero_Preferences.Sync.credentialsKeyPress(event)"/>
</row> </row>
<row> <row>
<label value="&zotero.preferences.sync.password;"/> <label value="&zotero.preferences.sync.password;"/>
<textbox id="sync-password" type="password" <textbox id="sync-password" type="password"
oninput="Zotero_Preferences.Sync.credentialsChange(event)" onkeypress="Zotero_Preferences.Sync.credentialsKeyPress(event);"/>
onchange="Zotero_Preferences.Sync.credentialsChange(event)"
onkeypress="Zotero_Preferences.Sync.credentialsKeyPress(event)"/>
</row> </row>
<row> <row>
<box/> <box/>
@ -219,7 +215,7 @@
<row> <row>
<box/> <box/>
<hbox> <hbox>
<button id="storage-verify" label="&zotero.preferences.sync.fileSyncing.verifyServer;" <button id="storage-verify" label="Verify Server"
oncommand="Zotero_Preferences.Sync.verifyStorageServer()"/> oncommand="Zotero_Preferences.Sync.verifyStorageServer()"/>
<button id="storage-abort" label="Stop" hidden="true"/> <button id="storage-abort" label="Stop" hidden="true"/>
<progressmeter id="storage-progress" hidden="true" <progressmeter id="storage-progress" hidden="true"
@ -233,10 +229,7 @@
<hbox class="storage-settings-download-options" align="center"> <hbox class="storage-settings-download-options" align="center">
<label value="&zotero.preferences.sync.fileSyncing.download;"/> <label value="&zotero.preferences.sync.fileSyncing.download;"/>
<menulist id="storage-user-download-mode" <menulist class="storage-mode" preference="pref-storage-downloadMode-personal" style="margin-left: 0">
class="storage-mode"
preference="pref-storage-downloadMode-personal"
style="margin-left: 0">
<menupopup> <menupopup>
<menuitem label="&zotero.preferences.sync.fileSyncing.download.onDemand;" value="on-demand"/> <menuitem label="&zotero.preferences.sync.fileSyncing.download.onDemand;" value="on-demand"/>
<menuitem label="&zotero.preferences.sync.fileSyncing.download.atSyncTime;" value="on-sync"/> <menuitem label="&zotero.preferences.sync.fileSyncing.download.atSyncTime;" value="on-sync"/>
@ -248,15 +241,12 @@
<!-- Group Libraries --> <!-- Group Libraries -->
<checkbox label="&zotero.preferences.sync.fileSyncing.groups;" <checkbox label="&zotero.preferences.sync.fileSyncing.groups;"
preference="pref-storage-groups-enabled" preference="pref-group-storage-enabled"
oncommand="Zotero_Preferences.Sync.updateStorageSettingsGroupsUI()"/> oncommand="Zotero_Preferences.Sync.updateStorageSettingsGroups(this.checked)"/>
<hbox class="storage-settings-download-options" align="center"> <hbox class="storage-settings-download-options" align="center">
<label value="&zotero.preferences.sync.fileSyncing.download;"/> <label value="&zotero.preferences.sync.fileSyncing.download;"/>
<menulist id="storage-groups-download-mode" <menulist class="storage-mode" preference="pref-storage-downloadMode-groups" style="margin-left: 0">
class="storage-mode"
preference="pref-storage-downloadMode-groups"
style="margin-left: 0">
<menupopup> <menupopup>
<menuitem label="&zotero.preferences.sync.fileSyncing.download.onDemand;" value="on-demand"/> <menuitem label="&zotero.preferences.sync.fileSyncing.download.onDemand;" value="on-demand"/>
<menuitem label="&zotero.preferences.sync.fileSyncing.download.atSyncTime;" value="on-sync"/> <menuitem label="&zotero.preferences.sync.fileSyncing.download.atSyncTime;" value="on-sync"/>
@ -266,8 +256,8 @@
<separator class="thin"/> <separator class="thin"/>
<vbox id="storage-terms"> <vbox>
<hbox style="margin-top: .4em; display: block" align="center"> <hbox id="storage-terms" style="margin-top: .4em; display: block" align="center">
<label>&zotero.preferences.sync.fileSyncing.tos1;</label> <label>&zotero.preferences.sync.fileSyncing.tos1;</label>
<label class="zotero-text-link" href="https://www.zotero.org/support/terms/terms_of_service" value="&zotero.preferences.sync.fileSyncing.tos2;"/> <label class="zotero-text-link" href="https://www.zotero.org/support/terms/terms_of_service" value="&zotero.preferences.sync.fileSyncing.tos2;"/>
<label>&zotero.preferences.period;</label> <label>&zotero.preferences.period;</label>
@ -277,47 +267,82 @@
</vbox> </vbox>
</tabpanel> </tabpanel>
<tabpanel id="sync-reset" orient="vertical"> <tabpanel id="zotero-reset" orient="vertical">
<!-- This doesn't wrap without an explicit width, for some reason --> <!-- This doesn't wrap without an explicit width, for some reason -->
<description id="reset-sync-warning" width="45em">&zotero.preferences.sync.reset.warning1;<label style="margin-left: 0; margin-right: 0" class="zotero-text-link" href="http://zotero.org/support/kb/sync_reset_options">&zotero.preferences.sync.reset.warning2;</label>&zotero.preferences.sync.reset.warning3;</description> <description width="45em">&zotero.preferences.sync.reset.warning1;<label style="margin-left: 0; margin-right: 0" class="zotero-text-link" href="http://zotero.org/support/kb/sync_reset_options">&zotero.preferences.sync.reset.warning2;</label>&zotero.preferences.sync.reset.warning3;</description>
<div id="sync-reset-form" xmlns="http://www.w3.org/1999/xhtml"> <groupbox>
<div id="sync-reset-library-menu-container"> <caption label="&zotero.preferences.sync.syncServer;"/>
<label>Library: <select id="sync-reset-library-menu"/></label>
</div>
<ul id="sync-reset-list"> <radiogroup oncommand="Zotero_Preferences.Sync.handleSyncResetSelect(this)">
<!--<li> <grid>
<p>&zotero.preferences.sync.reset.restoreFromServer;</p> <columns>
<p>&zotero.preferences.sync.reset.restoreFromServer.desc;</p> <column/>
</li>--> <column align="start" pack="start" flex="1"/>
</columns>
<li id="restore-to-server"> <rows id="sync-reset-rows">
<label> <!--
<input name="sync-reset-radiogroup" value="restore-to-server" type="radio"/> <row id="zotero-restore-from-server" selected="true">
<span class="sync-reset-option-name">&zotero.preferences.sync.reset.restoreToServer;</span> <radio/>
<span class="sync-reset-option-desc">&zotero.preferences.sync.reset.restoreToServer.desc;</span> <vbox onclick="this.previousSibling.click()">
</label> <label value="&zotero.preferences.sync.reset.restoreFromServer;"/>
</li> <description>&zotero.preferences.sync.reset.restoreFromServer.desc;</description>
</vbox>
</row>
<!--<li> <row id="zotero-restore-to-server">
<h2>&zotero.preferences.sync.reset.resetDataSyncHistory;</h2> <radio/>
<description>&zotero.preferences.sync.reset.resetDataSyncHistory.desc;</p> <vbox onclick="this.previousSibling.click()">
</li>--> <label value="&zotero.preferences.sync.reset.restoreToServer;"/>
<description>&zotero.preferences.sync.reset.restoreToServer.desc;</description>
</vbox>
</row>
-->
<row id="zotero-reset-data-sync-history">
<radio hidden="true"/>
<vbox onclick="this.previousSibling.click()">
<label value="&zotero.preferences.sync.reset.resetDataSyncHistory;"/>
<description>&zotero.preferences.sync.reset.resetDataSyncHistory.desc;</description>
</vbox>
</row>
</rows>
</grid>
</radiogroup>
<li id="reset-file-sync-history"> <hbox>
<label> <button label="&zotero.preferences.sync.reset.button;"
<input name="sync-reset-radiogroup" value="reset-file-sync-history" type="radio"/> oncommand="document.getElementById('sync-reset-rows').firstChild.firstChild.click(); Zotero_Preferences.Sync.handleSyncReset('full-sync')"/>
<span class="sync-reset-option-name">&zotero.preferences.sync.reset.resetFileSyncHistory;</span> </hbox>
<span class="sync-reset-option-desc">&zotero.preferences.sync.reset.resetFileSyncHistory.desc;</span> </groupbox>
</label>
</li>
</ul>
<button id="sync-reset-button" <groupbox>
disabled="disabled" <caption label="&zotero.preferences.sync.fileSyncing;"/>
onclick="Zotero_Preferences.Sync.reset()">&zotero.preferences.sync.reset.button;</button>
</div> <radiogroup oncommand="Zotero_Preferences.Sync.handleSyncResetSelect(this)">
<grid>
<columns>
<column/>
<column align="start" pack="start" flex="1"/>
</columns>
<rows id="storage-reset-rows">
<row id="zotero-reset-storage-history">
<radio hidden="true"/>
<vbox onclick="this.previousSibling.click()">
<label value="&zotero.preferences.sync.reset.resetFileSyncHistory;"/>
<description>&zotero.preferences.sync.reset.resetFileSyncHistory.desc;</description>
</vbox>
</row>
</rows>
</grid>
</radiogroup>
<hbox>
<button label="&zotero.preferences.sync.reset.button;"
oncommand="document.getElementById('storage-reset-rows').firstChild.firstChild.click(); Zotero_Preferences.Sync.handleSyncReset('reset-storage-history')"/>
</hbox>
</groupbox>
</tabpanel> </tabpanel>
</tabpanels> </tabpanels>
</tabbox> </tabbox>

View File

@ -1,14 +0,0 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
<window id="zotero-progress-meter-window"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
windowtype="alert:alert">
<vbox id="zotero-progress-text-box" flex="1">
<label/>
<progressmeter/>
</vbox>
</window>

View File

@ -0,0 +1,943 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2009 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
/**
* @fileOverview Tools for automatically retrieving a citation for the given PDF
*/
/**
* Front end for recognizing PDFs
* @namespace
*/
var Zotero_RecognizePDF = new function() {
var _progressWindow, _progressIndicator;
/**
* Checks whether a given PDF could theoretically be recognized
* @returns {Boolean} True if the PDF can be recognized, false if it cannot be
*/
this.canRecognize = function(/**Zotero.Item*/ item) {
return item.attachmentMIMEType
&& item.attachmentMIMEType == "application/pdf"
&& item.isTopLevelItem();
}
/**
* Retrieves metadata for the PDF(s) selected in the Zotero Pane, placing the PDFs as a children
* of the new items
*/
this.recognizeSelected = function() {
var installed = ZoteroPane_Local.checkPDFConverter();
if (!installed) {
return;
}
var items = ZoteroPane_Local.getSelectedItems();
if (!items) return;
var itemRecognizer = new Zotero_RecognizePDF.ItemRecognizer();
itemRecognizer.recognizeItems(items);
}
/**
* Retrieves metadata for a PDF and saves it as an item
*
* @param {nsIFile} file The PDF file to retrieve metadata for
* @param {Integer} libraryID The library in which to save the PDF
* @param {Function} stopCheckCallback Function that returns true if the
* process is to be interrupted
* @return {Promise} A promise resolved when PDF metadata has been retrieved
*/
this.recognize = Zotero.Promise.coroutine(function* (file, libraryID, stopCheckCallback) {
const MAX_PAGES = 15;
var me = this;
var lines = yield _extractText(file, MAX_PAGES);
// Look for DOI - Use only first 80 lines to avoid catching article references
var allText = lines.join("\n"),
firstChunk = lines.slice(0,80).join('\n'),
doi = Zotero.Utilities.cleanDOI(firstChunk),
promise;
Zotero.debug(allText);
if(!doi) {
// Look for a JSTOR stable URL, which can be converted to a DOI by prepending 10.2307
doi = firstChunk.match(/www.\jstor\.org\/stable\/(\S+)/i);
if(doi) {
doi = Zotero.Utilities.cleanDOI(
doi[1].indexOf('10.') == 0 ? doi[1] : '10.2307/' + doi[1]
);
}
}
var newItem;
if (doi) {
// Look up DOI
Zotero.debug("RecognizePDF: Found DOI: "+doi);
var translateDOI = new Zotero.Translate.Search();
translateDOI.setTranslator("11645bd1-0420-45c1-badb-53fb41eeb753");
translateDOI.setSearch({"itemType":"journalArticle", "DOI":doi});
try {
newItem = yield _promiseTranslate(translateDOI, libraryID);
return newItem;
}
catch (e) {
Zotero.debug("RecognizePDF: " + e);
}
}
else {
Zotero.debug("RecognizePDF: No DOI found in text");
}
// Look for ISBNs if no DOI
var isbns = _findISBNs(allText);
if (isbns.length) {
Zotero.debug("RecognizePDF: Found ISBNs: " + isbns);
var translate = new Zotero.Translate.Search();
translate.setSearch({"itemType":"book", "ISBN":isbns[0]});
try {
newItem = yield _promiseTranslate(translate, libraryID);
return newItem;
}
catch (e) {
// If no DOI or ISBN, query Google Scholar
Zotero.debug("RecognizePDF: " + e);
}
}
else {
Zotero.debug("RecognizePDF: No ISBN found in text");
}
return this.GSFullTextSearch.findItem(lines, libraryID, stopCheckCallback);
});
/**
* Get text from a PDF
* @param {nsIFile} file PDF
* @param {Number} pages Number of pages to extract
* @return {Promise}
*/
function _extractText(file, pages) {
var cacheFile = Zotero.File.pathToFile(Zotero.DataDirectory.dir);
cacheFile.append("recognizePDFcache.txt");
if(cacheFile.exists()) {
cacheFile.remove(false);
}
var {exec, args} = Zotero.Fulltext.getPDFConverterExecAndArgs();
args.push('-enc', 'UTF-8', '-nopgbrk', '-layout', '-l', pages, file.path, cacheFile.path);
Zotero.debug("RecognizePDF: Running " + exec.path + " " + args.map(arg => "'" + arg + "'").join(" "));
return Zotero.Utilities.Internal.exec(exec, args).then(function() {
if(!cacheFile.exists()) {
throw new Zotero.Exception.Alert("recognizePDF.couldNotRead");
}
try {
var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
inputStream.init(cacheFile, 0x01, 0o664, 0);
try {
var intlStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
.createInstance(Components.interfaces.nsIConverterInputStream);
intlStream.init(inputStream, "UTF-8", 65535,
Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
intlStream.QueryInterface(Components.interfaces.nsIUnicharLineInputStream);
// get the lines in this sample
var lines = [], str = {};
while(intlStream.readLine(str)) {
var line = str.value.trim();
if(line) lines.push(line);
}
} finally {
inputStream.close();
}
} finally {
cacheFile.remove(false);
}
return lines;
}, function() {
throw new Zotero.Exception.Alert("recognizePDF.couldNotRead");
});
}
/**
* Attach appropriate handlers to a Zotero.Translate instance and begin translation
* @return {Promise}
*/
var _promiseTranslate = Zotero.Promise.coroutine(function* (translate, libraryID) {
translate.setHandler("select", function(translate, items, callback) {
for(var i in items) {
var obj = {};
obj[i] = items[i];
callback(obj);
return;
}
});
/*translate.setHandler("done", function(translate, success) {
if(success && translate.newItems.length) {
deferred.resolve(translate.newItems[0]);
} else {
deferred.reject(translate.translator && translate.translator.length
? "Translation with " + translate.translator.map(t => t.label) + " failed"
: "Could not find a translator for given search item"
);
}
});*/
var newItems = yield translate.translate({
libraryID,
saveAttachments: false
});
if (newItems.length) {
return newItems[0];
}
throw new Error("No items found");
});
/**
* Search ISBNs in text
* @private
* @return {String[]} Array of ISBNs
*/
function _findISBNs(x) {
if(typeof(x) != "string") {
throw "findISBNs: argument must be a string";
}
var isbns = [];
// Match lines saying "isbn: " or "ISBN-10:" or similar, consider m-dashes and n-dashes as well
var pattern = /(SBN|sbn)[ \u2014\u2013\u2012-]?(10|13)?[: ]*([0-9X][0-9X \u2014\u2013\u2012-]+)/g;
var match;
while (match = pattern.exec(x)) {
var isbn = match[3];
isbn = isbn.replace(/[ \u2014\u2013\u2012-]/g, '');
if(isbn.length==20 || isbn.length==26) {
// Handle the case of two isbns (e.g. paper+hardback) next to each other
isbns.push(isbn.slice(0,isbn.length/2), isbn.slice(isbn.length/2));
} else if(isbn.length==23) {
// Handle the case of two isbns (10+13) next to each other
isbns.push(isbn.slice(0,10), isbn.slice(10));
} else if(isbn.length==10 || isbn.length==13) {
isbns.push(isbn);
}
}
// Validate ISBNs
var validIsbns = [], cleanISBN;
for (var i =0; i < isbns.length; i++) {
cleanISBN = Zotero.Utilities.cleanISBN(isbns[i]);
if(cleanISBN) validIsbns.push(cleanISBN);
}
return validIsbns;
}
/**
* @class Handles UI, etc. for recognizing multiple items
*/
this.ItemRecognizer = function () {
this._items = [];
}
this.ItemRecognizer.prototype = {
"_stopped": false,
"_itemsTotal": 0,
"_progressWindow": null,
"_progressIndicator": null,
/**
* Retreives metadata for the PDF items passed, displaying a progress dialog during conversion
* and placing the PDFs as a children of the new items
* @param {Zotero.Item[]} items
*/
"recognizeItems": function(items) {
var me = this;
this._items = items.slice();
this._itemTotal = items.length;
_progressWindow = this._progressWindow = window.openDialog("chrome://zotero/content/pdfProgress.xul", "", "chrome,close=yes,resizable=yes,dependent,dialog,centerscreen");
this._progressWindow.addEventListener("pageshow", function() { me._onWindowLoaded() }, false);
},
/**
* Halts recognition of PDFs
*/
"stop": function() {
this._stopped = true;
},
/**
* Halts recognition and closes window
*/
"close": function() {
this.stop();
this._progressWindow.close();
},
/**
* Called when the progress window has been opened; adds items to the tree and begins recognizing
* @param
*/
"_onWindowLoaded": function() {
// populate progress window
var treechildren = this._progressWindow.document.getElementById("treechildren");
this._rowIDs = [];
for(var i in this._items) {
var treeitem = this._progressWindow.document.createElement('treeitem');
var treerow = this._progressWindow.document.createElement('treerow');
this._rowIDs.push(this._items[i].id);
var treecell = this._progressWindow.document.createElement('treecell');
treecell.setAttribute("id", "item-"+this._items[i].id+"-icon");
treerow.appendChild(treecell);
treecell = this._progressWindow.document.createElement('treecell');
treecell.setAttribute("label", this._items[i].getField("title"));
treerow.appendChild(treecell);
treecell = this._progressWindow.document.createElement('treecell');
treecell.setAttribute("id", "item-"+this._items[i].id+"-title");
treerow.appendChild(treecell);
treeitem.appendChild(treerow);
treechildren.appendChild(treeitem);
}
var me = this;
this._progressWindow.document.getElementById("tree").addEventListener(
"dblclick", function(event) { me._onDblClick(event, this); });
this._cancelHandler = function() { me.stop() };
this._keypressCancelHandler = function(e) {
if(e.keyCode === KeyEvent.DOM_VK_ESCAPE) me.stop();
};
_progressIndicator = this._progressIndicator = this._progressWindow.document.getElementById("progress-indicator");
this._progressWindow.document.getElementById("cancel-button")
.addEventListener("command", this._cancelHandler, false);
// Also cancel if the user presses Esc
this._progressWindow.addEventListener("keypress", this._keypressCancelHandler);
this._progressWindow.addEventListener("close", this._cancelHandler, false);
Zotero_RecognizePDF.GSFullTextSearch.resetQueryLimit();
return this._recognizeItem();
},
/**
* Shifts an item off of this._items and recognizes it, then calls itself again if there are more
* @private
*/
"_recognizeItem": Zotero.Promise.coroutine(function* () {
const SUCCESS_IMAGE = "chrome://zotero/skin/tick.png";
const FAILURE_IMAGE = "chrome://zotero/skin/cross.png";
const LOADING_IMAGE = "chrome://global/skin/icons/loading_16.png";
if(!this._items.length) {
this._done();
return;
}
// Order here matters. Otherwise we may show an incorrect label
if(this._stopped) {
this._done(true);
return;
}
this._progressIndicator.value = (this._itemTotal-this._items.length)/this._itemTotal*100;
var item = this._items.shift(),
itemIcon = this._progressWindow.document.getElementById("item-"+item.id+"-icon"),
itemTitle = this._progressWindow.document.getElementById("item-"+item.id+"-title"),
rowNumber = this._rowIDs.indexOf(item.id);
itemIcon.setAttribute("src", LOADING_IMAGE);
itemTitle.setAttribute("label", "");
var file = item.getFile(), me = this;
try {
if (file) {
let newItem = yield Zotero_RecognizePDF.recognize(
file,
item.libraryID,
() => this._stopped
);
// If already stopped, delete
if (this._stopped) {
yield Zotero.Items.erase(newItem.id);
throw new Zotero.Exception.Alert('recognizePDF.stopped');
}
// put new item in same collections as the old one
let itemCollections = item.getCollections();
yield Zotero.DB.executeTransaction(function* () {
for (let i = 0; i < itemCollections.length; i++) {
let collection = Zotero.Collections.get(itemCollections[i]);
yield collection.addItem(newItem.id);
}
// put old item as a child of the new item
item.parentID = newItem.id;
yield item.save();
});
itemTitle.setAttribute("label", newItem.getField("title"));
itemIcon.setAttribute("src", SUCCESS_IMAGE);
this._rowIDs[rowNumber] = newItem.id;
return this._recognizeItem();
}
else {
throw new Zotero.Exception.Alert("recognizePDF.fileNotFound");
}
}
catch (e) {
Zotero.logError(e);
itemTitle.setAttribute(
"label",
e instanceof Zotero.Exception.Alert
? e.message
: Zotero.getString("recognizePDF.error")
);
itemIcon.setAttribute("src", FAILURE_IMAGE);
// Don't show "completed" label if stopped on last item
if (this._stopped && !this._items.length) {
this._done(true);
} else {
return this._recognizeItem();
}
}
finally {
// scroll to this item
this._progressWindow.document.getElementById("tree").treeBoxObject.scrollToRow(
Math.max(0, this._itemTotal - this._items.length - 4)
);
}
}),
/**
* Cleans up after items are recognized, disabling the cancel button and
* making the progress window close on blur.
* @param {Boolean} cancelled Whether the process was cancelled
*/
"_done": function(cancelled) {
this._progressIndicator.value = 100;
// Switch out cancel for close
var cancelButton = this._progressWindow.document.getElementById("cancel-button"),
me = this;
cancelButton.label = Zotero.getString("recognizePDF.close.label");
cancelButton.removeEventListener("command", this._cancelHandler, false);
cancelButton.addEventListener("command", function() { me.close() }, false);
this._progressWindow.removeEventListener("keypress", this._keypressCancelHandler);
this._progressWindow.addEventListener("keypress", function() { me.close() });
if(Zotero.isMac) {
// On MacOS X, the windows are not always on top, so we hide them on
// blur to avoid clutter
this._setCloseTimer();
}
this._progressWindow.document.getElementById("label").value =
cancelled ? Zotero.getString("recognizePDF.cancelled.label")
: Zotero.getString("recognizePDF.complete.label");
},
/**
* Set a timer after which the window will close automatically. If the
* window is refocused, clear the timer and do not attempt to auto-close
* any more
* @private
*/
"_setCloseTimer": function() {
var me = this, win = this._progressWindow;
var focusListener = function() {
if(!win.zoteroCloseTimeoutID) return;
win.clearTimeout(win.zoteroCloseTimeoutID);
delete win.zoteroCloseTimeoutID;
win.removeEventListener('blur', blurListener, false);
win.removeEventListener('focus', focusListener, false);
};
var blurListener = function() {
// Close window after losing focus for 5 seconds
win.zoteroCloseTimeoutID = win.setTimeout(function() { win.close() }, 5000);
// Prevent auto-close if we gain focus again
win.addEventListener("focus", focusListener, false);
};
win.addEventListener("blur", blurListener, false);
},
/**
* Focus items in Zotero library when double-clicking them in the Retrieve
* metadata window.
* @param {Event} event
* @param {tree} tree XUL tree object
* @private
*/
"_onDblClick": function(event, tree) {
if (event && tree && event.type == "dblclick") {
var itemID = this._rowIDs[tree.treeBoxObject.getRowAt(event.clientX, event.clientY)];
if(!itemID) return;
// Get the right window. In tab mode, it's the container window
var lastWin = (window.ZoteroTab ? window.ZoteroTab.containerWindow : window);
if (lastWin.ZoteroOverlay) {
lastWin.ZoteroOverlay.toggleDisplay(true);
}
lastWin.ZoteroPane.selectItem(itemID, false, true);
lastWin.focus();
}
}
};
/**
* Singleton for querying Google Scholar. Ensures that all queries are
* sequential and respect the delay inbetween queries.
* @namespace
*/
this.GSFullTextSearch = new function() {
const GOOGLE_SCHOLAR_QUERY_DELAY = 2000; // In ms
var queryLimitReached = false,
inProgress = false,
queue = [],
stopCheckCallback; // As long as we process one query at a time, this is ok
// Load nsICookieManager2
Components.utils.import("resource://gre/modules/Services.jsm");
var cookieService = Services.cookies;
/**
* Reset "Query Limit Reached" flag, so that we attempt to query Google again
*/
this.resetQueryLimit = function() {
queryLimitReached = false;
};
/**
* Queue up item for Google Scholar query
* @param {String[]} lines Lines of text to use for full-text query
* @param {Integer | null} libraryID Library to save the item to
* @param {Function} stopCheckCallback Function that returns true if the
* process is to be interrupted
* @return {Promise} A promise resolved when PDF metadata has been retrieved
*/
this.findItem = function(lines, libraryID, stopCheckCallback) {
if(!inProgress && queryLimitReached) {
// There's no queue, so we can reject immediately
return Zotero.Promise.reject(new Zotero.Exception.Alert("recognizePDF.limit"));
}
var deferred = Zotero.Promise.defer();
queue.push({
deferred: deferred,
lines: lines,
libraryID: libraryID,
stopCheckCallback: stopCheckCallback
});
_processQueue();
return deferred.promise;
};
/**
* Process Google Scholar queue
* @private
* @param {Boolean} proceed Whether we should pop the next item off the queue
* This should not be true unless being called after processing
* another item
*/
function _processQueue(proceed) {
if(inProgress && !proceed) return; //only one at a time
if(!queue.length) {
inProgress = false;
return;
}
inProgress = true;
if(queryLimitReached) {
// Irreversibly blocked. Reject remaining items in queue
var item;
while(item = queue.shift()) {
item.deferred.reject(new Zotero.Exception.Alert("recognizePDF.limit"));
}
_processQueue(true); // Wrap it up
} else {
var item = queue.shift();
stopCheckCallback = item.stopCheckCallback;
if(stopCheckCallback && stopCheckCallback()) {
item.deferred.reject(new Zotero.Exception.Alert('recognizePDF.stopped'));
_processQueue(true);
return;
}
item.deferred.resolve(
Zotero.Promise.try(function () {
var lines = getGoodLines(item.lines);
return queryGoogle(lines, item.libraryID, 3); // Try querying 3 times
})
.finally(function() { _processQueue(true); })
);
}
}
/**
* Select lines that are good candidates for Google Scholar query
* @private
* @param {String[]} lines
* @return {String[]}
*/
function getGoodLines(lines) {
// Use only first column from multi-column lines
const lineRe = /^[\s_]*([^\s]+(?: [^\s_]+)+)/;
var cleanedLines = [], cleanedLineLengths = [];
for(var i=0; i<lines.length && cleanedLines.length<100; i++) {
var m = lineRe.exec(
lines[i]
// Replace non-breaking spaces
.replace(/\xA0/g, ' ')
);
if(m && m[1].split(' ').length > 3) {
cleanedLines.push(m[1]);
cleanedLineLengths.push(m[1].length);
}
}
// Get (not quite) median length
var lineLengthsLength = cleanedLineLengths.length;
if(lineLengthsLength < 20
|| cleanedLines[0] === "This is a digital copy of a book that was preserved for generations on library shelves before it was carefully scanned by Google as part of a project") {
throw new Zotero.Exception.Alert("recognizePDF.noOCR");
}
var sortedLengths = cleanedLineLengths.sort(),
medianLength = sortedLengths[Math.floor(lineLengthsLength/2)];
// Pick lines within 6 chars of the median (this is completely arbitrary)
var goodLines = [],
uBound = medianLength + 6,
lBound = medianLength - 6;
for (var i=0; i<lineLengthsLength; i++) {
if(cleanedLineLengths[i] > lBound && cleanedLineLengths[i] < uBound) {
// Strip quotation marks so they don't mess up search query quoting
var line = cleanedLines[i].replace('"', '');
goodLines.push(line);
}
}
return goodLines;
}
/**
* Query Google Scholar
* @private
* @param {String[]} goodLines
* @param {Integer | null} libraryID
* @param {Integer} tries Number of queries to attempt before giving up
* @return {Promise} A promise resolved when PDF metadata has been retrieved
*/
var queryGoogle = Zotero.Promise.coroutine(function* (goodLines, libraryID, tries) {
if(tries <= 0) throw new Zotero.Exception.Alert("recognizePDF.noMatches");
// Take the relevant parts of some lines (exclude hyphenated word)
var queryString = "", queryStringWords = 0, nextLine = 0;
while(queryStringWords < 25) {
if(!goodLines.length) throw new Zotero.Exception.Alert("recognizePDF.noMatches");
var words = goodLines.splice(nextLine, 1)[0].split(/\s+/);
// Try to avoid picking adjacent strings so the odds of them appearing in another
// document quoting our document is low. Every 7th line is a magic value
nextLine = (nextLine + 7) % goodLines.length;
// Get rid of first and last words
words.shift();
words.pop();
// Make sure there are no long words (probably OCR mistakes)
var skipLine = false;
for(var i=0; i<words.length; i++) {
if(words[i].length > 20) {
skipLine = true;
break;
}
}
// Add words to query
if(!skipLine && words.length) {
queryStringWords += words.length;
queryString += '"'+words.join(" ")+'" ';
}
}
Zotero.debug("RecognizePDF: Query string " + queryString);
var url = "https://scholar.google.com/scholar?q="+encodeURIComponent(queryString)+"&hl=en&lr=&btnG=Search",
delay = GOOGLE_SCHOLAR_QUERY_DELAY - (Date.now() - Zotero.HTTP.lastGoogleScholarQueryTime);
// Delay
if (delay > 0) {
yield Zotero.Promise.delay(delay);
}
Zotero.HTTP.lastGoogleScholarQueryTime = Date.now();
try {
let xmlhttp = yield Zotero.HTTP.request("GET", url, { "responseType": "document" })
.then(
function (xmlhttp) {
return _checkCaptchaOK(xmlhttp, 3);
},
function (e) {
return _checkCaptchaError(e, 3);
}
);
let doc = xmlhttp.response,
deferred = Zotero.Promise.defer(),
translate = new Zotero.Translate.Web();
translate.setTranslator("57a00950-f0d1-4b41-b6ba-44ff0fc30289");
translate.setDocument(Zotero.HTTP.wrapDocument(doc, url));
translate.setHandler("translators", function(translate, detected) {
if(detected.length) {
deferred.resolve(_promiseTranslate(translate, libraryID));
} else {
deferred.resolve(Zotero.Promise.try(function() {
return queryGoogle(goodLines, libraryID, tries-1);
}));
}
});
translate.getTranslators();
return deferred.promise;
}
catch (e) {
if(e.name == "recognizePDF.limit") {
queryLimitReached = true;
}
throw e;
}
});
/**
* Check for CAPTCHA on a page with HTTP 200 status
* @private
* @param {XMLHttpRequest} xmlhttp
* @param {Integer} tries Number of queries to attempt before giving up
* @return {Promise} A promise resolved when PDF metadata has been retrieved
*/
function _checkCaptchaOK(xmlhttp, tries) {
if(stopCheckCallback && stopCheckCallback()) {
throw new Zotero.Exception.Alert('recognizePDF.stopped');
}
Zotero.debug("RecognizePDF: (" + xmlhttp.status + ") Got page with title " + xmlhttp.response.title);
if(Zotero.Utilities.xpath(xmlhttp.response, "//form[@action='Captcha']").length) {
Zotero.debug("RecognizePDF: Found CAPTCHA on page.");
return _solveCaptcha(xmlhttp, tries);
}
return xmlhttp;
}
/**
* Check for CAPTCHA on an error page. Handle 403 and 503 pages
* @private
* @param {Zotero.HTTP.UnexpectedStatusException} e HTTP response error object
* @param {Integer} tries Number of queries to attempt before giving up
* @param {Boolean} dontClearCookies Whether to attempt to clear cookies in
* in order to get CAPTCHA to show up
* @return {Promise} A promise resolved when PDF metadata has been retrieved
*/
var _checkCaptchaError = Zotero.Promise.coroutine(function* (e, tries, dontClearCookies) {
if(stopCheckCallback && stopCheckCallback()) {
throw new Zotero.Exception.Alert('recognizePDF.stopped');
}
Zotero.debug("RecognizePDF: Checking for CAPTCHA on Google Scholar error page (" + e.status + ")");
// Check for captcha on error page
if(e instanceof Zotero.HTTP.UnexpectedStatusException
&& (e.status == 403 || e.status == 503) && e.xmlhttp.response) {
if(_extractCaptchaFormData(e.xmlhttp.response)) {
Zotero.debug("RecognizePDF: CAPTCHA found");
return _solveCaptcha(e.xmlhttp, tries);
} else if(!dontClearCookies && e.xmlhttp.channel) { // Make sure we can obtain original URL
// AFAICT, for 403 errors, GS just says "sorry, try later",
// but if you clear cookies, you get a CAPTCHA
Zotero.debug("RecognizePDF: No CAPTCHA detected on page. Clearing cookies.");
if(!_clearGSCookies(e.xmlhttp.channel.originalURI.host)) {
//user said no or no cookies removed
throw new Zotero.Exception.Alert('recognizePDF.limit');
}
// Redo GET request
Zotero.debug("RecognizePDF: Reloading page after clearing cookies.");
return Zotero.HTTP.request(
"GET", e.xmlhttp.channel.originalURI.spec, { "responseType": "document" }
)
.then(
function (xmlhttp) {
return _checkCaptchaOK(xmlhttp, tries);
},
function (e) {
return _checkCaptchaError(e, tries, true); // Don't try this again
}
);
}
Zotero.debug("RecognizePDF: Google Scholar returned an unexpected page"
+ " with status " + e.status);
throw new Zotero.Exception.Alert('recognizePDF.limit');
}
throw e;
});
/**
* Prompt user to enter CPATCHA
* @private
* @param {XMLHttpRequest} xmlhttp
* @param {Integer} [tries] Number of queries to attempt before giving up
* @return {Promise} A promise resolved when PDF metadata has been retrieved
*/
function _solveCaptcha(xmlhttp, tries) {
var doc = xmlhttp.response;
if(tries === undefined) tries = 3;
if(!tries) {
Zotero.debug("RecognizePDF: Failed to solve CAPTCHA after multiple attempts.");
throw new Zotero.Exception.Alert('recognizePDF.limit');
}
tries--;
var formData = doc && _extractCaptchaFormData(doc);
if(!formData) {
Zotero.debug("RecognizePDF: Could not find CAPTCHA on page.");
throw new Zotero.Exception.Alert('recognizePDF.limit');
}
var io = { dataIn: {
title: Zotero.getString("recognizePDF.captcha.title"),
description: Zotero.getString("recognizePDF.captcha.description"),
imgUrl: formData.img
}};
_progressWindow.openDialog("chrome://zotero/content/captcha.xul", "",
"chrome,modal,resizable=no,centerscreen", io);
if(!io.dataOut) {
Zotero.debug("RecognizePDF: No CAPTCHA entered");
throw new Zotero.Exception.Alert('recognizePDF.limit');
}
Zotero.debug('RecognizePDF: User entered "' + io.dataOut.captcha + '" for CAPTCHA');
formData.input.captcha = io.dataOut.captcha;
var url = '', prop;
for(prop in formData.input) {
url += '&' + encodeURIComponent(prop) + '='
+ encodeURIComponent(formData.input[prop]);
}
url = formData.action + '?' + url.substr(1);
return Zotero.HTTP.promise("GET", url, {"responseType":"document"})
.then(function(xmlhttp) {
return _checkCaptchaOK(xmlhttp, tries);
},
function(e) {
return _checkCaptchaError(e, tries);
});
}
/**
* Extract CAPTCHA form-related data from the CAPTCHA page
* @private
* @param {Document} doc DOM document object for the CAPTCHA page
* @return {Object} Object containing data describing CAPTCHA form
*/
function _extractCaptchaFormData(doc) {
var formData = {};
var img = doc.getElementsByTagName('img')[0];
if(!img) return;
formData.img = img.src;
var form = doc.forms[0];
if(!form) return;
formData.action = form.action;
formData.input = {};
var inputs = form.getElementsByTagName('input');
for(var i=0, n=inputs.length; i<n; i++) {
if(!inputs[i].name) continue;
formData.input[inputs[i].name] = inputs[i].value;
}
formData.continue = "https://scholar.google.com";
return formData;
}
/**
* Clear Google cookies to get the CAPTCHA page to appear
* @private
* @param {String} host Host of the Google Scholar page (in case it's proxied)
* @return {Boolean} Whether any cookies were cleared
*/
function _clearGSCookies(host) {
/* There don't seem to be any negative effects of deleting GDSESS
if(!Zotero.isStandalone) {
//ask user first
var response = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService)
.confirm(null, "Clear Google Scholar cookies?",
"Google Scholar is attempting to block further queries. We can "
+ "clear certain cookies and try again. This may affect some "
+ "temporary Google preferences or it may log you out. May we clear"
+ " your Google Scholar cookies?");
if(!response) return;
}*/
var removed = false, cookies = cookieService.getCookiesFromHost(host);
while(cookies.hasMoreElements()) {
var cookie = cookies.getNext().QueryInterface(Components.interfaces.nsICookie2);
if(["GDSESS", "PREF"].indexOf(cookie.name) !== -1) { // GDSESS doesn't seem to always be enough
Zotero.debug("RecognizePDF: Removing cookie " + cookie.name + " for host "
+ cookie.host + " and path " + cookie.path);
cookieService.remove(cookie.host, cookie.name, cookie.path, false);
removed = true;
}
}
if(!removed) {
Zotero.debug("RecognizePDF: No cookies removed");
}
return removed;
}
};
}

View File

@ -1,219 +0,0 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2018 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
/**
* @fileOverview Tools for automatically retrieving a citation for the given PDF
*/
/**
* Front end for recognizing PDFs
* @namespace
*/
var Zotero_RecognizePDF_Dialog = new function () {
const SUCCESS_IMAGE = 'chrome://zotero/skin/tick.png';
const FAILURE_IMAGE = 'chrome://zotero/skin/cross.png';
const LOADING_IMAGE = 'chrome://zotero/skin/arrow_refresh.png';
let _progressWindow = null;
let _progressIndicator = null;
let _rowIDs = [];
this.open = function() {
if (_progressWindow) {
_progressWindow.focus();
return;
}
_progressWindow = window.openDialog('chrome://zotero/content/recognizePDFDialog.xul', '', 'chrome,close=yes,resizable=yes,dependent,dialog,centerscreen');
_progressWindow.addEventListener('pageshow', _onWindowLoaded.bind(this), false);
};
function close() {
_progressWindow.close();
}
function _getImageByStatus(status) {
if (status === Zotero.RecognizePDF.ROW_PROCESSING) {
return LOADING_IMAGE;
}
else if (status === Zotero.RecognizePDF.ROW_FAILED) {
return FAILURE_IMAGE;
}
else if (status === Zotero.RecognizePDF.ROW_SUCCEEDED) {
return SUCCESS_IMAGE;
}
return '';
}
function _rowToTreeItem(row) {
let treeitem = _progressWindow.document.createElement('treeitem');
treeitem.setAttribute('id', 'item-' + row.id);
let treerow = _progressWindow.document.createElement('treerow');
let treecell = _progressWindow.document.createElement('treecell');
treecell.setAttribute('id', 'item-' + row.id + '-icon');
treecell.setAttribute('src', _getImageByStatus(row.status));
treerow.appendChild(treecell);
treecell = _progressWindow.document.createElement('treecell');
treecell.setAttribute('label', row.fileName);
treerow.appendChild(treecell);
treecell = _progressWindow.document.createElement('treecell');
treecell.setAttribute('id', 'item-' + row.id + '-title');
treecell.setAttribute('label', row.message);
treerow.appendChild(treecell);
treeitem.appendChild(treerow);
return treeitem;
}
function _onWindowLoaded() {
let rows = Zotero.RecognizePDF.getRows();
_rowIDs = [];
let treechildren = _progressWindow.document.getElementById('treechildren');
for (let row of rows) {
_rowIDs.push(row.id);
let treeitem = _rowToTreeItem(row);
treechildren.appendChild(treeitem);
}
_progressWindow.document.getElementById('tree').addEventListener('dblclick',
function (event) {
_onDblClick(event, this);
}
);
_progressIndicator = _progressWindow.document.getElementById('progress-indicator');
_progressWindow.document.getElementById('cancel-button')
.addEventListener('command', function () {
close();
Zotero.RecognizePDF.cancel();
}, false);
_progressWindow.document.getElementById('minimize-button')
.addEventListener('command', function () {
close();
}, false);
_progressWindow.document.getElementById('close-button')
.addEventListener('command', function () {
close();
Zotero.RecognizePDF.cancel();
}, false);
_progressWindow.addEventListener('keypress', function (e) {
if (e.keyCode === KeyEvent.DOM_VK_ESCAPE) {
// If done processing, Esc is equivalent to Close rather than Minimize
if (Zotero.RecognizePDF.getTotal() == Zotero.RecognizePDF.getProcessedTotal()) {
Zotero.RecognizePDF.cancel();
}
close();
}
});
_progressWindow.addEventListener('unload', function () {
Zotero.RecognizePDF.removeListener('rowadded');
Zotero.RecognizePDF.removeListener('rowupdated');
Zotero.RecognizePDF.removeListener('rowdeleted');
_progressWindow = null;
_progressIndicator = null;
_rowIDs = [];
});
_updateProgress();
Zotero.RecognizePDF.addListener('rowadded', function (row) {
_rowIDs.push(row.id);
let treeitem = _rowToTreeItem(row);
treechildren.appendChild(treeitem);
_updateProgress();
});
Zotero.RecognizePDF.addListener('rowupdated', function (row) {
let itemIcon = _progressWindow.document.getElementById('item-' + row.id + '-icon');
let itemTitle = _progressWindow.document.getElementById('item-' + row.id + '-title');
itemIcon.setAttribute('src', _getImageByStatus(row.status));
itemTitle.setAttribute('label', row.message);
_updateProgress();
});
Zotero.RecognizePDF.addListener('rowdeleted', function (row) {
_rowIDs.splice(_rowIDs.indexOf(row.id), 1);
let treeitem = _progressWindow.document.getElementById('item-' + row.id);
treeitem.parentNode.removeChild(treeitem);
_updateProgress();
});
}
function _updateProgress() {
if (!_progressWindow) return;
let total = Zotero.RecognizePDF.getTotal();
let processed = Zotero.RecognizePDF.getProcessedTotal();
_progressIndicator.value = processed * 100 / total;
if (processed === total) {
_progressWindow.document.getElementById("cancel-button").hidden = true;
_progressWindow.document.getElementById("minimize-button").hidden = true;
_progressWindow.document.getElementById("close-button").hidden = false;
_progressWindow.document.getElementById("label").value = Zotero.getString('recognizePDF.complete.label');
}
else {
_progressWindow.document.getElementById("cancel-button").hidden = false;
_progressWindow.document.getElementById("minimize-button").hidden = false;
_progressWindow.document.getElementById("close-button").hidden = true;
_progressWindow.document.getElementById("label").value = Zotero.getString('recognizePDF.recognizing.label');
}
}
/**
* Focus items in Zotero library when double-clicking them in the Retrieve
* metadata window.
* @param {Event} event
* @param {tree} tree XUL tree object
* @private
*/
async function _onDblClick(event, tree) {
if (event && tree && event.type === 'dblclick') {
let itemID = _rowIDs[tree.treeBoxObject.getRowAt(event.clientX, event.clientY)];
if (!itemID) return;
let item = await Zotero.Items.getAsync(itemID);
if (!item) return;
if (item.parentItemID) itemID = item.parentItemID;
if (window.ZoteroOverlay) {
window.ZoteroOverlay.toggleDisplay(true);
}
window.ZoteroPane.selectItem(itemID, false, true);
window.focus();
}
}
};

View File

@ -236,12 +236,14 @@
id="zotero-items-column-hasAttachment" hidden="true" id="zotero-items-column-hasAttachment" hidden="true"
class="treecol-image" class="treecol-image"
label="&zotero.tabs.attachments.label;" label="&zotero.tabs.attachments.label;"
src="chrome://zotero/skin/attach-small.png"
zotero-persist="width ordinal hidden sortActive sortDirection"/> zotero-persist="width ordinal hidden sortActive sortDirection"/>
<splitter class="tree-splitter"/> <splitter class="tree-splitter"/>
<treecol <treecol
id="zotero-items-column-numNotes" hidden="true" id="zotero-items-column-numNotes" hidden="true"
class="treecol-image" class="treecol-image"
label="&zotero.tabs.notes.label;" label="&zotero.tabs.notes.label;"
src="chrome://zotero/skin/treeitem-note-small.png"
zotero-persist="width ordinal hidden sortActive sortDirection"/> zotero-persist="width ordinal hidden sortActive sortDirection"/>
</treecols> </treecols>
<treechildren/> <treechildren/>

View File

@ -139,15 +139,6 @@ const ZoteroStandalone = new function() {
this.updateQuickCopyOptions = function () { this.updateQuickCopyOptions = function () {
var selected = false;
try {
selected = Zotero.getActiveZoteroPane()
.getSelectedItems()
.filter(item => item.isRegularItem())
.length;
}
catch (e) {}
var format = Zotero.QuickCopy.getFormatFromURL(Zotero.QuickCopy.lastActiveURL); var format = Zotero.QuickCopy.getFormatFromURL(Zotero.QuickCopy.lastActiveURL);
format = Zotero.QuickCopy.unserializeSetting(format); format = Zotero.QuickCopy.unserializeSetting(format);
@ -155,18 +146,16 @@ const ZoteroStandalone = new function() {
var copyBibliography = document.getElementById('menu_copyBibliography'); var copyBibliography = document.getElementById('menu_copyBibliography');
var copyExport = document.getElementById('menu_copyExport'); var copyExport = document.getElementById('menu_copyExport');
copyCitation.hidden = !selected || format.mode != 'bibliography'; copyCitation.hidden = format.mode != 'bibliography';
copyBibliography.hidden = !selected || format.mode != 'bibliography'; copyBibliography.hidden = format.mode != 'bibliography';
copyExport.hidden = !selected || format.mode != 'export'; copyExport.hidden = format.mode != 'export';
if (format.mode == 'export') { if (format.mode == 'export') {
try { try {
let obj = Zotero.Translators.get(format.id); let obj = Zotero.Translators.get(format.id);
copyExport.label = Zotero.getString('quickCopy.copyAs', obj.label); copyExport.label = Zotero.getString('quickCopy.copyAs', obj.label);
} }
catch (e) { catch (e) {
if (!(e instanceof Zotero.Exception.UnloadedDataException && e.dataType == 'translators')) {
Zotero.logError(e); Zotero.logError(e);
}
copyExport.hidden = true; copyExport.hidden = true;
} }
} }
@ -281,8 +270,6 @@ ZoteroStandalone.DebugOutput = {
submit: function () { submit: function () {
// 'Zotero' isn't defined yet when this function is created, so do it inline // 'Zotero' isn't defined yet when this function is created, so do it inline
return Zotero.Promise.coroutine(function* () { return Zotero.Promise.coroutine(function* () {
Zotero.debug("Submitting debug output");
Components.utils.import("resource://zotero/config.js"); Components.utils.import("resource://zotero/config.js");
var url = ZOTERO_CONFIG.REPOSITORY_URL + "report?debug=1"; var url = ZOTERO_CONFIG.REPOSITORY_URL + "report?debug=1";

View File

@ -60,7 +60,7 @@
<!--EDIT--> <!--EDIT-->
<commandset id="editMenuCommands"/> <commandset id="editMenuCommands"/>
<command id="cmd_find" <command id="cmd_find"
oncommand="document.getElementById('zotero-tb-search').select()"/> oncommand="document.getElementById('zotero-tb-search').focus();"/>
</commandset> </commandset>
<keyset id="mainKeyset"> <keyset id="mainKeyset">

View File

@ -52,9 +52,9 @@ var Zotero_Tag_Color_Chooser = new function() {
colorPicker.setAttribute('tileWidth', 24); colorPicker.setAttribute('tileWidth', 24);
colorPicker.setAttribute('tileHeight', 24); colorPicker.setAttribute('tileHeight', 24);
colorPicker.colors = [ colorPicker.colors = [
'#FF6666', '#FF8C19', '#999999', '#990000', '#CC9933', '#FF9900',
'#5FB236', '#009980', '#2EA8E5', '#FFCC00', '#007439', '#1049A9',
'#576DD9', '#A28AE5', '#A6507B' '#9999FF', '#CC66CC', '#993399'
]; ];
var maxTags = document.getElementById('max-tags'); var maxTags = document.getElementById('max-tags');

View File

@ -30,7 +30,7 @@
for (let type of types) { for (let type of types) {
var fieldIDs = Zotero.ItemFields.getItemTypeFields(type.id); var fieldIDs = Zotero.ItemFields.getItemTypeFields(type.id);
var baseFields = {}; var baseFields = {};
for (let fieldID of fieldIDs) { for (let fieldID in fieldIDs) {
if (baseMappedFields.includes(fieldID)) { if (baseMappedFields.includes(fieldID)) {
baseFields[fieldID] = Zotero.ItemFields.getBaseIDFromTypeAndField(type.id, fieldID); baseFields[fieldID] = Zotero.ItemFields.getBaseIDFromTypeAndField(type.id, fieldID);
} }

View File

@ -63,7 +63,7 @@ var Zotero_TranslatorTesters = new function() {
try { try {
for(var i=0; i<translators.length; i++) { for(var i=0; i<translators.length; i++) {
if (includeTranslators.length if (includeTranslators.length
&& !includeTranslators.some(x => translators[i].label.includes(x))) continue; && !includeTranslators.includes(translators[i].label)) continue;
if (skipTranslators && skipTranslators[translators[i].translatorID]) continue; if (skipTranslators && skipTranslators[translators[i].translatorID]) continue;
testers.push(new Zotero_TranslatorTester(translators[i], type)); testers.push(new Zotero_TranslatorTester(translators[i], type));
}; };
@ -299,17 +299,9 @@ Zotero_TranslatorTester._sanitizeItem = function(item, testItem, keepValidFields
// remove fields to be ignored // remove fields to be ignored
if(!keepValidFields && "accessDate" in item) delete item.accessDate; if(!keepValidFields && "accessDate" in item) delete item.accessDate;
// Sort tags //sort tags, if they're still there
if (item.tags && Array.isArray(item.tags)) { if(item.tags && typeof item.tags === "object" && "sort" in item.tags) {
// Normalize tags -- necessary until tests are updated for 5.0 item.tags = Zotero.Utilities.arrayUnique(item.tags).sort();
if (testItem) {
item.tags = Zotero.Translate.Base.prototype._cleanTags(item.tags);
}
item.tags.sort((a, b) => {
if (a.tag < b.tag) return -1;
if (b.tag < a.tag) return 1;
return 0;
});
} }
return item; return item;
@ -399,25 +391,54 @@ Zotero_TranslatorTester.prototype._runTestsRecursively = function(testDoneCallba
/** /**
* Fetches the page for a given test and runs it * Fetches the page for a given test and runs it
*
* This function is only applicable in Firefox; it is overridden in translator_global.js in Chrome * This function is only applicable in Firefox; it is overridden in translator_global.js in Chrome
* and Safari. * and Safari
* * @param {Object} test Test to execute
* @param {Object} test - Test to execute * @param {Document} doc DOM document to test against
* @param {Function} testDoneCallback - A callback to be executed when test is complete * @param {Function} testDoneCallback A callback to be executed when test is complete
*/ */
Zotero_TranslatorTester.prototype.fetchPageAndRunTest = function (test, testDoneCallback) { Zotero_TranslatorTester.prototype.fetchPageAndRunTest = function(test, testDoneCallback) {
Zotero.HTTP.processDocuments( var timer = Components.classes["@mozilla.org/timer;1"].
test.url, createInstance(Components.interfaces.nsITimer);
(doc) => { timer.initWithCallback({"notify":function() {
this.runTest(test, doc, function (obj, test, status, message) { try {
if (hiddenBrowser) Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
} catch(e) {}
}}, TEST_RUN_TIMEOUT, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
var me = this;
var runTest = function(doc) {
me.runTest(test, doc, function(obj, test, status, message) {
try {
timer.cancel();
} catch(e) {};
if(hiddenBrowser) Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
testDoneCallback(obj, test, status, message); testDoneCallback(obj, test, status, message);
}); });
};
var hiddenBrowser = Zotero.HTTP.processDocuments(test.url,
function(doc) {
if(test.defer) {
me._debug(this, "TranslatorTesting: Waiting "
+ (Zotero_TranslatorTester.DEFER_DELAY/1000)
+ " second(s) for page content to settle"
);
Zotero.setTimeout(() => runTest(doc), Zotero_TranslatorTester.DEFER_DELAY);
} else {
runTest(doc);
}
},
null,
function(e) {
testDoneCallback(this, test, "failed", "Translation failed to initialize: "+e);
},
true
);
// No hidden browser returned from translation-server processDocuments()
if (hiddenBrowser) {
hiddenBrowser.docShell.allowMetaRedirects = true;
} }
)
.catch(function (e) {
testDoneCallback(this, test, "failed", "Translation failed to initialize: " + e);
}.bind(this))
}; };
/** /**
@ -510,9 +531,7 @@ Zotero_TranslatorTester.prototype._runTestTranslate = function(translate, transl
} }
translate.setTranslator(this.translator); translate.setTranslator(this.translator);
translate.translate({ translate.translate(false);
libraryID: false
});
}; };
/** /**
@ -609,9 +628,7 @@ Zotero_TranslatorTester.prototype.newTest = function(doc, testReadyCallback) {
}); });
translate.setHandler("done", function(obj, returnValue) { me._createTest(obj, multipleMode, returnValue, testReadyCallback) }); translate.setHandler("done", function(obj, returnValue) { me._createTest(obj, multipleMode, returnValue, testReadyCallback) });
translate.capitalizeTitles = false; translate.capitalizeTitles = false;
translate.translate({ translate.translate(false);
libraryID: false
});
}; };
/** /**

View File

@ -154,7 +154,7 @@ Zotero.API = {
return 'groups/' + Zotero.Groups.getGroupIDFromLibraryID(libraryID); return 'groups/' + Zotero.Groups.getGroupIDFromLibraryID(libraryID);
default: default:
throw new Error(`Invalid type '${type}'`); throw new Error(`Invalid type '${type}`);
} }
} }
}; };

View File

@ -24,7 +24,6 @@
*/ */
Zotero.Attachments = new function(){ Zotero.Attachments = new function(){
// Keep in sync with Zotero.Schema.integrityCheck()
this.LINK_MODE_IMPORTED_FILE = 0; this.LINK_MODE_IMPORTED_FILE = 0;
this.LINK_MODE_IMPORTED_URL = 1; this.LINK_MODE_IMPORTED_URL = 1;
this.LINK_MODE_LINKED_FILE = 2; this.LINK_MODE_LINKED_FILE = 2;
@ -40,7 +39,6 @@ Zotero.Attachments = new function(){
* @param {Integer} [options.libraryID] * @param {Integer} [options.libraryID]
* @param {Integer[]|String[]} [options.parentItemID] - Parent item to add item to * @param {Integer[]|String[]} [options.parentItemID] - Parent item to add item to
* @param {Integer[]} [options.collections] - Collection keys or ids to add new item to * @param {Integer[]} [options.collections] - Collection keys or ids to add new item to
* @param {String} [options.fileBaseName]
* @param {String} [options.contentType] * @param {String} [options.contentType]
* @param {String} [options.charset] * @param {String} [options.charset]
* @param {Object} [options.saveOptions] - Options to pass to Zotero.Item::save() * @param {Object} [options.saveOptions] - Options to pass to Zotero.Item::save()
@ -51,24 +49,15 @@ Zotero.Attachments = new function(){
var libraryID = options.libraryID; var libraryID = options.libraryID;
var file = Zotero.File.pathToFile(options.file); var file = Zotero.File.pathToFile(options.file);
var path = file.path;
var leafName = file.leafName;
var parentItemID = options.parentItemID; var parentItemID = options.parentItemID;
var collections = options.collections; var collections = options.collections;
var fileBaseName = options.fileBaseName;
var contentType = options.contentType; var contentType = options.contentType;
var charset = options.charset; var charset = options.charset;
var saveOptions = options.saveOptions; var saveOptions = options.saveOptions;
if (fileBaseName) { var newName = Zotero.File.getValidFileName(file.leafName);
let ext = Zotero.File.getExtension(path);
var newName = fileBaseName + (ext != '' ? '.' + ext : '');
}
else {
var newName = Zotero.File.getValidFileName(OS.Path.basename(leafName));
}
if (leafName.endsWith(".lnk")) { if (file.leafName.endsWith(".lnk")) {
throw new Error("Cannot add Windows shortcut"); throw new Error("Cannot add Windows shortcut");
} }
if (parentItemID && collections) { if (parentItemID && collections) {
@ -105,8 +94,6 @@ Zotero.Attachments = new function(){
// Copy file to unique filename, which automatically shortens long filenames // Copy file to unique filename, which automatically shortens long filenames
newFile = Zotero.File.copyToUnique(file, newFile); newFile = Zotero.File.copyToUnique(file, newFile);
yield Zotero.File.setNormalFilePermissions(newFile.path);
if (!contentType) { if (!contentType) {
contentType = yield Zotero.MIME.getMIMETypeFromFile(newFile); contentType = yield Zotero.MIME.getMIMETypeFromFile(newFile);
} }
@ -176,18 +163,13 @@ Zotero.Attachments = new function(){
/** /**
* @param {Object} options - 'file', 'url', 'title', 'contentType', 'charset', 'parentItemID', 'singleFile' * @param {Object} options - 'file', 'url', 'title', 'contentType', 'charset', 'parentItemID'
* @return {Promise<Zotero.Item>} * @return {Promise<Zotero.Item>}
*/ */
this.importSnapshotFromFile = Zotero.Promise.coroutine(function* (options) { this.importSnapshotFromFile = Zotero.Promise.coroutine(function* (options) {
Zotero.debug('Importing snapshot from file'); Zotero.debug('Importing snapshot from file');
var file = Zotero.File.pathToFile(options.file); var file = Zotero.File.pathToFile(options.file);
// TODO: Fix main filename when copying directory, though in that case it's probably
// from our own export and already clean
var fileName = options.singleFile
? Zotero.File.getValidFileName(file.leafName)
: file.leafName;
var url = options.url; var url = options.url;
var title = options.title; var title = options.title;
var contentType = options.contentType; var contentType = options.contentType;
@ -198,7 +180,7 @@ Zotero.Attachments = new function(){
throw new Error("parentItemID not provided"); throw new Error("parentItemID not provided");
} }
var attachmentItem, itemID, destDir, newPath; var attachmentItem, itemID, destDir, newFile;
try { try {
yield Zotero.DB.executeTransaction(function* () { yield Zotero.DB.executeTransaction(function* () {
// Create a new attachment // Create a new attachment
@ -211,7 +193,6 @@ Zotero.Attachments = new function(){
attachmentItem.attachmentLinkMode = this.LINK_MODE_IMPORTED_URL; attachmentItem.attachmentLinkMode = this.LINK_MODE_IMPORTED_URL;
attachmentItem.attachmentContentType = contentType; attachmentItem.attachmentContentType = contentType;
attachmentItem.attachmentCharset = charset; attachmentItem.attachmentCharset = charset;
attachmentItem.attachmentPath = 'storage:' + fileName;
// DEBUG: this should probably insert access date too so as to // DEBUG: this should probably insert access date too so as to
// create a proper item, but at the moment this is only called by // create a proper item, but at the moment this is only called by
@ -221,23 +202,16 @@ Zotero.Attachments = new function(){
var storageDir = Zotero.getStorageDirectory(); var storageDir = Zotero.getStorageDirectory();
destDir = this.getStorageDirectory(attachmentItem); destDir = this.getStorageDirectory(attachmentItem);
yield OS.File.removeDir(destDir.path); yield OS.File.removeDir(destDir.path);
newPath = OS.Path.join(destDir.path, fileName);
// Copy single file to new directory
if (options.singleFile) {
yield this.createDirectoryForItem(attachmentItem);
yield OS.File.copy(file.path, newPath);
}
// Copy entire parent directory (for HTML snapshots)
else {
file.parent.copyTo(storageDir, destDir.leafName); file.parent.copyTo(storageDir, destDir.leafName);
}
// Point to copied file
newFile = destDir.clone();
newFile.append(file.leafName);
attachmentItem.attachmentPath = newFile.path;
yield attachmentItem.save();
}.bind(this)); }.bind(this));
yield _postProcessFile( yield _postProcessFile(attachmentItem, newFile, contentType, charset);
attachmentItem,
Zotero.File.pathToFile(newPath),
contentType,
charset
);
} }
catch (e) { catch (e) {
Zotero.logError(e); Zotero.logError(e);
@ -259,18 +233,8 @@ Zotero.Attachments = new function(){
/** /**
* @param {Object} options * @param {Object} options - 'libraryID', 'url', 'parentItemID', 'collections', 'title',
* @param {Integer} options.libraryID * 'fileBaseName', 'contentType', 'cookieSandbox', 'saveOptions'
* @param {String} options.url
* @param {Integer} [options.parentItemID]
* @param {Integer[]} [options.collections]
* @param {String} [options.title]
* @param {String} [options.fileBaseName]
* @param {Boolean} [options.renameIfAllowedType=false]
* @param {String} [options.contentType]
* @param {String} [options.referrer]
* @param {CookieSandbox} [options.cookieSandbox]
* @param {Object} [options.saveOptions]
* @return {Promise<Zotero.Item>} - A promise for the created attachment item * @return {Promise<Zotero.Item>} - A promise for the created attachment item
*/ */
this.importFromURL = Zotero.Promise.coroutine(function* (options) { this.importFromURL = Zotero.Promise.coroutine(function* (options) {
@ -280,9 +244,7 @@ Zotero.Attachments = new function(){
var collections = options.collections; var collections = options.collections;
var title = options.title; var title = options.title;
var fileBaseName = options.fileBaseName; var fileBaseName = options.fileBaseName;
var renameIfAllowedType = options.renameIfAllowedType;
var contentType = options.contentType; var contentType = options.contentType;
var referrer = options.referrer;
var cookieSandbox = options.cookieSandbox; var cookieSandbox = options.cookieSandbox;
var saveOptions = options.saveOptions; var saveOptions = options.saveOptions;
@ -304,7 +266,7 @@ Zotero.Attachments = new function(){
// Save using a hidden browser // Save using a hidden browser
var nativeHandlerImport = function () { var nativeHandlerImport = function () {
return new Zotero.Promise(function (resolve, reject) { return new Zotero.Promise(function (resolve, reject) {
var browser = Zotero.HTTP.loadDocuments( var browser = Zotero.HTTP.processDocuments(
url, url,
Zotero.Promise.coroutine(function* () { Zotero.Promise.coroutine(function* () {
let channel = browser.docShell.currentDocumentChannel; let channel = browser.docShell.currentDocumentChannel;
@ -312,7 +274,7 @@ Zotero.Attachments = new function(){
if (channel.responseStatus < 200 || channel.responseStatus >= 400) { if (channel.responseStatus < 200 || channel.responseStatus >= 400) {
reject(new Error("Invalid response " + channel.responseStatus + " " reject(new Error("Invalid response " + channel.responseStatus + " "
+ channel.responseStatusText + " for '" + url + "'")); + channel.responseStatusText + " for '" + url + "'"));
return false; return;
} }
} }
try { try {
@ -344,11 +306,6 @@ Zotero.Attachments = new function(){
// Save using remote web browser persist // Save using remote web browser persist
var externalHandlerImport = Zotero.Promise.coroutine(function* (contentType) { var externalHandlerImport = Zotero.Promise.coroutine(function* (contentType) {
// Rename attachment
if (renameIfAllowedType && !fileBaseName && this.getRenamedFileTypes().includes(contentType)) {
let parentItem = Zotero.Items.get(parentItemID);
fileBaseName = this.getFileBaseNameFromItem(parentItem);
}
if (fileBaseName) { if (fileBaseName) {
let ext = _getExtensionFromURL(url, contentType); let ext = _getExtensionFromURL(url, contentType);
var fileName = fileBaseName + (ext != '' ? '.' + ext : ''); var fileName = fileBaseName + (ext != '' ? '.' + ext : '');
@ -366,8 +323,9 @@ Zotero.Attachments = new function(){
// Create a temporary directory to save to within the storage directory. // Create a temporary directory to save to within the storage directory.
// We don't use the normal temp directory because people might have 'storage' // We don't use the normal temp directory because people might have 'storage'
// symlinked to another volume, which makes moving complicated. // symlinked to another volume, which makes moving complicated.
var tmpDir = (yield this.createTemporaryStorageDirectory()).path; var tmpDir = yield this.createTemporaryStorageDirectory();
var tmpFile = OS.Path.join(tmpDir, fileName); var tmpFile = tmpDir.clone();
tmpFile.append(fileName);
// Save to temp dir // Save to temp dir
var deferred = Zotero.Promise.defer(); var deferred = Zotero.Promise.defer();
@ -378,15 +336,10 @@ Zotero.Attachments = new function(){
var nsIURL = Components.classes["@mozilla.org/network/standard-url;1"] var nsIURL = Components.classes["@mozilla.org/network/standard-url;1"]
.createInstance(Components.interfaces.nsIURL); .createInstance(Components.interfaces.nsIURL);
nsIURL.spec = url; nsIURL.spec = url;
var headers = {}; Zotero.Utilities.Internal.saveURI(wbp, nsIURL, tmpFile);
if (referrer) {
headers.Referer = referrer;
}
Zotero.Utilities.Internal.saveURI(wbp, nsIURL, tmpFile, headers);
yield deferred.promise; yield deferred.promise;
let sample = yield Zotero.File.getContentsAsync(tmpFile, null, 1000); let sample = yield Zotero.File.getSample(tmpFile);
try { try {
if (contentType == 'application/pdf' && if (contentType == 'application/pdf' &&
Zotero.MIME.sniffForMIMEType(sample) != 'application/pdf') { Zotero.MIME.sniffForMIMEType(sample) != 'application/pdf') {
@ -420,22 +373,25 @@ Zotero.Attachments = new function(){
if (collections) { if (collections) {
attachmentItem.setCollections(collections); attachmentItem.setCollections(collections);
} }
attachmentItem.attachmentPath = 'storage:' + fileName;
var itemID = yield attachmentItem.save(saveOptions); var itemID = yield attachmentItem.save(saveOptions);
Zotero.Fulltext.queueItem(attachmentItem); // Create a new folder for this item in the storage directory
destDir = this.getStorageDirectory(attachmentItem);
yield OS.File.move(tmpDir.path, destDir.path);
var destFile = destDir.clone();
destFile.append(fileName);
// DEBUG: Does this fail if 'storage' is symlinked to another drive? // Refetch item to update path
destDir = this.getStorageDirectory(attachmentItem).path; attachmentItem.attachmentPath = destFile.path;
yield OS.File.move(tmpDir, destDir); yield attachmentItem.save(saveOptions);
}.bind(this)); }.bind(this));
} catch (e) { } catch (e) {
try { try {
if (tmpDir) { if (tmpDir && tmpDir.exists()) {
yield OS.File.removeDir(tmpDir, { ignoreAbsent: true }); tmpDir.remove(true);
} }
if (destDir) { if (destDir && destDir.exists()) {
yield OS.File.removeDir(destDir, { ignoreAbsent: true }); destDir.remove(true);
} }
} }
catch (e) { catch (e) {
@ -444,6 +400,16 @@ Zotero.Attachments = new function(){
throw e; throw e;
} }
// We don't have any way of knowing that the file is flushed to disk,
// so we just wait a second before indexing and hope for the best.
// We'll index it later if it fails. (This may not be necessary.)
//
// If this is removed, the afterEach() delay in the server_connector /connector/saveSnapshot
// tests can also be removed.
setTimeout(function () {
Zotero.Fulltext.indexItems([attachmentItem.id]);
}, 1000);
return attachmentItem; return attachmentItem;
}.bind(this)); }.bind(this));
@ -614,10 +580,11 @@ Zotero.Attachments = new function(){
contentType = "application/pdf"; contentType = "application/pdf";
} }
var tmpDir = (yield this.createTemporaryStorageDirectory()).path; var tmpDir = yield this.createTemporaryStorageDirectory();
try { try {
var tmpFile = tmpDir.clone();
var fileName = Zotero.File.truncateFileName(_getFileNameFromURL(url, contentType), 100); var fileName = Zotero.File.truncateFileName(_getFileNameFromURL(url, contentType), 100);
var tmpFile = OS.Path.join(tmpDir, fileName); tmpFile.append(fileName);
// If we're using the title from the document, make some adjustments // If we're using the title from the document, make some adjustments
if (!options.title) { if (!options.title) {
@ -632,11 +599,9 @@ Zotero.Attachments = new function(){
} }
} }
if ((contentType === 'text/html' || contentType === 'application/xhtml+xml') if (contentType === 'text/html' || contentType === 'application/xhtml+xml') {
// Documents from XHR don't work here
&& document instanceof Ci.nsIDOMDocument) {
Zotero.debug('Saving document with saveDocument()'); Zotero.debug('Saving document with saveDocument()');
yield Zotero.Utilities.Internal.saveDocument(document, tmpFile); yield Zotero.Utilities.Internal.saveDocument(document, tmpFile.path);
} }
else { else {
Zotero.debug("Saving file with saveURI()"); Zotero.debug("Saving file with saveURI()");
@ -678,14 +643,16 @@ Zotero.Attachments = new function(){
if (collections && collections.length) { if (collections && collections.length) {
attachmentItem.setCollections(collections); attachmentItem.setCollections(collections);
} }
attachmentItem.attachmentPath = 'storage:' + fileName;
var itemID = yield attachmentItem.save(); var itemID = yield attachmentItem.save();
Zotero.Fulltext.queueItem(attachmentItem); // Create a new folder for this item in the storage directory
destDir = this.getStorageDirectory(attachmentItem);
yield OS.File.move(tmpDir.path, destDir.path);
var destFile = destDir.clone();
destFile.append(fileName);
// DEBUG: Does this fail if 'storage' is symlinked to another drive? attachmentItem.attachmentPath = destFile.path;
destDir = this.getStorageDirectory(attachmentItem).path; yield attachmentItem.save();
yield OS.File.move(tmpDir, destDir);
}.bind(this)); }.bind(this));
} }
catch (e) { catch (e) {
@ -693,11 +660,11 @@ Zotero.Attachments = new function(){
// Clean up // Clean up
try { try {
if (tmpDir) { if (tmpDir && tmpDir.exists()) {
yield OS.File.removeDir(tmpDir, { ignoreAbsent: true }); tmpDir.remove(true);
} }
if (destDir) { if (destDir && destDir.exists()) {
yield OS.File.removeDir(destDir, { ignoreAbsent: true }); destDir.remove(true);
} }
} }
catch (e) { catch (e) {
@ -707,6 +674,21 @@ Zotero.Attachments = new function(){
throw e; throw e;
} }
// We don't have any way of knowing that the file is flushed to disk,
// so we just wait a second before indexing and hope for the best.
// We'll index it later if it fails. (This may not be necessary.)
if (contentType == 'application/pdf') {
setTimeout(function () {
Zotero.Fulltext.indexPDF(attachmentItem.getFilePath(), attachmentItem.id);
}, 1000);
}
else if (Zotero.MIME.isTextType(contentType)) {
// wbp.saveDocument consumes the document context (in Zotero.Utilities.Internal.saveDocument)
// Seems like a mozilla bug, but nothing on bugtracker.
// Either way, we don't rely on Zotero.Fulltext.indexDocument here anymore
yield Zotero.Fulltext.indexItems(attachmentItem.id, true, true);
}
return attachmentItem; return attachmentItem;
}); });
@ -812,30 +794,6 @@ Zotero.Attachments = new function(){
} }
this.getRenamedFileTypes = function () {
try {
var types = Zotero.Prefs.get('autoRenameFiles.fileTypes');
return types ? types.split(',') : [];
}
catch (e) {
return [];
}
};
this.getRenamedFileBaseNameIfAllowedType = async function (parentItem, file) {
var types = this.getRenamedFileTypes();
var contentType = file.endsWith('.pdf')
// Don't bother reading file if there's a .pdf extension
? 'application/pdf'
: await Zotero.MIME.getMIMETypeFromFile(file);
if (!types.includes(contentType)) {
return false;
}
return this.getFileBaseNameFromItem(parentItem);
}
/** /**
* Create directory for attachment files within storage directory * Create directory for attachment files within storage directory
* *
@ -1129,75 +1087,13 @@ Zotero.Attachments = new function(){
/** /**
* Move attachment item, including file, to another library * Copy attachment item, including files, to another library
*/
this.moveAttachmentToLibrary = async function (attachment, libraryID, parentItemID) {
if (attachment.libraryID == libraryID) {
throw new Error("Attachment is already in library " + libraryID);
}
Zotero.DB.requireTransaction();
var newAttachment = attachment.clone(libraryID);
if (attachment.isImportedAttachment()) {
// Attachment path isn't copied over by clone() if libraryID is different
newAttachment.attachmentPath = attachment.attachmentPath;
}
if (parentItemID) {
newAttachment.parentID = parentItemID;
}
await newAttachment.save();
// Move files over if they exist
var oldDir;
var newDir;
if (newAttachment.isImportedAttachment()) {
oldDir = this.getStorageDirectory(attachment).path;
if (await OS.File.exists(oldDir)) {
newDir = this.getStorageDirectory(newAttachment).path;
// Target directory shouldn't exist, but remove it if it does
//
// Testing for directories in OS.File, used by removeDir(), is broken on Travis,
// so use nsIFile
if (Zotero.automatedTest) {
let nsIFile = Zotero.File.pathToFile(newDir);
if (nsIFile.exists()) {
nsIFile.remove(true);
}
}
else {
await OS.File.removeDir(newDir, { ignoreAbsent: true });
}
await OS.File.move(oldDir, newDir);
}
}
try {
await attachment.erase();
}
catch (e) {
// Move files back if old item can't be deleted
if (newAttachment.isImportedAttachment()) {
try {
await OS.File.move(newDir, oldDir);
}
catch (e) {
Zotero.logError(e);
}
}
throw e;
}
return newAttachment.id;
};
/**
* Copy attachment item, including file, to another library
*/ */
this.copyAttachmentToLibrary = Zotero.Promise.coroutine(function* (attachment, libraryID, parentItemID) { this.copyAttachmentToLibrary = Zotero.Promise.coroutine(function* (attachment, libraryID, parentItemID) {
var linkMode = attachment.attachmentLinkMode;
if (attachment.libraryID == libraryID) { if (attachment.libraryID == libraryID) {
throw new Error("Attachment is already in library " + libraryID); throw ("Attachment is already in library " + libraryID);
} }
Zotero.DB.requireTransaction(); Zotero.DB.requireTransaction();

View File

@ -16,13 +16,13 @@ Zotero.Cite = {
* Remove specified item IDs in-place from a citeproc-js bibliography object returned * Remove specified item IDs in-place from a citeproc-js bibliography object returned
* by makeBibliography() * by makeBibliography()
* @param {bib} citeproc-js bibliography object * @param {bib} citeproc-js bibliography object
* @param {Set} itemsToRemove Set of items to remove * @param {Array} itemsToRemove Array of items to remove
*/ */
"removeFromBibliography":function(bib, itemsToRemove) { "removeFromBibliography":function(bib, itemsToRemove) {
var removeItems = []; var removeItems = [];
for(let i in bib[0].entry_ids) { for(let i in bib[0].entry_ids) {
for(let j in bib[0].entry_ids[i]) { for(let j in bib[0].entry_ids[i]) {
if(itemsToRemove.has(`${bib[0].entry_ids[i][j]}`)) { if(itemsToRemove[bib[0].entry_ids[i][j]]) {
removeItems.push(i); removeItems.push(i);
break; break;
} }
@ -302,7 +302,7 @@ Zotero.Cite = {
var sessionID = id.substr(0, slashIndex), var sessionID = id.substr(0, slashIndex),
session = Zotero.Integration.sessions[sessionID], session = Zotero.Integration.sessions[sessionID],
item; item;
if (session) { if(session) {
item = session.embeddedZoteroItems[id.substr(slashIndex+1)]; item = session.embeddedZoteroItems[id.substr(slashIndex+1)];
} }
@ -315,104 +315,6 @@ Zotero.Cite = {
} else { } else {
return Zotero.Items.get(id); return Zotero.Items.get(id);
} }
},
extraToCSL: function (extra) {
return extra.replace(/^([A-Za-z \-]+)(:\s*.+)/gm, function (_, field, value) {
var originalField = field;
var field = field.toLowerCase().replace(/ /g, '-');
// Fields from https://aurimasv.github.io/z2csl/typeMap.xml
switch (field) {
// Standard fields
case 'abstract':
case 'accessed':
case 'annote':
case 'archive':
case 'archive-place':
case 'author':
case 'authority':
case 'call-number':
case 'chapter-number':
case 'citation-label':
case 'citation-number':
case 'collection-editor':
case 'collection-number':
case 'collection-title':
case 'composer':
case 'container':
case 'container-author':
case 'container-title':
case 'container-title-short':
case 'dimensions':
case 'director':
case 'edition':
case 'editor':
case 'editorial-director':
case 'event':
case 'event-date':
case 'event-place':
case 'first-reference-note-number':
case 'genre':
case 'illustrator':
case 'interviewer':
case 'issue':
case 'issued':
case 'jurisdiction':
case 'keyword':
case 'language':
case 'locator':
case 'medium':
case 'note':
case 'number':
case 'number-of-pages':
case 'number-of-volumes':
case 'original-author':
case 'original-date':
case 'original-publisher':
case 'original-publisher-place':
case 'original-title':
case 'page':
case 'page-first':
case 'publisher':
case 'publisher-place':
case 'recipient':
case 'references':
case 'reviewed-author':
case 'reviewed-title':
case 'scale':
case 'section':
case 'source':
case 'status':
case 'submitted':
case 'title':
case 'title-short':
case 'translator':
case 'version':
case 'volume':
case 'year-suffix':
break;
// Uppercase fields
case 'doi':
case 'isbn':
case 'issn':
case 'pmcid':
case 'pmid':
case 'url':
field = field.toUpperCase();
break;
// Weirdo
case 'archive-location':
field = 'archive_location';
break;
// Don't change other lines
default:
field = originalField;
}
return field + value;
});
} }
}; };
@ -596,7 +498,7 @@ Zotero.Cite.System.prototype = {
/** /**
* citeproc-js system function for getting items * citeproc-js system function for getting items
* See http://gsl-nagoya-u.net/http/pub/citeproc-doc.html#retrieveitem * See http://gsl-nagoya-u.net/http/pub/citeproc-doc.html#retrieveitem
* @param {String|Integer} item - Item ID, or string item for embedded citations * @param {String|Integer} Item ID, or string item for embedded citations
* @return {Object} citeproc-js item * @return {Object} citeproc-js item
*/ */
"retrieveItem":function retrieveItem(item) { "retrieveItem":function retrieveItem(item) {
@ -607,10 +509,10 @@ Zotero.Cite.System.prototype = {
} else if(typeof item === "string" && (slashIndex = item.indexOf("/")) !== -1) { } else if(typeof item === "string" && (slashIndex = item.indexOf("/")) !== -1) {
// is an embedded item // is an embedded item
var sessionID = item.substr(0, slashIndex); var sessionID = item.substr(0, slashIndex);
var session = Zotero.Integration.sessions[sessionID]; var session = Zotero.Integration.sessions[sessionID]
if(session) { if(session) {
var embeddedCitation = session.embeddedItems[item.substr(slashIndex+1)]; var embeddedCitation = session.embeddedItems[item.substr(slashIndex+1)];
if (embeddedCitation) { if(embeddedCitation) {
embeddedCitation.id = item; embeddedCitation.id = item;
return embeddedCitation; return embeddedCitation;
} }
@ -624,7 +526,7 @@ Zotero.Cite.System.prototype = {
} }
if(!zoteroItem) { if(!zoteroItem) {
throw new Error("Zotero.Cite.System.retrieveItem called on non-item "+item); throw "Zotero.Cite.System.retrieveItem called on non-item "+item;
} }
var cslItem = Zotero.Utilities.itemToCSLJSON(zoteroItem); var cslItem = Zotero.Utilities.itemToCSLJSON(zoteroItem);
@ -654,41 +556,21 @@ Zotero.Cite.System.prototype = {
* @return {String|Boolean} The locale as a string if it exists, or false if it doesn't * @return {String|Boolean} The locale as a string if it exists, or false if it doesn't
*/ */
"retrieveLocale":function retrieveLocale(lang) { "retrieveLocale":function retrieveLocale(lang) {
return Zotero.Cite.Locale.get(lang); var protHandler = Components.classes["@mozilla.org/network/protocol;1?name=chrome"]
}
};
Zotero.Cite.Locale = {
_cache: new Map(),
get: function (locale) {
var str = this._cache.get(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); .createInstance(Components.interfaces.nsIProtocolHandler);
let channel = protHandler.newChannel(protHandler.newURI(uri)); try {
let cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"] var channel = protHandler.newChannel(protHandler.newURI("chrome://zotero/content/locale/csl/locales-"+lang+".xml", "UTF-8", null));
.createInstance(Components.interfaces.nsIConverterInputStream); var rawStream = channel.open();
cstream.init(channel.open(), "UTF-8", 0, 0); } catch(e) {
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();
this._cache.set(locale, str);
return str;
}
catch (e) {
//Zotero.debug(e);
return false; return false;
} }
var converterStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
.createInstance(Components.interfaces.nsIConverterInputStream);
converterStream.init(rawStream, "UTF-8", 65535,
Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
var str = {};
converterStream.readString(channel.contentLength, str);
converterStream.close();
return str.value;
} }
}; };

View File

@ -0,0 +1,390 @@
var CSL_HOST = {
debug: function (str) {
Zotero.debug("CSL: " + str);
},
error: function (str) {
Zotero.debug("CSL error: " + str);
}
};
function DOMParser() {
return Components.classes["@mozilla.org/xmlextras/domparser;1"]
.createInstance(Components.interfaces.nsIDOMParser);
};
var CSL_IS_IE;
var CSL_CHROME = function () {
if ("undefined" == typeof DOMParser || CSL_IS_IE) {
CSL_IS_IE = true;
DOMParser = function() {};
DOMParser.prototype.parseFromString = function(str, contentType) {
if ("undefined" != typeof ActiveXObject) {
var xmldata = new ActiveXObject('MSXML.DomDocument');
xmldata.async = false;
xmldata.loadXML(str);
return xmldata;
} else if ("undefined" != typeof XMLHttpRequest) {
var xmldata = new XMLHttpRequest;
if (!contentType) {
contentType = 'text/xml';
}
xmldata.open('GET', 'data:' + contentType + ';charset=utf-8,' + encodeURIComponent(str), false);
if(xmldata.overrideMimeType) {
xmldata.overrideMimeType(contentType);
}
xmldata.send(null);
return xmldata.responseXML;
}
};
this.hasAttributes = function (node) {
var ret;
if (node.attributes && node.attributes.length) {
ret = true;
} else {
ret = false;
}
return ret;
};
} else {
this.hasAttributes = function (node) {
var ret;
if (node.attributes && node.attributes.length) {
ret = true;
} else {
ret = false;
}
return ret;
};
}
this.importNode = function (doc, srcElement) {
if ("undefined" == typeof doc.importNode) {
var ret = this._importNode(doc, srcElement, true);
} else {
var ret = doc.importNode(srcElement, true);
}
return ret;
};
this._importNode = function(doc, node, allChildren) {
switch (node.nodeType) {
case 1:
var newNode = doc.createElement(node.nodeName);
if (node.attributes && node.attributes.length > 0)
for (var i = 0, il = node.attributes.length; i < il;)
newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i++].nodeName));
if (allChildren && node.childNodes && node.childNodes.length > 0)
for (var i = 0, il = node.childNodes.length; i < il;)
newNode.appendChild(this._importNode(doc, node.childNodes[i++], allChildren));
return newNode;
break;
case 3:
case 4:
case 8:
}
};
this.parser = new DOMParser();
var str = "<docco><institution institution-parts=\"long\" delimiter=\", \" substitute-use-first=\"1\" use-last=\"1\"><institution-part name=\"long\"/></institution></docco>";
var inst_doc = this.parser.parseFromString(str, "text/xml");
var inst_node = inst_doc.getElementsByTagName("institution");
this.institution = inst_node.item(0);
var inst_part_node = inst_doc.getElementsByTagName("institution-part");
this.institutionpart = inst_part_node.item(0);
this.ns = "http://purl.org/net/xbiblio/csl";
};
CSL_CHROME.prototype.clean = function (xml) {
xml = xml.replace(/<\?[^?]+\?>/g, "");
xml = xml.replace(/<![^>]+>/g, "");
xml = xml.replace(/^\s+/, "");
xml = xml.replace(/\s+$/, "");
xml = xml.replace(/^\n*/, "");
return xml;
};
CSL_CHROME.prototype.getStyleId = function (myxml, styleName) {
var text = "";
var tagName = "id";
if (styleName) {
tagName = "title";
}
var node = myxml.getElementsByTagName(tagName);
if (node && node.length) {
node = node.item(0);
}
if (node) {
text = node.textContent;
}
if (!text) {
text = node.innerText;
}
if (!text) {
text = node.innerHTML;
}
return text;
};
CSL_CHROME.prototype.children = function (myxml) {
var children, pos, len, ret;
if (myxml) {
ret = [];
children = myxml.childNodes;
for (pos = 0, len = children.length; pos < len; pos += 1) {
if (children[pos].nodeName != "#text") {
ret.push(children[pos]);
}
}
return ret;
} else {
return [];
}
};
CSL_CHROME.prototype.nodename = function (myxml) {
var ret = myxml.nodeName;
return ret;
};
CSL_CHROME.prototype.attributes = function (myxml) {
var ret, attrs, attr, key, xml, pos, len;
ret = new Object();
if (myxml && this.hasAttributes(myxml)) {
attrs = myxml.attributes;
for (pos = 0, len=attrs.length; pos < len; pos += 1) {
attr = attrs[pos];
ret["@" + attr.name] = attr.value;
}
}
return ret;
};
CSL_CHROME.prototype.content = function (myxml) {
var ret;
if ("undefined" != typeof myxml.textContent) {
ret = myxml.textContent;
} else if ("undefined" != typeof myxml.innerText) {
ret = myxml.innerText;
} else {
ret = myxml.txt;
}
return ret;
};
CSL_CHROME.prototype.namespace = {
"xml":"http://www.w3.org/XML/1998/namespace"
}
CSL_CHROME.prototype.numberofnodes = function (myxml) {
if (myxml) {
return myxml.length;
} else {
return 0;
}
};
CSL_CHROME.prototype.getAttributeName = function (attr) {
var ret = attr.name;
return ret;
}
CSL_CHROME.prototype.getAttributeValue = function (myxml,name,namespace) {
var ret = "";
if (namespace) {
name = namespace+":"+name;
}
if (myxml && this.hasAttributes(myxml) && myxml.getAttribute(name)) {
ret = myxml.getAttribute(name);
}
return ret;
}
CSL_CHROME.prototype.getNodeValue = function (myxml,name) {
var ret = null;
if (name){
var vals = myxml.getElementsByTagName(name);
if (vals.length > 0) {
if ("undefined" != typeof vals[0].textContent) {
ret = vals[0].textContent;
} else if ("undefined" != typeof vals[0].innerText) {
ret = vals[0].innerText;
} else {
ret = vals[0].text;
}
}
}
if (ret === null && myxml && myxml.childNodes && (myxml.childNodes.length == 0 || (myxml.childNodes.length == 1 && myxml.firstChild.nodeName == "#text"))) {
if ("undefined" != typeof myxml.textContent) {
ret = myxml.textContent;
} else if ("undefined" != typeof myxml.innerText) {
ret = myxml.innerText;
} else {
ret = myxml.text;
}
}
if (ret === null) {
ret = myxml;
}
return ret;
}
CSL_CHROME.prototype.setAttributeOnNodeIdentifiedByNameAttribute = function (myxml,nodename,partname,attrname,val) {
var pos, len, xml, nodes, node;
if (attrname.slice(0,1) === '@'){
attrname = attrname.slice(1);
}
nodes = myxml.getElementsByTagName(nodename);
for (pos = 0, len = nodes.length; pos < len; pos += 1) {
node = nodes[pos];
if (node.getAttribute("name") != partname) {
continue;
}
node.setAttribute(attrname, val);
}
}
CSL_CHROME.prototype.deleteNodeByNameAttribute = function (myxml,val) {
var pos, len, node, nodes;
nodes = myxml.childNodes;
for (pos = 0, len = nodes.length; pos < len; pos += 1) {
node = nodes[pos];
if (!node || node.nodeType == node.TEXT_NODE) {
continue;
}
if (this.hasAttributes(node) && node.getAttribute("name") == val) {
myxml.removeChild(nodes[pos]);
}
}
}
CSL_CHROME.prototype.deleteAttribute = function (myxml,attr) {
myxml.removeAttribute(attr);
}
CSL_CHROME.prototype.setAttribute = function (myxml,attr,val) {
if (!myxml.ownerDocument) {
myxml = myxml.firstChild;
}
if (["function", "unknown"].indexOf(typeof myxml.setAttribute) > -1) {
myxml.setAttribute(attr, val);
}
return false;
}
CSL_CHROME.prototype.nodeCopy = function (myxml) {
var cloned_node = myxml.cloneNode(true);
return cloned_node;
}
CSL_CHROME.prototype.getNodesByName = function (myxml,name,nameattrval) {
var ret, nodes, node, pos, len;
ret = [];
nodes = myxml.getElementsByTagName(name);
for (pos = 0, len = nodes.length; pos < len; pos += 1) {
node = nodes.item(pos);
if (nameattrval && !(this.hasAttributes(node) && node.getAttribute("name") == nameattrval)) {
continue;
}
ret.push(node);
}
return ret;
}
CSL_CHROME.prototype.nodeNameIs = function (myxml,name) {
if (name == myxml.nodeName) {
return true;
}
return false;
}
CSL_CHROME.prototype.makeXml = function (myxml) {
var ret, topnode;
if (!myxml) {
myxml = "<docco><bogus/></docco>";
}
myxml = myxml.replace(/\s*<\?[^>]*\?>\s*\n*/g, "");
var nodetree = this.parser.parseFromString(myxml, "application/xml");
return nodetree.firstChild;
};
CSL_CHROME.prototype.insertChildNodeAfter = function (parent,node,pos,datexml) {
var myxml, xml;
myxml = this.importNode(node.ownerDocument, datexml);
parent.replaceChild(myxml, node);
return parent;
};
CSL_CHROME.prototype.insertPublisherAndPlace = function(myxml) {
var group = myxml.getElementsByTagName("group");
for (var i = 0, ilen = group.length; i < ilen; i += 1) {
var node = group.item(i);
var skippers = [];
for (var j = 0, jlen = node.childNodes.length; j < jlen; j += 1) {
if (node.childNodes.item(j).nodeType !== 1) {
skippers.push(j);
}
}
if (node.childNodes.length - skippers.length === 2) {
var twovars = [];
for (var j = 0, jlen = 2; j < jlen; j += 1) {
if (skippers.indexOf(j) > -1) {
continue;
}
var child = node.childNodes.item(j);
var subskippers = [];
for (var k = 0, klen = child.childNodes.length; k < klen; k += 1) {
if (child.childNodes.item(k).nodeType !== 1) {
subskippers.push(k);
}
}
if (child.childNodes.length - subskippers.length === 0) {
twovars.push(child.getAttribute('variable'));
if (child.getAttribute('suffix')
|| child.getAttribute('prefix')) {
twovars = [];
break;
}
}
}
if (twovars.indexOf("publisher") > -1 && twovars.indexOf("publisher-place") > -1) {
node.setAttribute('has-publisher-and-publisher-place', true);
}
}
}
};
CSL_CHROME.prototype.addMissingNameNodes = function(myxml) {
var nameslist = myxml.getElementsByTagName("names");
for (var i = 0, ilen = nameslist.length; i < ilen; i += 1) {
var names = nameslist.item(i);
var namelist = names.getElementsByTagName("name");
if ((!namelist || namelist.length === 0)
&& names.parentNode.tagName.toLowerCase() !== "substitute") {
var doc = names.ownerDocument;
var name = doc.createElement("name");
names.appendChild(name);
}
}
};
CSL_CHROME.prototype.addInstitutionNodes = function(myxml) {
var names, thenames, institution, theinstitution, name, thename, xml, pos, len;
names = myxml.getElementsByTagName("names");
for (pos = 0, len = names.length; pos < len; pos += 1) {
thenames = names.item(pos);
name = thenames.getElementsByTagName("name");
if (name.length == 0) {
continue;
}
institution = thenames.getElementsByTagName("institution");
if (institution.length == 0) {
theinstitution = this.importNode(myxml.ownerDocument, this.institution);
theinstitutionpart = theinstitution.getElementsByTagName("institution-part").item(0);
thename = name.item(0);
thenames.insertBefore(theinstitution, thename.nextSibling);
for (var j = 0, jlen = CSL.INSTITUTION_KEYS.length; j < jlen; j += 1) {
var attrname = CSL.INSTITUTION_KEYS[j];
var attrval = thename.getAttribute(attrname);
if (attrval) {
theinstitutionpart.setAttribute(attrname, attrval);
}
}
var nameparts = thename.getElementsByTagName("name-part");
for (var j = 0, jlen = nameparts.length; j < jlen; j += 1) {
if ('family' === nameparts[j].getAttribute('name')) {
for (var k = 0, klen = CSL.INSTITUTION_KEYS.length; k < klen; k += 1) {
var attrname = CSL.INSTITUTION_KEYS[k];
var attrval = nameparts[j].getAttribute(attrname);
if (attrval) {
theinstitutionpart.setAttribute(attrname, attrval);
}
}
}
}
}
}
};
CSL_CHROME.prototype.flagDateMacros = function(myxml) {
var pos, len, thenode, thedate;
nodes = myxml.getElementsByTagName("macro");
for (pos = 0, len = nodes.length; pos < len; pos += 1) {
thenode = nodes.item(pos);
thedate = thenode.getElementsByTagName("date");
if (thedate.length) {
thenode.setAttribute('macro-has-date', 'true');
}
}
};

File diff suppressed because it is too large Load Diff

View File

@ -25,8 +25,8 @@
"use strict"; "use strict";
Zotero.CollectionTreeRow = function (collectionTreeView, type, ref, level, isOpen) { Zotero.CollectionTreeRow = function(type, ref, level, isOpen)
this.view = collectionTreeView; {
this.type = type; this.type = type;
this.ref = ref; this.ref = ref;
this.level = level || 0 this.level = level || 0
@ -381,8 +381,8 @@ Zotero.CollectionTreeRow.prototype.getChildTags = Zotero.Promise.coroutine(funct
case 'bucket': case 'bucket':
return []; return [];
} }
var results = yield this.getSearchResults(true); var results = yield this.getSearchResults();
return Zotero.Tags.getAllWithinSearchResults(results); return Zotero.Tags.getAllWithinItemsList(results);
}); });

View File

@ -76,14 +76,6 @@ Object.defineProperty(Zotero.CollectionTreeView.prototype, "selectedTreeRow", {
}); });
Object.defineProperty(Zotero.CollectionTreeView.prototype, 'window', {
get: function () {
return this._ownerDocument.defaultView;
},
enumerable: true
});
/* /*
* Called by the tree itself * Called by the tree itself
*/ */
@ -95,13 +87,6 @@ Zotero.CollectionTreeView.prototype.setTree = Zotero.Promise.coroutine(function*
} }
this._treebox = treebox; this._treebox = treebox;
if (!this._ownerDocument) {
try {
this._ownerDocument = treebox.treeBody.ownerDocument;
}
catch (e) {}
}
// Add a keypress listener for expand/collapse // Add a keypress listener for expand/collapse
var tree = this._treebox.treeBody.parentNode; var tree = this._treebox.treeBody.parentNode;
tree.addEventListener('keypress', function(event) { tree.addEventListener('keypress', function(event) {
@ -193,7 +178,7 @@ Zotero.CollectionTreeView.prototype.refresh = Zotero.Promise.coroutine(function*
// //
this._addRowToArray( this._addRowToArray(
newRows, newRows,
new Zotero.CollectionTreeRow(this, 'library', { libraryID: Zotero.Libraries.userLibraryID }), new Zotero.CollectionTreeRow('library', { libraryID: Zotero.Libraries.userLibraryID }),
added++ added++
); );
added += yield this._expandRow(newRows, 0); added += yield this._expandRow(newRows, 0);
@ -205,12 +190,12 @@ Zotero.CollectionTreeView.prototype.refresh = Zotero.Promise.coroutine(function*
if (groups.length) { if (groups.length) {
this._addRowToArray( this._addRowToArray(
newRows, newRows,
new Zotero.CollectionTreeRow(this, 'separator', false), new Zotero.CollectionTreeRow('separator', false),
added++ added++
); );
this._addRowToArray( this._addRowToArray(
newRows, newRows,
new Zotero.CollectionTreeRow(this, 'header', { new Zotero.CollectionTreeRow('header', {
id: "group-libraries-header", id: "group-libraries-header",
label: Zotero.getString('pane.collections.groupLibraries'), label: Zotero.getString('pane.collections.groupLibraries'),
libraryID: -1 libraryID: -1
@ -220,7 +205,7 @@ Zotero.CollectionTreeView.prototype.refresh = Zotero.Promise.coroutine(function*
for (let group of groups) { for (let group of groups) {
this._addRowToArray( this._addRowToArray(
newRows, newRows,
new Zotero.CollectionTreeRow(this, 'group', group), new Zotero.CollectionTreeRow('group', group),
added++ added++
); );
added += yield this._expandRow(newRows, added - 1); added += yield this._expandRow(newRows, added - 1);
@ -240,12 +225,12 @@ Zotero.CollectionTreeView.prototype.refresh = Zotero.Promise.coroutine(function*
if (feeds.length) { if (feeds.length) {
this._addRowToArray( this._addRowToArray(
newRows, newRows,
new Zotero.CollectionTreeRow(this, 'separator', false), new Zotero.CollectionTreeRow('separator', false),
added++ added++
); );
this._addRowToArray( this._addRowToArray(
newRows, newRows,
new Zotero.CollectionTreeRow(this, 'header', { new Zotero.CollectionTreeRow('header', {
id: "feed-libraries-header", id: "feed-libraries-header",
label: Zotero.getString('pane.collections.feedLibraries'), label: Zotero.getString('pane.collections.feedLibraries'),
libraryID: -1 libraryID: -1
@ -255,7 +240,7 @@ Zotero.CollectionTreeView.prototype.refresh = Zotero.Promise.coroutine(function*
for (let feed of feeds) { for (let feed of feeds) {
this._addRowToArray( this._addRowToArray(
newRows, newRows,
new Zotero.CollectionTreeRow(this, 'feed', feed), new Zotero.CollectionTreeRow('feed', feed),
added++ added++
); );
} }
@ -609,7 +594,7 @@ Zotero.CollectionTreeView.prototype._addSortedRow = Zotero.Promise.coroutine(fun
} }
} }
this._addRow( this._addRow(
new Zotero.CollectionTreeRow(this, 'collection', collection, level), new Zotero.CollectionTreeRow('collection', collection, level),
beforeRow beforeRow
); );
} }
@ -649,7 +634,7 @@ Zotero.CollectionTreeView.prototype._addSortedRow = Zotero.Promise.coroutine(fun
} }
} }
this._addRow( this._addRow(
new Zotero.CollectionTreeRow(this, 'search', search, level), new Zotero.CollectionTreeRow('search', search, level),
beforeRow beforeRow
); );
} }
@ -784,7 +769,7 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col)
case 'search': case 'search':
// Keep in sync with Zotero.(Collection|Search).prototype.treeViewImage // Keep in sync with Zotero.(Collection|Search).prototype.treeViewImage
if (Zotero.isMac) { if (Zotero.isMac) {
return `chrome://zotero-platform/content/treesource-${collectionType}${Zotero.hiDPISuffix}.png`; return "chrome://zotero-platform/content/treesource-" + collectionType + ".png";
} }
break; break;
@ -1347,7 +1332,7 @@ Zotero.CollectionTreeView.prototype._expandRow = Zotero.Promise.coroutine(functi
let beforeRow = row + 1 + newRows; let beforeRow = row + 1 + newRows;
this._addRowToArray( this._addRowToArray(
rows, rows,
new Zotero.CollectionTreeRow(this, 'collection', collections[i], level + 1), new Zotero.CollectionTreeRow('collection', collections[i], level + 1),
beforeRow beforeRow
); );
newRows++; newRows++;
@ -1363,7 +1348,7 @@ Zotero.CollectionTreeView.prototype._expandRow = Zotero.Promise.coroutine(functi
for (var i = 0, len = savedSearches.length; i < len; i++) { for (var i = 0, len = savedSearches.length; i < len; i++) {
this._addRowToArray( this._addRowToArray(
rows, rows,
new Zotero.CollectionTreeRow(this, 'search', savedSearches[i], level + 1), new Zotero.CollectionTreeRow('search', savedSearches[i], level + 1),
row + 1 + newRows row + 1 + newRows
); );
newRows++; newRows++;
@ -1373,7 +1358,7 @@ Zotero.CollectionTreeView.prototype._expandRow = Zotero.Promise.coroutine(functi
// Add "My Publications" // Add "My Publications"
this._addRowToArray( this._addRowToArray(
rows, rows,
new Zotero.CollectionTreeRow(this, new Zotero.CollectionTreeRow(
'publications', 'publications',
{ {
libraryID, libraryID,
@ -1391,7 +1376,7 @@ Zotero.CollectionTreeView.prototype._expandRow = Zotero.Promise.coroutine(functi
let d = new Zotero.Duplicates(libraryID); let d = new Zotero.Duplicates(libraryID);
this._addRowToArray( this._addRowToArray(
rows, rows,
new Zotero.CollectionTreeRow(this, 'duplicates', d, level + 1), new Zotero.CollectionTreeRow('duplicates', d, level + 1),
row + 1 + newRows row + 1 + newRows
); );
newRows++; newRows++;
@ -1406,7 +1391,7 @@ Zotero.CollectionTreeView.prototype._expandRow = Zotero.Promise.coroutine(functi
s.addCondition('unfiled', 'true'); s.addCondition('unfiled', 'true');
this._addRowToArray( this._addRowToArray(
rows, rows,
new Zotero.CollectionTreeRow(this, 'unfiled', s, level + 1), new Zotero.CollectionTreeRow('unfiled', s, level + 1),
row + 1 + newRows row + 1 + newRows
); );
newRows++; newRows++;
@ -1420,7 +1405,7 @@ Zotero.CollectionTreeView.prototype._expandRow = Zotero.Promise.coroutine(functi
}; };
this._addRowToArray( this._addRowToArray(
rows, rows,
new Zotero.CollectionTreeRow(this, 'trash', ref, level + 1), new Zotero.CollectionTreeRow('trash', ref, level + 1),
row + 1 + newRows row + 1 + newRows
); );
newRows++; newRows++;
@ -1480,7 +1465,7 @@ Zotero.CollectionTreeView.prototype._saveOpenStates = Zotero.Promise.coroutine(f
for (var id in state) { for (var id in state) {
var m = id.match(/^C([0-9]+)$/); var m = id.match(/^C([0-9]+)$/);
if (m) { if (m) {
if (!(yield Zotero.Collections.getAsync(parseInt(m[1])))) { if (!(yield Zotero.Collections.getAsync(m[1]))) {
delete state[id]; delete state[id];
} }
continue; continue;
@ -1488,7 +1473,7 @@ Zotero.CollectionTreeView.prototype._saveOpenStates = Zotero.Promise.coroutine(f
var m = id.match(/^G([0-9]+)$/); var m = id.match(/^G([0-9]+)$/);
if (m) { if (m) {
if (!Zotero.Groups.get(parseInt(m[1]))) { if (!Zotero.Groups.get(m[1])) {
delete state[id]; delete state[id];
} }
continue; continue;
@ -1621,7 +1606,6 @@ Zotero.CollectionTreeView.prototype.canDropCheck = function (row, orient, dataTr
if (dataType == 'zotero/item') { if (dataType == 'zotero/item') {
var ids = data; var ids = data;
var items = Zotero.Items.get(ids); var items = Zotero.Items.get(ids);
items = Zotero.Items.keepParents(items);
var skip = true; var skip = true;
for (let item of items) { for (let item of items) {
// Can only drag top-level items // Can only drag top-level items
@ -1829,7 +1813,6 @@ Zotero.CollectionTreeView.prototype.canDropCheckAsync = Zotero.Promise.coroutine
if (treeRow.ref.libraryID != draggedCollection.libraryID) { if (treeRow.ref.libraryID != draggedCollection.libraryID) {
// Disallow if linked collection already exists // Disallow if linked collection already exists
if (yield draggedCollection.getLinkedCollection(treeRow.ref.libraryID, true)) { if (yield draggedCollection.getLinkedCollection(treeRow.ref.libraryID, true)) {
Zotero.debug("Linked collection already exists in library");
return false; return false;
} }
@ -1841,7 +1824,6 @@ Zotero.CollectionTreeView.prototype.canDropCheckAsync = Zotero.Promise.coroutine
// If this is allowed in the future for the root collection, // If this is allowed in the future for the root collection,
// need to allow drag only to root // need to allow drag only to root
if (yield descendent.getLinkedCollection(treeRow.ref.libraryID, true)) { if (yield descendent.getLinkedCollection(treeRow.ref.libraryID, true)) {
Zotero.debug("Linked subcollection already exists in library");
return false; return false;
} }
} }
@ -2133,7 +2115,6 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
} }
if (targetTreeRow.isPublications()) { if (targetTreeRow.isPublications()) {
items = Zotero.Items.keepParents(items);
let io = this._treebox.treeBody.ownerDocument.defaultView let io = this._treebox.treeBody.ownerDocument.defaultView
.ZoteroPane.showPublicationsWizard(items); .ZoteroPane.showPublicationsWizard(items);
if (!io) { if (!io) {
@ -2205,11 +2186,12 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
} }
*/ */
let lastWin = Services.wm.getMostRecentWindow("navigator:browser"); let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
let lastWin = wm.getMostRecentWindow("navigator:browser");
lastWin.openDialog('chrome://zotero/content/merge.xul', '', 'chrome,modal,centerscreen', io); lastWin.openDialog('chrome://zotero/content/merge.xul', '', 'chrome,modal,centerscreen', io);
yield Zotero.DB.executeTransaction(function* () { yield Zotero.DB.executeTransaction(function* () {
// DEBUG: This probably needs to be updated if this starts being used
for (let obj of io.dataOut) { for (let obj of io.dataOut) {
yield obj.ref.save(); yield obj.ref.save();
} }
@ -2244,23 +2226,24 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
} }
else if (dataType == 'text/x-moz-url' || dataType == 'application/x-moz-file') { else if (dataType == 'text/x-moz-url' || dataType == 'application/x-moz-file') {
var targetLibraryID = targetTreeRow.ref.libraryID; var targetLibraryID = targetTreeRow.ref.libraryID;
if (targetTreeRow.isCollection()) { if (targetTreeRow.isCollection()) {
var parentCollectionID = targetTreeRow.ref.id; var parentCollectionID = targetTreeRow.ref.id;
} }
else { else {
var parentCollectionID = false; var parentCollectionID = false;
} }
var addedItems = [];
for (var i=0; i<data.length; i++) { for (var i=0; i<data.length; i++) {
var file = data[i]; var file = data[i];
if (dataType == 'text/x-moz-url') { if (dataType == 'text/x-moz-url') {
var url = data[i]; var url = data[i];
let item;
if (url.indexOf('file:///') == 0) { if (url.indexOf('file:///') == 0) {
let win = Services.wm.getMostRecentWindow("navigator:browser"); var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
// If dragging currently loaded page, only convert to // If dragging currently loaded page, only convert to
// file if not an HTML document // file if not an HTML document
if (win.content.location.href != url || if (win.content.location.href != url ||
@ -2278,7 +2261,9 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
// Still string, so remote URL // Still string, so remote URL
if (typeof file == 'string') { if (typeof file == 'string') {
let win = Services.wm.getMostRecentWindow("navigator:browser"); var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
win.ZoteroPane.addItemFromURL(url, 'temporaryPDFHack', null, row); // TODO: don't do this win.ZoteroPane.addItemFromURL(url, 'temporaryPDFHack', null, row); // TODO: don't do this
continue; continue;
} }
@ -2287,13 +2272,13 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
} }
if (dropEffect == 'link') { if (dropEffect == 'link') {
item = yield Zotero.Attachments.linkFromFile({ yield Zotero.Attachments.linkFromFile({
file: file, file: file,
collections: parentCollectionID ? [parentCollectionID] : undefined collections: parentCollectionID ? [parentCollectionID] : undefined
}); });
} }
else { else {
item = yield Zotero.Attachments.importFromFile({ yield Zotero.Attachments.importFromFile({
file: file, file: file,
libraryID: targetLibraryID, libraryID: targetLibraryID,
collections: parentCollectionID ? [parentCollectionID] : undefined collections: parentCollectionID ? [parentCollectionID] : undefined
@ -2308,12 +2293,7 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
} }
} }
} }
addedItems.push(item);
} }
// Automatically retrieve metadata for PDFs
Zotero.RecognizePDF.autoRecognizeItems(addedItems);
} }
}); });
@ -2378,12 +2358,11 @@ Zotero.CollectionTreeCache = {
"clear": function () { "clear": function () {
this.lastTreeRow = null; this.lastTreeRow = null;
this.lastSearch = null; this.lastSearch = null;
if (this.lastTempTable) { if(this.lastTempTable) {
let tableName = this.lastTempTable; // Drop the last temp table when we can. We don't wait on this because it can cause a
let id = Zotero.DB.addCallback('commit', async function () { // deadlock: this waits on open transactions, but a transaction could be waiting on
await Zotero.DB.queryAsync("DROP TABLE IF EXISTS " + tableName); // ItemTreeView::notify(), which waits on ItemTreeView::refresh(), which calls this.
Zotero.DB.removeCallback('commit', id); Zotero.DB.queryTx("DROP TABLE IF EXISTS " + this.lastTempTable).done();
});
} }
this.lastTempTable = null; this.lastTempTable = null;
this.lastResults = null; this.lastResults = null;

View File

@ -1,192 +0,0 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2017 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
/**
* This is a HTTP-based integration interface for Zotero. The actual
* heavy lifting occurs in the connector and/or wherever the connector delegates the heavy
* lifting to.
*/
Zotero.HTTPIntegrationClient = {
deferredResponse: null,
sendCommandPromise: Zotero.Promise.resolve(),
sendCommand: async function(command, args=[]) {
let payload = JSON.stringify({command, arguments: args});
function sendCommand() {
Zotero.HTTPIntegrationClient.deferredResponse = Zotero.Promise.defer();
Zotero.HTTPIntegrationClient.sendResponse.apply(Zotero.HTTPIntegrationClient,
[200, 'application/json', payload]);
return Zotero.HTTPIntegrationClient.deferredResponse.promise;
}
// Force issued commands to occur sequentially, since these are really just
// a sequence of HTTP requests and responses.
// We might want to consider something better later, but this has the advantage of
// being easy to interface with as a Client, as you don't need SSE or WS.
if (command != 'Document.complete') {
Zotero.HTTPIntegrationClient.sendCommandPromise =
Zotero.HTTPIntegrationClient.sendCommandPromise.then(sendCommand, sendCommand);
} else {
await Zotero.HTTPIntegrationClient.sendCommandPromise;
sendCommand();
}
return Zotero.HTTPIntegrationClient.sendCommandPromise;
}
};
Zotero.HTTPIntegrationClient.Application = function() {
this.primaryFieldType = "Http";
this.secondaryFieldType = "Http";
this.outputFormat = 'html';
this.supportedNotes = ['footnotes'];
};
Zotero.HTTPIntegrationClient.Application.prototype = {
getActiveDocument: async function() {
let result = await Zotero.HTTPIntegrationClient.sendCommand('Application.getActiveDocument');
this.outputFormat = result.outputFormat || this.outputFormat;
this.supportedNotes = result.supportedNotes || this.supportedNotes;
return new Zotero.HTTPIntegrationClient.Document(result.documentID);
}
};
/**
* See integrationTests.js
*/
Zotero.HTTPIntegrationClient.Document = function(documentID) {
this._documentID = documentID;
};
for (let method of ["activate", "canInsertField", "displayAlert", "getDocumentData",
"setDocumentData", "setBibliographyStyle"]) {
Zotero.HTTPIntegrationClient.Document.prototype[method] = async function() {
return Zotero.HTTPIntegrationClient.sendCommand("Document."+method,
[this._documentID].concat(Array.prototype.slice.call(arguments)));
};
}
// @NOTE Currently unused, prompts are done using the connector
Zotero.HTTPIntegrationClient.Document.prototype._displayAlert = async function(dialogText, icon, buttons) {
var ps = Services.prompt;
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_OK)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING);
switch (buttons) {
case DIALOG_BUTTONS_OK:
buttonFlags = ps.BUTTON_POS_0_DEFAULT + ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK; break;
case DIALOG_BUTTONS_OK_CANCEL:
buttonFlags = ps.BUTTON_POS_0_DEFAULT + ps.STD_OK_CANCEL_BUTTONS; break;
case DIALOG_BUTTONS_YES_NO:
buttonFlags = ps.BUTTON_POS_0_DEFAULT + ps.STD_YES_NO_BUTTONS; break;
case DIALOG_BUTTONS_YES_NO_CANCEL:
buttonFlags = ps.BUTTON_POS_0_DEFAULT + ps.BUTTON_POS_0 * ps.BUTTON_TITLE_YES +
ps.BUTTON_POS_1 * ps.BUTTON_TITLE_NO +
ps.BUTTON_POS_2 * ps.BUTTON_TITLE_CANCEL; break;
}
var result = ps.confirmEx(
null,
"Zotero",
dialogText,
buttonFlags,
null, null, null,
null,
{}
);
switch (buttons) {
default:
break;
case DIALOG_BUTTONS_OK_CANCEL:
case DIALOG_BUTTONS_YES_NO:
result = (result+1)%2; break;
case DIALOG_BUTTONS_YES_NO_CANCEL:
result = result == 0 ? 2 : result == 2 ? 0 : 1; break;
}
await this.activate();
return result;
}
Zotero.HTTPIntegrationClient.Document.prototype.cleanup = async function() {};
Zotero.HTTPIntegrationClient.Document.prototype.cursorInField = async function(fieldType) {
var retVal = await Zotero.HTTPIntegrationClient.sendCommand("Document.cursorInField", [this._documentID, fieldType]);
if (!retVal) return null;
return new Zotero.HTTPIntegrationClient.Field(this._documentID, retVal);
};
Zotero.HTTPIntegrationClient.Document.prototype.insertField = async function(fieldType, noteType) {
var retVal = await Zotero.HTTPIntegrationClient.sendCommand("Document.insertField", [this._documentID, fieldType, parseInt(noteType) || 0]);
return new Zotero.HTTPIntegrationClient.Field(this._documentID, retVal);
};
Zotero.HTTPIntegrationClient.Document.prototype.getFields = async function(fieldType) {
var retVal = await Zotero.HTTPIntegrationClient.sendCommand("Document.getFields", [this._documentID, fieldType]);
return retVal.map(field => new Zotero.HTTPIntegrationClient.Field(this._documentID, field));
};
Zotero.HTTPIntegrationClient.Document.prototype.convert = async function(fields, fieldType, noteTypes) {
fields = fields.map((f) => f._id);
await Zotero.HTTPIntegrationClient.sendCommand("Field.convert", [this._documentID, fields, fieldType, noteTypes]);
};
Zotero.HTTPIntegrationClient.Document.prototype.complete = async function() {
Zotero.HTTPIntegrationClient.inProgress = false;
Zotero.HTTPIntegrationClient.sendCommand("Document.complete", [this._documentID]);
};
/**
* See integrationTests.js
*/
Zotero.HTTPIntegrationClient.Field = function(documentID, json) {
this._documentID = documentID;
this._id = json.id;
this._code = json.code;
this._text = json.text;
this._noteIndex = json.noteIndex;
};
Zotero.HTTPIntegrationClient.Field.prototype = {};
for (let method of ["delete", "select", "removeCode"]) {
Zotero.HTTPIntegrationClient.Field.prototype[method] = async function() {
return Zotero.HTTPIntegrationClient.sendCommand("Field."+method,
[this._documentID, this._id].concat(Array.prototype.slice.call(arguments)));
};
}
Zotero.HTTPIntegrationClient.Field.prototype.getText = async function() {
return this._text;
};
Zotero.HTTPIntegrationClient.Field.prototype.setText = async function(text, isRich) {
// The HTML will be stripped by Google Docs and and since we're
// caching this value, we need to strip it ourselves
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
.createInstance(Components.interfaces.nsIDOMParser);
var doc = parser.parseFromString(text, "text/html");
this._text = doc.documentElement.textContent;
return Zotero.HTTPIntegrationClient.sendCommand("Field.setText", [this._documentID, this._id, text, true]);
};
Zotero.HTTPIntegrationClient.Field.prototype.getCode = async function() {
return this._code;
};
Zotero.HTTPIntegrationClient.Field.prototype.setCode = async function(code) {
this._code = code;
return Zotero.HTTPIntegrationClient.sendCommand("Field.setCode", [this._documentID, this._id, code]);
};
Zotero.HTTPIntegrationClient.Field.prototype.getNoteIndex = async function() {
return this._noteIndex;
};
Zotero.HTTPIntegrationClient.Field.prototype.equals = async function(arg) {
return this._id === arg._id;
};

View File

@ -1,83 +0,0 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2017 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
***** END LICENSE BLOCK *****
*/
/**
* Adds integration endpoints related to doc integration via HTTP/connector.
*
* document/execCommand initiates an integration command and responds with the
* next request for the http client (e.g. 'Application.getDocument').
* The client should respond to document/respond with the payload and expect
* another response with the next request, until it receives 'Document.complete'
* at which point the integration transaction is considered complete.
*/
Zotero.Server.Endpoints['/connector/document/execCommand'] = function() {};
Zotero.Server.Endpoints['/connector/document/execCommand'].prototype = {
supportedMethods: ["POST"],
supportedDataTypes: ["application/json"],
permitBookmarklet: true,
init: function(data, sendResponse) {
if (Zotero.HTTPIntegrationClient.inProgress) {
// This will focus the last integration window if present
Zotero.Integration.execCommand('http', data.command, data.docId);
sendResponse(503, 'text/plain', 'Integration transaction is already in progress')
return;
}
Zotero.HTTPIntegrationClient.inProgress = true;
Zotero.HTTPIntegrationClient.sendResponse = sendResponse;
Zotero.Integration.execCommand('http', data.command, data.docId);
},
};
Zotero.Server.Endpoints['/connector/document/respond'] = function() {};
Zotero.Server.Endpoints['/connector/document/respond'].prototype = {
supportedMethods: ["POST"],
supportedDataTypes: ["application/json"],
permitBookmarklet: true,
init: function(data, sendResponse) {
data = JSON.parse(data);
if (data && data.error) {
// Apps Script stack is a JSON object
if (typeof data.stack != "string") {
data.stack = JSON.stringify(data.stack);
}
Zotero.HTTPIntegrationClient.deferredResponse.reject(data);
} else {
Zotero.HTTPIntegrationClient.deferredResponse.resolve(data);
}
Zotero.HTTPIntegrationClient.sendResponse = sendResponse;
}
};
// For managing macOS integration and progress window focus
Zotero.Server.Endpoints['/connector/sendToBack'] = function() {};
Zotero.Server.Endpoints['/connector/sendToBack'].prototype = {
supportedMethods: ["POST"],
supportedDataTypes: ["application/json"],
permitBookmarklet: true,
init: function() {
Zotero.Utilities.Internal.sendToBack();
},
};

View File

@ -219,6 +219,7 @@ Zotero.CreatorTypes = new function() {
Zotero.CachedTypes.apply(this, arguments); Zotero.CachedTypes.apply(this, arguments);
this.constructor.prototype = new Zotero.CachedTypes(); this.constructor.prototype = new Zotero.CachedTypes();
this.getTypesForItemType = getTypesForItemType;
this.isValidForItemType = isValidForItemType; this.isValidForItemType = isValidForItemType;
this._typeDesc = 'creator type'; this._typeDesc = 'creator type';
@ -236,8 +237,11 @@ Zotero.CreatorTypes = new function() {
this.init = Zotero.Promise.coroutine(function* () { this.init = Zotero.Promise.coroutine(function* () {
yield this.constructor.prototype.init.apply(this); yield this.constructor.prototype.init.apply(this);
var sql = "SELECT itemTypeID, creatorTypeID AS id, creatorType AS name, primaryField " var sql = "SELECT itemTypeID, creatorTypeID AS id, creatorType AS name "
+ "FROM itemTypeCreatorTypes NATURAL JOIN creatorTypes"; + "FROM itemTypeCreatorTypes NATURAL JOIN creatorTypes "
// DEBUG: sort needs to be on localized strings in itemPane.js
// (though still put primary field at top)
+ "ORDER BY primaryField=1 DESC, name";
var rows = yield Zotero.DB.queryAsync(sql); var rows = yield Zotero.DB.queryAsync(sql);
for (let i=0; i<rows.length; i++) { for (let i=0; i<rows.length; i++) {
let row = rows[i]; let row = rows[i];
@ -247,20 +251,7 @@ Zotero.CreatorTypes = new function() {
} }
_creatorTypesByItemType[itemTypeID].push({ _creatorTypesByItemType[itemTypeID].push({
id: row.id, id: row.id,
name: row.name, name: row.name
primaryField: row.primaryField,
localizedName: this.getLocalizedString(row.name)
});
}
// Sort primary field first, then by localized name
for (let itemTypeID in _creatorTypesByItemType) {
_creatorTypesByItemType[itemTypeID].sort((a, b) => {
if (a.primaryField != b.primaryField) return b.primaryField - a.primaryField;
return Zotero.localeCompare(a.localizedName, b.localizedName);
});
_creatorTypesByItemType[itemTypeID].forEach((x) => {
delete x.primaryField;
delete x.localizedName;
}); });
} }
@ -276,10 +267,11 @@ Zotero.CreatorTypes = new function() {
}); });
this.getTypesForItemType = function (itemTypeID) { function getTypesForItemType(itemTypeID) {
if (!_creatorTypesByItemType[itemTypeID]) { if (!_creatorTypesByItemType[itemTypeID]) {
return []; throw new Error("Creator types not loaded for itemTypeID " + itemTypeID);
} }
return _creatorTypesByItemType[itemTypeID]; return _creatorTypesByItemType[itemTypeID];
} }
@ -459,44 +451,44 @@ Zotero.ItemTypes = new function() {
// HiDPI images available // HiDPI images available
case 'attachment-link': case 'attachment-link':
case 'attachment-pdf':
case 'attachment-web-link': case 'attachment-web-link':
case 'artwork': case 'artwork':
case 'audioRecording': case 'audioRecording':
case 'bill': case 'bill':
case 'blogPost':
case 'book': case 'book':
case 'bookSection': case 'bookSection':
case 'case':
case 'computerProgram': case 'computerProgram':
case 'dictionaryEntry':
case 'email':
case 'encyclopediaArticle':
case 'film': case 'film':
case 'forumPost': case 'forumPost':
case 'hearing':
case 'instantMessage': case 'instantMessage':
case 'interview': case 'interview':
case 'journalArticle': case 'journalArticle':
case 'letter': case 'letter':
case 'magazineArticle': case 'magazineArticle':
case 'manuscript':
case 'newspaperArticle': case 'newspaperArticle':
case 'note': case 'note':
case 'patent':
case 'presentation':
case 'report': case 'report':
case 'statute':
case 'thesis':
case 'webpage': case 'webpage':
return "chrome://zotero/skin/treeitem-" + itemType + suffix + ".png"; return "chrome://zotero/skin/treeitem-" + itemType + suffix + ".png";
// No HiDPI images available // No HiDPI images available
case 'attachment-snapshot': case 'attachment-snapshot':
case 'attachment-pdf':
case 'blogPost':
case 'case':
case 'conferencePaper': case 'conferencePaper':
case 'dictionaryEntry':
case 'email':
case 'encyclopediaArticle':
case 'hearing':
case 'manuscript':
case 'map': case 'map':
case 'patent':
case 'podcast': case 'podcast':
case 'presentation':
case 'radioBroadcast': case 'radioBroadcast':
case 'statute':
case 'thesis':
case 'tvBroadcast': case 'tvBroadcast':
case 'videoRecording': case 'videoRecording':
return "chrome://zotero/skin/treeitem-" + itemType + ".png"; return "chrome://zotero/skin/treeitem-" + itemType + ".png";

View File

@ -80,8 +80,7 @@ Zotero.defineProperty(Zotero.Collection.prototype, 'parent', {
set: function(val) { set: function(val) {
Zotero.debug("WARNING: Zotero.Collection.prototype.parent has been deprecated -- use .parentID or .parentKey", 2); Zotero.debug("WARNING: Zotero.Collection.prototype.parent has been deprecated -- use .parentID or .parentKey", 2);
this.parentID = val; this.parentID = val;
}, }
enumerable: false
}); });
Zotero.defineProperty(Zotero.Collection.prototype, 'treeViewID', { Zotero.defineProperty(Zotero.Collection.prototype, 'treeViewID', {
@ -92,9 +91,8 @@ Zotero.defineProperty(Zotero.Collection.prototype, 'treeViewID', {
Zotero.defineProperty(Zotero.Collection.prototype, 'treeViewImage', { Zotero.defineProperty(Zotero.Collection.prototype, 'treeViewImage', {
get: function () { get: function () {
// Keep in sync with collectionTreeView::getImageSrc()
if (Zotero.isMac) { if (Zotero.isMac) {
return `chrome://zotero-platform/content/treesource-collection${Zotero.hiDPISuffix}.png`; return "chrome://zotero-platform/content/treesource-collection.png";
} }
return "chrome://zotero/skin/treesource-collection" + Zotero.hiDPISuffix + ".png"; return "chrome://zotero/skin/treesource-collection" + Zotero.hiDPISuffix + ".png";
} }
@ -429,7 +427,7 @@ Zotero.Collection.prototype.removeItems = Zotero.Promise.coroutine(function* (it
return; return;
} }
var current = this.getChildItems(true, true); var current = this.getChildItems(true);
Zotero.DB.requireTransaction(); Zotero.DB.requireTransaction();
for (let i=0; i<itemIDs.length; i++) { for (let i=0; i<itemIDs.length; i++) {
@ -571,7 +569,6 @@ Zotero.Collection.prototype._eraseData = Zotero.Promise.coroutine(function* (env
var descendents = this.getDescendents(false, null, true); var descendents = this.getDescendents(false, null, true);
var items = []; var items = [];
var libraryHasTrash = Zotero.Libraries.hasTrash(this.libraryID);
var del = []; var del = [];
var itemsToUpdate = []; var itemsToUpdate = [];
@ -589,23 +586,19 @@ Zotero.Collection.prototype._eraseData = Zotero.Promise.coroutine(function* (env
} }
// Descendent items // Descendent items
else { else {
// Trash/delete items // Delete items from DB
if (env.options.deleteItems) { if (env.options.deleteItems) {
del.push(descendents[i].id); del.push(descendents[i].id);
} }
else {
// If item isn't being removed or is just moving to the trash, mark for update
if (!env.options.deleteItems || libraryHasTrash) {
itemsToUpdate.push(descendents[i].id); itemsToUpdate.push(descendents[i].id);
} }
} }
} }
if (del.length) { if (del.length) {
if (libraryHasTrash) { if (Zotero.Libraries.hasTrash(this.libraryID)) {
yield this.ChildObjects.trash(del); yield this.ChildObjects.trash(del);
} } else {
// If library doesn't have trash, just erase
else {
Zotero.debug(Zotero.Libraries.getName(this.libraryID) + " library does not have trash. " Zotero.debug(Zotero.Libraries.getName(this.libraryID) + " library does not have trash. "
+ this.ChildObjects._ZDO_Objects + " will be erased"); + this.ChildObjects._ZDO_Objects + " will be erased");
let options = {}; let options = {};
@ -645,7 +638,7 @@ Zotero.Collection.prototype._eraseData = Zotero.Promise.coroutine(function* (env
env.deletedObjectIDs = collections; env.deletedObjectIDs = collections;
// Update collection cache for descendant items // Update collection cache for descendant items
if (itemsToUpdate.length) { if (!env.options.deleteItems) {
let deletedCollections = new Set(env.deletedObjectIDs); let deletedCollections = new Set(env.deletedObjectIDs);
itemsToUpdate.forEach(itemID => { itemsToUpdate.forEach(itemID => {
let item = Zotero.Items.get(itemID); let item = Zotero.Items.get(itemID);
@ -698,7 +691,8 @@ Zotero.Collection.prototype.fromJSON = function (json) {
this.name = json.name; this.name = json.name;
this.parentKey = json.parentCollection ? json.parentCollection : false; this.parentKey = json.parentCollection ? json.parentCollection : false;
this.setRelations(json.relations || {}); // TODO
//this.setRelations(json.relations);
} }
@ -712,7 +706,7 @@ Zotero.Collection.prototype.toJSON = function (options = {}) {
obj.name = this.name; obj.name = this.name;
obj.parentCollection = this.parentKey ? this.parentKey : false; obj.parentCollection = this.parentKey ? this.parentKey : false;
obj.relations = this.getRelations(); obj.relations = {}; // TEMP
return this._postToJSON(env); return this._postToJSON(env);
} }

View File

@ -31,12 +31,10 @@ Zotero.Creators = new function() {
var _cache = {}; var _cache = {};
this.init = Zotero.Promise.coroutine(function* () { this.init = Zotero.Promise.coroutine(function* () {
var repaired = false;
var sql = "SELECT * FROM creators"; var sql = "SELECT * FROM creators";
var rows = yield Zotero.DB.queryAsync(sql); var rows = yield Zotero.DB.queryAsync(sql);
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
let row = rows[i]; let row = rows[i];
try {
_cache[row.creatorID] = this.cleanData({ _cache[row.creatorID] = this.cleanData({
// Avoid "DB column 'name' not found" warnings from the DB row Proxy // Avoid "DB column 'name' not found" warnings from the DB row Proxy
firstName: row.firstName, firstName: row.firstName,
@ -44,21 +42,6 @@ Zotero.Creators = new function() {
fieldMode: row.fieldMode fieldMode: row.fieldMode
}); });
} }
catch (e) {
// Automatically fix DB errors and try again
if (!repaired) {
Zotero.logError(e);
Zotero.logError("Trying integrity check to fix creator error");
yield Zotero.Schema.integrityCheck(true);
repaired = true;
rows = yield Zotero.DB.queryAsync(sql);
i = -1;
continue;
}
throw e;
}
}
}); });
/* /*

View File

@ -1,5 +0,0 @@
"use strict";
Zotero.DataCache = {
};

View File

@ -285,7 +285,7 @@ Zotero.DataObject.prototype._setParentKey = function(key) {
/** /**
* Returns all relations of the object * Returns all relations of the object
* *
* @return {Object} - Object with predicates as keys and arrays of values * @return {Object} - Object with predicates as keys and arrays of URIs as values
*/ */
Zotero.DataObject.prototype.getRelations = function () { Zotero.DataObject.prototype.getRelations = function () {
this._requireData('relations'); this._requireData('relations');
@ -402,19 +402,8 @@ Zotero.DataObject.prototype.removeRelation = function (predicate, object) {
Zotero.DataObject.prototype.setRelations = function (newRelations) { Zotero.DataObject.prototype.setRelations = function (newRelations) {
this._requireData('relations'); this._requireData('relations');
if (typeof newRelations != 'object') {
throw new Error(`Relations must be an object (${typeof newRelations} given)`);
}
var oldRelations = this._relations; var oldRelations = this._relations;
// Limit predicates to letters and colons for now
for (let p in newRelations) {
if (!/^[a-z]+:[a-z]+$/i.test(p)) {
throw new Error(`Invalid relation predicate '${p}'`);
}
}
// Relations are stored internally as a flat array with individual predicate-object pairs, // Relations are stored internally as a flat array with individual predicate-object pairs,
// so convert the incoming relations to that // so convert the incoming relations to that
var newRelationsFlat = this.ObjectsClass.flattenRelations(newRelations); var newRelationsFlat = this.ObjectsClass.flattenRelations(newRelations);
@ -1171,7 +1160,7 @@ Zotero.DataObject.prototype.updateSynced = Zotero.Promise.coroutine(function* (s
*/ */
Zotero.DataObject.prototype.erase = Zotero.Promise.coroutine(function* (options = {}) { Zotero.DataObject.prototype.erase = Zotero.Promise.coroutine(function* (options = {}) {
if (!options || typeof options != 'object') { if (!options || typeof options != 'object') {
throw new Error("'options' must be an object (" + typeof options + ")"); throw new Error("'options' must be an object");
} }
var env = { var env = {
@ -1249,19 +1238,15 @@ Zotero.DataObject.prototype._finalizeErase = Zotero.Promise.coroutine(function*
}); });
Zotero.DataObject.prototype.toResponseJSON = function (options = {}) { Zotero.DataObject.prototype.toResponseJSON = function (options) {
// TODO: library block? // TODO: library block?
var json = { return {
key: this.key, key: this.key,
version: this.version, version: this.version,
meta: {}, meta: {},
data: this.toJSON(options) data: this.toJSON(options)
}; };
if (options.version) {
json.version = json.data.version = options.version;
}
return json;
} }
@ -1289,9 +1274,6 @@ Zotero.DataObject.prototype._postToJSON = function (env) {
if (env.mode == 'patch') { if (env.mode == 'patch') {
env.obj = Zotero.DataObjectUtilities.patch(env.options.patchBase, env.obj); env.obj = Zotero.DataObjectUtilities.patch(env.options.patchBase, env.obj);
} }
if (env.options.includeVersion === false) {
delete env.obj.version;
}
return env.obj; return env.obj;
} }

View File

@ -234,9 +234,14 @@ Zotero.DataObjectUtilities = {
}, },
_conditionsChanged: function (data1, data2) { _conditionsChanged: function (data1, data2) {
if (!data2 || data1.length != data2.length) return true; if (!data2) return true;
for (let i = 0; i < data1.length; i++) { var pred1 = Object.keys(data1);
if (!Zotero.Searches.conditionEquals(data1[i], data2[i])) { pred1.sort();
var pred2 = Object.keys(data2);
pred2.sort();
if (!Zotero.Utilities.arrayEquals(pred1, pred2)) return false;
for (let i in pred1) {
if (!Zotero.Utilities.arrayEquals(pred1[i], pred2[i])) {
return true; return true;
} }
} }

View File

@ -231,12 +231,6 @@ Zotero.DataObjects.prototype.getLoaded = function () {
} }
Zotero.DataObjects.prototype.getAllIDs = function (libraryID) {
var sql = `SELECT ${this._ZDO_id} FROM ${this._ZDO_table} WHERE libraryID=?`;
return Zotero.DB.columnQueryAsync(sql, [libraryID]);
};
Zotero.DataObjects.prototype.getAllKeys = function (libraryID) { Zotero.DataObjects.prototype.getAllKeys = function (libraryID) {
var sql = "SELECT key FROM " + this._ZDO_table + " WHERE libraryID=?"; var sql = "SELECT key FROM " + this._ZDO_table + " WHERE libraryID=?";
return Zotero.DB.columnQueryAsync(sql, [libraryID]); return Zotero.DB.columnQueryAsync(sql, [libraryID]);
@ -325,11 +319,6 @@ Zotero.DataObjects.prototype.exists = function (id) {
} }
Zotero.DataObjects.prototype.existsByKey = function (key) {
return !!this.getIDFromLibraryAndKey(id);
}
/** /**
* @return {Object} Object with 'libraryID' and 'key' * @return {Object} Object with 'libraryID' and 'key'
*/ */
@ -340,10 +329,9 @@ Zotero.DataObjects.prototype.getLibraryAndKeyFromID = function (id) {
Zotero.DataObjects.prototype.getIDFromLibraryAndKey = function (libraryID, key) { Zotero.DataObjects.prototype.getIDFromLibraryAndKey = function (libraryID, key) {
if (!libraryID) throw new Error("Library ID not provided"); if (!libraryID) {
// TEMP: Just warn for now throw new Error("libraryID not provided");
//if (!key) throw new Error("Key not provided"); }
if (!key) Zotero.logError("Key not provided");
return (this._objectIDs[libraryID] && this._objectIDs[libraryID][key]) return (this._objectIDs[libraryID] && this._objectIDs[libraryID][key])
? this._objectIDs[libraryID][key] : false; ? this._objectIDs[libraryID][key] : false;
} }
@ -921,7 +909,6 @@ Zotero.DataObjects.prototype.getPrimaryDataSQLPart = function (part) {
* *
* @param {Integer|Integer[]} ids - Object ids * @param {Integer|Integer[]} ids - Object ids
* @param {Object} [options] - See Zotero.DataObject.prototype.erase * @param {Object} [options] - See Zotero.DataObject.prototype.erase
* @param {Function} [options.onProgress] - f(progress, progressMax)
* @return {Promise} * @return {Promise}
*/ */
Zotero.DataObjects.prototype.erase = Zotero.Promise.coroutine(function* (ids, options = {}) { Zotero.DataObjects.prototype.erase = Zotero.Promise.coroutine(function* (ids, options = {}) {
@ -933,9 +920,6 @@ Zotero.DataObjects.prototype.erase = Zotero.Promise.coroutine(function* (ids, op
continue; continue;
} }
yield obj.erase(options); yield obj.erase(options);
if (options.onProgress) {
options.onProgress(i + 1, ids.length);
}
} }
this.unload(ids); this.unload(ids);
}.bind(this)); }.bind(this));

View File

@ -111,18 +111,9 @@ Zotero.defineProperty(Zotero.Feed.prototype, 'isFeed', {
value: true value: true
}); });
Zotero.defineProperty(Zotero.Feed.prototype, 'allowsLinkedFiles', {
value: false
});
Zotero.defineProperty(Zotero.Feed.prototype, 'libraryTypes', { Zotero.defineProperty(Zotero.Feed.prototype, 'libraryTypes', {
value: Object.freeze(Zotero.Feed._super.prototype.libraryTypes.concat(['feed'])) value: Object.freeze(Zotero.Feed._super.prototype.libraryTypes.concat(['feed']))
}); });
Zotero.defineProperty(Zotero.Feed.prototype, 'libraryTypeID', {
get: () => undefined
});
Zotero.defineProperty(Zotero.Feed.prototype, 'unreadCount', { Zotero.defineProperty(Zotero.Feed.prototype, 'unreadCount', {
get: function() { return this._feedUnreadCount; } get: function() { return this._feedUnreadCount; }
}); });

View File

@ -203,8 +203,8 @@ Zotero.FeedItem.prototype.toggleRead = Zotero.Promise.coroutine(function* (state
* Uses the item url to translate an existing feed item. * Uses the item url to translate an existing feed item.
* If libraryID empty, overwrites feed item, otherwise saves * If libraryID empty, overwrites feed item, otherwise saves
* in the library * in the library
* @param libraryID {Integer} save item in library * @param libraryID {int} save item in library
* @param collectionID {Integer} add item to collection * @param collectionID {int} add item to collection
* @return {Promise<FeedItem|Item>} translated feed item * @return {Promise<FeedItem|Item>} translated feed item
*/ */
Zotero.FeedItem.prototype.translate = Zotero.Promise.coroutine(function* (libraryID, collectionID) { Zotero.FeedItem.prototype.translate = Zotero.Promise.coroutine(function* (libraryID, collectionID) {
@ -234,12 +234,10 @@ Zotero.FeedItem.prototype.translate = Zotero.Promise.coroutine(function* (librar
} }
// Load document // Load document
let hiddenBrowser = Zotero.HTTP.loadDocuments( let hiddenBrowser = Zotero.HTTP.processDocuments(
this.getField('url'), this.getField('url'),
doc => deferred.resolve(doc), item => deferred.resolve(item),
() => {}, ()=>{}, error, true
error,
true
); );
let doc = yield deferred.promise; let doc = yield deferred.promise;
@ -254,7 +252,23 @@ Zotero.FeedItem.prototype.translate = Zotero.Promise.coroutine(function* (librar
if (!translators || !translators.length) { if (!translators || !translators.length) {
Zotero.debug("No translators detected for feed item " + this.id + " with URL " + this.getField('url') + Zotero.debug("No translators detected for feed item " + this.id + " with URL " + this.getField('url') +
' -- cloning item instead', 2); ' -- cloning item instead', 2);
let item = yield this.clone(libraryID, collectionID, doc); let dbItem = this.clone(libraryID);
if (collectionID) {
dbItem.addToCollection(collectionID);
}
yield dbItem.saveTx();
let item = {title: dbItem.getField('title'), itemType: dbItem.itemType};
// Add snapshot
if (Zotero.Libraries.get(libraryID).filesEditable) {
item.attachments = [{title: "Snapshot"}];
yield Zotero.Attachments.importFromDocument({
document: doc,
parentItemID: dbItem.id
});
}
progressWindow.Translation.itemDoneHandler()(null, null, item); progressWindow.Translation.itemDoneHandler()(null, null, item);
progressWindow.Translation.doneHandler(null, true); progressWindow.Translation.doneHandler(null, true);
return; return;
@ -267,12 +281,6 @@ Zotero.FeedItem.prototype.translate = Zotero.Promise.coroutine(function* (librar
let result = yield translate.translate({libraryID, collections: collectionID ? [collectionID] : false}) let result = yield translate.translate({libraryID, collections: collectionID ? [collectionID] : false})
.then(items => items ? items[0] : false); .then(items => items ? items[0] : false);
Zotero.Browser.deleteHiddenBrowser(hiddenBrowser); Zotero.Browser.deleteHiddenBrowser(hiddenBrowser);
if (!result) {
let item = yield this.clone(libraryID, collectionID, doc);
progressWindow.Translation.itemDoneHandler()(null, null, item);
progressWindow.Translation.doneHandler(null, true);
return;
}
return result; return result;
} }
@ -299,30 +307,3 @@ Zotero.FeedItem.prototype.translate = Zotero.Promise.coroutine(function* (librar
return this; return this;
}); });
/**
* Clones the feed item (usually, when proper translation is unavailable)
* @param libraryID {Integer} save item in library
* @param collectionID {Integer} add item to collection
* @return {Promise<FeedItem|Item>} translated feed item
*/
Zotero.FeedItem.prototype.clone = Zotero.Promise.coroutine(function* (libraryID, collectionID, doc) {
let dbItem = Zotero.Item.prototype.clone.call(this, libraryID);
if (collectionID) {
dbItem.addToCollection(collectionID);
}
yield dbItem.saveTx();
let item = {title: dbItem.getField('title'), itemType: dbItem.itemType, attachments: []};
// Add snapshot
if (Zotero.Libraries.get(libraryID).filesEditable) {
item.attachments = [{title: "Snapshot"}];
yield Zotero.Attachments.importFromDocument({
document: doc,
parentItemID: dbItem.id
});
}
return item;
});

View File

@ -86,10 +86,6 @@ Zotero.defineProperty(Zotero.Group.prototype, 'id', {
set: function(v) { return this.groupID = v; } set: function(v) { return this.groupID = v; }
}); });
Zotero.defineProperty(Zotero.Group.prototype, 'allowsLinkedFiles', {
value: false
});
// Create accessors // Create accessors
(function() { (function() {
let accessors = ['name', 'description', 'version']; let accessors = ['name', 'description', 'version'];

View File

@ -105,8 +105,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'itemID', {
get: function() { get: function() {
Zotero.debug("Item.itemID is deprecated -- use Item.id"); Zotero.debug("Item.itemID is deprecated -- use Item.id");
return this._id; return this._id;
}, }
enumerable: false
}); });
Zotero.defineProperty(Zotero.Item.prototype, 'libraryID', { Zotero.defineProperty(Zotero.Item.prototype, 'libraryID', {
get: function() { return this._libraryID; }, get: function() { return this._libraryID; },
@ -145,9 +144,6 @@ Zotero.defineProperty(Zotero.Item.prototype, 'parentItemKey', {
get: function() { return this.parentKey; }, get: function() { return this.parentKey; },
set: function(val) { return this.parentKey = val; } set: function(val) { return this.parentKey = val; }
}); });
Zotero.defineProperty(Zotero.Item.prototype, 'parentItem', {
get: function() { return Zotero.Items.get(this.parentID) || undefined; },
});
Zotero.defineProperty(Zotero.Item.prototype, 'firstCreator', { Zotero.defineProperty(Zotero.Item.prototype, 'firstCreator', {
@ -353,10 +349,7 @@ Zotero.Item.prototype._parseRowData = function(row) {
break; break;
case 'attachmentLinkMode': case 'attachmentLinkMode':
val = val !== null val = val !== null ? parseInt(val) : false;
? parseInt(val)
// Shouldn't happen
: Zotero.Attachments.LINK_MODE_IMPORTED_URL;
break; break;
case 'attachmentPath': case 'attachmentPath':
@ -1051,7 +1044,8 @@ Zotero.Item.prototype.setCreator = function (orderIndex, data) {
var msg = "Creator type '" + Zotero.CreatorTypes.getName(data.creatorTypeID) + "' " var msg = "Creator type '" + Zotero.CreatorTypes.getName(data.creatorTypeID) + "' "
+ "isn't valid for " + Zotero.ItemTypes.getName(itemTypeID) + "isn't valid for " + Zotero.ItemTypes.getName(itemTypeID)
+ " -- changing to primary creator"; + " -- changing to primary creator";
Zotero.warn(msg); Zotero.debug(msg, 2);
Components.utils.reportError(msg);
data.creatorTypeID = Zotero.CreatorTypes.getPrimaryIDForType(itemTypeID); data.creatorTypeID = Zotero.CreatorTypes.getPrimaryIDForType(itemTypeID);
} }
@ -1379,9 +1373,7 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
// Parent item (DB update is done below after collection removals) // Parent item (DB update is done below after collection removals)
var parentItemKey = this.parentKey; var parentItemKey = this.parentKey;
var parentItemID = parentItemKey var parentItemID = this.ObjectsClass.getIDFromLibraryAndKey(this.libraryID, parentItemKey) || null;
? (this.ObjectsClass.getIDFromLibraryAndKey(this.libraryID, parentItemKey) || null)
: null;
if (this._changed.parentKey) { if (this._changed.parentKey) {
if (isNew) { if (isNew) {
if (!parentItemID) { if (!parentItemID) {
@ -1625,11 +1617,6 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
yield Zotero.DB.queryAsync(sql, [parentItemID, this.id]); yield Zotero.DB.queryAsync(sql, [parentItemID, this.id]);
} }
// There's no reload for parentKey, so clear it here
if (this._changed.parentKey) {
this._clearChanged('parentKey');
}
// Note // Note
if ((isNew && this.isNote()) || this._changed.note) { if ((isNew && this.isNote()) || this._changed.note) {
if (!isNew) { if (!isNew) {
@ -1643,7 +1630,8 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
let noteText = this._noteText ? this._noteText : ''; let noteText = this._noteText ? this._noteText : '';
// Add <div> wrapper if not present // Add <div> wrapper if not present
if (!noteText.match(/^<div class="zotero-note znv[0-9]+">[\s\S]*<\/div>$/)) { if (!noteText.match(/^<div class="zotero-note znv[0-9]+">[\s\S]*<\/div>$/)) {
noteText = Zotero.Notes.notePrefix + noteText + Zotero.Notes.noteSuffix; // Keep consistent with getNote()
noteText = '<div class="zotero-note znv1">' + noteText + '</div>';
} }
let params = [ let params = [
@ -2121,15 +2109,6 @@ Zotero.Item.prototype.numAttachments = function (includeTrashed) {
} }
Zotero.Item.prototype.numNonHTMLFileAttachments = function () {
this._requireData('childItems');
return this.getAttachments()
.map(itemID => Zotero.Items.get(itemID))
.filter(item => item.isFileAttachment() && item.attachmentContentType != 'text/html')
.length;
};
Zotero.Item.prototype.getFile = function () { Zotero.Item.prototype.getFile = function () {
Zotero.debug("Zotero.Item.prototype.getFile() is deprecated -- use getFilePath[Async]()", 2); Zotero.debug("Zotero.Item.prototype.getFile() is deprecated -- use getFilePath[Async]()", 2);
@ -2206,21 +2185,13 @@ Zotero.Item.prototype.getFilePath = function () {
// //
// These should only exist if they weren't converted in the 80 DB upgrade step because // These should only exist if they weren't converted in the 80 DB upgrade step because
// the file couldn't be found. // the file couldn't be found.
if (path.startsWith('AAAA')) { if (Zotero.isMac && path.startsWith('AAAA')) {
// These can only be resolved on Macs
if (!Zotero.isMac) {
Zotero.debug(`Can't resolve old-style attachment path '${path}' on non-Mac platform`);
this._updateAttachmentStates(false);
return false;
}
let file = Components.classes["@mozilla.org/file/local;1"] let file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile); .createInstance(Components.interfaces.nsILocalFile);
try { try {
file.persistentDescriptor = path; file.persistentDescriptor = path;
} }
catch (e) { catch (e) {
Zotero.debug(`Can't resolve old-style attachment path '${path}'`);
this._updateAttachmentStates(false); this._updateAttachmentStates(false);
return false; return false;
} }
@ -2228,7 +2199,7 @@ Zotero.Item.prototype.getFilePath = function () {
// If valid, convert this to a regular string in the background // If valid, convert this to a regular string in the background
Zotero.DB.queryAsync( Zotero.DB.queryAsync(
"UPDATE itemAttachments SET path=? WHERE itemID=?", "UPDATE itemAttachments SET path=? WHERE itemID=?",
[file.path, this._id] [file.leafName, this._id]
); );
return file.path; return file.path;
@ -2431,78 +2402,73 @@ Zotero.Item.prototype.fileExistsCached = function () {
/** /*
* Rename file associated with an attachment * Rename file associated with an attachment
* *
* @param {String} newName * -1 Destination file exists -- use _force_ to overwrite
* @param {Boolean} [overwrite=false] - Overwrite file if one exists * -2 Error renaming
* @param {Boolean} [unique=false] - Add suffix to create unique filename if necessary * false Attachment file not found
* @return {Number|false} -- true - Rename successful
* -1 - Destination file exists; use _force_ to overwrite
* -2 - Error renaming
* false - Attachment file not found
*/ */
Zotero.Item.prototype.renameAttachmentFile = async function (newName, overwrite = false, unique = false) { Zotero.Item.prototype.renameAttachmentFile = Zotero.Promise.coroutine(function* (newName, overwrite) {
var origPath = await this.getFilePathAsync(); var origPath = yield this.getFilePathAsync();
if (!origPath) { if (!origPath) {
Zotero.debug("Attachment file not found in renameAttachmentFile()", 2); Zotero.debug("Attachment file not found in renameAttachmentFile()", 2);
return false; return false;
} }
try { try {
let origName = OS.Path.basename(origPath); var origName = OS.Path.basename(origPath);
if (this.isImportedAttachment()) { var origModDate = (yield OS.File.stat(origPath)).lastModificationDate;
var origModDate = (await OS.File.stat(origPath)).lastModificationDate;
}
// No change newName = Zotero.File.getValidFileName(newName);
// Ignore if no change
if (origName === newName) { if (origName === newName) {
Zotero.debug("Filename has not changed"); Zotero.debug("Filename has not changed");
return true; return true;
} }
var destPath = OS.Path.join(OS.Path.dirname(origPath), newName);
var destName = OS.Path.basename(destPath);
// Update mod time and clear hash so the file syncs // Update mod time and clear hash so the file syncs
// TODO: use an integer counter instead of mod time for change detection // TODO: use an integer counter instead of mod time for change detection
// Update mod time first, because it may fail for read-only files on Windows // Update mod time first, because it may fail for read-only files on Windows
if (this.isImportedAttachment()) { yield OS.File.setDates(origPath, null, null);
await OS.File.setDates(origPath, null, null); var result = yield OS.File.move(origPath, destPath, { noOverwrite: !overwrite })
// If no overwriting and file exists, return -1
.catch(OS.File.Error, function (e) {
if (e.becauseExists) {
return -1;
}
throw e;
});
if (result) {
return result;
} }
newName = await Zotero.File.rename( yield this.relinkAttachmentFile(destPath);
origPath,
newName,
{
overwrite,
unique
}
);
let destPath = OS.Path.join(OS.Path.dirname(origPath), newName);
await this.relinkAttachmentFile(destPath);
if (this.isImportedAttachment()) { if (this.isImportedAttachment()) {
this.attachmentSyncedHash = null; this.attachmentSyncedHash = null;
this.attachmentSyncState = "to_upload"; this.attachmentSyncState = "to_upload";
await this.saveTx({ skipAll: true }); yield this.saveTx({ skipAll: true });
} }
return true; return true;
} }
catch (e) { catch (e) {
Zotero.logError(e);
// Restore original modification date in case we managed to change it // Restore original modification date in case we managed to change it
if (this.isImportedAttachment()) {
try { try {
OS.File.setDates(origPath, null, origModDate); OS.File.setDates(origPath, null, origModDate);
} catch (e) { } catch (e) {
Zotero.debug(e, 2); Zotero.debug(e, 2);
} }
} Zotero.debug(e);
Components.utils.reportError(e);
return -2; return -2;
} }
}; });
/** /**
@ -2714,8 +2680,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentMIMEType', {
get: function() { get: function() {
Zotero.debug(".attachmentMIMEType deprecated -- use .attachmentContentType"); Zotero.debug(".attachmentMIMEType deprecated -- use .attachmentContentType");
return this.attachmentContentType; return this.attachmentContentType;
}, }
enumerable: false
}); });
/** /**
@ -2811,7 +2776,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentFilename', {
if (!path) { if (!path) {
return ''; return '';
} }
var prefixedPath = path.match(/^(?:attachments|storage):(.*)$/); var prefixedPath = path.match(/^(?:attachments|storage):(.+)$/);
if (prefixedPath) { if (prefixedPath) {
return prefixedPath[1]; return prefixedPath[1];
} }
@ -3115,6 +3080,7 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentHash', {
* *
* - Currently works on HTML, PDF and plaintext attachments * - Currently works on HTML, PDF and plaintext attachments
* - Paragraph breaks will be lost in PDF content * - Paragraph breaks will be lost in PDF content
* - For PDFs, will return empty string if Zotero.Fulltext.pdfConverterIsRegistered() is false
* *
* @return {Promise<String>} - A promise for attachment text or empty string if unavailable * @return {Promise<String>} - A promise for attachment text or empty string if unavailable
*/ */
@ -3168,6 +3134,10 @@ Zotero.defineProperty(Zotero.Item.prototype, 'attachmentText', {
} }
if (reindex) { if (reindex) {
if (!Zotero.Fulltext.pdfConverterIsRegistered()) {
Zotero.debug("PDF converter is unavailable -- returning empty .attachmentText", 3);
return '';
}
yield Zotero.Fulltext.indexItems(this.id, false); yield Zotero.Fulltext.indexItems(this.id, false);
} }
@ -3535,7 +3505,7 @@ Zotero.Item.prototype.setCollections = function (collectionIDsOrKeys) {
var id = this.ContainerObjectsClass.getIDFromLibraryAndKey(this.libraryID, val); var id = this.ContainerObjectsClass.getIDFromLibraryAndKey(this.libraryID, val);
if (!id) { if (!id) {
let e = new Error("Collection " + val + " not found for item " + this.libraryKey); let e = new Error("Collection " + val + " not found for item " + this.libraryKey);
e.name = "ZoteroMissingObjectError"; e.name = "ZoteroObjectNotFoundError";
throw e; throw e;
} }
return id; return id;
@ -3987,89 +3957,6 @@ Zotero.Item.prototype.clone = function (libraryID, options = {}) {
} }
/**
* @param {Zotero.Item} item
* @param {Integer} libraryID
* @return {Zotero.Item} - New item
*/
Zotero.Item.prototype.moveToLibrary = async function (libraryID, onSkippedAttachment) {
if (!this.isEditable) {
throw new Error("Can't move item in read-only library");
}
var library = Zotero.Libraries.get(libraryID);
Zotero.debug("Moving item to " + library.name);
if (!library.editable) {
throw new Error("Can't move item to read-only library");
}
var filesEditable = library.filesEditable;
var allowsLinkedFiles = library.allowsLinkedFiles;
var newItem = await Zotero.DB.executeTransaction(async function () {
// Create new clone item in target library
var newItem = this.clone(libraryID);
var newItemID = await newItem.save({
skipSelect: true
});
if (this.isNote()) {
// Delete old item
await this.erase();
return newItem;
}
// For regular items, add child items
// Child notes
var noteIDs = this.getNotes();
var notes = Zotero.Items.get(noteIDs);
for (let note of notes) {
let newNote = note.clone(libraryID);
newNote.parentID = newItemID;
await newNote.save({
skipSelect: true
});
}
// Child attachments
var attachmentIDs = this.getAttachments();
var attachments = Zotero.Items.get(attachmentIDs);
for (let attachment of attachments) {
let linkMode = attachment.attachmentLinkMode;
// Skip linked files if not allowed in destination
if (!allowsLinkedFiles && linkMode == Zotero.Attachments.LINK_MODE_LINKED_FILE) {
Zotero.debug("Target library doesn't support linked files -- skipping attachment");
if (onSkippedAttachment) {
await onSkippedAttachment(attachment);
}
continue;
}
// Skip files if not allowed in destination
if (!filesEditable && linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
Zotero.debug("Target library doesn't allow file editing -- skipping attachment");
if (onSkippedAttachment) {
await onSkippedAttachment(attachment);
}
continue;
}
await Zotero.Attachments.moveAttachmentToLibrary(
attachment, libraryID, newItemID
);
}
return newItem;
}.bind(this));
// Delete old item. Do this outside of a transaction so we don't leave stranded files
// in the target library if deleting fails.
await this.eraseTx();
return newItem;
};
Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) { Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) {
Zotero.DB.requireTransaction(); Zotero.DB.requireTransaction();
@ -4085,13 +3972,13 @@ Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) {
? (yield this.ObjectsClass.getByLibraryAndKeyAsync(this.libraryID, parentItem)) ? (yield this.ObjectsClass.getByLibraryAndKeyAsync(this.libraryID, parentItem))
: null; : null;
if (parentItem && !env.options.skipParentRefresh) { if (parentItem) {
Zotero.Notifier.queue('refresh', 'item', parentItem.id); Zotero.Notifier.queue('refresh', 'item', parentItem.id);
} }
// // Delete associated attachment files // // Delete associated attachment files
if (this.isAttachment()) { if (this.isAttachment()) {
let linkMode = this.attachmentLinkMode; let linkMode = this.getAttachmentLinkMode();
// If link only, nothing to delete // If link only, nothing to delete
if (linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) { if (linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
try { try {
@ -4118,9 +4005,7 @@ Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) {
for (let i=0; i<toDelete.length; i++) { for (let i=0; i<toDelete.length; i++) {
let obj = yield this.ObjectsClass.getAsync(toDelete[i]); let obj = yield this.ObjectsClass.getAsync(toDelete[i]);
// Copy all options other than 'tx', which would cause a deadlock // Copy all options other than 'tx', which would cause a deadlock
let options = { let options = {};
skipParentRefresh: true
};
Object.assign(options, env.options); Object.assign(options, env.options);
delete options.tx; delete options.tx;
yield obj.erase(options); yield obj.erase(options);
@ -4144,7 +4029,7 @@ Zotero.Item.prototype._eraseData = Zotero.Promise.coroutine(function* (env) {
yield Zotero.DB.queryAsync('DELETE FROM items WHERE itemID=?', this.id); yield Zotero.DB.queryAsync('DELETE FROM items WHERE itemID=?', this.id);
if (parentItem && !env.options.skipParentRefresh) { if (parentItem) {
yield parentItem.reload(['primaryData', 'childItems'], true); yield parentItem.reload(['primaryData', 'childItems'], true);
parentItem.clearBestAttachmentState(); parentItem.clearBestAttachmentState();
} }
@ -4194,9 +4079,6 @@ Zotero.Item.prototype.fromJSON = function (json) {
case 'mtime': case 'mtime':
// Handled below // Handled below
case 'collections': case 'collections':
case 'parentItem':
case 'deleted':
case 'inPublications':
break; break;
case 'accessDate': case 'accessDate':
@ -4225,6 +4107,15 @@ Zotero.Item.prototype.fromJSON = function (json) {
this[field] = val; this[field] = val;
break; break;
case 'parentItem':
this.parentKey = val;
break;
case 'deleted':
case 'inPublications':
this[field] = !!val;
break;
case 'creators': case 'creators':
this.setCreators(json.creators); this.setCreators(json.creators);
break; break;
@ -4312,13 +4203,6 @@ Zotero.Item.prototype.fromJSON = function (json) {
let note = json.note; let note = json.note;
this.setNote(note !== undefined ? note : ""); this.setNote(note !== undefined ? note : "");
} }
// Update boolean fields that might not be present in JSON
['deleted', 'inPublications'].forEach(field => {
if (json[field] || this[field]) {
this[field] = !!json[field];
}
});
} }
@ -4406,9 +4290,7 @@ Zotero.Item.prototype.toJSON = function (options = {}) {
} }
// My Publications // My Publications
if (this._inPublications if (this._inPublications || mode == 'full') {
// Include in 'full' mode, but only in My Library
|| (mode == 'full' && this.library && this.library.libraryType == 'user')) {
obj.inPublications = this._inPublications; obj.inPublications = this._inPublications;
} }

View File

@ -260,11 +260,6 @@ Zotero.ItemFields = new function() {
throw new Error("Invalid field '" + baseField + '" for base field'); throw new Error("Invalid field '" + baseField + '" for base field');
} }
// If field isn't a base field, return it if it's valid for the type
if (!this.isBaseField(baseFieldID)) {
return this.isValidForType(baseFieldID, itemTypeID) ? baseFieldID : false;
}
return _baseTypeFields[itemTypeID][baseFieldID]; return _baseTypeFields[itemTypeID][baseFieldID];
} }

View File

@ -315,22 +315,8 @@ Zotero.Items = function() {
item._clearChanged('itemData'); item._clearChanged('itemData');
// Display titles // Display titles
try {
item.updateDisplayTitle() item.updateDisplayTitle()
} }
catch (e) {
// A few item types need creators to be loaded. Instead of making
// updateDisplayTitle() async and loading conditionally, just catch the error
// and load on demand
if (e instanceof Zotero.Exception.UnloadedDataException) {
yield item.loadDataType('creators');
item.updateDisplayTitle()
}
else {
throw e;
}
}
}
}); });
@ -347,9 +333,6 @@ Zotero.Items = function() {
Zotero.debug("Fixing incorrect creator indexes for item " + item.libraryKey Zotero.debug("Fixing incorrect creator indexes for item " + item.libraryKey
+ " (" + numCreators + ", " + maxOrderIndex + ")", 2); + " (" + numCreators + ", " + maxOrderIndex + ")", 2);
var i = numCreators; var i = numCreators;
if (!item._changed.creators) {
item._changed.creators = {};
}
while (i <= maxOrderIndex) { while (i <= maxOrderIndex) {
item._changed.creators[i] = true; item._changed.creators[i] = true;
i++; i++;
@ -429,10 +412,6 @@ Zotero.Items = function() {
// Convert non-HTML notes on-the-fly // Convert non-HTML notes on-the-fly
if (note !== "") { if (note !== "") {
if (typeof note == 'number') {
note = '' + note;
}
if (typeof note == 'string') {
if (!note.substr(0, 36).match(/^<div class="zotero-note znv[0-9]+">/)) { if (!note.substr(0, 36).match(/^<div class="zotero-note znv[0-9]+">/)) {
note = Zotero.Utilities.htmlSpecialChars(note); note = Zotero.Utilities.htmlSpecialChars(note);
note = Zotero.Notes.notePrefix + '<p>' note = Zotero.Notes.notePrefix + '<p>'
@ -449,12 +428,6 @@ Zotero.Items = function() {
let endLen = 6; // "</div>".length let endLen = 6; // "</div>".length
note = note.substr(startLen, note.length - startLen - endLen); note = note.substr(startLen, note.length - startLen - endLen);
} }
// Clear null notes
else {
note = '';
notesToUpdate.push([item.id, '']);
}
}
item._noteText = note ? note : ''; item._noteText = note ? note : '';
item._loaded.note = true; item._loaded.note = true;
@ -749,8 +722,6 @@ Zotero.Items = function() {
this.merge = function (item, otherItems) { this.merge = function (item, otherItems) {
Zotero.debug("Merging items");
return Zotero.DB.executeTransaction(function* () { return Zotero.DB.executeTransaction(function* () {
var otherItemIDs = []; var otherItemIDs = [];
var itemURI = Zotero.URI.getItemURI(item); var itemURI = Zotero.URI.getItemURI(item);
@ -774,10 +745,7 @@ Zotero.Items = function() {
} }
// Add relations to master // Add relations to master
let oldRelations = otherItem.getRelations(); item.setRelations(otherItem.getRelations());
for (let pred in oldRelations) {
oldRelations[pred].forEach(obj => item.addRelation(pred, obj));
}
// Remove merge-tracking relations from other item, so that there aren't two // Remove merge-tracking relations from other item, so that there aren't two
// subjects for a given deleted object // subjects for a given deleted object
@ -901,70 +869,39 @@ Zotero.Items = function() {
/** /**
* @param {Integer} libraryID - Library to delete from * @param {Integer} libraryID - Library to delete from
* @param {Object} [options] * @param {Integer} [days] - Only delete items deleted more than this many days ago
* @param {Function} [options.onProgress] - fn(progress, progressMax) * @param {Integer} [limit]
* @param {Integer} [options.days] - Only delete items deleted more than this many days ago
* @param {Integer} [options.limit] - Number of items to delete
*/ */
this.emptyTrash = async function (libraryID, options = {}) { this.emptyTrash = Zotero.Promise.coroutine(function* (libraryID, days, limit) {
if (arguments.length > 2 || typeof arguments[1] == 'number') {
Zotero.warn("Zotero.Items.emptyTrash() has changed -- update your code");
options.days = arguments[1];
options.limit = arguments[2];
}
if (!libraryID) { if (!libraryID) {
throw new Error("Library ID not provided"); throw new Error("Library ID not provided");
} }
var t = new Date(); var t = new Date();
var deleted = await this.getDeleted(libraryID, false, options.days); var deletedIDs = [];
if (options.limit) { deletedIDs = yield this.getDeleted(libraryID, true, days);
deleted = deleted.slice(0, options.limit); if (deletedIDs.length) {
yield Zotero.Utilities.Internal.forEachChunkAsync(deletedIDs, 50, Zotero.Promise.coroutine(function* (chunk) {
yield this.erase(chunk);
yield Zotero.Notifier.trigger('refresh', 'trash', libraryID);
}.bind(this)));
} }
var processed = 0; if (deletedIDs.length) {
if (deleted.length) { Zotero.debug("Emptied " + deletedIDs.length + " item(s) from trash in " + (new Date() - t) + " ms");
let toDelete = { }
top: [],
child: [] return deletedIDs.length;
};
deleted.forEach((item) => {
item.isTopLevelItem() ? toDelete.top.push(item.id) : toDelete.child.push(item.id)
}); });
// Show progress meter during deletions
let eraseOptions = options.onProgress
? {
onProgress: function (progress, progressMax) {
options.onProgress(processed + progress, deleted.length);
}
}
: undefined;
for (let x of ['top', 'child']) {
await Zotero.Utilities.Internal.forEachChunkAsync(
toDelete[x],
1000,
async function (chunk) {
await this.erase(chunk, eraseOptions);
processed += chunk.length;
}.bind(this)
);
}
Zotero.debug("Emptied " + deleted.length + " item(s) from trash in " + (new Date() - t) + " ms");
}
return deleted.length;
};
/** /**
* Start idle observer to delete trashed items older than a certain number of days * Start idle observer to delete trashed items older than a certain number of days
*/ */
this._emptyTrashIdleObserver = null; this._emptyTrashIdleObserver = null;
this._emptyTrashTimeoutID = null; this._emptyTrashTimer = null;
this.startEmptyTrashTimer = function () { this.startEmptyTrashTimer = function () {
this._emptyTrashIdleObserver = { this._emptyTrashIdleObserver = {
observe: (subject, topic, data) => { observe: (subject, topic, data) => {
@ -980,31 +917,30 @@ Zotero.Items = function() {
// //
// TODO: increase number after dealing with slow // TODO: increase number after dealing with slow
// tag.getLinkedItems() call during deletes // tag.getLinkedItems() call during deletes
let num = 50; var num = 10;
this.emptyTrash( this.emptyTrash(Zotero.Libraries.userLibraryID, days, num)
Zotero.Libraries.userLibraryID, .then(deleted => {
{
days,
limit: num
}
)
.then((deleted) => {
if (!deleted) { if (!deleted) {
this._emptyTrashTimeoutID = null; this._emptyTrashTimer = null;
return; return;
} }
// Set a timer to do more every few seconds // Set a timer to do more every few seconds
this._emptyTrashTimeoutID = setTimeout(() => { if (!this._emptyTrashTimer) {
this._emptyTrashIdleObserver.observe(null, 'timer-callback', null); this._emptyTrashTimer = Components.classes["@mozilla.org/timer;1"]
}, 2500); .createInstance(Components.interfaces.nsITimer);
}
this._emptyTrashTimer.init(
this._emptyTrashIdleObserver.observe,
5 * 1000,
Components.interfaces.nsITimer.TYPE_ONE_SHOT
);
}); });
} }
// When no longer idle, cancel timer // When no longer idle, cancel timer
else if (topic == 'back') { else if (topic == 'back') {
if (this._emptyTrashTimeoutID) { if (this._emptyTrashTimer) {
clearTimeout(this._emptyTrashTimeoutID); this._emptyTrashTimer.cancel();
this._emptyTrashTimeoutID = null;
} }
} }
} }
@ -1205,25 +1141,6 @@ Zotero.Items = function() {
}; };
/**
* Returns an array of items with children of selected parents removed
*
* @return {Zotero.Item[]}
*/
this.keepParents = function (items) {
var parentItems = new Set(
items
.filter(item => item.isTopLevelItem())
.map(item => item.id)
);
return items.filter(item => {
var parentItemID = item.parentItemID;
// Not a child item or not a child of one of the passed items
return !parentItemID || !parentItems.has(parentItemID);
});
}
/* /*
* Generate SQL to retrieve firstCreator field * Generate SQL to retrieve firstCreator field
* *
@ -1464,7 +1381,6 @@ Zotero.Items = function() {
return title.replace(/^[\[\'\"](.*)[\'\"\]]?$/, '$1') return title.replace(/^[\[\'\"](.*)[\'\"\]]?$/, '$1')
} }
Zotero.DataObjects.call(this); Zotero.DataObjects.call(this);
return this; return this;

View File

@ -142,7 +142,7 @@ Zotero.defineProperty(Zotero.Library.prototype, 'libraryTypeID', {
return Zotero.Groups.getGroupIDFromLibraryID(this._libraryID); return Zotero.Groups.getGroupIDFromLibraryID(this._libraryID);
default: default:
throw new Error(`Tried to get library type id for ${this._libraryType} library`); throw new Error(`Cannot return library type id for ${this._libraryType} library`);
} }
} }
}); });
@ -169,7 +169,6 @@ Zotero.defineProperty(Zotero.Library.prototype, 'name', {
return Zotero.getString('pane.collections.library'); return Zotero.getString('pane.collections.library');
} }
// This property is provided by the extending objects (Group, Feed) for other library types
throw new Error('Unhandled library type "' + this._libraryType + '"'); throw new Error('Unhandled library type "' + this._libraryType + '"');
} }
}); });
@ -190,10 +189,6 @@ Zotero.defineProperty(Zotero.Library.prototype, 'hasTrash', {
value: true value: true
}); });
Zotero.defineProperty(Zotero.Library.prototype, 'allowsLinkedFiles', {
value: true
});
// Create other accessors // Create other accessors
(function() { (function() {
let accessors = ['editable', 'filesEditable', 'storageVersion', 'archived']; let accessors = ['editable', 'filesEditable', 'storageVersion', 'archived'];

View File

@ -32,8 +32,7 @@ Zotero.Relations = new function () {
this._namespaces = { this._namespaces = {
dc: 'http://purl.org/dc/elements/1.1/', dc: 'http://purl.org/dc/elements/1.1/',
owl: 'http://www.w3.org/2002/07/owl#', owl: 'http://www.w3.org/2002/07/owl#'
mendeleyDB: 'http://zotero.org/namespaces/mendeleyDB#'
}; };
var _types = ['collection', 'item']; var _types = ['collection', 'item'];
@ -147,7 +146,7 @@ Zotero.Relations = new function () {
* @return {Object[]} - An array of objects with a Zotero.DataObject as 'subject' * @return {Object[]} - An array of objects with a Zotero.DataObject as 'subject'
* and a predicate string as 'predicate' * and a predicate string as 'predicate'
*/ */
this.getByObject = Zotero.Promise.coroutine(function* (objectType, object) { this.getByObject = function (objectType, object) {
var objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(objectType); var objectsClass = Zotero.DataObjectUtilities.getObjectsClassForObjectType(objectType);
var predicateIDs = []; var predicateIDs = [];
var o = _subjectPredicatesByObject[objectType] var o = _subjectPredicatesByObject[objectType]
@ -157,16 +156,13 @@ Zotero.Relations = new function () {
} }
var toReturn = []; var toReturn = [];
for (let predicateID in o) { for (let predicateID in o) {
for (let subjectID of o[predicateID]) { o[predicateID].forEach(subjectID => toReturn.push({
var subject = yield objectsClass.getAsync(subjectID); subject: objectsClass.get(subjectID),
toReturn.push({
subject: subject,
predicate: Zotero.RelationPredicates.getName(predicateID) predicate: Zotero.RelationPredicates.getName(predicateID)
}); }));
};
} }
return toReturn; return toReturn;
}); };
this.updateUser = Zotero.Promise.coroutine(function* (fromUserID, toUserID) { this.updateUser = Zotero.Promise.coroutine(function* (fromUserID, toUserID) {
@ -183,9 +179,9 @@ Zotero.Relations = new function () {
let objects = yield Zotero.DB.columnQueryAsync( let objects = yield Zotero.DB.columnQueryAsync(
sql, 'http://zotero.org/users/' + fromUserID + '/%' sql, 'http://zotero.org/users/' + fromUserID + '/%'
); );
Zotero.DB.addCurrentCallback("commit", function* () { Zotero.DB.addCurrentCallback("commit", function () {
for (let object of objects) { for (let object of objects) {
let subPrefs = yield this.getByObject(type, object); let subPrefs = this.getByObject(type, object);
let newObject = object.replace( let newObject = object.replace(
new RegExp("^http://zotero.org/users/" + fromUserID + "/(.*)"), new RegExp("^http://zotero.org/users/" + fromUserID + "/(.*)"),
"http://zotero.org/users/" + toUserID + "/$1" "http://zotero.org/users/" + toUserID + "/$1"

View File

@ -101,7 +101,7 @@ Zotero.defineProperty(Zotero.Search.prototype, 'treeViewID', {
Zotero.defineProperty(Zotero.Search.prototype, 'treeViewImage', { Zotero.defineProperty(Zotero.Search.prototype, 'treeViewImage', {
get: function () { get: function () {
if (Zotero.isMac) { if (Zotero.isMac) {
return `chrome://zotero-platform/content/treesource-search${Zotero.hiDPISuffix}.png`; return "chrome://zotero-platform/content/treesource-search.png";
} }
return "chrome://zotero/skin/treesource-search" + Zotero.hiDPISuffix + ".png"; return "chrome://zotero/skin/treesource-search" + Zotero.hiDPISuffix + ".png";
} }

View File

@ -338,9 +338,7 @@ Zotero.SearchConditions = new function(){
doesNotContain: true doesNotContain: true
}, },
table: 'itemNotes', table: 'itemNotes',
// Exclude note prefix and suffix field: 'note'
field: `SUBSTR(note, ${1 + Zotero.Notes.notePrefix.length}, `
+ `LENGTH(note) - ${Zotero.Notes.notePrefix.length + Zotero.Notes.noteSuffix.length})`
}, },
{ {
@ -350,9 +348,7 @@ Zotero.SearchConditions = new function(){
doesNotContain: true doesNotContain: true
}, },
table: 'items', table: 'items',
// Exclude note prefix and suffix field: 'note'
field: `SUBSTR(note, ${1 + Zotero.Notes.notePrefix.length}, `
+ `LENGTH(note) - ${Zotero.Notes.notePrefix.length + Zotero.Notes.noteSuffix.length})`
}, },
{ {

View File

@ -96,13 +96,6 @@ Zotero.Searches = function() {
} }
this.conditionEquals = function (data1, data2) {
return data1.condition === data2.condition
&& data1.operator === data2.operator
&& data1.value === data2.value;
},
this._loadConditions = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) { this._loadConditions = Zotero.Promise.coroutine(function* (libraryID, ids, idSQL) {
var sql = "SELECT savedSearchID, searchConditionID, condition, operator, value, required " var sql = "SELECT savedSearchID, searchConditionID, condition, operator, value, required "
+ "FROM savedSearches LEFT JOIN savedSearchConditions USING (savedSearchID) " + "FROM savedSearches LEFT JOIN savedSearchConditions USING (savedSearchID) "

View File

@ -154,20 +154,41 @@ Zotero.Tags = new function() {
/** /**
* Get all tags within the items of a temporary table of search results * Get all tags within the items of a Zotero.Search object
* *
* @param {String} tmpTable Temporary table with items to use * @param {Zotero.Search} search
* @param {Array} [types] Array of tag types to fetch * @param {Array} [types] Array of tag types to fetch
* @return {Promise<Object>} Promise for object with tag data in API JSON format, keyed by tagID * @return {Promise<Object>} Promise for object with tag data in API JSON format, keyed by tagID
*/ */
this.getAllWithinSearchResults = Zotero.Promise.coroutine(function* (tmpTable, types) { this.getAllWithinSearch = Zotero.Promise.coroutine(function* (search, types) {
var sql = "SELECT DISTINCT name AS tag, type FROM itemTags " var ids = yield search.search();
+ "JOIN tags USING (tagID) WHERE itemID IN " return this.getAllWithinItemsList(ids, types);
+ "(SELECT itemID FROM " + tmpTable + ") "; });
if (types) {
sql += "AND type IN (" + types.join() + ") ";
this.getAllWithinItemsList = Zotero.Promise.coroutine(function* (ids, types) {
if (!Array.isArray(ids)) {
throw new Error("ids must be an array");
} }
var rows = yield Zotero.DB.queryAsync(sql); if (!ids.length) {
return [];
}
var prefix = "SELECT DISTINCT name AS tag, type FROM itemTags "
+ "JOIN tags USING (tagID) WHERE itemID IN "
+ "(";
var suffix = ") ";
if (types) {
suffix += "AND type IN (" + types.join() + ") ";
}
// Don't include ids in debug output
Zotero.DB.logQuery(`${prefix}[...${ids.length}]${suffix}`);
var rows = yield Zotero.DB.queryAsync(
prefix + ids.map(id => parseInt(id)).join(",") + suffix,
false,
{ debug: false }
);
return rows.map((row) => this.cleanData(row)); return rows.map((row) => this.cleanData(row));
}); });
@ -431,11 +452,9 @@ Zotero.Tags = new function() {
Zotero.DB.requireTransaction(); Zotero.DB.requireTransaction();
var sql;
// Use given tags, as long as they're orphaned // Use given tags, as long as they're orphaned
if (tagIDs) { if (tagIDs) {
sql = "CREATE TEMPORARY TABLE tagDelete (tagID INT PRIMARY KEY)"; let sql = "CREATE TEMPORARY TABLE tagDelete (tagID INT PRIMARY KEY)";
yield Zotero.DB.queryAsync(sql); yield Zotero.DB.queryAsync(sql);
yield Zotero.Utilities.Internal.forEachChunkAsync( yield Zotero.Utilities.Internal.forEachChunkAsync(
tagIDs, tagIDs,
@ -448,17 +467,13 @@ Zotero.Tags = new function() {
); );
} }
); );
sql = "SELECT tagID AS id, name FROM tagDelete JOIN tags USING (tagID) "
// Skip tags that are still linked to items + "WHERE tagID NOT IN (SELECT tagID FROM itemTags)";
sql = "DELETE FROM tagDelete WHERE tagID IN (SELECT tagID FROM itemTags)";
yield Zotero.DB.queryAsync(sql);
sql = "SELECT tagID AS id, name FROM tagDelete JOIN tags USING (tagID)";
var toDelete = yield Zotero.DB.queryAsync(sql); var toDelete = yield Zotero.DB.queryAsync(sql);
} }
// Look for orphaned tags // Look for orphaned tags
else { else {
sql = "CREATE TEMPORARY TABLE tagDelete AS " var sql = "CREATE TEMPORARY TABLE tagDelete AS "
+ "SELECT tagID FROM tags WHERE tagID NOT IN (SELECT tagID FROM itemTags)"; + "SELECT tagID FROM tags WHERE tagID NOT IN (SELECT tagID FROM itemTags)";
yield Zotero.DB.queryAsync(sql); yield Zotero.DB.queryAsync(sql);
@ -467,10 +482,11 @@ Zotero.Tags = new function() {
sql = "SELECT tagID AS id, name FROM tagDelete JOIN tags USING (tagID)"; sql = "SELECT tagID AS id, name FROM tagDelete JOIN tags USING (tagID)";
var toDelete = yield Zotero.DB.queryAsync(sql); var toDelete = yield Zotero.DB.queryAsync(sql);
}
if (!toDelete.length) { if (!toDelete.length) {
return Zotero.DB.queryAsync("DROP TABLE tagDelete"); sql = "DROP TABLE tagDelete";
return Zotero.DB.queryAsync(sql);
}
} }
var ids = []; var ids = [];
@ -758,13 +774,12 @@ Zotero.Tags = new function() {
* @return {Q Promise} A Q promise for a data: URL for a PNG * @return {Q Promise} A Q promise for a data: URL for a PNG
*/ */
this.generateItemsListImage = function (colors, extraImage) { this.generateItemsListImage = function (colors, extraImage) {
var multiplier = Zotero.hiDPI ? 2 : 1; var multiplier = (extraImage && extraImage.indexOf('2x') != -1) ? 2 : 1;
var swatchWidth = 8 * multiplier; var swatchWidth = 8 * multiplier;
var separator = 3 * multiplier; var separator = 3 * multiplier;
var extraImageSeparator = 1 * multiplier; var extraImageSeparator = 1 * multiplier;
var extraImageWidth = 16 * multiplier; var extraImageWidth = 16 * multiplier;
var extraImageHeight = 16 * multiplier;
var canvasHeight = 16 * multiplier; var canvasHeight = 16 * multiplier;
var swatchHeight = 8 * multiplier; var swatchHeight = 8 * multiplier;
var prependExtraImage = true; var prependExtraImage = true;
@ -832,7 +847,7 @@ Zotero.Tags = new function() {
// When extra image has loaded, draw it // When extra image has loaded, draw it
img.onload = function () { img.onload = function () {
ctx.drawImage(img, x, 0, extraImageWidth, extraImageHeight); ctx.drawImage(img, x, 0);
var dataURI = canvas.toDataURL("image/png"); var dataURI = canvas.toDataURL("image/png");
var dataURIPromise = Zotero.Promise.resolve(dataURI); var dataURIPromise = Zotero.Promise.resolve(dataURI);
@ -853,7 +868,7 @@ Zotero.Tags = new function() {
// for the composite image once it's ready // for the composite image once it's ready
return _itemsListExtraImagePromises[extraImage] return _itemsListExtraImagePromises[extraImage]
.then(function (img) { .then(function (img) {
ctx.drawImage(img, x, 0, extraImageWidth, extraImageHeight); ctx.drawImage(img, x, 0);
var dataURI = canvas.toDataURL("image/png"); var dataURI = canvas.toDataURL("image/png");
var dataURIPromise = Zotero.Promise.resolve(dataURI); var dataURIPromise = Zotero.Promise.resolve(dataURI);

View File

@ -178,27 +178,8 @@ Zotero.DataDirectory = {
// New installation of 5.0+ with no data directory specified, so check all the places the data // New installation of 5.0+ with no data directory specified, so check all the places the data
// could be // could be
else { else {
Zotero.fxProfileAccessError = false;
dataDir = this.defaultDir; dataDir = this.defaultDir;
// If there's already a profile pointing to the default location, use a different
// data directory named after the profile, as long as one either doesn't exist yet or
// one does and it contains a database
try {
if ((yield Zotero.Profile.findOtherProfilesUsingDataDirectory(dataDir, false)).length) {
let profileName = OS.Path.basename(Zotero.Profile.dir).match(/[^.]+\.(.+)/)[1];
let newDataDir = this.defaultDir + ' ' + profileName;
if (!(yield OS.File.exists(newDataDir))
|| (yield OS.File.exists(OS.Path.join(newDataDir, dbFilename)))) {
dataDir = newDataDir;
}
}
}
catch (e) {
Zotero.logError(e);
}
// Check for ~/Zotero/zotero.sqlite // Check for ~/Zotero/zotero.sqlite
let dbFile = OS.Path.join(dataDir, dbFilename); let dbFile = OS.Path.join(dataDir, dbFilename);
if (yield OS.File.exists(dbFile)) { if (yield OS.File.exists(dbFile)) {
@ -246,7 +227,6 @@ Zotero.DataDirectory = {
Zotero.debug("An error occurred locating the Firefox profile; " Zotero.debug("An error occurred locating the Firefox profile; "
+ "not attempting to migrate from Zotero for Firefox"); + "not attempting to migrate from Zotero for Firefox");
Zotero.logError(e); Zotero.logError(e);
Zotero.fxProfileAccessError = true;
} }
if (defProfile) { if (defProfile) {
let profileDir = defProfile[0]; let profileDir = defProfile[0];
@ -255,7 +235,22 @@ Zotero.DataDirectory = {
// Read in prefs // Read in prefs
let prefsFile = OS.Path.join(profileDir, "prefs.js"); let prefsFile = OS.Path.join(profileDir, "prefs.js");
if (yield OS.File.exists(prefsFile)) { if (yield OS.File.exists(prefsFile)) {
let prefs = yield Zotero.Profile.readPrefsFromFile(prefsFile); // build sandbox
var sandbox = new Components.utils.Sandbox("http://www.example.com/");
Components.utils.evalInSandbox(
"var prefs = {};"+
"function user_pref(key, val) {"+
"prefs[key] = val;"+
"}"
, sandbox);
// remove comments
var prefsJs = yield Zotero.File.getContentsAsync(prefsFile);
prefsJs = prefsJs.replace(/^#[^\r\n]*$/mg, "");
// evaluate
Components.utils.evalInSandbox(prefsJs, sandbox);
var prefs = sandbox.prefs;
// Check for data dir pref // Check for data dir pref
if (prefs['extensions.zotero.dataDir'] && prefs['extensions.zotero.useDataDir']) { if (prefs['extensions.zotero.dataDir'] && prefs['extensions.zotero.useDataDir']) {
@ -274,6 +269,7 @@ Zotero.DataDirectory = {
throw { name: "NS_ERROR_FILE_NOT_FOUND" }; throw { name: "NS_ERROR_FILE_NOT_FOUND" };
} }
} }
if (nsIFile) {
try { try {
let dbFile = OS.Path.join(nsIFile.path, dbFilename); let dbFile = OS.Path.join(nsIFile.path, dbFilename);
let mtime = (yield OS.File.stat(dbFile)).lastModificationDate; let mtime = (yield OS.File.stat(dbFile)).lastModificationDate;
@ -291,12 +287,9 @@ Zotero.DataDirectory = {
// access the custom location in Firefox, use the Zotero profile, since // access the custom location in Firefox, use the Zotero profile, since
// there's at least some chance it's right. Otherwise, throw an error. // there's at least some chance it's right. Otherwise, throw an error.
if (!useProfile) { if (!useProfile) {
// The error message normally gets the path from the pref, but
// we got it from the prefs file, so include it here
e.dataDir = nsIFile.path;
throw e; throw e;
} }
Zotero.fxProfileAccessError = true; }
} }
} }
// If no custom dir specified, check for a subdirectory // If no custom dir specified, check for a subdirectory
@ -313,12 +306,11 @@ Zotero.DataDirectory = {
useProfile = false; useProfile = false;
} }
} }
// Legacy subdirectory doesn't exist or there was a problem accessing it, so
// just fall through to default location
catch (e) { catch (e) {
if (!(e instanceof OS.File.Error && e.becauseNoSuchFile)) {
Zotero.logError(e); Zotero.logError(e);
Zotero.fxProfileAccessError = true; // Same as above -- throw error if we don't already have a DB
if (!useProfile) {
throw e;
} }
} }
} }
@ -346,75 +338,7 @@ Zotero.DataDirectory = {
} }
Zotero.debug("Using data directory " + dataDir); Zotero.debug("Using data directory " + dataDir);
try {
yield Zotero.File.createDirectoryIfMissingAsync(dataDir); yield Zotero.File.createDirectoryIfMissingAsync(dataDir);
}
catch (e) {
if (e instanceof OS.File.Error
&& (('unixErrno' in e && e.unixErrno == OS.Constants.libc.EACCES)
|| ('winLastError' in e && e.winLastError == OS.Constants.Win.ERROR_ACCESS_DENIED))) {
Zotero.restarting = true;
let isDefaultDir = dataDir == Zotero.DataDirectory.defaultDir;
let ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.createInstance(Components.interfaces.nsIPromptService);
let buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
if (!isDefaultDir) {
buttonFlags += ps.BUTTON_POS_2 * ps.BUTTON_TITLE_IS_STRING;
}
let title = Zotero.getString('general.accessDenied');
let msg = Zotero.getString('dataDir.dirCannotBeCreated', [Zotero.appName, dataDir])
+ "\n\n"
+ Zotero.getString('dataDir.checkDirWriteAccess', Zotero.appName);
let index;
if (isDefaultDir) {
index = ps.confirmEx(null,
title,
msg,
buttonFlags,
Zotero.getString('dataDir.chooseNewDataDirectory'),
Zotero.getString('general.quit'),
null, null, {}
);
if (index == 0) {
let changed = yield Zotero.DataDirectory.choose(true);
if (!changed) {
Zotero.Utilities.Internal.quit();
}
}
else if (index == 1) {
Zotero.Utilities.Internal.quit();
}
}
else {
index = ps.confirmEx(null,
title,
msg,
buttonFlags,
Zotero.getString('dataDir.useDefaultLocation'),
Zotero.getString('general.quit'),
Zotero.getString('dataDir.chooseNewDataDirectory'),
null, {}
);
if (index == 0) {
Zotero.DataDirectory.set(Zotero.DataDirectory.defaultDir);
Zotero.Utilities.Internal.quit(true);
}
else if (index == 1) {
Zotero.Utilities.Internal.quit();
}
else if (index == 2) {
let changed = yield Zotero.DataDirectory.choose(true);
if (!changed) {
Zotero.Utilities.Internal.quit();
return;
}
}
}
return;
}
}
this._cache(dataDir); this._cache(dataDir);
}), }),
@ -710,7 +634,7 @@ Zotero.DataDirectory = {
let dontAskAgain = {}; let dontAskAgain = {};
let index = ps.confirmEx(null, let index = ps.confirmEx(null,
"Other Data Directory Found", "Other Data Directory Found",
"Zotero found a previous data directory within your Firefox profile, " "Zotero found a previous data directory within your Firefox profile directory, "
+ `last modified on ${mtime.toLocaleDateString()}. ` + `last modified on ${mtime.toLocaleDateString()}. `
+ "If items or files are missing from Zotero that were present in Zotero for Firefox, " + "If items or files are missing from Zotero that were present in Zotero for Firefox, "
+ "your previous data directory may not have been properly migrated to the new default location " + "your previous data directory may not have been properly migrated to the new default location "
@ -829,7 +753,6 @@ Zotero.DataDirectory = {
} }
// Skip migration if new dir on different drive and prompt // Skip migration if new dir on different drive and prompt
try {
if (yield this.isNewDirOnDifferentDrive(dataDir, newDir)) { if (yield this.isNewDirOnDifferentDrive(dataDir, newDir)) {
Zotero.debug(`New dataDir ${newDir} is on a different drive from ${dataDir} -- skipping migration`); Zotero.debug(`New dataDir ${newDir} is on a different drive from ${dataDir} -- skipping migration`);
Zotero.DataDirectory.newDirOnDifferentDrive = true; Zotero.DataDirectory.newDirOnDifferentDrive = true;
@ -839,12 +762,6 @@ Zotero.DataDirectory = {
+ Zotero.getString(`dataDir.migration.failure.full.automatic.text2`, Zotero.appName); + Zotero.getString(`dataDir.migration.failure.full.automatic.text2`, Zotero.appName);
return this.fullMigrationFailurePrompt(dataDir, newDir, error); return this.fullMigrationFailurePrompt(dataDir, newDir, error);
} }
}
catch (e) {
Zotero.logError("Error checking whether data directory is on different drive "
+ "-- skipping migration:\n\n" + e);
return false;
}
// Check for an existing pipe from other running versions of Zotero pointing at the same data // Check for an existing pipe from other running versions of Zotero pointing at the same data
// directory, and skip migration if found // directory, and skip migration if found

View File

@ -296,13 +296,13 @@ Zotero.Date = new function(){
// Parse 'yesterday'/'today'/'tomorrow' // Parse 'yesterday'/'today'/'tomorrow'
var lc = (string + '').toLowerCase(); var lc = (string + '').toLowerCase();
if (lc == 'yesterday' || (Zotero.isClient && lc === Zotero.getString('date.yesterday'))) { if (lc == 'yesterday' || (Zotero.getString && lc === Zotero.getString('date.yesterday'))) {
string = Zotero.Date.dateToSQL(new Date(Date.now() - 1000*60*60*24)).substr(0, 10); // no 'this' for translator sandbox string = Zotero.Date.dateToSQL(new Date(Date.now() - 1000*60*60*24)).substr(0, 10); // no 'this' for translator sandbox
} }
else if (lc == 'today' || (Zotero.isClient && lc == Zotero.getString('date.today'))) { else if (lc == 'today' || (Zotero.getString && lc == Zotero.getString('date.today'))) {
string = Zotero.Date.dateToSQL(new Date()).substr(0, 10); string = Zotero.Date.dateToSQL(new Date()).substr(0, 10);
} }
else if (lc == 'tomorrow' || (Zotero.isClient && lc == Zotero.getString('date.tomorrow'))) { else if (lc == 'tomorrow' || (Zotero.getString && lc == Zotero.getString('date.tomorrow'))) {
string = Zotero.Date.dateToSQL(new Date(Date.now() + 1000*60*60*24)).substr(0, 10); string = Zotero.Date.dateToSQL(new Date(Date.now() + 1000*60*60*24)).substr(0, 10);
} }
else { else {

View File

@ -31,8 +31,8 @@
// the same database is accessed simultaneously by multiple Zotero instances. // the same database is accessed simultaneously by multiple Zotero instances.
const DB_LOCK_EXCLUSIVE = true; const DB_LOCK_EXCLUSIVE = true;
Zotero.DBConnection = function(dbNameOrPath) { Zotero.DBConnection = function(dbName) {
if (!dbNameOrPath) { if (!dbName) {
throw ('DB name not provided in Zotero.DBConnection()'); throw ('DB name not provided in Zotero.DBConnection()');
} }
@ -70,18 +70,8 @@ Zotero.DBConnection = function(dbNameOrPath) {
return Zotero.Date.toUnixTimestamp(d); return Zotero.Date.toUnixTimestamp(d);
}); });
// Absolute path to DB // Private members
if (dbNameOrPath.startsWith('/') || (Zotero.isWin && dbNameOrPath.includes('\\'))) { this._dbName = dbName;
this._dbName = OS.Path.basename(dbNameOrPath).replace(/\.sqlite$/, '');
this._dbPath = dbNameOrPath;
this._externalDB = true;
}
// DB name in data directory
else {
this._dbName = dbNameOrPath;
this._dbPath = Zotero.DataDirectory.getDatabase(dbNameOrPath);
this._externalDB = false;
}
this._shutdown = false; this._shutdown = false;
this._connection = null; this._connection = null;
this._transactionID = null; this._transactionID = null;
@ -101,14 +91,6 @@ Zotero.DBConnection = function(dbNameOrPath) {
this._dbIsCorrupt = null this._dbIsCorrupt = null
this._transactionPromise = null; this._transactionPromise = null;
if (dbNameOrPath == 'zotero') {
this.IncompatibleVersionException = function (msg, dbClientVersion) {
this.message = msg;
this.dbClientVersion = dbClientVersion;
}
this.IncompatibleVersionException.prototype = Object.create(Error.prototype);
}
} }
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
@ -123,7 +105,7 @@ Zotero.DBConnection = function(dbNameOrPath) {
* @return void * @return void
*/ */
Zotero.DBConnection.prototype.test = function () { Zotero.DBConnection.prototype.test = function () {
return this._getConnectionAsync().then(() => {}); return this._getConnectionAsync().return();
} }
Zotero.DBConnection.prototype.getAsyncStatement = Zotero.Promise.coroutine(function* (sql) { Zotero.DBConnection.prototype.getAsyncStatement = Zotero.Promise.coroutine(function* (sql) {
@ -503,7 +485,7 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
// Run begin callbacks // Run begin callbacks
for (var i=0; i<this._callbacks.begin.length; i++) { for (var i=0; i<this._callbacks.begin.length; i++) {
if (this._callbacks.begin[i]) { if (this._callbacks.begin[i]) {
this._callbacks.begin[i](id); this._callbacks.begin[i]();
} }
} }
var conn = this._getConnection(options) || (yield this._getConnectionAsync(options)); var conn = this._getConnection(options) || (yield this._getConnectionAsync(options));
@ -534,13 +516,13 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
// Run temporary commit callbacks // Run temporary commit callbacks
var f; var f;
while (f = this._callbacks.current.commit.shift()) { while (f = this._callbacks.current.commit.shift()) {
yield Zotero.Promise.resolve(f(id)); yield Zotero.Promise.resolve(f());
} }
// Run commit callbacks // Run commit callbacks
for (var i=0; i<this._callbacks.commit.length; i++) { for (var i=0; i<this._callbacks.commit.length; i++) {
if (this._callbacks.commit[i]) { if (this._callbacks.commit[i]) {
yield this._callbacks.commit[i](id); yield this._callbacks.commit[i]();
} }
} }
@ -567,13 +549,13 @@ Zotero.DBConnection.prototype.executeTransaction = Zotero.Promise.coroutine(func
// Run temporary commit callbacks // Run temporary commit callbacks
var f; var f;
while (f = this._callbacks.current.rollback.shift()) { while (f = this._callbacks.current.rollback.shift()) {
yield Zotero.Promise.resolve(f(id)); yield Zotero.Promise.resolve(f());
} }
// Run rollback callbacks // Run rollback callbacks
for (var i=0; i<this._callbacks.rollback.length; i++) { for (var i=0; i<this._callbacks.rollback.length; i++) {
if (this._callbacks.rollback[i]) { if (this._callbacks.rollback[i]) {
yield Zotero.Promise.resolve(this._callbacks.rollback[i](id)); yield Zotero.Promise.resolve(this._callbacks.rollback[i]());
} }
} }
@ -836,10 +818,9 @@ Zotero.DBConnection.prototype.logQuery = function (sql, params = [], options) {
} }
Zotero.DBConnection.prototype.tableExists = Zotero.Promise.coroutine(function* (table, db) { Zotero.DBConnection.prototype.tableExists = Zotero.Promise.coroutine(function* (table) {
yield this._getConnectionAsync(); yield this._getConnectionAsync();
var prefix = db ? db + '.' : ''; var sql = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND tbl_name=?";
var sql = `SELECT COUNT(*) FROM ${prefix}sqlite_master WHERE type='table' AND tbl_name=?`;
var count = yield this.valueQueryAsync(sql, [table]); var count = yield this.valueQueryAsync(sql, [table]);
return !!count; return !!count;
}); });
@ -898,7 +879,7 @@ Zotero.DBConnection.prototype.vacuum = function () {
// TEMP // TEMP
Zotero.DBConnection.prototype.info = Zotero.Promise.coroutine(function* () { Zotero.DBConnection.prototype.info = Zotero.Promise.coroutine(function* () {
var info = {}; var info = {};
var pragmas = ['auto_vacuum', 'cache_size', 'main.locking_mode', 'page_size']; var pragmas = ['auto_vacuum', 'cache_size', 'locking_mode', 'page_size'];
for (let p of pragmas) { for (let p of pragmas) {
info[p] = yield Zotero.DB.valueQueryAsync(`PRAGMA ${p}`); info[p] = yield Zotero.DB.valueQueryAsync(`PRAGMA ${p}`);
} }
@ -913,13 +894,9 @@ Zotero.DBConnection.prototype.integrityCheck = Zotero.Promise.coroutine(function
Zotero.DBConnection.prototype.checkException = function (e) { Zotero.DBConnection.prototype.checkException = function (e) {
if (this._externalDB) {
return true;
}
if (e.message.includes(this.DB_CORRUPTION_STRING)) { if (e.message.includes(this.DB_CORRUPTION_STRING)) {
// Write corrupt marker to data directory // Write corrupt marker to data directory
var file = Zotero.File.pathToFile(this._dbPath + '.is.corrupt'); var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName, 'is.corrupt'));
Zotero.File.putContents(file, ''); Zotero.File.putContents(file, '');
this._dbIsCorrupt = true; this._dbIsCorrupt = true;
@ -970,11 +947,6 @@ Zotero.DBConnection.prototype.closeDatabase = Zotero.Promise.coroutine(function*
Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function* (suffix, force) { Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function* (suffix, force) {
if (this.skipBackup || this._externalDB || Zotero.skipLoading) {
this._debug("Skipping backup of database '" + this._dbName + "'", 1);
return false;
}
var storageService = Components.classes["@mozilla.org/storage/service;1"] var storageService = Components.classes["@mozilla.org/storage/service;1"]
.getService(Components.interfaces.mozIStorageService); .getService(Components.interfaces.mozIStorageService);
@ -1008,21 +980,27 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
}); });
try { try {
let corruptMarker = Zotero.File.pathToFile(this._dbPath + '.is.corrupt'); var corruptMarker = Zotero.File.pathToFile(
Zotero.DataDirectory.getDatabase(this._dbName, 'is.corrupt')
);
if (this._dbIsCorrupt || corruptMarker.exists()) { if (this.skipBackup || Zotero.skipLoading) {
this._debug("Skipping backup of database '" + this._dbName + "'", 1);
return false;
}
else if (this._dbIsCorrupt || corruptMarker.exists()) {
this._debug("Database '" + this._dbName + "' is marked as corrupt -- skipping backup", 1); this._debug("Database '" + this._dbName + "' is marked as corrupt -- skipping backup", 1);
return false; return false;
} }
let file = this._dbPath; var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName));
// For standard backup, make sure last backup is old enough to replace // For standard backup, make sure last backup is old enough to replace
if (!suffix && !force) { if (!suffix && !force) {
let backupFile = this._dbPath + '.bak'; var backupFile = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName, 'bak'));
if (yield OS.File.exists(backupFile)) { if (yield OS.File.exists(backupFile.path)) {
let currentDBTime = (yield OS.File.stat(file.path)).lastModificationDate; var currentDBTime = (yield OS.File.stat(file.path)).lastModificationDate;
let lastBackupTime = (yield OS.File.stat(backupFile)).lastModificationDate; var lastBackupTime = (yield OS.File.stat(backupFile.path)).lastModificationDate;
if (currentDBTime == lastBackupTime) { if (currentDBTime == lastBackupTime) {
Zotero.debug("Database '" + this._dbName + "' hasn't changed -- skipping backup"); Zotero.debug("Database '" + this._dbName + "' hasn't changed -- skipping backup");
return; return;
@ -1043,7 +1021,7 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
// Copy via a temporary file so we don't run into disk space issues // Copy via a temporary file so we don't run into disk space issues
// after deleting the old backup file // after deleting the old backup file
var tmpFile = this._dbPath + '.tmp'; var tmpFile = Zotero.DataDirectory.getDatabase(this._dbName, 'tmp');
if (yield OS.File.exists(tmpFile)) { if (yield OS.File.exists(tmpFile)) {
try { try {
yield OS.File.remove(tmpFile); yield OS.File.remove(tmpFile);
@ -1060,21 +1038,18 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
// the lock is lost // the lock is lost
try { try {
if (DB_LOCK_EXCLUSIVE) { if (DB_LOCK_EXCLUSIVE) {
yield this.queryAsync("PRAGMA main.locking_mode=NORMAL", false, { inBackup: true }); yield this.queryAsync("PRAGMA locking_mode=NORMAL", false, { inBackup: true });
} }
storageService.backupDatabaseFile( storageService.backupDatabaseFile(file, OS.Path.basename(tmpFile), file.parent);
Zotero.File.pathToFile(file),
OS.Path.basename(tmpFile),
Zotero.File.pathToFile(file).parent
);
} }
catch (e) { catch (e) {
Zotero.logError(e); Zotero.debug(e);
Components.utils.reportError(e);
return false; return false;
} }
finally { finally {
if (DB_LOCK_EXCLUSIVE) { if (DB_LOCK_EXCLUSIVE) {
yield this.queryAsync("PRAGMA main.locking_mode=EXCLUSIVE", false, { inBackup: true }); yield this.queryAsync("PRAGMA locking_mode=EXCLUSIVE", false, { inBackup: true });
} }
} }
@ -1105,7 +1080,7 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
// Special backup // Special backup
if (!suffix && numBackups > 1) { if (!suffix && numBackups > 1) {
// Remove oldest backup file // Remove oldest backup file
let targetFile = this._dbPath + '.' + (numBackups - 1) + '.bak'; var targetFile = Zotero.DataDirectory.getDatabase(this._dbName, (numBackups - 1) + '.bak');
if (yield OS.File.exists(targetFile)) { if (yield OS.File.exists(targetFile)) {
yield OS.File.remove(targetFile); yield OS.File.remove(targetFile);
} }
@ -1115,8 +1090,12 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
var targetNum = i; var targetNum = i;
var sourceNum = targetNum - 1; var sourceNum = targetNum - 1;
let targetFile = this._dbPath + '.' + targetNum + '.bak'; var targetFile = Zotero.DataDirectory.getDatabase(
let sourceFile = this._dbPath + '.' + (sourceNum ? sourceNum + '.bak' : 'bak') this._dbName, targetNum + '.bak'
);
var sourceFile = Zotero.DataDirectory.getDatabase(
this._dbName, sourceNum ? sourceNum + '.bak' : 'bak'
);
if (!(yield OS.File.exists(sourceFile))) { if (!(yield OS.File.exists(sourceFile))) {
continue; continue;
@ -1128,7 +1107,9 @@ Zotero.DBConnection.prototype.backupDatabase = Zotero.Promise.coroutine(function
} }
} }
let backupFile = this._dbPath + '.' + (suffix ? suffix + '.' : '') + 'bak'; var backupFile = Zotero.DataDirectory.getDatabase(
this._dbName, (suffix ? suffix + '.' : '') + 'bak'
);
// Remove old backup file // Remove old backup file
if (yield OS.File.exists(backupFile)) { if (yield OS.File.exists(backupFile)) {
@ -1165,11 +1146,11 @@ Zotero.DBConnection.prototype._getConnection = function (options) {
/* /*
* Retrieve a link to the data store asynchronously * Retrieve a link to the data store asynchronously
*/ */
Zotero.DBConnection.prototype._getConnectionAsync = async function (options) { Zotero.DBConnection.prototype._getConnectionAsync = Zotero.Promise.coroutine(function* (options) {
// If a backup is in progress, wait until it's done // If a backup is in progress, wait until it's done
if (this._backupPromise && this._backupPromise.isPending() && (!options || !options.inBackup)) { if (this._backupPromise && this._backupPromise.isPending() && (!options || !options.inBackup)) {
Zotero.debug("Waiting for database backup to complete", 2); Zotero.debug("Waiting for database backup to complete", 2);
await this._backupPromise; yield this._backupPromise;
} }
if (this._connection) { if (this._connection) {
@ -1180,50 +1161,48 @@ Zotero.DBConnection.prototype._getConnectionAsync = async function (options) {
} }
this._debug("Asynchronously opening database '" + this._dbName + "'"); this._debug("Asynchronously opening database '" + this._dbName + "'");
Zotero.debug(this._dbPath);
// Get the storage service // Get the storage service
var store = Components.classes["@mozilla.org/storage/service;1"]. var store = Components.classes["@mozilla.org/storage/service;1"].
getService(Components.interfaces.mozIStorageService); getService(Components.interfaces.mozIStorageService);
var file = this._dbPath; var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName));
var backupFile = this._dbPath + '.bak'; var backupFile = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName, 'bak'));
var fileName = OS.Path.basename(file);
var corruptMarker = this._dbPath + '.is.corrupt'; var fileName = this._dbName + '.sqlite';
catchBlock: try { catchBlock: try {
if (await OS.File.exists(corruptMarker)) { var corruptMarker = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName, 'is.corrupt'));
if (corruptMarker.exists()) {
throw new Error(this.DB_CORRUPTION_STRING); throw new Error(this.DB_CORRUPTION_STRING);
} }
this._connection = await Zotero.Promise.resolve(this.Sqlite.openConnection({ this._connection = yield Zotero.Promise.resolve(this.Sqlite.openConnection({
path: file path: file.path
})); }));
} }
catch (e) { catch (e) {
// Don't deal with corrupted external dbs
if (this._externalDB) {
throw e;
}
Zotero.logError(e); Zotero.logError(e);
if (e.message.includes(this.DB_CORRUPTION_STRING)) { if (e.message.includes(this.DB_CORRUPTION_STRING)) {
this._debug(`Database file '${fileName}' corrupted`, 1); this._debug("Database file '" + file.leafName + "' corrupted", 1);
// No backup file! Eek! // No backup file! Eek!
if (!await OS.File.exists(backupFile)) { if (!backupFile.exists()) {
this._debug("No backup file for DB '" + this._dbName + "' exists", 1); this._debug("No backup file for DB '" + this._dbName + "' exists", 1);
// Save damaged filed // Save damaged filed
this._debug('Saving damaged DB file with .damaged extension', 1); this._debug('Saving damaged DB file with .damaged extension', 1);
let damagedFile = this._dbPath + '.damaged'; var damagedFile = Zotero.File.pathToFile(
Zotero.DataDirectory.getDatabase(this._dbName, 'damaged')
);
Zotero.moveToUnique(file, damagedFile); Zotero.moveToUnique(file, damagedFile);
// Create new main database // Create new main database
var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName));
this._connection = store.openDatabase(file); this._connection = store.openDatabase(file);
if (await OS.File.exists(corruptMarker)) { if (corruptMarker.exists()) {
await OS.File.remove(corruptMarker); corruptMarker.remove(null);
} }
Zotero.alert( Zotero.alert(
@ -1236,21 +1215,24 @@ Zotero.DBConnection.prototype._getConnectionAsync = async function (options) {
// Save damaged file // Save damaged file
this._debug('Saving damaged DB file with .damaged extension', 1); this._debug('Saving damaged DB file with .damaged extension', 1);
let damagedFile = this._dbPath + '.damaged'; var damagedFile = Zotero.File.pathToFile(
Zotero.DataDirectory.getDatabase(this._dbName, 'damaged')
);
Zotero.moveToUnique(file, damagedFile); Zotero.moveToUnique(file, damagedFile);
// Test the backup file // Test the backup file
try { try {
Zotero.debug("Asynchronously opening DB connection"); Zotero.debug("Asynchronously opening DB connection");
this._connection = await Zotero.Promise.resolve(this.Sqlite.openConnection({ this._connection = yield Zotero.Promise.resolve(this.Sqlite.openConnection({
path: backupFile path: backupFile.path
})); }));
} }
// Can't open backup either // Can't open backup either
catch (e) { catch (e) {
// Create new main database // Create new main database
this._connection = await Zotero.Promise.resolve(this.Sqlite.openConnection({ var file = Zotero.File.pathToFile(Zotero.DataDirectory.getDatabase(this._dbName));
path: file this._connection = yield Zotero.Promise.resolve(this.Sqlite.openConnection({
path: file.path
})); }));
Zotero.alert( Zotero.alert(
@ -1259,8 +1241,8 @@ Zotero.DBConnection.prototype._getConnectionAsync = async function (options) {
Zotero.getString('db.dbRestoreFailed', fileName) Zotero.getString('db.dbRestoreFailed', fileName)
); );
if (await OS.File.exists(corruptMarker)) { if (corruptMarker.exists()) {
await OS.File.remove(corruptMarker); corruptMarker.remove(null);
} }
break catchBlock; break catchBlock;
@ -1271,7 +1253,7 @@ Zotero.DBConnection.prototype._getConnectionAsync = async function (options) {
// Copy backup file to main DB file // Copy backup file to main DB file
this._debug("Restoring database '" + this._dbName + "' from backup file", 1); this._debug("Restoring database '" + this._dbName + "' from backup file", 1);
try { try {
await OS.File.copy(backupFile, file); backupFile.copyTo(backupFile.parent, fileName);
} }
catch (e) { catch (e) {
// TODO: deal with low disk space // TODO: deal with low disk space
@ -1279,7 +1261,8 @@ Zotero.DBConnection.prototype._getConnectionAsync = async function (options) {
} }
// Open restored database // Open restored database
this._connection = await Zotero.Promise.resolve(this.Sqlite.openConnection({ var file = OS.Path.join(Zotero.DataDirectory.dir, fileName);
this._connection = yield Zotero.Promise.resolve(this.Sqlite.openConnection({
path: file path: file
})); }));
this._debug('Database restored', 1); this._debug('Database restored', 1);
@ -1288,13 +1271,13 @@ Zotero.DBConnection.prototype._getConnectionAsync = async function (options) {
Zotero.getString('general.warning'), Zotero.getString('general.warning'),
Zotero.getString('db.dbRestored', [ Zotero.getString('db.dbRestored', [
fileName, fileName,
Zotero.Date.getFileDateString(Zotero.File.pathToFile(backupFile)), Zotero.Date.getFileDateString(backupFile),
Zotero.Date.getFileTimeString(Zotero.File.pathToFile(backupFile)) Zotero.Date.getFileTimeString(backupFile)
]) ])
); );
if (await OS.File.exists(corruptMarker)) { if (corruptMarker.exists()) {
await OS.File.remove(corruptMarker); corruptMarker.remove(null);
} }
break catchBlock; break catchBlock;
@ -1304,21 +1287,20 @@ Zotero.DBConnection.prototype._getConnectionAsync = async function (options) {
throw (e); throw (e);
} }
if (!this._externalDB) {
if (DB_LOCK_EXCLUSIVE) { if (DB_LOCK_EXCLUSIVE) {
await this.queryAsync("PRAGMA main.locking_mode=EXCLUSIVE"); yield this.queryAsync("PRAGMA locking_mode=EXCLUSIVE");
} }
else { else {
await this.queryAsync("PRAGMA main.locking_mode=NORMAL"); yield this.queryAsync("PRAGMA locking_mode=NORMAL");
} }
// Set page cache size to 8MB // Set page cache size to 8MB
let pageSize = await this.valueQueryAsync("PRAGMA page_size"); var pageSize = yield this.valueQueryAsync("PRAGMA page_size");
let cacheSize = 8192000 / pageSize; var cacheSize = 8192000 / pageSize;
await this.queryAsync("PRAGMA cache_size=" + cacheSize); yield this.queryAsync("PRAGMA cache_size=" + cacheSize);
// Enable foreign key checks // Enable foreign key checks
await this.queryAsync("PRAGMA foreign_keys=true"); yield this.queryAsync("PRAGMA foreign_keys=true");
// Register idle observer for DB backup // Register idle observer for DB backup
Zotero.Schema.schemaUpdatePromise.then(() => { Zotero.Schema.schemaUpdatePromise.then(() => {
@ -1327,13 +1309,22 @@ Zotero.DBConnection.prototype._getConnectionAsync = async function (options) {
.getService(Components.interfaces.nsIIdleService); .getService(Components.interfaces.nsIIdleService);
idleService.addIdleObserver(this, 300); idleService.addIdleObserver(this, 300);
}); });
}
return this._connection; return this._connection;
}; });
Zotero.DBConnection.prototype._debug = function (str, level) { Zotero.DBConnection.prototype._debug = function (str, level) {
var prefix = this._dbName == 'zotero' ? '' : '[' + this._dbName + '] '; var prefix = this._dbName == 'zotero' ? '' : '[' + this._dbName + '] ';
Zotero.debug(prefix + str, level); Zotero.debug(prefix + str, level);
} }
// Initialize main database connection
Zotero.DB = new Zotero.DBConnection('zotero');
Zotero.DB.IncompatibleVersionException = function (msg, dbClientVersion) {
this.message = msg;
this.dbClientVersion = dbClientVersion;
}
Zotero.DB.IncompatibleVersionException.prototype = Object.create(Error.prototype);

View File

@ -139,7 +139,7 @@ Zotero.Debug = new function () {
} }
if (stack) { if (stack) {
message += '\n' + this.stackToString(stack); message += '\n' + Zotero.Debug.stackToString(stack);
} }
if (_console || _consoleViewer) { if (_console || _consoleViewer) {
@ -288,7 +288,7 @@ Zotero.Debug = new function () {
+ ':' + stack.lineNumber; + ':' + stack.lineNumber;
stack = stack.caller; stack = stack.caller;
} }
return this.filterStack(str).substr(1); return str.substr(1);
}; };

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