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
5 changed files with 304 additions and 13 deletions
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);
|
||||
}
|
||||
|
||||
_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) {
|
||||
|
|
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.lastRenameAssociatedFile", false);
|
||||
pref("extensions.zotero.lastViewedFolder", 'L');
|
||||
pref("extensions.zotero.lastLongTagMode", 0);
|
||||
|
||||
//Tag Cloud
|
||||
pref("extensions.zotero.tagCloud", false);
|
||||
|
|
Loading…
Add table
Reference in a new issue