commit 8ad23d7eea79f19676a199fc48d1966326d6d2a3
parent 403663664c9efece9a1171028302782704e4cd23
Author: Dan Stillman <dstillman@zotero.org>
Date: Mon, 29 Dec 2008 06:18:41 +0000
- Abstract attachment info pane into XBL binding
- Add attachment conflict resolution
Diffstat:
12 files changed, 672 insertions(+), 388 deletions(-)
diff --git a/chrome/content/zotero/bindings/attachmentbox.xml b/chrome/content/zotero/bindings/attachmentbox.xml
@@ -0,0 +1,430 @@
+<?xml version="1.0"?>
+<!--
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright (c) 2006 Center for History and New Media
+ George Mason University, Fairfax, Virginia, USA
+ http://chnm.gmu.edu
+
+ Licensed under the Educational Community License, Version 1.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.opensource.org/licenses/ecl1.php
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ ***** END LICENSE BLOCK *****
+-->
+
+<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd">
+<!-- <!DOCTYPE bindings SYSTEM "chrome://zotero/locale/attachmentbox.dtd"> -->
+
+<bindings xmlns="http://www.mozilla.org/xbl"
+ xmlns:xbl="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <binding id="attachment-box">
+ <resources>
+ <stylesheet src="chrome://zotero/skin/bindings/attachmentbox.css"/>
+ </resources>
+
+ <implementation>
+ <!--
+ Public properties
+ -->
+ <field name="editable">false</field>
+ <field name="displayGoButtons">false</field>
+ <field name="clickableLink">false</field>
+ <field name="displayButton">false</field>
+
+ <field name="buttonCaption"/>
+ <field name="clickHandler"/>
+
+ <!-- Modes are predefined settings groups for particular tasks -->
+ <field name="_mode">"view"</field>
+ <property name="mode" onget="return this._mode;">
+ <setter>
+ <![CDATA[
+ this.editable = false;
+ this.displayGoButtons = false;
+ this.clickableLink = false;
+ this.displayIndexed = false;
+ this.displayPages = false;
+
+ switch (val) {
+ case 'view':
+ this.displayGoButtons = true;
+ this.clickableLink = true;
+ this.displayIndexed = true;
+ this.displayPages = true;
+ break;
+
+ case 'edit':
+ this.editable = true;
+ this.displayGoButtons = true;
+ this.clickableLink = true;
+ this.displayIndexed = true;
+ this.displayPages = true;
+ break;
+
+ case 'merge':
+ this.displayButton = true;
+ break;
+
+ case 'mergeedit':
+ break;
+
+ default:
+ throw ("Invalid mode '" + val + "' in attachmentbox.xml");
+ }
+
+ this._mode = val;
+ document.getAnonymousNodes(this)[0].setAttribute('mode', val);
+ ]]>
+ </setter>
+ </property>
+
+ <field name="_item"/>
+ <property name="item"
+ onget="return this._item;"
+ onset="this._item = val; this.refresh();">
+ </property>
+
+ <!-- .ref is an alias for .item -->
+ <property name="ref"
+ onget="return this._item;"
+ onset="this._item = val; this.refresh();">
+ </property>
+
+
+ <!-- Private properties -->
+
+ <method name="refresh">
+ <body>
+ <![CDATA[
+ Zotero.debug('Refreshing attachment box');
+
+ var attachmentBox = document.getAnonymousNodes(this)[0];
+ var title = this._id('title');
+ var goButtons = this._id('go-buttons');
+ var viewButton = this._id('view');
+ var showButton = this._id('show');
+ var urlField = this._id('url');
+ var accessed = this._id('accessed');
+ var pagesRow = this._id('pages');
+ var indexBox = this._id('index-box');
+ var selectButton = this._id('select-button');
+
+ // DEBUG: this is annoying -- we really want to use an abstracted
+ // version of createValueElement() from itemPane.js
+ // (ideally in an XBL binding)
+
+ // Wrap title to multiple lines if necessary
+ while (title.hasChildNodes()) {
+ title.removeChild(title.firstChild);
+ }
+ var val = this.item.getField('title');
+
+ var firstSpace = val.indexOf(" ");
+ // Crop long uninterrupted text
+ if ((firstSpace == -1 && val.length > 29 ) || firstSpace > 29) {
+ title.setAttribute('crop', 'end');
+ title.setAttribute('value', val);
+ }
+ // Create a <description> element, essentially
+ else {
+ title.removeAttribute('value');
+ title.appendChild(document.createTextNode(val));
+ }
+
+ if (this.editable) {
+ title.className = 'zotero-clicky';
+
+ // For the time being, use a silly little popup
+ title.addEventListener('click', this.editTitle, false);
+ }
+
+ // View and Show buttons
+ if (this.displayGoButtons) {
+ goButtons.hidden = false;
+
+ var itemID = this.item.id;
+ var noLocateOnMissing = !this.editable;
+ viewButton.onclick = function (event) {
+ ZoteroPane.viewAttachment(itemID, event, noLocateOnMissing)
+ }
+ showButton.onclick = function (event) {
+ ZoteroPane.showAttachmentInFilesystem(itemID, noLocateOnMissing)
+ }
+ }
+ else {
+ goButtons.hidden = true;
+ }
+
+ var isImportedURL = this.item.attachmentLinkMode ==
+ Zotero.Attachments.LINK_MODE_IMPORTED_URL;
+
+ // Metadata for URL's
+ if (this.item.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL
+ || isImportedURL) {
+ // "View Page"/"View Snapshot" label
+ if (isImportedURL) {
+ var str = Zotero.getString('pane.item.attachments.view.snapshot');
+ }
+ else {
+ var str = Zotero.getString('pane.item.attachments.view.link');
+ }
+
+ showButton.setAttribute('hidden', !isImportedURL);
+
+ // URL
+ var urlSpec = this.item.getField('url');
+ urlField.setAttribute('value', urlSpec);
+ urlField.setAttribute('hidden', false);
+ if (this.clickableLink) {
+ urlField.onclick = function (event) {
+ ZoteroPane.loadURI(this.value, event)
+ };
+ urlField.className = 'text-link';
+ }
+ else {
+ urlField.className = '';
+ }
+
+ // Access date
+ accessed.setAttribute('value',
+ Zotero.getString('itemFields.accessDate') + ': '
+ + Zotero.Date.sqlToDate(this.item.getField('accessDate'), true).toLocaleString());
+ accessed.setAttribute('hidden', false);
+ }
+ // Metadata for files
+ else {
+ var str = Zotero.getString('pane.item.attachments.view.file');
+ showButton.setAttribute('hidden', false);
+ urlField.setAttribute('hidden', true);
+ accessed.setAttribute('hidden', true);
+ }
+
+ viewButton.setAttribute('label', str);
+
+ // Page count
+ if (this.displayPages) {
+ var pages = Zotero.Fulltext.getPages(this.item.id);
+ var pages = pages ? pages.total : null;
+ if (pages) {
+ var str = Zotero.getString('itemFields.pages') + ': ' + pages;
+ pagesRow.setAttribute('value', str);
+ pagesRow.setAttribute('hidden', false);
+ }
+ else {
+ pagesRow.setAttribute('hidden', true);
+ }
+ }
+ else {
+ pagesRow.setAttribute('hidden', true);
+ }
+
+ // Full-text index information
+ if (this.displayIndexed) {
+ this.updateItemIndexedState();
+ indexBox.hidden = false;
+ }
+ else {
+ indexBox.hidden = true;
+ }
+
+ // Note editor
+ var noteEditor = this._id('note-editor');
+ // Don't make note editable (at least for now)
+ if (this.mode == 'merge' || this.mode == 'mergeedit') {
+ noteEditor.mode = 'merge';
+ noteEditor.displayButton = false;
+ }
+ else {
+ noteEditor.mode = this.mode;
+ }
+ noteEditor.parent = null;
+ noteEditor.item = this.item;
+
+ if (this.displayButton) {
+ selectButton.label = this.buttonCaption;
+ selectButton.hidden = false;
+ selectButton.setAttribute('oncommand',
+ 'document.getBindingParent(this).clickHandler(this)');
+ }
+ else {
+ selectButton.hidden = true;
+ }
+ ]]>
+ </body>
+ </method>
+
+
+ <method name="editTitle">
+ <body>
+ <![CDATA[
+ var item = document.getBindingParent(this).item;
+ var oldTitle = item.getField('title');
+
+ var nsIPS = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Components.interfaces.nsIPromptService);
+
+ var newTitle = { value: oldTitle };
+ var checkState = { value: Zotero.Prefs.get('lastRenameAssociatedFile') };
+
+ while (true) {
+ // Don't show "Rename associated file" option for
+ // linked URLs
+ if (item.attachmentLinkMode ==
+ Zotero.Attachments.LINK_MODE_LINKED_URL) {
+ var result = nsIPS.prompt(
+ window,
+ '',
+ Zotero.getString('pane.item.attachments.rename.title'),
+ newTitle,
+ null,
+ {}
+ );
+
+ // If they hit cancel or left it blank
+ if (!result || !newTitle.value) {
+ return;
+ }
+
+ break;
+ }
+
+ var result = nsIPS.prompt(
+ window,
+ '',
+ Zotero.getString('pane.item.attachments.rename.title'),
+ newTitle,
+ Zotero.getString('pane.item.attachments.rename.renameAssociatedFile'),
+ checkState
+ );
+
+ // If they hit cancel or left it blank
+ if (!result || !newTitle.value) {
+ return;
+ }
+
+ Zotero.Prefs.set('lastRenameAssociatedFile', checkState.value);
+
+ // Rename associated file
+ if (checkState.value) {
+ var renamed = item.renameAttachmentFile(newTitle.value);
+ if (renamed == -1) {
+ var confirmed = confirm(newTitle.value + ' exists. Overwrite existing file?');
+ if (confirmed) {
+ item.renameAttachmentFile(newTitle.value, true);
+ break;
+ }
+ // If they said not to overwrite existing file,
+ // start again
+ continue;
+ }
+ else if (renamed == -2 || !renamed) {
+ alert(Zotero.getString('pane.item.attachments.rename.error'));
+ return;
+ }
+ }
+
+ break;
+ }
+
+ if (newTitle.value != oldTitle) {
+ item.setField('title', newTitle.value);
+ item.save();
+ }
+ ]]>
+ </body>
+ </method>
+
+
+ <!--
+ Update Indexed: (Yes|No|Partial) line
+ -->
+ <method name="updateItemIndexedState">
+ <body>
+ <![CDATA[
+ var indexBox = this._id('index-box');
+ var indexStatus = this._id('index-status');
+ var reindexButton = this._id('reindex');
+
+ var status = Zotero.Fulltext.getIndexedState(this.item.id);
+ 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 (this.editable &&
+ Zotero.Fulltext.pdfConverterIsRegistered() &&
+ Zotero.Fulltext.canReindex(this.item.id)) {
+ reindexButton.setAttribute('hidden', false);
+ }
+ else {
+ reindexButton.setAttribute('hidden', true);
+ }
+ ]]>
+ </body>
+ </method>
+
+
+ <method name="_id">
+ <parameter name="id"/>
+ <body>
+ <![CDATA[
+ return document.getAnonymousNodes(this)[0].getElementsByAttribute('id', id)[0];
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <content>
+ <vbox id="attachment-box" flex="1" orient="vertical"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <label id="title"/>
+ <hbox id="go-buttons">
+ <button id="view" flex="1"/>
+ <button id="show" label="&zotero.item.attachment.file.show;" flex="1"/>
+ </hbox>
+ <label id="url" crop="end"/>
+
+ <label id="accessed"/>
+ <label id="pages"/>
+
+ <hbox id="index-box">
+ <label id="index-status"/>
+ <toolbarbutton id="reindex" oncommand="ZoteroPane.reindexItem()"/>
+ </hbox>
+
+ <zoteronoteeditor id="note-editor" notitle="1" flex="1"/>
+
+ <button id="select-button" hidden="true"/>
+ </vbox>
+ </content>
+ </binding>
+</bindings>
diff --git a/chrome/content/zotero/bindings/merge.xml b/chrome/content/zotero/bindings/merge.xml
@@ -62,6 +62,11 @@
return;
}
+ // Check for note or attachment
+ if (this.type == 'item') {
+ this.type = this._getTypeFromItem(val);
+ }
+
// Clone object so changes in merge pane don't affect it
this._leftpane.ref = val.clone(true);
this._leftpane.original = val;
@@ -83,6 +88,11 @@
else {
// TODO: Make sure object is the correct type
+ // Check for note or attachment if not already set
+ if (this._leftpane.ref == 'deleted' && this.type == 'item') {
+ this.type = this._getTypeFromItem(val);
+ }
+
// Clone object so changes in merge pane don't affect it
this._rightpane.ref = val.clone(true);
this._rightpane.original = val;
@@ -208,6 +218,25 @@
</body>
</method>
+ <method name="_getTypeFromItem">
+ <parameter name="obj"/>
+ <body>
+ <![CDATA[
+ if (!(obj instanceof Zotero.Item)) {
+ throw ("obj is not a Zotero.Item in merge.xml");
+ }
+ if (obj.isAttachment()) {
+ return 'attachment';
+ }
+ else if (obj.isNote()) {
+ return 'note';
+ }
+ else {
+ return 'item';
+ }
+ ]]>
+ </body>
+ </method>
<method name="_id">
<parameter name="id"/>
@@ -281,6 +310,10 @@
elementName = 'zoteroitembox';
break;
+ case 'attachment':
+ elementName = 'zoteroattachmentbox';
+ break;
+
case 'note':
elementName = 'zoteronoteeditor';
break;
@@ -314,6 +347,7 @@
// Type-specific settings
switch (this.type) {
+ case 'attachment':
case 'note':
objbox.buttonCaption = 'Choose this version';
break;
diff --git a/chrome/content/zotero/bindings/noteeditor.xml b/chrome/content/zotero/bindings/noteeditor.xml
@@ -40,13 +40,12 @@
<field name="displayRelated">false</field>
<field name="displayButton">false</field>
+ <field name="buttonCaption"/>
<field name="parentClickHandler"/>
<field name="keyDownHandler"/>
<field name="commandHandler"/>
<field name="clickHandler"/>
- <field name="buttonCaption"/>
-
<!-- Modes are predefined settings groups for particular tasks -->
<field name="_mode">"view"</field>
<property name="mode" onget="return this._mode;">
@@ -78,7 +77,9 @@
break;
case 'mergeedit':
- this.editable = true;
+ // Not currently implemented
+ // (editing works, but value isn't saved)
+ //this.editable = true;
this.keyDownHandler = this.handleKeyDown;
break;
@@ -146,7 +147,6 @@
var parentbox = this._id('citeLabel');
var textbox = this._id('noteField');
var button = this._id('goButton');
- var button = this._id('goButton');
if (this.editable) {
textbox.removeAttribute('readonly');
diff --git a/chrome/content/zotero/bindings/styled-textbox.xml b/chrome/content/zotero/bindings/styled-textbox.xml
@@ -34,37 +34,45 @@
<field name="_loadHandler"/>
<field name="_commandString"/>
<field name="_eventHandler"/>
+ <field name="_editor"/>
+ <field name="_value"/>
<field name="_timer"/>
<field name="_focus"/>
+ <field name="_constructed"/>
+ <field name="_loadOnConstruct"/>
<constructor><![CDATA[
- this.mode = this.getAttribute('mode');
-
- this._iframe = document.getAnonymousElementByAttribute(this, "anonid", "rt-view");
- this._editor = null;
- this._value = null;
-
- this._rtfMap = {
- "\\":"\\\\",
- "<em>":"\\i ",
- "</em>":"\\i0 ",
- "<i>":"\\i ",
- "</i>":"\\i0 ",
- "<strong>":"\\b ",
- "</strong>":"\\b0 ",
- "<b>":"\\b ",
- "</b>":"\\b0 ",
- "<u>":"\\ul ",
- "</u>":"\\ul0 ",
- "<br />":"\x0B",
- "<sup>":"\\super ",
- "</sup>":"\\super0 ",
- "<sub>":"\\sub ",
- "</sub>":"\\sub0 ",
- // there's no way to mimic a tab stop in CSS without
- // tables, which wouldn't work here.
- '<span class="tab"> </span>':"\t"
- };
+ this.mode = this.getAttribute('mode');
+
+ this._iframe = document.getAnonymousElementByAttribute(this, "anonid", "rt-view");
+
+ this._rtfMap = {
+ "\\":"\\\\",
+ "<em>":"\\i ",
+ "</em>":"\\i0 ",
+ "<i>":"\\i ",
+ "</i>":"\\i0 ",
+ "<strong>":"\\b ",
+ "</strong>":"\\b0 ",
+ "<b>":"\\b ",
+ "</b>":"\\b0 ",
+ "<u>":"\\ul ",
+ "</u>":"\\ul0 ",
+ "<br />":"\x0B",
+ "<sup>":"\\super ",
+ "</sup>":"\\super0 ",
+ "<sub>":"\\sub ",
+ "</sub>":"\\sub0 ",
+ // there's no way to mimic a tab stop in CSS without
+ // tables, which wouldn't work here.
+ '<span class="tab"> </span>':"\t"
+ };
+
+ this._constructed = true;
+
+ if (this._loadOnConstruct) {
+ this._load();
+ }
]]></constructor>
<property name="mode">
@@ -178,7 +186,11 @@
Zotero.debug('No editor yet');
this._value = val;
- if (!this._loaded) {
+ if (!this._constructed) {
+ Zotero.debug('Styled textbox not yet constructed', 2);
+ this._loadOnConstruct = true;
+ }
+ else if (!this._loaded) {
this._load();
}
return ;
diff --git a/chrome/content/zotero/merge.js b/chrome/content/zotero/merge.js
@@ -42,13 +42,6 @@ var Zotero_Merge_Window = new function () {
switch (_mergeGroup.type) {
case 'item':
- var firstObj = _objects[0][0] == 'deleted' ? _objects[0][1] : _objects[0][0];
- if (firstObj.isNote()) {
- _mergeGroup.type = 'note';
- }
- else {
- _mergeGroup.type = 'item';
- }
break;
default:
@@ -122,14 +115,23 @@ var Zotero_Merge_Window = new function () {
// Adjust counter
_numObjects.value = _pos + 1;
- _mergeGroup.left = _objects[_pos][0];
- _mergeGroup.right = _objects[_pos][1];
-
- // Restore previously merged object into merge pane
- if (_merged[_pos]) {
- _mergeGroup.merge = _merged[_pos].ref;
- _mergeGroup.leftpane.removeAttribute("selected");
- _mergeGroup.rightpane.removeAttribute("selected");
+ try {
+ _mergeGroup.left = _objects[_pos][0];
+ _mergeGroup.right = _objects[_pos][1];
+
+ // Restore previously merged object into merge pane
+ if (_merged[_pos]) {
+ _mergeGroup.merge = _merged[_pos].ref;
+ _mergeGroup.leftpane.removeAttribute("selected");
+ _mergeGroup.rightpane.removeAttribute("selected");
+ }
+ }
+ catch (e) {
+ var prompt = Components.classes["@mozilla.org/network/default-prompt;1"]
+ .createInstance(Components.interfaces.nsIPrompt);
+ prompt.alert(Zotero.getString('general.error'), e);
+ _wizard.getButton('cancel').click();
+ return false;
}
if (_mergeGroup.type == 'item') {
diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js
@@ -50,7 +50,6 @@ 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;
@@ -83,7 +82,6 @@ var ZoteroPane = new function()
this.addAttachmentFromPage = addAttachmentFromPage;
this.viewAttachment = viewAttachment;
this.viewSelectedAttachment = viewSelectedAttachment;
- this.showSelectedAttachmentInFilesystem = showSelectedAttachmentInFilesystem;
this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog;
this.relinkAttachment = relinkAttachment;
this.reportErrors = reportErrors;
@@ -797,12 +795,7 @@ var ZoteroPane = new function()
if(item.ref.isNote()) {
var noteEditor = document.getElementById('zotero-note-editor');
- if (this.itemsView.readOnly) {
- noteEditor.mode = 'view';
- }
- else {
- noteEditor.mode = 'edit';
- }
+ noteEditor.mode = this.itemsView.readOnly ? 'view' : 'edit';
// 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
@@ -828,165 +821,9 @@ var ZoteroPane = new function()
}
else if(item.ref.isAttachment()) {
- // DEBUG: this is annoying -- we really want to use an abstracted
- // version of createValueElement() from itemPane.js
- // (ideally in an XBL binding)
-
- // Wrap title to multiple lines if necessary
- var label = document.getElementById('zotero-attachment-label');
- while (label.hasChildNodes())
- {
- label.removeChild(label.firstChild);
- }
- var val = item.getField('title');
-
- var firstSpace = val.indexOf(" ");
- // Crop long uninterrupted text
- if ((firstSpace == -1 && val.length > 29 ) || firstSpace > 29)
- {
- label.setAttribute('crop', 'end');
- label.setAttribute('value', val);
- }
- // Create a <description> element, essentially
- else
- {
- label.removeAttribute('value');
- label.appendChild(document.createTextNode(val));
- }
-
- // For the time being, use a silly little popup
- label.className = 'zotero-clicky';
- label.onclick = function(event){
- var nsIPS = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
- .getService(Components.interfaces.nsIPromptService);
-
- var newTitle = { value: val };
- var checkState = { value: Zotero.Prefs.get('lastRenameAssociatedFile') };
-
- while (true) {
- // Don't show "Rename associated file" option for
- // linked URLs
- if (item.ref.getAttachmentLinkMode() ==
- Zotero.Attachments.LINK_MODE_LINKED_URL) {
- var result = nsIPS.prompt(
- window,
- '',
- Zotero.getString('pane.item.attachments.rename.title'),
- newTitle,
- null,
- {}
- );
-
- // If they hit cancel or left it blank
- if (!result || !newTitle.value) {
- return;
- }
-
- break;
- }
-
- var result = nsIPS.prompt(
- window,
- '',
- Zotero.getString('pane.item.attachments.rename.title'),
- newTitle,
- Zotero.getString('pane.item.attachments.rename.renameAssociatedFile'),
- checkState
- );
-
- // If they hit cancel or left it blank
- if (!result || !newTitle.value) {
- return;
- }
-
- Zotero.Prefs.set('lastRenameAssociatedFile', checkState.value);
-
- // Rename associated file
- if (checkState.value) {
- var renamed = item.ref.renameAttachmentFile(newTitle.value);
- if (renamed == -1) {
- var confirmed = confirm(newTitle.value + ' exists. Overwrite existing file?');
- if (confirmed) {
- item.ref.renameAttachmentFile(newTitle.value, true);
- break;
- }
- // If they said not to overwrite existing file,
- // start again
- continue;
- }
- else if (renamed == -2 || !renamed) {
- alert(Zotero.getString('pane.item.attachments.rename.error'));
- return;
- }
- }
-
- break;
- }
-
- if (newTitle.value != val) {
- item.ref.setField('title', newTitle.value);
- item.ref.save();
- }
- }
-
-
- var isImportedURL = item.ref.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_IMPORTED_URL;
-
- // Metadata for URL's
- if (item.ref.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_LINKED_URL
- || isImportedURL)
- {
- // "View Page"/"View Snapshot" label
- if (isImportedURL)
- {
- var str = Zotero.getString('pane.item.attachments.view.snapshot');
- }
- else
- {
- var str = Zotero.getString('pane.item.attachments.view.link');
- }
-
- document.getElementById('zotero-attachment-show').setAttribute('hidden', !isImportedURL);
-
- // URL
- document.getElementById('zotero-attachment-url').setAttribute('value', item.getField('url'));
- document.getElementById('zotero-attachment-url').setAttribute('hidden', false);
- // Access date
- document.getElementById('zotero-attachment-accessed').setAttribute('value',
- Zotero.getString('itemFields.accessDate') + ': '
- + Zotero.Date.sqlToDate(item.getField('accessDate'), true).toLocaleString());
- document.getElementById('zotero-attachment-accessed').setAttribute('hidden', false);
- }
- // Metadata for files
- else
- {
- var str = Zotero.getString('pane.item.attachments.view.file');
- document.getElementById('zotero-attachment-show').setAttribute('hidden', false);
- document.getElementById('zotero-attachment-url').setAttribute('hidden', true);
- document.getElementById('zotero-attachment-accessed').setAttribute('hidden', true);
- }
-
- document.getElementById('zotero-attachment-view').setAttribute('label', str);
-
- // Display page count
- var pages = Zotero.Fulltext.getPages(item.ref.id);
- var pages = pages ? pages.total : null;
- 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.mode = 'edit';
- noteEditor.parent = null;
- noteEditor.item = item.ref;
+ var attachmentBox = document.getElementById('zotero-attachment-box');
+ attachmentBox.mode = this.itemsView.readOnly ? 'view' : 'edit';
+ attachmentBox.item = item.ref;
document.getElementById('zotero-item-pane-content').selectedIndex = 3;
}
@@ -1015,48 +852,6 @@ 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.id);
- 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.id)) {
- reindexButton.setAttribute('hidden', false);
- }
- else {
- reindexButton.setAttribute('hidden', true);
- }
- }
-
-
function reindexItem() {
var items = this.getSelectedItems();
if (!items) {
@@ -1070,7 +865,7 @@ var ZoteroPane = new function()
var itemID = items[i].id;
Zotero.Fulltext.indexItems(itemID, true);
}
- this.updateItemIndexedState();
+ document.getElementById('zotero-attachment-box').updateItemIndexedState();
}
@@ -2100,13 +1895,13 @@ var ZoteroPane = new function()
}
- function viewAttachment(itemID, event) {
+ function viewAttachment(itemID, event, noLocateOnMissing) {
var attachment = Zotero.Items.get(itemID);
if (!attachment.isAttachment()) {
throw ("Item " + itemID + " is not an attachment in ZoteroPane.viewAttachment()");
}
- if (attachment.getAttachmentLinkMode() == Zotero.Attachments.LINK_MODE_LINKED_URL) {
+ if (attachment.attachmentLinkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) {
this.loadURI(attachment.getField('url'), event);
return;
}
@@ -2146,61 +1941,67 @@ var ZoteroPane = new function()
}
}
else {
- this.showAttachmentNotFoundDialog(itemID);
+ this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing);
}
}
- function viewSelectedAttachment(event)
+ function viewSelectedAttachment(event, noLocateOnMissing)
{
if (this.itemsView && this.itemsView.selection.count == 1) {
- this.viewAttachment(this.getSelectedItems(true)[0], event);
+ this.viewAttachment(this.getSelectedItems(true)[0], event, noLocateOnMissing);
}
}
- function showSelectedAttachmentInFilesystem()
- {
- if (this.itemsView && this.itemsView.selection.count == 1) {
- var attachment = this.getSelectedItems()[0];
-
- if (attachment.getAttachmentLinkMode() != Zotero.Attachments.LINK_MODE_LINKED_URL)
- {
- var file = attachment.getFile();
- if (file){
+ this.showAttachmentInFilesystem = function (itemID, noLocateOnMissing) {
+ var attachment = Zotero.Items.get(itemID)
+ if (attachment.attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
+ var file = attachment.getFile();
+ if (file){
+ try {
+ file.reveal();
+ }
+ catch (e) {
+ // On platforms that don't support nsILocalFile.reveal() (e.g. Linux),
+ // "double-click" the parent directory
try {
- file.reveal();
+ var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile);
+ parent.launch();
}
+ // If launch also fails, try the OS handler
catch (e) {
- // On platforms that don't support nsILocalFile.reveal() (e.g. Linux),
- // "double-click" the parent directory
- try {
- var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile);
- parent.launch();
- }
- // If launch also fails, try the OS handler
- catch (e) {
- var uri = Components.classes["@mozilla.org/network/io-service;1"].
- getService(Components.interfaces.nsIIOService).
- newFileURI(parent);
- var protocolService =
- Components.classes["@mozilla.org/uriloader/external-protocol-service;1"].
- getService(Components.interfaces.nsIExternalProtocolService);
- protocolService.loadUrl(uri);
- }
+ var uri = Components.classes["@mozilla.org/network/io-service;1"].
+ getService(Components.interfaces.nsIIOService).
+ newFileURI(parent);
+ var protocolService =
+ Components.classes["@mozilla.org/uriloader/external-protocol-service;1"].
+ getService(Components.interfaces.nsIExternalProtocolService);
+ protocolService.loadUrl(uri);
}
}
- else {
- this.showAttachmentNotFoundDialog(attachment.id)
- }
+ }
+ else {
+ this.showAttachmentNotFoundDialog(attachment.id, noLocateOnMissing)
}
}
}
- function showAttachmentNotFoundDialog(itemID) {
+ function showAttachmentNotFoundDialog(itemID, noLocate) {
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
createInstance(Components.interfaces.nsIPromptService);
+
+
+ // Don't show Locate button
+ if (noLocate) {
+ var index = ps.alert(null,
+ Zotero.getString('pane.item.attachments.fileNotFound.title'),
+ Zotero.getString('pane.item.attachments.fileNotFound.text')
+ );
+ return;
+ }
+
var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
var index = ps.confirmEx(null,
diff --git a/chrome/content/zotero/overlay.xul b/chrome/content/zotero/overlay.xul
@@ -379,25 +379,7 @@
<button id="zotero-view-note-button" label="&zotero.notes.separate;" oncommand="ZoteroPane.openNoteWindow(this.getAttribute('noteID')); if(this.hasAttribute('sourceID')) ZoteroPane.selectItem(this.getAttribute('sourceID'));"/>
</vbox>
<!-- Attachment info pane -->
- <vbox id="zotero-view-attachment" flex="1">
- <label id="zotero-attachment-label"/>
- <hbox>
- <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>
- <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>
-
- <zoteronoteeditor id="zotero-attachment-note-editor" notitle="1" flex="1"/>
- </vbox>
+ <zoteroattachmentbox id="zotero-attachment-box" flex="1"/>
</deck>
</groupbox>
</vbox>
diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js
@@ -2949,21 +2949,51 @@ Zotero.Item.prototype.diff = function (item, includeMatches, ignoreOnlyDateModif
var diff = [];
var thisData = this.serialize();
var otherData = item.serialize();
+
var numDiffs = Zotero.Items.diff(thisData, otherData, diff, includeMatches);
diff[0].creators = [];
diff[1].creators = [];
- // TODO: creators
-
- // TODO: attachments
+ // TODO: creators?
+ // TODO: tags?
+ // TODO: related?
+ // TODO: annotations
- // TODO: notes
+ var changed = false;
- // TODO: tags
+ if (thisData.attachment) {
+ for (var field in thisData.attachment) {
+ changed = thisData.attachment[field] != otherData.attachment[field];
+ if (includeMatches || changed) {
+ if (!diff[0].attachment) {
+ diff[0].attachment = {};
+ diff[1].attachment = {};
+ }
+ diff[0].attachment[field] = thisData.attachment[field];
+ diff[1].attachment[field] = otherData.attachment[field];
+ }
+
+ if (changed) {
+ numDiffs++;
+ }
+ }
+ }
- // TODO: related
+ if (thisData.note != undefined) {
+ changed = thisData.note != otherData.note;
+ if (includeMatches || changed) {
+ diff[0].note = thisData.note;
+ diff[1].note = otherData.note;
+ }
+
+ if (changed) {
+ numDiffs++;
+ }
+ }
- // TODO: annotations
+ //Zotero.debug(thisData);
+ //Zotero.debug(otherData);
+ //Zotero.debug(diff);
// DEBUG: ignoreOnlyDateModified wouldn't work if includeMatches was set?
if (numDiffs == 0 ||
@@ -2980,10 +3010,6 @@ Zotero.Item.prototype.diff = function (item, includeMatches, ignoreOnlyDateModif
* Returns an unsaved copy of the item
*/
Zotero.Item.prototype.clone = function(includePrimary) {
- if (this.isAttachment()) {
- throw ('Cloning attachment items not supported in Zotero.Item.clone()');
- }
-
Zotero.debug('Cloning item ' + this.id);
Zotero.DB.beginTransaction();
@@ -3004,23 +3030,15 @@ Zotero.Item.prototype.clone = function(includePrimary) {
}
}
- // Note
- if (this.isNote()) {
- newItem.setNote(this.getNote());
- var parent = this.getSource();
- if (parent) {
- newItem.setSource(parent);
+ for (var field in obj.fields) {
+ var fieldID = Zotero.ItemFields.getID(field);
+ if (fieldID && Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
+ newItem.setField(field, obj.fields[field]);
}
}
+
// Regular item
- else {
- for (var field in obj.fields) {
- var fieldID = Zotero.ItemFields.getID(field);
- if (fieldID && Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
- newItem.setField(field, obj.fields[field]);
- }
- }
-
+ if (this.isRegularItem()) {
if (includePrimary) {
// newItem = loaded from db
// obj = in-memory
@@ -3054,6 +3072,21 @@ Zotero.Item.prototype.clone = function(includePrimary) {
}
}
}
+ else {
+ newItem.setNote(this.getNote());
+ var parent = this.getSource();
+ if (parent) {
+ newItem.setSource(parent);
+ }
+
+ if (this.isAttachment()) {
+ newItem.attachmentLinkMode = this.attachmentLinkMode;
+ newItem.attachmentMIMEType = this.attachmentMIMEType;
+ newItem.attachmentCharset = this.attachmentCharset;
+ newItem.attachmentPath = this.attachmentPath;
+ newItem.attachmentSyncState = this.attachmentSyncState;
+ }
+ }
if (obj.tags) {
for each(var tag in obj.tags) {
diff --git a/chrome/content/zotero/xpcom/sync.js b/chrome/content/zotero/xpcom/sync.js
@@ -1626,11 +1626,6 @@ Zotero.Sync.Server.Data = new function() {
continue;
}
}
- if (obj.isAttachment()) {
- var msg = "Reconciliation unimplemented for attachment items";
- alert(msg);
- throw(msg);
- }
reconcile = true;
break;
@@ -1894,7 +1889,6 @@ Zotero.Sync.Server.Data = new function() {
default:
alert('Delete reconciliation unimplemented for ' + types);
throw ('Delete reconciliation unimplemented for ' + types);
-
}
}
}
diff --git a/chrome/skin/default/zotero/bindings/attachmentbox.css b/chrome/skin/default/zotero/bindings/attachmentbox.css
@@ -0,0 +1,36 @@
+/* Don't collapse blank attachment titles, since it prevents renaming */
+#title
+{
+ min-height: 1.25em;
+}
+
+#view, #show
+{
+ list-style-image: url('chrome://zotero/skin/toolbar-go-arrow.png');
+ -moz-box-direction: reverse;
+ -moz-box-flex: 1;
+}
+
+#index-box
+{
+ -moz-box-align: center;
+}
+
+#reindex
+{
+ margin: 0;
+ padding: 0;
+ list-style-image: url(chrome://zotero/skin/arrow_refresh.png);
+}
+
+#reindex .toolbarbutton-icon
+{
+ margin: 0;
+ padding: 0;
+}
+
+#index-box > button
+{
+ font-size: .95em;
+ padding: 0;
+}
diff --git a/chrome/skin/default/zotero/overlay.css b/chrome/skin/default/zotero/overlay.css
@@ -274,58 +274,12 @@
list-style-image: url('chrome://zotero/skin/search-cancel-active.png');
}
-#zotero-attachment-view, #zotero-attachment-show
-{
- list-style-image: url('chrome://zotero/skin/toolbar-go-arrow.png');
- -moz-box-direction: reverse;
- -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-view-item > vbox
{
overflow: auto;
margin-left: 5px;
}
-/* Don't collapse blank attachment labels, since it prevents renaming */
-#zotero-attachment-label
-{
- 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;
diff --git a/chrome/skin/default/zotero/zotero.css b/chrome/skin/default/zotero/zotero.css
@@ -68,6 +68,12 @@ textbox[type="styled"]
-moz-binding: url('chrome://zotero/content/bindings/styled-textbox.xml#styled-textbox');
}
+zoteroattachmentbox
+{
+ -moz-binding: url('chrome://zotero/content/bindings/attachmentbox.xml#attachment-box');
+}
+
+
zoteronoteeditor
{
-moz-binding: url('chrome://zotero/content/bindings/noteeditor.xml#note-editor');