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:
parent
921fa8c0fa
commit
481d847951
185
chrome/content/zotero/longTagFixer.js
Normal file
185
chrome/content/zotero/longTagFixer.js
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
chrome/content/zotero/longTagFixer.xul
Normal file
74
chrome/content/zotero/longTagFixer.xul
Normal 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>
|
|
@ -951,6 +951,8 @@ Zotero.Sync.Server = new function () {
|
||||||
_error(response.firstChild.firstChild.nodeValue);
|
_error(response.firstChild.firstChild.nodeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_sessionLock = true;
|
||||||
|
|
||||||
// Strip XML declaration and convert to E4X
|
// Strip XML declaration and convert to E4X
|
||||||
var xml = new XML(xmlhttp.responseText.replace(/<\?xml.*\?>/, ''));
|
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') {
|
if (firstChild.localName == 'error') {
|
||||||
var [libraryID, key] = firstChild.getAttribute('missingItem').split('/');
|
switch (firstChild.getAttribute('code')) {
|
||||||
if (libraryID == Zotero.libraryID) {
|
case 'ITEM_MISSING':
|
||||||
libraryID = null;
|
var [libraryID, key] = firstChild.getAttribute('missingItem').split('/');
|
||||||
}
|
if (libraryID == Zotero.libraryID) {
|
||||||
var item = Zotero.Items.getByLibraryAndKey(libraryID, key);
|
libraryID = null;
|
||||||
if (item) {
|
}
|
||||||
Zotero.DB.rollbackAllTransactions();
|
var item = Zotero.Items.getByLibraryAndKey(libraryID, key);
|
||||||
item.updateClientDateModified();
|
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':
|
case 'item':
|
||||||
var diff = obj.diff(remoteObj, false, ["dateModified"]);
|
var diff = obj.diff(remoteObj, false, ["dateModified"]);
|
||||||
Zotero.debug('diff');
|
|
||||||
Zotero.debug(diff);
|
|
||||||
if (!diff) {
|
if (!diff) {
|
||||||
// Check if creators changed
|
// Check if creators changed
|
||||||
var creatorsChanged = false;
|
var creatorsChanged = false;
|
||||||
|
@ -1944,11 +1969,9 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
var r = remoteCreatorStore[Zotero.Creators.getLibraryKeyHash(creator.ref)];
|
var r = remoteCreatorStore[Zotero.Creators.getLibraryKeyHash(creator.ref)];
|
||||||
// Doesn't include dateModified
|
// Doesn't include dateModified
|
||||||
if (r && !r.equals(creator.ref)) {
|
if (r && !r.equals(creator.ref)) {
|
||||||
Zotero.debug('=');
|
|
||||||
creatorsChanged = true;
|
creatorsChanged = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Zotero.debug('-');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!creatorsChanged) {
|
if (!creatorsChanged) {
|
||||||
|
|
8
chrome/skin/default/zotero/longTagFixer.css
Normal file
8
chrome/skin/default/zotero/longTagFixer.css
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#zotero-new-tag-characters[invalid="true"] {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zotero-new-tag-character-count {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ pref("extensions.zotero.lastCreatorFieldMode",0);
|
||||||
pref("extensions.zotero.lastAbstractExpand",0);
|
pref("extensions.zotero.lastAbstractExpand",0);
|
||||||
pref("extensions.zotero.lastRenameAssociatedFile", false);
|
pref("extensions.zotero.lastRenameAssociatedFile", false);
|
||||||
pref("extensions.zotero.lastViewedFolder", 'L');
|
pref("extensions.zotero.lastViewedFolder", 'L');
|
||||||
|
pref("extensions.zotero.lastLongTagMode", 0);
|
||||||
|
|
||||||
//Tag Cloud
|
//Tag Cloud
|
||||||
pref("extensions.zotero.tagCloud", false);
|
pref("extensions.zotero.tagCloud", false);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user