- Abstract attachment info pane into XBL binding

- Add attachment conflict resolution
This commit is contained in:
Dan Stillman 2008-12-29 06:18:41 +00:00
parent 403663664c
commit 8ad23d7eea
12 changed files with 675 additions and 391 deletions

View File

@ -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>

View File

@ -62,6 +62,11 @@
return; 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 // Clone object so changes in merge pane don't affect it
this._leftpane.ref = val.clone(true); this._leftpane.ref = val.clone(true);
this._leftpane.original = val; this._leftpane.original = val;
@ -83,6 +88,11 @@
else { else {
// TODO: Make sure object is the correct type // 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 // Clone object so changes in merge pane don't affect it
this._rightpane.ref = val.clone(true); this._rightpane.ref = val.clone(true);
this._rightpane.original = val; this._rightpane.original = val;
@ -208,6 +218,25 @@
</body> </body>
</method> </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"> <method name="_id">
<parameter name="id"/> <parameter name="id"/>
@ -281,6 +310,10 @@
elementName = 'zoteroitembox'; elementName = 'zoteroitembox';
break; break;
case 'attachment':
elementName = 'zoteroattachmentbox';
break;
case 'note': case 'note':
elementName = 'zoteronoteeditor'; elementName = 'zoteronoteeditor';
break; break;
@ -314,6 +347,7 @@
// Type-specific settings // Type-specific settings
switch (this.type) { switch (this.type) {
case 'attachment':
case 'note': case 'note':
objbox.buttonCaption = 'Choose this version'; objbox.buttonCaption = 'Choose this version';
break; break;

View File

@ -40,13 +40,12 @@
<field name="displayRelated">false</field> <field name="displayRelated">false</field>
<field name="displayButton">false</field> <field name="displayButton">false</field>
<field name="buttonCaption"/>
<field name="parentClickHandler"/> <field name="parentClickHandler"/>
<field name="keyDownHandler"/> <field name="keyDownHandler"/>
<field name="commandHandler"/> <field name="commandHandler"/>
<field name="clickHandler"/> <field name="clickHandler"/>
<field name="buttonCaption"/>
<!-- Modes are predefined settings groups for particular tasks --> <!-- Modes are predefined settings groups for particular tasks -->
<field name="_mode">"view"</field> <field name="_mode">"view"</field>
<property name="mode" onget="return this._mode;"> <property name="mode" onget="return this._mode;">
@ -78,7 +77,9 @@
break; break;
case 'mergeedit': case 'mergeedit':
this.editable = true; // Not currently implemented
// (editing works, but value isn't saved)
//this.editable = true;
this.keyDownHandler = this.handleKeyDown; this.keyDownHandler = this.handleKeyDown;
break; break;
@ -146,7 +147,6 @@
var parentbox = this._id('citeLabel'); var parentbox = this._id('citeLabel');
var textbox = this._id('noteField'); var textbox = this._id('noteField');
var button = this._id('goButton'); var button = this._id('goButton');
var button = this._id('goButton');
if (this.editable) { if (this.editable) {
textbox.removeAttribute('readonly'); textbox.removeAttribute('readonly');

View File

@ -34,37 +34,45 @@
<field name="_loadHandler"/> <field name="_loadHandler"/>
<field name="_commandString"/> <field name="_commandString"/>
<field name="_eventHandler"/> <field name="_eventHandler"/>
<field name="_editor"/>
<field name="_value"/>
<field name="_timer"/> <field name="_timer"/>
<field name="_focus"/> <field name="_focus"/>
<field name="_constructed"/>
<field name="_loadOnConstruct"/>
<constructor><![CDATA[ <constructor><![CDATA[
this.mode = this.getAttribute('mode'); this.mode = this.getAttribute('mode');
this._iframe = document.getAnonymousElementByAttribute(this, "anonid", "rt-view"); this._iframe = document.getAnonymousElementByAttribute(this, "anonid", "rt-view");
this._editor = null;
this._value = null; this._rtfMap = {
"\\":"\\\\",
this._rtfMap = { "<em>":"\\i ",
"\\":"\\\\", "</em>":"\\i0 ",
"<em>":"\\i ", "<i>":"\\i ",
"</em>":"\\i0 ", "</i>":"\\i0 ",
"<i>":"\\i ", "<strong>":"\\b ",
"</i>":"\\i0 ", "</strong>":"\\b0 ",
"<strong>":"\\b ", "<b>":"\\b ",
"</strong>":"\\b0 ", "</b>":"\\b0 ",
"<b>":"\\b ", "<u>":"\\ul ",
"</b>":"\\b0 ", "</u>":"\\ul0 ",
"<u>":"\\ul ", "<br />":"\x0B",
"</u>":"\\ul0 ", "<sup>":"\\super ",
"<br />":"\x0B", "</sup>":"\\super0 ",
"<sup>":"\\super ", "<sub>":"\\sub ",
"</sup>":"\\super0 ", "</sub>":"\\sub0 ",
"<sub>":"\\sub ", // there's no way to mimic a tab stop in CSS without
"</sub>":"\\sub0 ", // tables, which wouldn't work here.
// there's no way to mimic a tab stop in CSS without '<span class="tab">&nbsp;</span>':"\t"
// tables, which wouldn't work here. };
'<span class="tab">&nbsp;</span>':"\t"
}; this._constructed = true;
if (this._loadOnConstruct) {
this._load();
}
]]></constructor> ]]></constructor>
<property name="mode"> <property name="mode">
@ -178,7 +186,11 @@
Zotero.debug('No editor yet'); Zotero.debug('No editor yet');
this._value = val; 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(); this._load();
} }
return ; return ;

View File

@ -42,13 +42,6 @@ var Zotero_Merge_Window = new function () {
switch (_mergeGroup.type) { switch (_mergeGroup.type) {
case 'item': 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; break;
default: default:
@ -122,14 +115,23 @@ var Zotero_Merge_Window = new function () {
// Adjust counter // Adjust counter
_numObjects.value = _pos + 1; _numObjects.value = _pos + 1;
_mergeGroup.left = _objects[_pos][0]; try {
_mergeGroup.right = _objects[_pos][1]; _mergeGroup.left = _objects[_pos][0];
_mergeGroup.right = _objects[_pos][1];
// Restore previously merged object into merge pane
if (_merged[_pos]) { // Restore previously merged object into merge pane
_mergeGroup.merge = _merged[_pos].ref; if (_merged[_pos]) {
_mergeGroup.leftpane.removeAttribute("selected"); _mergeGroup.merge = _merged[_pos].ref;
_mergeGroup.rightpane.removeAttribute("selected"); _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') { if (_mergeGroup.type == 'item') {

View File

@ -50,7 +50,6 @@ var ZoteroPane = new function()
this.updateTagFilter = updateTagFilter; this.updateTagFilter = updateTagFilter;
this.onCollectionSelected = onCollectionSelected; this.onCollectionSelected = onCollectionSelected;
this.itemSelected = itemSelected; this.itemSelected = itemSelected;
this.updateItemIndexedState = updateItemIndexedState;
this.reindexItem = reindexItem; this.reindexItem = reindexItem;
this.duplicateSelectedItem = duplicateSelectedItem; this.duplicateSelectedItem = duplicateSelectedItem;
this.deleteSelectedItem = deleteSelectedItem; this.deleteSelectedItem = deleteSelectedItem;
@ -83,7 +82,6 @@ var ZoteroPane = new function()
this.addAttachmentFromPage = addAttachmentFromPage; this.addAttachmentFromPage = addAttachmentFromPage;
this.viewAttachment = viewAttachment; this.viewAttachment = viewAttachment;
this.viewSelectedAttachment = viewSelectedAttachment; this.viewSelectedAttachment = viewSelectedAttachment;
this.showSelectedAttachmentInFilesystem = showSelectedAttachmentInFilesystem;
this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog; this.showAttachmentNotFoundDialog = showAttachmentNotFoundDialog;
this.relinkAttachment = relinkAttachment; this.relinkAttachment = relinkAttachment;
this.reportErrors = reportErrors; this.reportErrors = reportErrors;
@ -797,12 +795,7 @@ var ZoteroPane = new function()
if(item.ref.isNote()) { if(item.ref.isNote()) {
var noteEditor = document.getElementById('zotero-note-editor'); var noteEditor = document.getElementById('zotero-note-editor');
if (this.itemsView.readOnly) { noteEditor.mode = this.itemsView.readOnly ? 'view' : 'edit';
noteEditor.mode = 'view';
}
else {
noteEditor.mode = 'edit';
}
// If loading new or different note, disable undo while we repopulate the text field // 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 // 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()) { else if(item.ref.isAttachment()) {
// DEBUG: this is annoying -- we really want to use an abstracted var attachmentBox = document.getElementById('zotero-attachment-box');
// version of createValueElement() from itemPane.js attachmentBox.mode = this.itemsView.readOnly ? 'view' : 'edit';
// (ideally in an XBL binding) attachmentBox.item = item.ref;
// 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;
document.getElementById('zotero-item-pane-content').selectedIndex = 3; 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() { function reindexItem() {
var items = this.getSelectedItems(); var items = this.getSelectedItems();
if (!items) { if (!items) {
@ -1070,7 +865,7 @@ var ZoteroPane = new function()
var itemID = items[i].id; var itemID = items[i].id;
Zotero.Fulltext.indexItems(itemID, true); 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); var attachment = Zotero.Items.get(itemID);
if (!attachment.isAttachment()) { if (!attachment.isAttachment()) {
throw ("Item " + itemID + " is not an attachment in ZoteroPane.viewAttachment()"); 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); this.loadURI(attachment.getField('url'), event);
return; return;
} }
@ -2146,61 +1941,67 @@ var ZoteroPane = new function()
} }
} }
else { else {
this.showAttachmentNotFoundDialog(itemID); this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing);
} }
} }
function viewSelectedAttachment(event) function viewSelectedAttachment(event, noLocateOnMissing)
{ {
if (this.itemsView && this.itemsView.selection.count == 1) { 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() this.showAttachmentInFilesystem = function (itemID, noLocateOnMissing) {
{ var attachment = Zotero.Items.get(itemID)
if (this.itemsView && this.itemsView.selection.count == 1) { if (attachment.attachmentLinkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
var attachment = this.getSelectedItems()[0]; var file = attachment.getFile();
if (file){
if (attachment.getAttachmentLinkMode() != Zotero.Attachments.LINK_MODE_LINKED_URL) try {
{ file.reveal();
var file = attachment.getFile(); }
if (file){ catch (e) {
// On platforms that don't support nsILocalFile.reveal() (e.g. Linux),
// "double-click" the parent directory
try { try {
file.reveal(); var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile);
parent.launch();
} }
// If launch also fails, try the OS handler
catch (e) { catch (e) {
// On platforms that don't support nsILocalFile.reveal() (e.g. Linux), var uri = Components.classes["@mozilla.org/network/io-service;1"].
// "double-click" the parent directory getService(Components.interfaces.nsIIOService).
try { newFileURI(parent);
var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile); var protocolService =
parent.launch(); Components.classes["@mozilla.org/uriloader/external-protocol-service;1"].
} getService(Components.interfaces.nsIExternalProtocolService);
// If launch also fails, try the OS handler protocolService.loadUrl(uri);
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);
}
} }
} }
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"]. var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
createInstance(Components.interfaces.nsIPromptService); 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) var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL); + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_CANCEL);
var index = ps.confirmEx(null, var index = ps.confirmEx(null,

View File

@ -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'));"/> <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> </vbox>
<!-- Attachment info pane --> <!-- Attachment info pane -->
<vbox id="zotero-view-attachment" flex="1"> <zoteroattachmentbox id="zotero-attachment-box" 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>
</deck> </deck>
</groupbox> </groupbox>
</vbox> </vbox>

View File

@ -2949,22 +2949,52 @@ Zotero.Item.prototype.diff = function (item, includeMatches, ignoreOnlyDateModif
var diff = []; var diff = [];
var thisData = this.serialize(); var thisData = this.serialize();
var otherData = item.serialize(); var otherData = item.serialize();
var numDiffs = Zotero.Items.diff(thisData, otherData, diff, includeMatches); var numDiffs = Zotero.Items.diff(thisData, otherData, diff, includeMatches);
diff[0].creators = []; diff[0].creators = [];
diff[1].creators = []; diff[1].creators = [];
// TODO: creators // TODO: creators?
// TODO: tags?
// TODO: attachments // TODO: related?
// TODO: notes
// TODO: tags
// TODO: related
// TODO: annotations // TODO: annotations
var changed = false;
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++;
}
}
}
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++;
}
}
//Zotero.debug(thisData);
//Zotero.debug(otherData);
//Zotero.debug(diff);
// DEBUG: ignoreOnlyDateModified wouldn't work if includeMatches was set? // DEBUG: ignoreOnlyDateModified wouldn't work if includeMatches was set?
if (numDiffs == 0 || if (numDiffs == 0 ||
(ignoreOnlyDateModified && numDiffs == 1 (ignoreOnlyDateModified && numDiffs == 1
@ -2980,10 +3010,6 @@ Zotero.Item.prototype.diff = function (item, includeMatches, ignoreOnlyDateModif
* Returns an unsaved copy of the item * Returns an unsaved copy of the item
*/ */
Zotero.Item.prototype.clone = function(includePrimary) { 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.debug('Cloning item ' + this.id);
Zotero.DB.beginTransaction(); Zotero.DB.beginTransaction();
@ -3004,23 +3030,15 @@ Zotero.Item.prototype.clone = function(includePrimary) {
} }
} }
// Note for (var field in obj.fields) {
if (this.isNote()) { var fieldID = Zotero.ItemFields.getID(field);
newItem.setNote(this.getNote()); if (fieldID && Zotero.ItemFields.isValidForType(fieldID, itemTypeID)) {
var parent = this.getSource(); newItem.setField(field, obj.fields[field]);
if (parent) {
newItem.setSource(parent);
} }
} }
// Regular item // Regular item
else { if (this.isRegularItem()) {
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 (includePrimary) { if (includePrimary) {
// newItem = loaded from db // newItem = loaded from db
// obj = in-memory // 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) { if (obj.tags) {
for each(var tag in obj.tags) { for each(var tag in obj.tags) {

View File

@ -1626,11 +1626,6 @@ Zotero.Sync.Server.Data = new function() {
continue; continue;
} }
} }
if (obj.isAttachment()) {
var msg = "Reconciliation unimplemented for attachment items";
alert(msg);
throw(msg);
}
reconcile = true; reconcile = true;
break; break;
@ -1894,7 +1889,6 @@ Zotero.Sync.Server.Data = new function() {
default: default:
alert('Delete reconciliation unimplemented for ' + types); alert('Delete reconciliation unimplemented for ' + types);
throw ('Delete reconciliation unimplemented for ' + types); throw ('Delete reconciliation unimplemented for ' + types);
} }
} }
} }

View File

@ -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;
}

View File

@ -274,58 +274,12 @@
list-style-image: url('chrome://zotero/skin/search-cancel-active.png'); 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 #zotero-view-item > vbox
{ {
overflow: auto; overflow: auto;
margin-left: 5px; 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 #zotero-splitter
{ {
border-top: none; border-top: none;

View File

@ -68,6 +68,12 @@ textbox[type="styled"]
-moz-binding: url('chrome://zotero/content/bindings/styled-textbox.xml#styled-textbox'); -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 zoteronoteeditor
{ {
-moz-binding: url('chrome://zotero/content/bindings/noteeditor.xml#note-editor'); -moz-binding: url('chrome://zotero/content/bindings/noteeditor.xml#note-editor');