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
This commit is contained in:
Dan Stillman 2009-05-29 11:49:55 +00:00
parent 921fa8c0fa
commit 481d847951
5 changed files with 304 additions and 13 deletions

View file

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

View file

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

View file

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

View file

@ -0,0 +1,8 @@
#zotero-new-tag-characters[invalid="true"] {
color: red;
}
#zotero-new-tag-character-count {
font-weight: bold;
margin-right: 0;
}

View file

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