- Added UI elements for fulltext indexing, including new Search prefpane

- Added pdfinfo support to determine number of PDF pages (requires custom binary)
- Automatic downloading and installation of customized Xpdf tools from zotero.org

Other changes:

- Various API Zotero.Fulltext API changes
- Prevent buggy automatic prefpane sizing
- New 'refresh' Notifier action to just reselect the currently selected item without sending a 'modify' trigger
- Zotero.File.putContents(file, str) -- can only handle ASCII text
- Zotero.isLinux property
This commit is contained in:
Dan Stillman 2007-07-15 18:18:22 +00:00
parent f5761d507a
commit d921d8239a
19 changed files with 1466 additions and 170 deletions

View File

@ -50,6 +50,8 @@ var ZoteroPane = new function()
this.updateTagFilter = updateTagFilter;
this.onCollectionSelected = onCollectionSelected;
this.itemSelected = itemSelected;
this.updateItemIndexedState = updateItemIndexedState;
this.reindexItem = reindexItem;
this.duplicateSelectedItem = duplicateSelectedItem;
this.deleteSelectedItem = deleteSelectedItem;
this.deleteSelectedCollection = deleteSelectedCollection;
@ -852,6 +854,20 @@ var ZoteroPane = new function()
document.getElementById('zotero-attachment-view').setAttribute('label', str);
// Display page count
var pages = Zotero.Fulltext.getPages(item.ref.getID()).total;
var pagesRow = document.getElementById('zotero-attachment-pages');
if (pages) {
var str = Zotero.getString('itemFields.pages') + ': ' + pages;
pagesRow.setAttribute('value', str);
pagesRow.setAttribute('hidden', false);
}
else {
pagesRow.setAttribute('hidden', true);
}
this.updateItemIndexedState();
var noteEditor = document.getElementById('zotero-attachment-note-editor');
noteEditor.item = null;
noteEditor.note = item.ref;
@ -869,7 +885,7 @@ var ZoteroPane = new function()
document.getElementById('zotero-item-pane-content').selectedIndex = 0;
var label = document.getElementById('zotero-view-selected-label');
if (this.itemsView && this.itemsView.selection.count) {
label.value = Zotero.getString('pane.item.selected.multiple', this.itemsView.selection.count);
}
@ -881,6 +897,65 @@ var ZoteroPane = new function()
}
/*
* Update Indexed: (Yes|No|Partial) line in attachment info pane
*/
function updateItemIndexedState() {
var indexBox = document.getElementById('zotero-attachment-index-box');
var indexStatus = document.getElementById('zotero-attachment-index-status');
var reindexButton = document.getElementById('zotero-attachment-reindex');
var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex);
var status = Zotero.Fulltext.getIndexedState(item.ref.getID());
var str = 'fulltext.indexState.';
switch (status) {
case Zotero.Fulltext.INDEX_STATE_UNAVAILABLE:
str += 'unavailable';
break;
case Zotero.Fulltext.INDEX_STATE_UNINDEXED:
str = 'general.no';
break;
case Zotero.Fulltext.INDEX_STATE_PARTIAL:
str += 'partial';
break;
case Zotero.Fulltext.INDEX_STATE_INDEXED:
str = 'general.yes';
break;
}
str = Zotero.getString('fulltext.indexState.indexed') + ': ' +
Zotero.getString(str);
indexStatus.setAttribute('value', str);
// Reindex button tooltip (string stored in zotero.properties)
var str = Zotero.getString('pane.items.menu.reindexItem');
reindexButton.setAttribute('tooltiptext', str);
if (Zotero.Fulltext.canReindex(item.ref.getID())) {
reindexButton.setAttribute('hidden', false);
}
else {
reindexButton.setAttribute('hidden', true);
}
}
function reindexItem() {
var items = this.getSelectedItems();
if (!items) {
return false;
}
for (var i=0; i<items.length; i++) {
if (!items[i].isAttachment()) {
continue;
}
var itemID = items[i].getID();
Zotero.Fulltext.indexItems(itemID, true);
}
this.updateItemIndexedState();
}
function duplicateSelectedItem() {
var newItemID = this.getSelectedItems()[0].clone();
var newItem = Zotero.Items.get(newItemID);
@ -1280,7 +1355,9 @@ var ZoteroPane = new function()
sep3: 9,
exportItems: 10,
createBib: 11,
loadReport: 12
loadReport: 12,
sep4: 13,
reindexItem: 14
};
var menu = document.getElementById('zotero-itemmenu');
@ -1297,6 +1374,22 @@ var ZoteroPane = new function()
var multiple = '.multiple';
hide.push(m.showInLibrary, m.sep1, m.addNote, m.attachSnapshot,
m.attachLink, m.sep2, m.duplicateItem);
// If all items can be reindexed, show option
var items = this.getSelectedItems();
var canIndex = true;
for (var i=0; i<items.length; i++) {
if (!Zotero.Fulltext.canReindex()) {
canIndex = false;
break;
}
}
if (canIndex) {
show.push(m.sep4, m.reindexItem);
}
else {
hide.push(m.sep4, m.reindexItem);
}
}
// Single item selected
else
@ -1324,12 +1417,21 @@ var ZoteroPane = new function()
if (item.isAttachment()) {
hide.push(m.duplicateItem);
// If not linked URL, show reindex line
if (Zotero.Fulltext.canReindex(item.getID())) {
show.push(m.sep4, m.reindexItem);
}
else {
hide.push(m.sep4, m.reindexItem);
}
}
else {
show.push(m.duplicateItem);
hide.push(m.sep4, m.reindexItem);
}
}
}
// No items selected
else
{
// Show in Library
@ -1342,7 +1444,7 @@ var ZoteroPane = new function()
disable.push(m.showInLibrary, m.duplicateItem, m.deleteItem,
m.deleteFromLibrary, m.exportItems, m.createBib, m.loadReport);
hide.push(m.addNote, m.attachSnapshot, m.attachLink, m.sep2);
hide.push(m.addNote, m.attachSnapshot, m.attachLink, m.sep2, m.sep4, m.reindexItem);
}
// Remove from collection
@ -1361,6 +1463,7 @@ var ZoteroPane = new function()
menu.childNodes[m.exportItems].setAttribute('label', Zotero.getString('pane.items.menu.export' + multiple));
menu.childNodes[m.createBib].setAttribute('label', Zotero.getString('pane.items.menu.createBib' + multiple));
menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.items.menu.generateReport' + multiple));
menu.childNodes[m.reindexItem].setAttribute('label', Zotero.getString('pane.items.menu.reindexItem' + multiple));
for (var i in disable)
{

View File

@ -101,6 +101,8 @@
<menuitem oncommand="Zotero_File_Interface.exportItems();"/>
<menuitem oncommand="Zotero_File_Interface.bibliographyFromItems();"/>
<menuitem oncommand="Zotero_Report_Interface.loadItemReport()"/>
<menuseparator/>
<menuitem oncommand="ZoteroPane.reindexItem();"/>
</popup>
</popupset>
@ -305,10 +307,17 @@
<button id="zotero-attachment-view" flex="1" oncommand="ZoteroPane.viewSelectedAttachment(event);"/>
<button id="zotero-attachment-show" label="&zotero.item.attachment.file.show;" flex="1" oncommand="ZoteroPane.showSelectedAttachmentInFilesystem()"/>
</hbox>
<vbox>
<label id="zotero-attachment-url" class="text-link" crop="end" onclick="ZoteroPane.loadURI(this.value, event)"/>
<label id="zotero-attachment-accessed"/>
</vbox>
<label id="zotero-attachment-url" class="text-link" crop="end" onclick="ZoteroPane.loadURI(this.value, event)"/>
<label id="zotero-attachment-accessed"/>
<label id="zotero-attachment-pages"/>
<hbox id="zotero-attachment-index-box">
<label id="zotero-attachment-index-status"/>
<toolbarbutton id="zotero-attachment-reindex" oncommand="ZoteroPane.reindexItem()"/>
</hbox>
<noteeditor id="zotero-attachment-note-editor" notitle="1" flex="1"/>
</vbox>
</deck>

View File

@ -33,6 +33,7 @@ function init()
populateQuickCopyList();
updateQuickCopyInstructions();
initSearchPane();
}
@ -261,9 +262,9 @@ function refreshQuickCopySiteList() {
treeitem.appendChild(treerow);
treechildren.appendChild(treeitem);
}
}
function deleteSelectedQuickCopySite() {
var tree = document.getElementById('quickCopy-siteSettings');
var treeitem = tree.lastChild.childNodes[tree.currentIndex];
@ -291,6 +292,467 @@ function updateQuickCopyInstructions() {
}
function rebuildIndexPrompt() {
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_IS_STRING)
+ (ps.BUTTON_POS_2) * (ps.BUTTON_TITLE_CANCEL);
var index = ps.confirmEx(null,
Zotero.getString('zotero.preferences.search.rebuildIndex'),
Zotero.getString('zotero.preferences.search.rebuildWarning',
Zotero.getString('zotero.preferences.search.indexUnindexed')),
buttonFlags,
Zotero.getString('zotero.preferences.search.rebuildIndex'),
Zotero.getString('zotero.preferences.search.indexUnindexed'),
null, null, {});
if (index == 0) {
Zotero.Fulltext.rebuildIndex();
}
else if (index == 1) {
Zotero.Fulltext.rebuildIndex(true)
}
updateIndexStats();
}
function clearIndexPrompt() {
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_IS_STRING)
+ (ps.BUTTON_POS_2) * (ps.BUTTON_TITLE_CANCEL);
var index = ps.confirmEx(null,
Zotero.getString('zotero.preferences.search.clearIndex'),
Zotero.getString('zotero.preferences.search.clearWarning',
Zotero.getString('zotero.preferences.search.clearNonLinkedURLs')),
buttonFlags,
Zotero.getString('zotero.preferences.search.clearIndex'),
Zotero.getString('zotero.preferences.search.clearNonLinkedURLs'),
null, null, {});
if (index == 0) {
Zotero.Fulltext.clearIndex();
}
else if (index == 1) {
Zotero.Fulltext.clearIndex(true);
}
updateIndexStats();
}
function initSearchPane() {
document.getElementById('fulltext-rebuildIndex').setAttribute('label',
Zotero.getString('zotero.preferences.search.rebuildIndex'));
document.getElementById('fulltext-clearIndex').setAttribute('label',
Zotero.getString('zotero.preferences.search.clearIndex'));
updatePDFToolsStatus();
}
/*
* Update window according to installation status for PDF tools
* (e.g. status line, install/update button, etc.)
*/
function updatePDFToolsStatus() {
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()) {
var utils = new Zotero.Utilities();
// 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 = utils.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', '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 = utils.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', '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
*/
function checkPDFToolsDownloadVersion() {
var url = Zotero.Fulltext.pdfToolsDownloadBaseURL
+ Zotero.platform.replace(' ', '-') + '.latest';
// Find latest version for this platform
var sent = Zotero.Utilities.HTTP.doGet(url, function (xmlhttp) {
try {
if (xmlhttp.status == 200) {
var converterIsRegistered = Zotero.Fulltext.pdfConverterIsRegistered();
var infoIsRegistered = Zotero.Fulltext.pdfInfoIsRegistered();
var bothRegistered = converterIsRegistered && infoIsRegistered;
var converterVersion = xmlhttp.responseText.split(/\s/)[0];
var infoVersion = xmlhttp.responseText.split(/\s/)[1];
var converterVersionAvailable = converterVersion &&
(!converterIsRegistered ||
Zotero.Fulltext.pdfConverterVersion == 'UNKNOWN' ||
converterVersion > Zotero.Fulltext.pdfConverterVersion);
var infoVersionAvailable = infoVersion &&
(!infoIsRegistered ||
Zotero.Fulltext.pdfInfoVersion == 'UNKNOWN' ||
infoVersion > Zotero.Fulltext.pdfInfoVersion);
var bothAvailable = converterVersionAvailable && infoVersionAvailable;
/*
Zotero.debug(converterIsRegistered);
Zotero.debug(infoIsRegistered);
Zotero.debug(converterVersion);
Zotero.debug(infoVersion);
Zotero.debug(Zotero.Fulltext.pdfConverterVersion);
Zotero.debug(Zotero.Fulltext.pdfInfoVersion);
Zotero.debug(converterVersionAvailable);
Zotero.debug(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);
}
// New version available -- display update prompt
else {
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) {
tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
[Zotero.Fulltext.pdfConverterName, converterVersion]);
msg += '- ' + tvp + '\n';
}
if (infoVersionAvailable) {
tvp = Zotero.getString('zotero.preferences.search.pdf.toolVersionPlatform',
[Zotero.Fulltext.pdfInfoName, infoVersion]);
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) {
var installVersions = {
converter: converterVersionAvailable ?
converterVersion : null,
info: infoVersionAvailable ?
infoVersion : null
};
installPDFTools(installVersions);
}
}
}
// Version not found for platform
else if (xmlhttp.status == 404) {
onPDFToolsDownloadError(404);
}
}
catch (e) {
onPDFToolsDownloadError(e);
}
});
// Browser is offline
if (!sent) {
onPDFToolsDownloadError();
}
}
/*
* Begin installation of specified PDF tools from server -- does a HEAD call to
* make sure file exists and then calls downloadPDFTool() if so
*/
function installPDFTools(installVersions) {
if (!installVersions) {
installVersions = {
converter: true,
info: true
};
}
// We install the converter first if it's available
var url = Zotero.Fulltext.pdfToolsDownloadBaseURL;
if (installVersions.converter) {
var tool = 'converter';
var version = installVersions.converter;
url += Zotero.Fulltext.pdfConverterFileName + '-' + installVersions.converter;
}
else if (installVersions.info) {
var tool = 'info';
var version = installVersions.info;
url += Zotero.Fulltext.pdfInfoFileName + '-' + installVersions.info;
}
else {
return;
}
// Find latest version for this platform
var sent = Zotero.Utilities.HTTP.doHead(url, function (xmlhttp) {
try {
if (xmlhttp.status == 200) {
// If doing both and on converter, chain pdfinfo
if (installVersions.converter && installVersions.info) {
downloadPDFTool(tool, version, function () {
return installPDFTools({ info: installVersions.info });
});
}
else {
downloadPDFTool(tool, version);
}
}
// Version not found for platform
else if (xmlhttp.status == 404) {
onPDFToolsDownloadError(404);
}
}
catch (e) {
onPDFToolsDownloadError(e);
}
});
// Browser is offline
if (!sent) {
onPDFToolsDownloadError();
}
}
/*
* Download and install specified PDF tool
*/
function downloadPDFTool(tool, version, callback) {
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
if (tool == 'converter') {
var fileName = Zotero.Fulltext.pdfConverterFileName;
}
else {
var fileName = Zotero.Fulltext.pdfInfoFileName;
}
var url = Zotero.Fulltext.pdfToolsDownloadBaseURL + fileName + '-' + version;
var uri = ioService.newURI(url, null, null);
var file = Zotero.getZoteroDirectory();
file.append(fileName);
var fileURL = ioService.newFileURI(file);
const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
var wbp = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
.createInstance(nsIWBP);
var progressListener = new Zotero.WebProgressFinishListener(function () {
// Set permissions to 755
if (Zotero.isMac) {
file.permissions = 33261;
}
else if (Zotero.isLinux) {
file.permissions = 493;
}
// Write the version number to a file
var versionFile = Zotero.getZoteroDirectory();
versionFile.append(fileName + '.version');
Zotero.File.putContents(versionFile, version + '');
Zotero.Fulltext.registerPDFTool(tool);
// Used to install info tool after converter
if (callback) {
callback();
}
// If done
else {
updatePDFToolsStatus();
}
});
/*
var tr = Components.classes["@mozilla.org/transfer;1"].
createInstance(Components.interfaces.nsITransfer);
tr.init(uri, fileURL, "", null, null, null, wbp);
*/
document.getElementById('pdftools-update-button').disabled = true;
var str = Zotero.getString('zotero.preferences.search.pdf.downloading');
document.getElementById('pdftools-update-button').setAttribute('label', str);
wbp.progressListener = progressListener;
Zotero.debug("Saving " + uri.spec + " to " + fileURL.spec);
wbp.saveURI(uri, null, null, null, null, fileURL);
}
function onPDFToolsDownloadError(e) {
if (e == 404) {
var str = Zotero.getString('zotero.preferences.search.pdf.toolDownloadsNotAvailable',
Zotero.Fulltext.pdfToolsName) + ' '
+ Zotero.getString('zotero.preferences.search.pdf.viewManualInstructions');
}
else if (e) {
Components.utils.reportError(e);
var str = Zotero.getString('zotero.preferences.search.pdf.toolsDownloadError', Zotero.Fulltext.pdfToolsName)
+ ' ' + Zotero.getString('zotero.preferences.search.pdf.tryAgainOrViewManualInstructions');
}
else {
var info = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULAppInfo);
var browser = info.name; // Returns "Firefox" for Firefox
var str = Zotero.getString('general.browserIsOffline', browser);
}
alert(str);
}
function updateIndexStats() {
var stats = Zotero.Fulltext.getIndexStats();
document.getElementById('fulltext-stats-indexed').
lastChild.setAttribute('value', stats.indexed);
document.getElementById('fulltext-stats-partial').
lastChild.setAttribute('value', stats.partial);
document.getElementById('fulltext-stats-unindexed').
lastChild.setAttribute('value', stats.unindexed);
document.getElementById('fulltext-stats-words').
lastChild.setAttribute('value', stats.words);
}
/*
* Unused
*/
function revealDataDirectory() {
var dataDir = Zotero.getZoteroDirectory();
dataDir.QueryInterface(Components.interfaces.nsILocalFile);
try {
dataDir.reveal();
}
catch (e) {
// TODO: This won't work on Linux
}
}
function onOpenURLSelected()
{

View File

@ -66,10 +66,10 @@ To add a new preference:
<rows>
<row>
<hbox>
<hbox id="position-menu-box-label">
<label value="&zotero.preferences.position;" control="positionMenu"/>
</hbox>
<hbox>
<hbox id="position-menu-box">
<menulist id="positionMenu" preference="pref-zoteroPaneOnTop">
<menupopup>
<menuitem label="&zotero.preferences.position.above;" value="true"/>
@ -151,6 +151,104 @@ To add a new preference:
</prefpane>
<prefpane id="zotero-prefpane-search" label="&zotero.preferences.prefpane.search;"
onpaneload="updateIndexStats()">
<preferences>
<preference id="pref-fulltext-textMaxLength" name="extensions.zotero.fulltext.textMaxLength" type="int"/>
<preference id="pref-fulltext-pdfmaxpages" name="extensions.zotero.fulltext.pdfMaxPages" type="int"/>
</preferences>
<groupbox>
<caption label="&zotero.preferences.search.fulltextCache;"/>
<hbox>
<button id="fulltext-rebuildIndex" flex="1" oncommand="rebuildIndexPrompt()"/>
<button id="fulltext-clearIndex" flex="1" oncommand="clearIndexPrompt()"/>
</hbox>
<grid id="fulltext-settings">
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row>
<hbox>
<label value="&zotero.preferences.fulltext.textMaxLength;"/>
</hbox>
<hbox>
<textbox size="10" preference="pref-fulltext-textMaxLength"/>
<label value="(&zotero.preferences.default; 500000)"/>
</hbox>
</row>
</rows>
</grid>
</groupbox>
<groupbox id="pdftools-box">
<caption label="&zotero.preferences.search.pdfIndexing;"/>
<label id="pdfconverter-status"/>
<label id="pdfinfo-status"/>
<label id="pdftools-required" hidden="true"/>
<hbox>
<button id="pdftools-update-button" flex="1" oncommand="checkPDFToolsDownloadVersion()"/>
</hbox>
<label id="pdftools-documentation-link" hidden="true"/>
<grid id="pdftools-settings" hidden="true">
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row>
<hbox>
<label value="&zotero.preferences.fulltext.pdfMaxPages;"/>
</hbox>
<hbox>
<textbox size="5" preference="pref-fulltext-pdfmaxpages"/>
<label value="(&zotero.preferences.default; 100)"/>
</hbox>
</row>
</rows>
</grid>
</groupbox>
<groupbox id="fulltext-stats">
<caption label="&zotero.preferences.search.indexStats;"/>
<grid>
<columns>
<column/>
<column/>
</columns>
<rows>
<row id="fulltext-stats-indexed">
<label value="Indexed:"/>
<label/>
</row>
<row id="fulltext-stats-partial">
<label value="Partial:"/>
<label/>
</row>
<row id="fulltext-stats-unindexed">
<label value="Unindexed:"/>
<label/>
</row>
<row id="fulltext-stats-words">
<label value="Words:"/>
<label/>
</row>
</rows>
</grid>
</groupbox>
</prefpane>
<prefpane id="zotero-prefpane-export" label="&zotero.preferences.prefpane.export;">
<preferences>
<preference id="pref-quickCopy-setting" name="extensions.zotero.export.quickCopy.setting" type="string"/>

View File

@ -35,6 +35,7 @@ Zotero.Attachments = new function(){
this.importFromDocument = importFromDocument;
this.getFileBaseNameFromItem = getFileBaseNameFromItem;
this.createDirectoryForItem = createDirectoryForItem;
this.getStorageDirectory = getStorageDirectory;
this.getPath = getPath;
var self = this;
@ -499,12 +500,14 @@ Zotero.Attachments = new function(){
if (mimeType == 'application/pdf') {
var f = function() {
Zotero.Fulltext.indexPDF(file, itemID);;
Zotero.Fulltext.indexPDF(file, itemID);
Zotero.Notifier.trigger('refresh', 'item', itemID);
};
}
else {
var f = function() {
Zotero.Fulltext.indexDocument(document, itemID);
Zotero.Notifier.trigger('refresh', 'item', itemID);
};
}
@ -780,8 +783,7 @@ Zotero.Attachments = new function(){
* Create directory for attachment files within storage directory
*/
function createDirectoryForItem(itemID) {
var dir = Zotero.getStorageDirectory();
dir.append(itemID);
var dir = this.getStorageDirectory(itemID);
if (!dir.exists()) {
dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755);
}
@ -789,9 +791,16 @@ Zotero.Attachments = new function(){
}
function getStorageDirectory(itemID) {
var dir = Zotero.getStorageDirectory();
dir.append(itemID);
return dir;
}
/*
* Gets a relative path for imported attachments and an absolute path
* for files outside the storage directory
* Gets a relative descriptor for imported attachments and a persistent
* descriptor for files outside the storage directory
*/
function getPath(file, linkMode) {
if (linkMode == self.LINK_MODE_IMPORTED_URL ||

View File

@ -25,6 +25,7 @@ Zotero.File = new function(){
this.getClosestDirectory = getClosestDirectory;
this.getSample = getSample;
this.getContents = getContents;
this.putContents = putContents;
this.getCharsetFromFile = getCharsetFromFile;
this.addCharsetListener = addCharsetListener;
@ -114,6 +115,23 @@ Zotero.File = new function(){
}
/*
* Write string to a file, overwriting existing file if necessary
*
* Note: Can only handle ASCII text!
*/
function putContents(file, str) {
if (file.exists()) {
file.remove(null);
}
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
foStream.write(str, str.length);
foStream.close();
}
/*
* Not implemented, but it'd sure be great if it were
*/

View File

@ -25,11 +25,10 @@ Zotero.Fulltext = new function(){
const CACHE_FILE = '.zotero-ft-cache';
this.init = init;
this.registerPDFToText = registerPDFToText;
this.cacheIsOutdated = cacheIsOutdated;
this.rebuildCache = rebuildCache;
this.registerPDFTool = registerPDFTool;
this.pdfConverterIsRegistered = pdfConverterIsRegistered;
this.pdfInfoIsRegistered = pdfInfoIsRegistered;
this.isCachedMIMEType = isCachedMIMEType;
this.indexWord = indexWord;
this.indexWords = indexWords;
this.indexDocument = indexDocument;
this.indexString = indexString;
@ -39,16 +38,62 @@ Zotero.Fulltext = new function(){
this.findTextInFile = findTextInFile;
this.findTextInItems = findTextInItems;
this.clearItemWords = clearItemWords;
this.getPages = getPages;
this.getTotalPagesFromFile = getTotalPagesFromFile;
this.getChars = getChars;
this.getTotalCharsFromFile = getTotalCharsFromFile;
this.setChars = setChars;
this.setPages = setPages;
this.getIndexedState = getIndexedState;
this.getIndexStats = getIndexStats;
this.canReindex = canReindex;
this.rebuildIndex = rebuildIndex;
this.clearIndex = clearIndex;
this.clearCacheFile = clearCacheFile;
this.clearCacheFiles = clearCacheFiles;
//this.clearItemContent = clearItemContent;
this.purgeUnusedWords = purgeUnusedWords;
this.HTMLToText = HTMLToText;
this.semanticSplitter = semanticSplitter;
var _pdftotext = null;
this.__defineGetter__("pdfToolsDownloadBaseURL", function() { return 'http://www.zotero.org/download/xpdf/'; });
this.__defineGetter__("pdfToolsName", function() { return 'Xpdf'; });
this.__defineGetter__("pdfToolsURL", function() { return 'http://www.foolabs.com/xpdf/'; });
this.__defineGetter__("pdfConverterName", function() { return 'pdftotext'; });
this.__defineGetter__("pdfInfoName", function() { return 'pdfinfo'; });
this.__defineGetter__("pdfConverterCacheFile", function () { return '.zotero-ft-cache'; });
this.__defineGetter__("pdfInfoCacheFile", function () { return '.zotero-ft-info'; });
this.__defineGetter__("INDEX_STATE_UNAVAILABLE", function () { return 0; });
this.__defineGetter__("INDEX_STATE_UNINDEXED", function () { return 1; });
this.__defineGetter__("INDEX_STATE_PARTIAL", function () { return 2; });
this.__defineGetter__("INDEX_STATE_INDEXED", function () { return 3; });
var _pdfConverterVersion = null;
var _pdfConverterFileName = null;
var _pdfConverter = null; // nsIFile to executable
var _pdfInfoVersion = null;
var _pdfInfoFileName = null;
var _pdfInfo = null; // nsIFile to executable
var self = this;
function init() {
this.registerPDFToText();
var platform = Zotero.platform.replace(' ', '-');
_pdfConverterFileName = this.pdfConverterName + '-' + platform;
_pdfInfoFileName = this.pdfInfoName + '-' + platform;
if (Zotero.isWin) {
_pdfConverterFileName += '.exe';
}
this.__defineGetter__("pdfConverterFileName", function() { return _pdfConverterFileName; });
this.__defineGetter__("pdfConverterVersion", function() { return _pdfConverterVersion; });
this.__defineGetter__("pdfInfoFileName", function() { return _pdfInfoFileName; });
this.__defineGetter__("pdfInfoVersion", function() { return _pdfInfoVersion; });
this.registerPDFTool('converter');
this.registerPDFTool('info');
}
@ -58,67 +103,89 @@ Zotero.Fulltext = new function(){
* {platform} is navigator.platform, with spaces replaced by hyphens
* e.g. "Win32", "Linux-i686", "MacPPC", "MacIntel", etc.
*/
function registerPDFToText() {
function registerPDFTool(tool) {
var errMsg = false;
var exec = Zotero.getZoteroDirectory();
var fileName = 'pdftotext-' + Zotero.platform.replace(' ', '-');
if (Zotero.isWin) {
fileName += '.exe';
switch (tool) {
case 'converter':
var toolName = this.pdfConverterName;
var fileName = _pdfConverterFileName
break;
case 'info':
var toolName = this.pdfInfoName;
var fileName = _pdfInfoFileName
break;
default:
throw ("Invalid PDF tool type '" + tool + "' in Zotero.Fulltext.registerPDFTool()");
}
var errMsg = false;
exec.append(fileName);
if (exec.exists()) {
// DEBUG: I'm not sure isSymlink() actually works on any platforms
if (exec.isSymlink()) {
exec = exec.target;
if (!exec.target) {
errMsg = fileName + ' symlink target not found';
exec = null;
}
else {
_pdftotext = exec;
}
}
else {
_pdftotext = exec;
}
}
else {
exec = null;
errMsg = fileName + ' not found';
}
if (_pdftotext) {
Zotero.debug('pdftotext registered at ' + _pdftotext.path);
}
else {
_pdftotext = null;
Zotero.debug(errMsg + ' -- PDF indexing disabled');
}
}
function cacheIsOutdated(){
var sql = "SELECT version FROM version WHERE schema='fulltext'";
return Zotero.DB.valueQuery(sql) < FULLTEXT_VERSION;
}
function rebuildCache(){
Zotero.DB.beginTransaction();
Zotero.DB.query("DELETE FROM fulltextWords");
Zotero.DB.query("DELETE FROM fulltextItems");
//Zotero.DB.query("DELETE FROM fulltextContent");
var sql = "SELECT itemID FROM itemAttachments";
var items = Zotero.DB.columnQuery(sql);
if (items) {
this.indexItems(items);
if (!exec) {
if (tool == 'converter') {
Zotero.debug(errMsg + ' -- PDF indexing disabled');
}
return false;
}
Zotero.DB.commitTransaction();
versionFile = exec.parent;
versionFile.append(fileName + '.version');
if (versionFile.exists()) {
var version = Zotero.File.getSample(versionFile).split(/[\r\n\s]/)[0];
}
if (!version) {
var version = 'UNKNOWN';
}
switch (tool) {
case 'converter':
_pdfConverter = exec;
_pdfConverterVersion = version;
break;
case 'info':
_pdfInfo = exec;
_pdfInfoVersion = version;
break;
}
Zotero.debug(toolName + ' version ' + version + ' registered at ' + exec.path);
return true;
}
function pdfConverterIsRegistered() {
return !!_pdfConverter;
}
function pdfInfoIsRegistered() {
return !!_pdfInfo;
}
/*
* Returns true if MIME type is converted to text and cached before indexing
* (e.g. application/pdf is run through pdftotext)
*/
function isCachedMIMEType(mimeType) {
switch (mimeType) {
case 'application/pdf':
@ -128,29 +195,6 @@ Zotero.Fulltext = new function(){
}
/*
* Index a single word
*
* Note: not used
*/
function indexWord(itemID, word){
Zotero.DB.beginTransaction();
var sql = "SELECT wordID FROM fulltextWords WHERE word=?";
var wordID = Zotero.DB.valueQuery(sql, {string:word});
if (!wordID){
var sql = "INSERT INTO fulltextWords (word) VALUES (?)";
var wordID = Zotero.DB.query(sql, {string:word});
}
var sql = "INSERT OR IGNORE INTO fulltextItems VALUES (?,?)";
Zotero.DB.query(sql, [wordID, itemID]);
Zotero.DB.commitTransaction();
}
/*
* Index multiple words at once
*/
@ -179,9 +223,12 @@ Zotero.Fulltext = new function(){
existing['_' + wordIDs[i]['word']] = wordIDs[i]['wordID'];
}
Zotero.DB.query("REPLACE INTO fulltextItems (itemID, version) VALUES (?,?)",
[itemID, FULLTEXT_VERSION]);
// Handle bound parameters manually for optimal speed
var statement1 = Zotero.DB.getStatement("INSERT INTO fulltextWords (word) VALUES (?)");
var statement2 = Zotero.DB.getStatement("INSERT OR IGNORE INTO fulltextItems VALUES (?,?)");
var statement2 = Zotero.DB.getStatement("INSERT OR IGNORE INTO fulltextItemWords VALUES (?,?)");
for each(var word in words){
if (existing['_' + word]){
@ -206,19 +253,26 @@ Zotero.Fulltext = new function(){
function indexString(text, charset, itemID){
var words = semanticSplitter(text, charset);
Zotero.DB.beginTransaction();
this.clearItemWords(itemID);
this.indexWords(itemID, words);
/*
var sql = "REPLACE INTO fulltextContent (itemID, textContent) VALUES (?,?)";
Zotero.DB.query(sql, [itemID, {string:text}]);
*/
Zotero.DB.commitTransaction();
try {
Zotero.UnresponsiveScriptIndicator.disable();
var words = semanticSplitter(text, charset);
Zotero.DB.beginTransaction();
this.clearItemWords(itemID);
this.indexWords(itemID, words);
/*
var sql = "REPLACE INTO fulltextContent (itemID, textContent) VALUES (?,?)";
Zotero.DB.query(sql, [itemID, {string:text}]);
*/
Zotero.DB.commitTransaction();
}
finally {
Zotero.UnresponsiveScriptIndicator.enable();
}
}
@ -241,20 +295,23 @@ Zotero.Fulltext = new function(){
var text = document.body.innerHTML;
var max = Zotero.Prefs.get('fulltext.textMaxLength');
if (text.length > max) {
Zotero.debug('Only indexing first ' + max + ' characters of item '
var maxLength = Zotero.Prefs.get('fulltext.textMaxLength');
if (text.length > maxLength) {
Zotero.debug('Only indexing first ' + maxLength + ' characters of item '
+ itemID + ' in indexDocument()');
text = text.substr(0, max);
text = text.substr(0, maxLength);
}
text = text.replace(/(>)/g, '$1 ');
text = this.HTMLToText(text);
this.indexString(text, document.characterSet, itemID);
var charsIndexed = Math.min(maxLength, text.length);
this.setChars(itemID, { indexed: charsIndexed, total: text.length });
}
function indexFile(file, mimeType, charset, itemID){
function indexFile(file, mimeType, charset, itemID, maxLength, isCacheFile) {
if (!file.exists()){
Zotero.debug('File not found in indexFile()', 2);
return false;
@ -267,8 +324,22 @@ Zotero.Fulltext = new function(){
return false;
}
if (maxLength == undefined || maxLength === true) {
maxLength = Zotero.Prefs.get('fulltext.textMaxLength');
}
// If maxLength is explicitly false, index everything
else if (maxLength === false || maxLength === null) {
maxLength = false;
}
if (mimeType == 'application/pdf') {
return this.indexPDF(file, itemID);
try {
Zotero.UnresponsiveScriptIndicator.disable();
return this.indexPDF(file, itemID, !maxLength);
}
finally {
Zotero.UnresponsiveScriptIndicator.enable();
}
}
if (mimeType.substr(0, 5)!='text/'){
@ -283,23 +354,36 @@ Zotero.Fulltext = new function(){
Zotero.debug('Indexing file ' + file.path);
var maxLength = Zotero.Prefs.get('fulltext.textMaxLength');
var text = Zotero.File.getContents(file, charset, maxLength);
// Split elements to avoid word concatentation
text = text.replace(/(>)/g, '$1 ');
text = this.HTMLToText(text);
this.indexString(text, charset, itemID);
// Record number of characters indexed
if (!isCacheFile) {
var totalChars = this.getTotalCharsFromFile(itemID);
if (maxLength) {
var charsIndexed = Math.min(maxLength, totalChars);
}
else {
var charsIndexed = totalChars;
}
this.setChars(itemID, { indexed: charsIndexed, total: totalChars });
}
return true;
}
/*
* Run PDF through pdftotext to generate .zotero-ft-cache and pass the
* text file back to indexFile()
* Run PDF through pdfinfo and pdftotext to generate .zotero-ft-info
* and .zotero-ft-cache, and pass the text file back to indexFile()
*
* @param allPages If true, index all pages rather than pdfMaxPages
*/
function indexPDF(file, itemID) {
if (!_pdftotext) {
function indexPDF(file, itemID, allPages) {
if (!_pdfConverter) {
return false;
}
@ -313,25 +397,63 @@ Zotero.Fulltext = new function(){
else {
var cacheFile = file.parent;
}
cacheFile.append(CACHE_FILE);
cacheFile.append(this.pdfConverterCacheFile);
if (_pdfInfo) {
var infoFile = cacheFile.parent;
infoFile.append(this.pdfInfoCacheFile);
Zotero.debug('Running pdfinfo ' + file.path + '" "' + infoFile.path + '"');
var proc = Components.classes["@mozilla.org/process/util;1"].
createInstance(Components.interfaces.nsIProcess);
proc.init(_pdfInfo);
var args = [file.path, infoFile.path];
proc.run(true, args, args.length);
var totalPages = this.getTotalPagesFromFile(itemID);
}
else {
Zotero.debug(this.pdfInfoName + " is not available");
}
var maxPages = Zotero.Prefs.get('fulltext.pdfMaxPages');
Zotero.debug('Running pdftotext -nopgbrk '
+ (allPages ? '' : '-l ' + maxPages) + ' "' + file.path + '" "'
+ cacheFile.path + '"');
var proc = Components.classes["@mozilla.org/process/util;1"].
createInstance(Components.interfaces.nsIProcess);
proc.init(_pdftotext);
var maxPages = Zotero.Prefs.get('fulltext.pdfMaxPages');
Zotero.debug('Running pdftotext -nopgbrk -l ' + maxPages +
' "' + file.path + '" "' + cacheFile.path + '"');
var args = ['-nopgbrk', '-l', maxPages, file.path, cacheFile.path];
proc.init(_pdfConverter);
var args = ['-nopgbrk'];
if (allPages) {
if (totalPages) {
var pagesIndexed = totalPages;
}
}
else {
args.push('-l', maxPages);
var pagesIndexed = Math.min(maxPages, totalPages);
}
args.push(file.path, cacheFile.path);
proc.run(true, args, args.length);
if (cacheFile.exists()) {
return this.indexFile(cacheFile, 'text/plain', 'utf-8', itemID);
if (!cacheFile.exists()) {
Zotero.debug("Cache file doesn't exist!");
return false;
}
return false;
Zotero.DB.beginTransaction();
this.indexFile(cacheFile, 'text/plain', 'utf-8', itemID, false, true);
this.setPages(itemID, { indexed: pagesIndexed, total: totalPages });
Zotero.DB.commitTransaction();
return true;
}
function indexItems(items){
function indexItems(items, complete) {
if (items.constructor.name != 'Array') {
items = [items];
}
@ -352,12 +474,9 @@ Zotero.Fulltext = new function(){
}
this.indexFile(file, i.getAttachmentMimeType(),
i.getAttachmentCharset(), i.getID());
i.getAttachmentCharset(), i.getID(), !complete);
}
var sql = "REPLACE INTO version (schema,version) VALUES (?,?)";
Zotero.DB.query(sql, ['fulltext', FULLTEXT_VERSION]);
Zotero.DB.commitTransaction();
}
@ -497,10 +616,273 @@ Zotero.Fulltext = new function(){
function clearItemWords(itemID){
Zotero.DB.beginTransaction();
Zotero.DB.query("DELETE FROM fulltextItems WHERE itemID=" + itemID);
Zotero.DB.query("DELETE FROM fulltextItemWords WHERE itemID=" + itemID);
Zotero.DB.commitTransaction();
// Delete fulltext cache file if there is one
this.clearCacheFile(itemID);
}
function getPages(itemID, force) {
var sql = "SELECT indexedPages AS indexed, totalPages AS total "
+ "FROM fulltextItems WHERE itemID=?";
var result = Zotero.DB.rowQuery(sql, itemID);
return result ? result : { indexed: null, total: null };
}
/*
* Gets the number of pages from the PDF info cache file
*/
function getTotalPagesFromFile(itemID) {
var item = Zotero.Items.get(itemID);
var file = Zotero.Attachments.getStorageDirectory(item.getID());
file.append(this.pdfInfoCacheFile);
if (!file.exists()) {
return false;
}
var contents = Zotero.File.getContents(file);
try {
// Parse pdfinfo output
var pages = contents.match('Pages:[^0-9]+([0-9]+)')[1];
}
catch (e) {
Zotero.debug(e);
return false;
}
return pages;
}
function getChars(itemID) {
var sql = "SELECT indexedChars AS indexed, totalChars AS total "
+ "FROM fulltextItems WHERE itemID=?";
var result = Zotero.DB.rowQuery(sql, itemID);
return result ? result : { indexed: null, total: null };
}
/*
* Gets the number of characters from the PDF converter cache file
*/
function getTotalCharsFromFile(itemID) {
var item = Zotero.Items.get(itemID);
switch (item.getAttachmentMimeType()) {
case 'application/pdf':
var file = Zotero.Attachments.getStorageDirectory(itemID);
file.append(this.pdfConverterCacheFile);
if (!file.exists()) {
return false;
}
break;
default:
var file = item.getFile();
if (!file) {
return false;
}
}
return Zotero.File.getContents(file).length;
}
function setPages(itemID, obj) {
var sql = "UPDATE fulltextItems SET indexedPages=?, totalPages=? WHERE itemID=?";
Zotero.DB.query(sql, [obj.indexed ? obj.indexed : null,
obj.total ? obj.total : null, itemID]);
}
function setChars(itemID, obj) {
var sql = "UPDATE fulltextItems SET indexedChars=?, totalChars=? WHERE itemID=?";
Zotero.DB.query(sql, [obj.indexed ? obj.indexed : null,
obj.total ? obj.total : null, itemID]);
}
/*
* Gets the indexed state of an item,
*/
function getIndexedState(itemID) {
var item = Zotero.Items.get(itemID);
if (!item) {
throw ("Invalid item " + itemID + " in Zotero.Fulltext.getIndexedState()");
}
if (!item.isAttachment()) {
throw ('Item ' + itemID + ' is not an attachment in Zotero.Fulltext.getIndexedState()');
}
switch (item.getAttachmentMimeType()) {
// Use pages for PDFs
case 'application/pdf':
var pages = this.getPages(itemID);
var indexedPages = pages.indexed;
var totalPages = pages.total;
if (!totalPages) {
if (!indexedPages) {
var status = this.INDEX_STATE_UNINDEXED;
}
else {
var status = this.INDEX_STATE_UNAVAILABLE;
}
}
else if (!indexedPages) {
var status = this.INDEX_STATE_UNINDEXED;
}
else if (indexedPages < totalPages) {
var status = this.INDEX_STATE_PARTIAL;
}
else {
var status = this.INDEX_STATE_INDEXED;
}
break;
// Use chars
default:
var chars = this.getChars(itemID);
var indexedChars = chars.indexed;
var totalChars = chars.total;
if (!totalChars) {
if (!indexedChars) {
var status = this.INDEX_STATE_UNINDEXED;
}
else {
var status = this.INDEX_STATE_UNAVAILABLE;
}
}
else if (!indexedChars) {
var status = this.INDEX_STATE_UNINDEXED;
}
else if (indexedChars < totalChars) {
var status = this.INDEX_STATE_PARTIAL;
}
else {
var status = this.INDEX_STATE_INDEXED;
}
}
return status;
}
function getIndexStats() {
var sql = "SELECT COUNT(*) FROM fulltextItems WHERE "
+ "(indexedPages IS NOT NULL AND indexedPages=totalPages) OR "
+ "(indexedChars IS NOT NULL AND indexedChars=totalChars)"
var indexed = Zotero.DB.valueQuery(sql);
var sql = "SELECT COUNT(*) FROM fulltextItems WHERE "
+ "(indexedPages IS NOT NULL AND indexedPages<totalPages) OR "
+ "(indexedChars IS NOT NULL AND indexedChars<totalChars)"
var partial = Zotero.DB.valueQuery(sql);
var sql = "SELECT COUNT(*) FROM itemAttachments WHERE itemID NOT IN "
+ "(SELECT itemID FROM fulltextItems WHERE "
+ "indexedPages IS NOT NULL OR indexedChars IS NOT NULL)";
var unindexed = Zotero.DB.valueQuery(sql);
var sql = "SELECT COUNT(*) FROM fulltextWords";
var words = Zotero.DB.valueQuery(sql);
return { indexed: indexed, partial: partial, unindexed: unindexed,
words: words };
}
/*
* Returns true if an item can be reindexed
*
* Item must be a non-web-link attachment that isn't already fully indexed
*/
function canReindex(itemID) {
var item = Zotero.Items.get(itemID);
if (item && item.isAttachment() && item.getAttachmentLinkMode() !=
Zotero.Attachments.LINK_MODE_LINKED_URL) {
switch (this.getIndexedState(itemID)) {
case this.INDEX_STATE_UNAVAILABLE:
case this.INDEX_STATE_UNINDEXED:
case this.INDEX_STATE_PARTIAL:
return true;
}
}
return false;
}
function rebuildIndex(unindexedOnly){
Zotero.DB.beginTransaction();
// Get all attachments other than web links
var sql = "SELECT itemID FROM itemAttachments WHERE linkMode!="
+ Zotero.Attachments.LINK_MODE_LINKED_URL;
if (unindexedOnly) {
sql += " AND itemID NOT IN (SELECT itemID FROM fulltextItems "
+ "WHERE indexedChars IS NOT NULL OR indexedPages IS NOT NULL)";
}
var items = Zotero.DB.columnQuery(sql);
if (items) {
Zotero.DB.query("DELETE FROM fulltextItemWords WHERE itemID IN (" + sql + ")");
Zotero.DB.query("DELETE FROM fulltextItems WHERE itemID IN (" + sql + ")");
this.indexItems(items);
}
Zotero.DB.commitTransaction();
}
/*
* Clears full-text word index and all full-text cache files
*/
function clearIndex(skipLinkedURLs) {
Zotero.DB.beginTransaction();
var sql = "DELETE FROM fulltextItems";
if (skipLinkedURLs) {
var linkSQL = "SELECT itemID FROM itemAttachments WHERE linkMode ="
+ Zotero.Attachments.LINK_MODE_LINKED_URL;
sql += " WHERE itemID NOT IN (" + linkSQL + ")";
}
Zotero.DB.query(sql);
sql = "DELETE FROM fulltextItemWords";
if (skipLinkedURLs) {
sql += " WHERE itemID NOT IN (" + linkSQL + ")";
}
Zotero.DB.query(sql);
if (skipLinkedURLs) {
this.purgeUnusedWords();
}
else {
Zotero.DB.query("DELETE FROM fulltextWords");
}
this.clearCacheFiles();
Zotero.DB.commitTransaction();
}
/*
* Clears cache file for an item
*/
function clearCacheFile(itemID) {
var item = Zotero.Items.get(itemID);
if (!item) {
return;
}
if (!item.isAttachment()) {
Zotero.debug("Item " + itemID + " is not an attachment in Zotero.Fulltext.clearCacheFile()");
return;
}
Zotero.debug('Clearing full-text cache file for item ' + itemID);
switch (item.getAttachmentMimeType()) {
case 'application/pdf':
var cacheFile = _getItemCacheFile();
@ -509,6 +891,22 @@ Zotero.Fulltext = new function(){
}
break;
}
}
/*
* Clear cache files for all attachments
*/
function clearCacheFiles(skipLinkedURLs) {
var sql = "SELECT itemID FROM itemAttachments";
if (skipLinkedURLs) {
sql += " WHERE linkMode != " + Zotero.Attachments.LINK_MODE_LINKED_URL;
}
var items = Zotero.DB.columnQuery(sql);
for (var i=0; i<items.length; i++) {
this.clearCacheFile(items[i]);
}
}
@ -521,7 +919,7 @@ Zotero.Fulltext = new function(){
function purgeUnusedWords(){
var sql = "DELETE FROM fulltextWords WHERE wordID NOT IN "
+ "(SELECT wordID FROM fulltextItems)";
+ "(SELECT wordID FROM fulltextItemWords)";
Zotero.DB.query(sql);
}
@ -626,7 +1024,7 @@ Zotero.Fulltext = new function(){
function _getItemCacheFile(itemID) {
var cacheFile = Zotero.getStorageDirectory();
cacheFile.append(itemID);
cacheFile.append(CACHE_FILE);
cacheFile.append(self.pdfConverterCacheFile);
return cacheFile;
}
}

View File

@ -237,9 +237,20 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids)
var madeChanges = false;
var sort = false;
this.selection.selectEventsSuppressed = true;
var savedSelection = this.saveSelection();
// If refreshing a single item, just unselect and reselect it
if (action == 'refresh') {
if (savedSelection.length == 1 && savedSelection[0] == ids[0]) {
this.selection.clearSelection();
this.rememberSelection(savedSelection);
}
return;
}
this.selection.selectEventsSuppressed = true;
// See if we're in the active window
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);

View File

@ -82,7 +82,7 @@ Zotero.Notifier = new function(){
* Possible values:
*
* event: 'add', 'modify', 'delete', 'move' ('c', for changing parent),
* 'remove' (ci, it)
* 'remove' (ci, it), 'refresh'
* type - 'collection', 'search', 'item', 'collection-item', 'item-tag', 'tag'
* ids - single id or array of ids
*

View File

@ -119,15 +119,6 @@ Zotero.Schema = new function(){
var up2 = _updateSchema('system');
var up3 = _updateSchema('scrapers');
// Rebuild fulltext cache if necessary
if (Zotero.Fulltext.cacheIsOutdated()){
try {
Zotero.Fulltext.rebuildCache();
}
catch (e) {
Components.utils.reportError(e);
}
}
Zotero.DB.commitTransaction();
}
catch(e){
@ -1105,6 +1096,8 @@ Zotero.Schema = new function(){
}
}
// 1.0.0b4.r5
if (i==34) {
Zotero.DB.query("ALTER TABLE annotations ADD collapsed BOOL");
Zotero.DB.query("ALTER TABLE annotations ADD dateModified DATETIME");
@ -1112,6 +1105,23 @@ Zotero.Schema = new function(){
Zotero.DB.query("UPDATE annotations SET dateModified = DATETIME('now')");
Zotero.DB.query("UPDATE highlights SET dateModified = DATETIME('now')");
}
if (i==35) {
Zotero.DB.query("CREATE TABLE fulltextItemWords (\n wordID INT,\n itemID INT,\n PRIMARY KEY (wordID, itemID),\n FOREIGN KEY (wordID) REFERENCES fulltextWords(wordID),\n FOREIGN KEY (itemID) REFERENCES items(itemID)\n);");
Zotero.DB.query("INSERT INTO fulltextItemWords SELECT * FROM fulltextItems");
Zotero.DB.query("DROP TABLE fulltextItems");
Zotero.DB.query("CREATE TABLE fulltextItems (\n itemID INT,\n version INT,\n PRIMARY KEY (itemID),\n FOREIGN KEY (itemID) REFERENCES items(itemID)\n);");
Zotero.DB.query("INSERT INTO fulltextItems SELECT DISTINCT itemID, 1 FROM fulltextItemWords");
}
if (i==36) {
Zotero.DB.query("ALTER TABLE fulltextItems ADD indexedPages INT");
Zotero.DB.query("ALTER TABLE fulltextItems ADD totalPages INT");
Zotero.DB.query("ALTER TABLE fulltextItems ADD indexedChars INT");
Zotero.DB.query("ALTER TABLE fulltextItems ADD totalChars INT");
Zotero.DB.query("DELETE FROM version WHERE schema='fulltext'");
Zotero.DB.query("VACUUM");
}
}
_updateSchema('userdata');

View File

@ -1354,7 +1354,7 @@ Zotero.SearchConditions = new function(){
contains: true,
doesNotContain: true
},
table: 'fulltextItems',
table: 'fulltextItemWords',
field: 'word',
special: true
},

View File

@ -122,6 +122,7 @@ var Zotero = new function(){
this.platform = win.navigator.platform;
this.isMac = (this.platform.substr(0, 3) == "Mac");
this.isWin = (this.platform.substr(0, 3) == "Win");
this.isLinux = (this.platform.substr(0, 5) == "Linux");
// Locale
var ph = Components.classes["@mozilla.org/network/protocol;1?name=http"].

View File

@ -1,5 +1,7 @@
<!ENTITY zotero.preferences.title "Zotero Preferences">
<!ENTITY zotero.preferences.default "Default:">
<!ENTITY zotero.preferences.prefpane.general "General">
<!ENTITY zotero.preferences.userInterface "User Interface">
@ -27,6 +29,13 @@
<!ENTITY zotero.preferences.openurl.server "Resolver:">
<!ENTITY zotero.preferences.openurl.version "Version:">
<!ENTITY zotero.preferences.prefpane.search "Search">
<!ENTITY zotero.preferences.search.fulltextCache "Full-Text Cache">
<!ENTITY zotero.preferences.search.pdfIndexing "PDF Indexing">
<!ENTITY zotero.preferences.search.indexStats "Index Statistics">
<!ENTITY zotero.preferences.fulltext.textMaxLength "Maximum characters to index per file:">
<!ENTITY zotero.preferences.fulltext.pdfMaxPages "Maximum pages to index per file:">
<!ENTITY zotero.preferences.prefpane.export "Export">

View File

@ -1,6 +1,7 @@
general.error = Error
general.warning = Warning
general.dontShowWarningAgain = Don't show this warning again.
general.browserIsOffline = %S is currently in offline mode.
general.locate = Locate...
general.restartRequired = Restart Required
general.restartRequiredForChange = Firefox must be restarted for the change to take effect.
@ -10,6 +11,12 @@ general.restartLater = Restart later
general.errorHasOccurred = An error has occurred.
general.restartFirefox = Please restart Firefox.
general.restartFirefoxAndTryAgain = Please restart Firefox and try again.
general.checkForUpdate = Check for update
general.install = Install
general.updateAvailable = Update Available
general.upgrade = Upgrade
general.yes = Yes
general.no = No
upgrade.failed = Upgrading of the Zotero database failed:
upgrade.advanceMessage = Press %S to upgrade now.
@ -74,6 +81,8 @@ pane.items.menu.createBib = Create Bibliography from Selected Item...
pane.items.menu.createBib.multiple = Create Bibliography from Selected Items...
pane.items.menu.generateReport = Generate Report from Selected Item...
pane.items.menu.generateReport.multiple = Generate Report from Selected Items...
pane.items.menu.reindexItem = Reindex Item
pane.items.menu.reindexItem.multiple = Reindex Items
pane.item.selected.zero = No items selected
pane.item.selected.multiple = %S items selected
@ -313,6 +322,29 @@ zotero.preferences.update.error = Error
zotero.preferences.openurl.resolversFound.zero = %S resolvers found
zotero.preferences.openurl.resolversFound.singular = %S resolver found
zotero.preferences.openurl.resolversFound.plural = %S resolvers found
zotero.preferences.search.rebuildIndex = Rebuild Index
zotero.preferences.search.rebuildWarning = Do you want to rebuild the entire index? This may take a while.\n\nTo index only items that haven't been indexed, use %S.
zotero.preferences.search.clearIndex = Clear Index
zotero.preferences.search.clearWarning = After clearing the index, attachment content will no longer be searchable.\n\nWeb link attachments cannot be reindexed without revisiting the page. To leave web links indexed, choose %S.
zotero.preferences.search.clearNonLinkedURLs = Clear All Except Web Links
zotero.preferences.search.indexUnindexed = Index Unindexed Items
zotero.preferences.search.pdf.toolRegistered = %S is installed
zotero.preferences.search.pdf.toolNotRegistered = %S is NOT installed
zotero.preferences.search.pdf.toolsRequired = PDF indexing requires the %1$S and %2$S utilities from the %3$S project.
zotero.preferences.search.pdf.automaticInstall = Zotero can automatically download and install these applications from zotero.org for certain platforms.
zotero.preferences.search.pdf.advancedUsers = Advanced users may wish to view the %S for manual installation instructions.
zotero.preferences.search.pdf.documentationLink = documentation
zotero.preferences.search.pdf.checkForInstaller = Check for installer
zotero.preferences.search.pdf.downloading = Downloading...
zotero.preferences.search.pdf.toolDownloadsNotAvailable = The %S utilities are not currently available for your platform via zotero.org.
zotero.preferences.search.pdf.viewManualInstructions = View the documentation for manual installation instructions.
zotero.preferences.search.pdf.availableDownloads = Available downloads for %1$S from %2$S:
zotero.preferences.search.pdf.availableUpdates = Available updates for %1$S from %2$S:
zotero.preferences.search.pdf.toolVersionPlatform = %1$S version %2$S
zotero.preferences.search.pdf.zoteroCanInstallVersion = Zotero can automatically install it into the Zotero data directory.
zotero.preferences.search.pdf.zoteroCanInstallVersions = Zotero can automatically install these applications into the Zotero data directory.
zotero.preferences.search.pdf.toolsDownloadError = An error occurred while attempting to download the %S utilities from zotero.org.
zotero.preferences.search.pdf.tryAgainOrViewManualInstructions = Please try again later, or view the documentation for manual installation instructions.
zotero.preferences.export.quickCopy.bibStyles = Bibliographic Styles
zotero.preferences.export.quickCopy.exportFormats = Export Formats
zotero.preferences.export.quickCopy.instructions = Quick Copy allows you to copy selected references to the clipboard by pressing a shortcut key (%S) or dragging items into a text box on a web page.
@ -371,6 +403,9 @@ searchConditions.fulltextContent = Attachment Content
searchConditions.programmingLanguage = Programming Language
searchConditions.fileTypeID = Attachment File Type
fulltext.indexState.indexed = Indexed
fulltext.indexState.unavailable = Unknown
fulltext.indexState.partial = Partial
exportOptions.exportNotes = Export Notes
exportOptions.exportFileData = Export Files

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

View File

@ -233,6 +233,27 @@
-moz-box-flex: 1;
}
/* Reindex button in attachment pane */
#zotero-attachment-index-box
{
-moz-box-align: center;
}
#zotero-attachment-reindex
{
margin: 0;
padding: 0;
list-style-image: url(chrome://zotero/skin/arrow_refresh.png);
}
#zotero-attachment-reindex .toolbarbutton-icon
{
margin: 0;
padding: 0;
}
#zotero-go-to-url[disabled=true], #zotero-openurl[disabled=true]
{
list-style-image: url('chrome://zotero/skin/toolbar-go-arrow-disabled.png');
@ -250,6 +271,18 @@
min-height: 1.25em;
}
#zotero-attachment-index-box
{
-moz-box-align: center;
}
#zotero-attachment-index-box > button
{
font-size: .95em;
padding: 0;
}
#zotero-splitter
{
border-top: none;

View File

@ -3,6 +3,16 @@ prefwindow .chromeclass-toolbar
display: -moz-box !important; /* Ignore toolbar collapse button on OS X */
}
/* Prevent bugs in automatic prefpane sizing in Firefox 2.0
From http://forums.mozillazine.org/viewtopic.php?p=2883233&sid=e1285f81ea9c824363802ea5ca96c9b2
*/
prefwindow {
width: 45em;
}
prefwindow > prefpane > vbox.content-box {
height: 42em;
}
radio[pane]
{
min-width: 5.5em;
@ -11,46 +21,64 @@ radio[pane]
-moz-box-pack: end;
}
/* General pane icon */
/* Remove extraneous padding */
vbox > *:first-child
{
margin-top: 0;
}
vbox > *:last-child
{
margin-bottom: 0;
}
hbox > *:first-child
{
margin-left: 0;
}
/*
hbox
{
-moz-outline: 1px dashed green;
}
vbox
{
-moz-outline: 1px dashed yellow;
}
label
{
-moz-outline: 1px dashed pink;
}
*/
/* Links within messages */
label label[class=text-link]
{
margin: 0;
}
/* General pane */
radio[pane=zotero-prefpane-general]
{
list-style-image: url("chrome://zotero/skin/prefs-general.png");
}
/*
* Export pane icon
*/
radio[pane=zotero-prefpane-export]
grid row:not(:first-child)
{
list-style-image: url("chrome://zotero/skin/prefs-export.png");
margin-top: .3em;
}
/*
* Shortcut Keys pane icon
*
* Use the Gear icon until we find a keyboard icon
*/
radio[pane=zotero-prefpane-keys]
{
list-style-image: url("chrome://zotero/skin/prefs-keys.png");
}
/*
* Advanced pane icon
*
* Use the Gear icon until we find a better icon
*/
radio[pane=zotero-prefpane-advanced]
{
list-style-image: url("chrome://zotero/skin/prefs-advanced.png");
}
/* General pane */
grid row hbox:first-child
{
-moz-box-pack: end;
-moz-box-pack: end; /* Right-justify left column */
}
#position-menu-box-label, #position-menu-box
{
-moz-box-align: center;
}
#fontSize
@ -90,7 +118,51 @@ grid row hbox:first-child
}
/*
* Search pane
*/
radio[pane=zotero-prefpane-search]
{
list-style-image: url("chrome://zotero/skin/prefs-search.png");
}
#zotero-prefpane-search groupbox > label, #zotero-prefpane-search groupbox > vbox, #zotero-prefpane-search groupbox > hbox
{
margin: .5em 0;
}
#zotero-prefpane-search groupbox > label:first-child
{
margin-top: 0;
}
#pdfinfo-status
{
margin-top: 0 !important;
}
#fulltext-settings hbox, #pdftools-settings hbox
{
-moz-box-align: center;
}
#fulltext-settings row > hbox:last-child, #pdftools-settings row > hbox:last-child
{
margin-left: .5em;
}
#fulltext-stats row > label:first-child
{
text-align: right;
}
/* Export pane */
radio[pane=zotero-prefpane-export]
{
list-style-image: url("chrome://zotero/skin/prefs-export.png");
}
#quickCopy-instructions, #zotero-prefpane-export vbox {
margin-bottom: 1em;
}
@ -119,6 +191,11 @@ grid row hbox:first-child
/* Shortcut Keys pane */
radio[pane=zotero-prefpane-keys]
{
list-style-image: url("chrome://zotero/skin/prefs-keys.png");
}
#zotero-prefpane-keys row
{
-moz-box-align: center;
@ -139,3 +216,12 @@ grid row hbox:first-child
margin: .75em 0;
font-size: .85em;
}
/*
* Advanced pane
*/
radio[pane=zotero-prefpane-advanced]
{
list-style-image: url("chrome://zotero/skin/prefs-advanced.png");
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,4 +1,4 @@
-- 34
-- 36
-- This file creates tables containing user-specific data -- any changes
-- to existing tables made here must be mirrored in transition steps in
@ -205,18 +205,32 @@ CREATE TABLE IF NOT EXISTS savedSearchConditions (
FOREIGN KEY (savedSearchID) REFERENCES savedSearches(savedSearchID)
);
CREATE TABLE IF NOT EXISTS fulltextItems (
itemID INT,
version INT,
indexedPages INT,
totalPages INT,
indexedChars INT,
totalChars INT,
PRIMARY KEY (itemID),
FOREIGN KEY (itemID) REFERENCES items(itemID)
);
CREATE INDEX IF NOT EXISTS fulltextItems_version ON fulltextItems(version);
CREATE TABLE IF NOT EXISTS fulltextWords (
wordID INTEGER PRIMARY KEY,
word TEXT UNIQUE
);
CREATE INDEX IF NOT EXISTS fulltextWords_word ON fulltextWords(word);
CREATE TABLE IF NOT EXISTS fulltextItems (
CREATE TABLE IF NOT EXISTS fulltextItemWords (
wordID INT,
itemID INT,
PRIMARY KEY (wordID, itemID)
PRIMARY KEY (wordID, itemID),
FOREIGN KEY (wordID) REFERENCES fulltextWords(wordID),
FOREIGN KEY (itemID) REFERENCES items(itemID)
);
CREATE INDEX IF NOT EXISTS fulltextItems_itemID ON fulltextItems(itemID);
CREATE INDEX IF NOT EXISTS fulltextItemWords_itemID ON fulltextItemWords(itemID);
CREATE TABLE IF NOT EXISTS translators (
translatorID TEXT PRIMARY KEY,