commit 481d84795111af162eeb94e530d5c2629bad4752
parent 921fa8c0fa5606eb27de43d0fcca12b98479d9fd
Author: Dan Stillman <dstillman@zotero.org>
Date: Fri, 29 May 2009 11:49:55 +0000
Long tag fixer tool -- runs automatically if server returns a long tag error, giving the option to split, edit, or delete the offending tag
Needs testing and refinement
- Also fixes server unlock after sync errors
Diffstat:
5 files changed, 304 insertions(+), 13 deletions(-)
diff --git a/chrome/content/zotero/longTagFixer.js b/chrome/content/zotero/longTagFixer.js
@@ -0,0 +1,185 @@
+var Zotero_Long_Tag_Fixer = new function () {
+ var _oldTag = window.arguments[0];
+ var _callback = window.arguments[1];
+
+ this.init = function () {
+ document.getElementById('zotero-old-tag').value = _oldTag;
+
+ var lastMode = Zotero.Prefs.get('lastLongTagMode');
+ if (!lastMode) {
+ lastMode = 0;
+ }
+ this.switchMode(lastMode);
+ }
+
+ this.switchMode = function (index) {
+ var dialog = document.getElementById('zotero-long-tag-fixer');
+
+ document.getElementById('zotero-new-tag-actions').selectedIndex = index;
+
+ // TODO: localize
+ switch (index) {
+ case 0:
+ var buttonLabel = "Save Tags";
+ this.updateTagList();
+ break;
+
+ case 1:
+ var buttonLabel = "Save Tag";
+ document.getElementById('zotero-new-tag-editor').value = _oldTag;
+ this.updateEditLength(_oldTag.length)
+ break;
+
+ case 2:
+ var buttonLabel = "Delete Tag";
+ dialog.getButton('accept').disabled = false;
+ break;
+ }
+
+ document.getElementById('zotero-long-tag-fixer').getButton('accept').label = buttonLabel;
+
+ Zotero.Prefs.set('lastLongTagMode', index);
+ }
+
+
+ /**
+ * Split tags and populate list
+ */
+ this.updateTagList = function () {
+ var listbox = document.getElementById('zotero-new-tag-list');
+ while (listbox.childNodes.length) {
+ listbox.removeChild(listbox.lastChild);
+ }
+
+ var delimiter = document.getElementById('zotero-old-tag-delimiter').value;
+ if (delimiter) {
+ var re = new RegExp("\\s*" + delimiter + "\\s*");
+ var tags = _oldTag.split(re);
+ }
+
+ var acceptButton = document.getElementById('zotero-long-tag-fixer').getButton('accept');
+ if (!delimiter || tags.length < 2) {
+ acceptButton.disabled = true;
+ return;
+ }
+ else {
+ acceptButton.disabled = false;
+ }
+
+ tags.sort();
+ for (var i=0; i<tags.length; i++) {
+ if (i==0 || tags[i] == tags[i-1]) {
+ continue;
+ }
+ var li = listbox.appendItem(tags[i]);
+ li.setAttribute('type', 'checkbox');
+ li.setAttribute('checked', 'true');
+ }
+ }
+
+
+ this.deselectAll = function () {
+ var lis = document.getElementById('zotero-new-tag-list').getElementsByTagName('listitem');
+ for (var i=0; i<lis.length; i++) {
+ lis[i].checked = false;
+ }
+ }
+
+
+ this.selectAll = function () {
+ var lis = document.getElementById('zotero-new-tag-list').getElementsByTagName('listitem');
+ for (var i=0; i<lis.length; i++) {
+ lis[i].checked = true;
+ }
+ }
+
+
+ this.updateEditLength = function (len) {
+ document.getElementById('zotero-new-tag-character-count').value = len;
+ var invalid = len == 0 || len > 255;
+ document.getElementById('zotero-new-tag-characters').setAttribute('invalid', invalid);
+ document.getElementById('zotero-long-tag-fixer').getButton('accept').disabled = invalid;
+ }
+
+
+ this.cancel = function () {
+ if (_callback) {
+ _callback(false);
+ }
+ }
+
+
+ this.save = function () {
+ try {
+
+ var index = document.getElementById('zotero-new-tag-actions').selectedIndex;
+
+ // Search for all matching tags across all libraries
+ var sql = "SELECT tagID FROM tags WHERE name=?";
+ var oldTagIDs = Zotero.DB.columnQuery(sql, _oldTag);
+
+ switch (index) {
+ // Split
+ case 0:
+ // Get checked tags
+ var listbox = document.getElementById('zotero-new-tag-list');
+ var len = Zotero.isFx3 ? listbox.childNodes.length : listbox.childElementCount;
+ var newTags = [];
+ for (var i=0; i<len; i++) {
+ var li = listbox.childNodes[i];
+ if (li.getAttribute('checked') == 'true') {
+ newTags.push(li.getAttribute('label'));
+ }
+ }
+
+ Zotero.DB.beginTransaction();
+
+ // Add new tags to all items linked to each matching old tag
+ for (var i=0; i<oldTagIDs.length; i++) {
+ var tag = Zotero.Tags.get(oldTagIDs[i]);
+ var items = tag.getLinkedItems();
+ if (items) {
+ for (var j=0; j<items.length; j++) {
+ items[j].addTags(newTags, tag.type);
+ }
+ }
+ }
+
+ // Remove old tags
+ Zotero.Tags.erase(oldTagIDs);
+ Zotero.Tags.purge();
+ Zotero.DB.commitTransaction();
+ break;
+
+ // Edit
+ case 1:
+ var value = document.getElementById('zotero-new-tag-editor').value;
+ Zotero.DB.beginTransaction();
+ for (var i=0; i<oldTagIDs.length; i++) {
+ var tag = Zotero.Tags.get(oldTagIDs[i]);
+ tag.name = value;
+ tag.save();
+ }
+ Zotero.DB.commitTransaction();
+ break;
+
+ // Delete
+ case 2:
+ Zotero.DB.beginTransaction();
+ Zotero.Tags.erase(oldTagIDs);
+ Zotero.Tags.purge();
+ Zotero.DB.commitTransaction();
+ break;
+ }
+
+ if (_callback) {
+ _callback(true);
+ }
+
+ }
+ catch (e) {
+ Zotero.debug(e);
+ throw (e);
+ }
+ }
+}
diff --git a/chrome/content/zotero/longTagFixer.xul b/chrome/content/zotero/longTagFixer.xul
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://zotero/skin/longTagFixer.css" type="text/css"?>
+
+<dialog id="zotero-long-tag-fixer" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept,cancel"
+ onload="Zotero_Long_Tag_Fixer.init()"
+ ondialogcancel="Zotero_Long_Tag_Fixer.cancel()"
+ ondialogaccept="Zotero_Long_Tag_Fixer.save()"
+ title="Sync Error"> <!-- TODO: localize -->
+
+ <script src="include.js"/>
+ <script src="longTagFixer.js"/>
+
+ <!-- TODO: localize -->
+ <label value="The following tag in your Zotero library is too long to sync to the server:"/>
+ <groupbox>
+ <textbox id="zotero-old-tag" multiline="true" rows="4" readonly="true" class="plain"/>
+ </groupbox>
+ <label>Synced tags must be shorter than 256 characters.</label>
+
+ <separator class="thin"/>
+
+ <label value="You can either split the tag into multiple tags, edit the tag manually to shorten it, or delete it."/>
+
+ <separator/>
+
+ <tabbox id="zotero-new-tag-actions">
+ <tabs oncommand="Zotero_Long_Tag_Fixer.switchMode(this.selectedIndex)">
+ <tab label="Split"/>
+ <tab label="Edit"/>
+ <tab label="Delete"/>
+ </tabs>
+ <tabpanels>
+ <!-- Split -->
+ <vbox>
+ <hbox align="center">
+ <label>Split at the </label>
+ <textbox id="zotero-old-tag-delimiter" size="1" maxLength="1" value=";"
+ oninput="Zotero_Long_Tag_Fixer.updateTagList()"/>
+ <label>character</label>
+ </hbox>
+
+ <separator class="thin"/>
+
+ <listbox id="zotero-new-tag-list" rows="8"/>
+
+ <separator class="thin"/>
+
+ <label value="Unchecked tags will not be saved."/>
+
+ <hbox>
+ <button label="Deselect All" oncommand="Zotero_Long_Tag_Fixer.deselectAll()"/>
+ <button label="Select All" oncommand="Zotero_Long_Tag_Fixer.selectAll()"/>
+ </hbox>
+ </vbox>
+
+ <!-- Edit -->
+ <vbox>
+ <textbox id="zotero-new-tag-editor" multiline="true" flex="1"
+ oninput="Zotero_Long_Tag_Fixer.updateEditLength(this.value.length)"/>
+ <hbox id="zotero-new-tag-characters">
+ <label id="zotero-new-tag-character-count"/>
+ <label value="characters"/>
+ </hbox>
+ </vbox>
+
+ <!-- Delete -->
+ <vbox align="center" pack="center">
+ <label value="The tag will be deleted from all items."/>
+ </vbox>
+ </tabpanels>
+ </tabbox>
+</dialog>
diff --git a/chrome/content/zotero/xpcom/sync.js b/chrome/content/zotero/xpcom/sync.js
@@ -951,6 +951,8 @@ Zotero.Sync.Server = new function () {
_error(response.firstChild.firstChild.nodeValue);
}
+ _sessionLock = true;
+
// Strip XML declaration and convert to E4X
var xml = new XML(xmlhttp.responseText.replace(/<\?xml.*\?>/, ''));
@@ -1463,15 +1465,40 @@ Zotero.Sync.Server = new function () {
}
- if (firstChild.localName == 'error' && firstChild.getAttribute('code') == 'ITEM_MISSING') {
- var [libraryID, key] = firstChild.getAttribute('missingItem').split('/');
- if (libraryID == Zotero.libraryID) {
- libraryID = null;
- }
- var item = Zotero.Items.getByLibraryAndKey(libraryID, key);
- if (item) {
- Zotero.DB.rollbackAllTransactions();
- item.updateClientDateModified();
+ if (firstChild.localName == 'error') {
+ switch (firstChild.getAttribute('code')) {
+ case 'ITEM_MISSING':
+ var [libraryID, key] = firstChild.getAttribute('missingItem').split('/');
+ if (libraryID == Zotero.libraryID) {
+ libraryID = null;
+ }
+ var item = Zotero.Items.getByLibraryAndKey(libraryID, key);
+ if (item) {
+ Zotero.DB.rollbackAllTransactions();
+ item.updateClientDateModified();
+ }
+ break;
+
+ case 'TAG_TOO_LONG':
+ var tag = xmlhttp.responseXML.firstChild.getElementsByTagName('tag');
+ if (tag.length) {
+ Zotero.DB.rollbackAllTransactions();
+
+ var tag = tag[0].firstChild.nodeValue;
+ setTimeout(function () {
+ var callback = function (success) {
+ if (success) {
+ Zotero.Sync.Runner.sync();
+ }
+ };
+
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Components.interfaces.nsIWindowMediator);
+ var lastWin = wm.getMostRecentWindow("navigator:browser");
+ lastWin.openDialog('chrome://zotero/content/longTagFixer.xul', '', 'chrome,modal,centerscreen', tag, callback);
+ }, 1);
+ }
+ break;
}
}
}
@@ -1925,8 +1952,6 @@ Zotero.Sync.Server.Data = new function() {
case 'item':
var diff = obj.diff(remoteObj, false, ["dateModified"]);
- Zotero.debug('diff');
- Zotero.debug(diff);
if (!diff) {
// Check if creators changed
var creatorsChanged = false;
@@ -1944,11 +1969,9 @@ Zotero.Sync.Server.Data = new function() {
var r = remoteCreatorStore[Zotero.Creators.getLibraryKeyHash(creator.ref)];
// Doesn't include dateModified
if (r && !r.equals(creator.ref)) {
- Zotero.debug('=');
creatorsChanged = true;
break;
}
- Zotero.debug('-');
}
}
if (!creatorsChanged) {
diff --git a/chrome/skin/default/zotero/longTagFixer.css b/chrome/skin/default/zotero/longTagFixer.css
@@ -0,0 +1,8 @@
+#zotero-new-tag-characters[invalid="true"] {
+ color: red;
+}
+
+#zotero-new-tag-character-count {
+ font-weight: bold;
+ margin-right: 0;
+}
diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js
@@ -42,6 +42,7 @@ pref("extensions.zotero.lastCreatorFieldMode",0);
pref("extensions.zotero.lastAbstractExpand",0);
pref("extensions.zotero.lastRenameAssociatedFile", false);
pref("extensions.zotero.lastViewedFolder", 'L');
+pref("extensions.zotero.lastLongTagMode", 0);
//Tag Cloud
pref("extensions.zotero.tagCloud", false);