Addresses #1146, Check for duplicate items functionality
Ben's duplicate detection code, with the integration reworked a bit Very rough, so currently requires creation of a boolean extensions.zotero.debugShowDuplicates pref to view the Actions menu option
This commit is contained in:
parent
18c1287dd5
commit
0746824c0f
5 changed files with 120 additions and 2 deletions
|
@ -225,6 +225,10 @@ var ZoteroPane = new function()
|
||||||
sep.nextSibling.nextSibling.nextSibling.hidden = false;
|
sep.nextSibling.nextSibling.nextSibling.hidden = false;
|
||||||
sep.nextSibling.nextSibling.nextSibling.nextSibling.hidden = false;
|
sep.nextSibling.nextSibling.nextSibling.nextSibling.hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Zotero.Prefs.get('debugShowDuplicates')) {
|
||||||
|
document.getElementById('zotero-tb-actions-showDuplicates').hidden = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -796,6 +800,7 @@ var ZoteroPane = new function()
|
||||||
var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
var itemgroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
||||||
itemgroup.setSearch('');
|
itemgroup.setSearch('');
|
||||||
itemgroup.setTags(getTagSelection());
|
itemgroup.setTags(getTagSelection());
|
||||||
|
itemgroup.showDuplicates = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Zotero.UnresponsiveScriptIndicator.disable();
|
Zotero.UnresponsiveScriptIndicator.disable();
|
||||||
|
@ -825,6 +830,23 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.showDuplicates = function () {
|
||||||
|
if (this.collectionsView.selection.count == 1 && this.collectionsView.selection.currentIndex != -1) {
|
||||||
|
var itemGroup = this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex);
|
||||||
|
itemGroup.showDuplicates = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Zotero.UnresponsiveScriptIndicator.disable();
|
||||||
|
this.itemsView.refresh();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Zotero.UnresponsiveScriptIndicator.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function itemSelected()
|
function itemSelected()
|
||||||
{
|
{
|
||||||
if (!Zotero.stateCheck()) {
|
if (!Zotero.stateCheck()) {
|
||||||
|
@ -998,7 +1020,7 @@ var ZoteroPane = new function()
|
||||||
if (this.itemsView._itemGroup.isCollection()) {
|
if (this.itemsView._itemGroup.isCollection()) {
|
||||||
var noPrompt = true;
|
var noPrompt = true;
|
||||||
}
|
}
|
||||||
// Do nothing in search view
|
// Do nothing in search and share views
|
||||||
else if (this.itemsView._itemGroup.isSearch() ||
|
else if (this.itemsView._itemGroup.isSearch() ||
|
||||||
this.itemsView._itemGroup.isShare()) {
|
this.itemsView._itemGroup.isShare()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -128,6 +128,8 @@
|
||||||
label="Search for Shared Libraries" oncommand="Zotero.Zeroconf.findInstances()"/>
|
label="Search for Shared Libraries" oncommand="Zotero.Zeroconf.findInstances()"/>
|
||||||
<menuseparator id="zotero-tb-actions-plugins-separator"/>
|
<menuseparator id="zotero-tb-actions-plugins-separator"/>
|
||||||
<menuitem id="zotero-tb-actions-timeline" label="&zotero.toolbar.timeline.label;" oncommand="Zotero_Timeline_Interface.loadTimeline()"/>
|
<menuitem id="zotero-tb-actions-timeline" label="&zotero.toolbar.timeline.label;" oncommand="Zotero_Timeline_Interface.loadTimeline()"/>
|
||||||
|
<!-- TODO: localize <menuitem id="zotero-tb-actions-duplicate" label="&zotero.toolbar.duplicate.label;" oncommand="ZoteroPane.showDuplicates()"/>-->
|
||||||
|
<menuitem id="zotero-tb-actions-showDuplicates" label="Show Duplicates" oncommand="ZoteroPane.showDuplicates()" hidden="true"/>
|
||||||
<menuseparator id="zotero-tb-actions-sync-separator"/>
|
<menuseparator id="zotero-tb-actions-sync-separator"/>
|
||||||
<menuitem hidden="true" label="Sync Debugging" disabled="true"/>
|
<menuitem hidden="true" label="Sync Debugging" disabled="true"/>
|
||||||
<menuitem hidden="true" label=" Clear Server Data" oncommand="Zotero.Sync.Server.clear()"/>
|
<menuitem hidden="true" label=" Clear Server Data" oncommand="Zotero.Sync.Server.clear()"/>
|
||||||
|
|
|
@ -37,6 +37,7 @@ Zotero.CollectionTreeView = function()
|
||||||
this.itemToSelect = null;
|
this.itemToSelect = null;
|
||||||
this._highlightedRows = {};
|
this._highlightedRows = {};
|
||||||
this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share']);
|
this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share']);
|
||||||
|
this.showDuplicates = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1095,7 +1096,16 @@ Zotero.ItemGroup.prototype.getChildItems = function()
|
||||||
|
|
||||||
var s = this.getSearchObject();
|
var s = this.getSearchObject();
|
||||||
try {
|
try {
|
||||||
var ids = s.search();
|
var ids;
|
||||||
|
if (this.showDuplicates) {
|
||||||
|
var duplicates = new Zotero.Duplicate;
|
||||||
|
var tmpTable = s.search(true);
|
||||||
|
ids = duplicates.getIDs(tmpTable);
|
||||||
|
Zotero.DB.query("DROP TABLE " + tmpTable);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ids = s.search();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
Zotero.DB.rollbackAllTransactions();
|
Zotero.DB.rollbackAllTransactions();
|
||||||
|
|
83
chrome/content/zotero/xpcom/duplicate.js
Normal file
83
chrome/content/zotero/xpcom/duplicate.js
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
***** 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 *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
Zotero.Duplicate = function(duplicateID) {
|
||||||
|
this._id = duplicateID ? duplicateID : null;
|
||||||
|
this._itemIDs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.Duplicate.prototype.__defineGetter__('id', function () { return this._id; });
|
||||||
|
|
||||||
|
Zotero.Duplicate.prototype.getIDs = function(idsTable) {
|
||||||
|
if (!idsTable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var minLen = 5, percentLen = 1./3, checkLen, i, j;
|
||||||
|
|
||||||
|
var sql = "SELECT itemID, value AS val "
|
||||||
|
+ "FROM " + idsTable + " NATURAL JOIN itemData "
|
||||||
|
+ "NATURAL JOIN itemDataValues "
|
||||||
|
+ "WHERE fieldID BETWEEN 110 AND 113 AND "
|
||||||
|
+ "itemID NOT IN (SELECT itemID FROM itemAttachments) "
|
||||||
|
+ "ORDER BY val";
|
||||||
|
|
||||||
|
var results = Zotero.DB.query(sql);
|
||||||
|
|
||||||
|
var resultsLen = results.length;
|
||||||
|
this._itemIDs = [];
|
||||||
|
|
||||||
|
for (i = 0; i < resultsLen; i++) {
|
||||||
|
results[i].len = results[i].val.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < resultsLen; i++) {
|
||||||
|
// title must be at least minLen long to be a duplicate
|
||||||
|
if (results[i].len < minLen) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = i + 1; j < resultsLen; j++) {
|
||||||
|
// duplicates must match the first checkLen characters
|
||||||
|
// checkLen = percentLen * the length of the longer title
|
||||||
|
checkLen = (results[i].len >= results[j].len) ?
|
||||||
|
parseInt(percentLen * results[i].len) : parseInt(percentLen * results[j].len);
|
||||||
|
checkLen = (checkLen > results[i].len) ? results[i].len : checkLen;
|
||||||
|
checkLen = (checkLen > results[j].len) ? results[j].len : checkLen;
|
||||||
|
checkLen = (checkLen < minLen) ? minLen : checkLen;
|
||||||
|
|
||||||
|
if (results[i].val.substr(0, checkLen) == results[j].val.substr(0, checkLen)) {
|
||||||
|
// include results[i] when a duplicate is first found
|
||||||
|
if (j == i + 1) {
|
||||||
|
this._itemIDs.push(results[i].itemID);
|
||||||
|
}
|
||||||
|
this._itemIDs.push(results[j].itemID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = j - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._itemIDs;
|
||||||
|
}
|
|
@ -67,6 +67,7 @@
|
||||||
<!ENTITY zotero.toolbar.import.label "Import...">
|
<!ENTITY zotero.toolbar.import.label "Import...">
|
||||||
<!ENTITY zotero.toolbar.export.label "Export Library...">
|
<!ENTITY zotero.toolbar.export.label "Export Library...">
|
||||||
<!ENTITY zotero.toolbar.timeline.label "Create Timeline">
|
<!ENTITY zotero.toolbar.timeline.label "Create Timeline">
|
||||||
|
<!ENTITY zotero.toolbar.duplicate.label "Show Duplicates">
|
||||||
<!ENTITY zotero.toolbar.preferences.label "Preferences...">
|
<!ENTITY zotero.toolbar.preferences.label "Preferences...">
|
||||||
<!ENTITY zotero.toolbar.documentation.label "Documentation">
|
<!ENTITY zotero.toolbar.documentation.label "Documentation">
|
||||||
<!ENTITY zotero.toolbar.about.label "About Zotero">
|
<!ENTITY zotero.toolbar.about.label "About Zotero">
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue