Addresses #513, Deleted Items folder

- Still experimental, but committing for testing
- Sync conflicts with deleted items aren't yet supported

Unrelated: deprecated ZoteroPane.deleteSelectedItem() in favor of more accurately named deleteSelectedItems()
This commit is contained in:
Dan Stillman 2009-01-28 21:25:06 +00:00
parent 6709555dd9
commit eb79a0f659
16 changed files with 359 additions and 57 deletions

View file

@ -146,13 +146,17 @@
var parentbox = this._id('citeLabel');
var textbox = this._id('noteField');
var textboxReadOnly = this._id('noteFieldReadOnly');
var button = this._id('goButton');
if (this.editable) {
textbox.removeAttribute('readonly');
textbox.hidden = false;
textboxReadOnly.hidden = true;
}
else {
textbox.setAttribute('readonly', 'true');
textbox.hidden = true;
textboxReadOnly.hidden = false;
textbox = textboxReadOnly;
}
//var scrollPos = textbox.inputField.scrollTop;
@ -361,7 +365,10 @@
<content>
<xul:vbox xbl:inherits="flex">
<xul:label id="citeLabel"/>
<xul:textbox id="noteField" type="styled" mode="note" timeout="1000" flex="1"/>
<xul:textbox id="noteField" type="styled" mode="note"
timeout="1000" flex="1" hidden="true"/>
<xul:textbox id="noteFieldReadOnly" type="styled" mode="note"
readonly="true" flex="1" hidden="true"/>
<xul:hbox id="linksbox" hidden="true">
<xul:linksbox id="links" flex="1"/>
</xul:hbox>

View file

@ -58,6 +58,7 @@ var ZoteroItemPane = new function() {
return;
}
_deck = document.getElementById('zotero-view-item');
_itemBox = document.getElementById('zotero-editpane-item-box');
_notesList = document.getElementById('zotero-editpane-dynamic-notes');
_notesLabel = document.getElementById('zotero-editpane-notes-label');
@ -76,7 +77,7 @@ var ZoteroItemPane = new function() {
// Force blur() when clicking off a textbox to another item in middle
// pane, since for some reason it's not being called automatically
if (_itemBeingEdited && _itemBeingEdited != thisItem) {
switch (_tabs.selectedIndex) {
switch (_deck.selectedIndex) {
// Info
case 0:
// TODO: fix
@ -100,7 +101,7 @@ var ZoteroItemPane = new function() {
_itemBeingEdited = thisItem;
_loaded = {};
loadPane(_tabs.selectedIndex, mode);
loadPane(_deck.selectedIndex, mode);
}

View file

@ -52,7 +52,6 @@ var ZoteroPane = new function()
this.itemSelected = itemSelected;
this.reindexItem = reindexItem;
this.duplicateSelectedItem = duplicateSelectedItem;
this.deleteSelectedItem = deleteSelectedItem;
this.deleteSelectedCollection = deleteSelectedCollection;
this.editSelectedCollection = editSelectedCollection;
this.copySelectedItemsToClipboard = copySelectedItemsToClipboard;
@ -552,7 +551,7 @@ var ZoteroPane = new function()
event.keyCode == event.DOM_VK_DELETE) {
// If Cmd or Ctrl delete, delete from Library (with prompt)
var fromDB = event.metaKey || (!Zotero.isMac && event.ctrlKey);
ZoteroPane.deleteSelectedItem(fromDB);
ZoteroPane.deleteSelectedItems(fromDB);
event.preventDefault();
return;
}
@ -827,11 +826,21 @@ var ZoteroPane = new function()
return;
}
// Display restore button if items selected in Trash
if (this.itemsView && this.itemsView.selection.count) {
document.getElementById('zotero-item-restore-button').hidden
= !this.itemsView._itemGroup.isTrash();
}
var tabs = document.getElementById('zotero-view-tabs');
if (this.itemsView && this.itemsView.selection.count == 1 && this.itemsView.selection.currentIndex != -1)
{
var item = this.itemsView._getItemAtRow(this.itemsView.selection.currentIndex);
if(item.ref.isNote()) {
tabs.hidden = true;
var noteEditor = document.getElementById('zotero-note-editor');
noteEditor.mode = this.itemsView.readOnly ? 'view' : 'edit';
@ -846,19 +855,27 @@ var ZoteroPane = new function()
noteEditor.enableUndo();
document.getElementById('zotero-view-note-button').setAttribute('noteID',item.ref.id);
if(item.ref.getSource())
{
document.getElementById('zotero-view-note-button').setAttribute('sourceID',item.ref.getSource());
var viewButton = document.getElementById('zotero-view-note-button');
if (this.itemsView.readOnly) {
viewButton.hidden = true;
}
else
{
document.getElementById('zotero-view-note-button').removeAttribute('sourceID');
else {
viewButton.hidden = false;
viewButton.setAttribute('noteID', item.ref.id);
if (item.ref.getSource()) {
viewButton.setAttribute('sourceID', item.ref.getSource());
}
else {
viewButton.removeAttribute('sourceID');
}
}
document.getElementById('zotero-item-pane-content').selectedIndex = 2;
}
else if(item.ref.isAttachment()) {
tabs.hidden = true;
var attachmentBox = document.getElementById('zotero-attachment-box');
attachmentBox.mode = this.itemsView.readOnly ? 'view' : 'edit';
attachmentBox.item = item.ref;
@ -869,12 +886,22 @@ var ZoteroPane = new function()
// Regular item
else
{
ZoteroItemPane.viewItem(item.ref, this.itemsView.readOnly ? 'view' : false);
document.getElementById('zotero-item-pane-content').selectedIndex = 1;
if (this.itemsView.readOnly) {
document.getElementById('zotero-view-item').selectedIndex = 0;
ZoteroItemPane.viewItem(item.ref, 'view');
tabs.hidden = true;
}
else {
ZoteroItemPane.viewItem(item.ref);
tabs.selectedIndex = document.getElementById('zotero-view-item').selectedIndex;
tabs.hidden = false;
}
}
}
else
{
tabs.hidden = true;
document.getElementById('zotero-item-pane-content').selectedIndex = 0;
var label = document.getElementById('zotero-view-selected-label');
@ -927,11 +954,15 @@ var ZoteroPane = new function()
}
this.deleteSelectedItem = function () {
Zotero.debug("ZoteroPane.deleteSelectedItem() is deprecated -- use ZoteroPane.deleteSelectedItems()");
this.deleteSelectedItems();
}
/*
* _force_ deletes item from DB even if removing from a collection or search
*/
function deleteSelectedItem(force)
{
this.deleteSelectedItems = function (force) {
if (this.itemsView && this.itemsView.selection.count > 0) {
if (!force){
if (this.itemsView._itemGroup.isCollection()) {
@ -997,6 +1028,35 @@ var ZoteroPane = new function()
}
}
this.restoreSelectedItems = function () {
var items = this.getSelectedItems();
if (!items) {
return;
}
Zotero.DB.beginTransaction();
for (var i=0; i<items.length; i++) {
items[i].deleted = false;
items[i].save();
}
Zotero.DB.commitTransaction();
}
this.emptyTrash = function () {
var prompt = Components.classes["@mozilla.org/network/default-prompt;1"]
.getService(Components.interfaces.nsIPrompt);
var result = prompt.confirm("",
Zotero.getString('pane.collections.emptyTrash') + "\n\n" +
Zotero.getString('general.actionCannotBeUndone'));
if (result) {
Zotero.Items.emptyTrash();
}
}
function editSelectedCollection()
{
if (this.collectionsView.selection.count > 0) {
@ -1253,14 +1313,14 @@ var ZoteroPane = new function()
exportCollection: 7,
createBibCollection: 8,
exportFile: 9,
loadReport: 10
loadReport: 10,
emptyTrash: 11
};
// Collection
if (this.collectionsView.selection.count == 1 &&
this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex).isCollection())
{
var hide = [m.newCollection, m.newSavedSearch, m.exportFile];
var show = [m.newSubcollection, m.sep1, m.editSelectedCollection, m.removeCollection,
m.sep2, m.exportCollection, m.createBibCollection, m.loadReport];
if (this.itemsView.rowCount>0) {
@ -1275,6 +1335,7 @@ var ZoteroPane = new function()
var disable = [m.exportCollection, m.createBibCollection, m.loadReport];
}
// Adjust labels
menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.rename.collection'));
menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.collection'));
menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.collection'));
@ -1284,7 +1345,6 @@ var ZoteroPane = new function()
// Saved Search
else if (this.collectionsView.selection.count == 1 &&
this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex).isSearch()) {
var hide = [m.newCollection, m.newSavedSearch, m.newSubcollection, m.sep1, m.exportFile]
var show = [m.editSelectedCollection, m.removeCollection, m.sep2, m.exportCollection,
m.createBibCollection, m.loadReport];
@ -1296,17 +1356,21 @@ var ZoteroPane = new function()
var disable = [m.exportCollection, m.createBibCollection, m.loadReport];
}
// Adjust labels
menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.edit.savedSearch'));
menu.childNodes[m.removeCollection].setAttribute('label', Zotero.getString('pane.collections.menu.remove.savedSearch'));
menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.savedSearch'));
menu.childNodes[m.createBibCollection].setAttribute('label', Zotero.getString('pane.collections.menu.createBib.savedSearch'));
menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.savedSearch'));
}
// Trash
else if (this.collectionsView.selection.count == 1 &&
this.collectionsView._getItemAtRow(this.collectionsView.selection.currentIndex).isTrash()) {
var show = [m.emptyTrash];
}
// Library
else
{
var hide = [m.newSubcollection, m.editSelectedCollection, m.removeCollection, m.sep2,
m.exportCollection, m.createBibCollection, m.loadReport];
var show = [m.newCollection, m.newSavedSearch, m.sep1, m.exportFile];
}
@ -1320,9 +1384,9 @@ var ZoteroPane = new function()
menu.childNodes[enable[i]].setAttribute('disabled', false);
}
for (var i in hide)
{
menu.childNodes[hide[i]].setAttribute('hidden', true);
// Hide all items by default
for each(var pos in m) {
menu.childNodes[pos].setAttribute('hidden', true);
}
for (var i in show)

View file

@ -90,6 +90,7 @@
<menuitem oncommand="Zotero_File_Interface.bibliographyFromCollection();"/>
<menuitem label="&zotero.toolbar.export.label;" oncommand="Zotero_File_Interface.exportFile()"/>
<menuitem oncommand="Zotero_Report_Interface.loadCollectionReport()"/>
<menuitem label="&zotero.toolbar.emptyTrash.label;" oncommand="ZoteroPane.emptyTrash();"/>
</popup>
<popup id="zotero-itemmenu" onpopupshowing="ZoteroPane.buildItemContextMenu();">
<menuitem label="&zotero.items.menu.showInLibrary;" oncommand="ZoteroPane.selectItem(this.parentNode.getAttribute('itemID'), true)"/>
@ -99,8 +100,8 @@
<menuitem label="&zotero.items.menu.attach.link;" oncommand="ZoteroPane.addAttachmentFromPage(true, this.parentNode.getAttribute('itemID'));"/>
<menuseparator/>
<menuitem label="&zotero.items.menu.duplicateItem;" oncommand="ZoteroPane.duplicateSelectedItem();"/>
<menuitem oncommand="ZoteroPane.deleteSelectedItem();"/>
<menuitem oncommand="ZoteroPane.deleteSelectedItem(true);"/>
<menuitem oncommand="ZoteroPane.deleteSelectedItems();"/>
<menuitem oncommand="ZoteroPane.deleteSelectedItems(true);"/>
<menuseparator/>
<menuitem oncommand="Zotero_File_Interface.exportItems();"/>
<menuitem oncommand="Zotero_File_Interface.bibliographyFromItems();"/>
@ -358,6 +359,9 @@
<toolbarbutton id="zotero-tb-fullscreen" tooltiptext="&zotero.toolbar.fullscreen.tooltip;" oncommand="ZoteroPane.fullScreen();"/>
<toolbarbutton class="tabs-closebutton" oncommand="ZoteroPane.toggleDisplay()"/>
</hbox>
<!-- TODO: localize -->
<button id="zotero-item-restore-button" label="Restore to Library"
oncommand="ZoteroPane.restoreSelectedItems()" hidden="true"/>
<groupbox flex="1">
<caption>
<tabs id="zotero-view-tabs" onselect="document.getElementById('zotero-view-item').selectedIndex = this.selectedIndex;" hidden="true">
@ -368,7 +372,7 @@
<tab label="&zotero.tabs.related.label;"/>
</tabs>
</caption>
<deck id="zotero-item-pane-content" selectedIndex="0" flex="1" onselect="document.getElementById('zotero-view-tabs').setAttribute('hidden',(this.selectedIndex != 1));">
<deck id="zotero-item-pane-content" selectedIndex="0" flex="1">
<box pack="center" align="center">
<label id="zotero-view-selected-label"/>
</box>

View file

@ -164,6 +164,11 @@ Zotero.CollectionTreeView.prototype.refresh = function()
}
}
var deletedItems = Zotero.Items.getDeleted();
if (deletedItems) {
this._showItem(new Zotero.ItemGroup('trash', null), 0, this._dataItems.length);
}
this._refreshHashMap();
// Update the treebox's row count
@ -1037,6 +1042,12 @@ Zotero.ItemGroup.prototype.isShare = function()
return this.type == 'share';
}
Zotero.ItemGroup.prototype.isTrash = function()
{
return this.type == 'trash';
}
Zotero.ItemGroup.prototype.getName = function()
{
if (this.isCollection()) {
@ -1051,6 +1062,9 @@ Zotero.ItemGroup.prototype.getName = function()
else if (this.isShare()) {
return this.ref.name;
}
else if (this.isTrash()) {
return Zotero.getString('pane.collections.trash');
}
else {
return "";
}
@ -1098,12 +1112,18 @@ Zotero.ItemGroup.prototype.getSearchObject = function() {
}
includeScopeChildren = true;
}
else if (this.isTrash()) {
s.addCondition('deleted', 'true');
}
else if (!this.isSearch()) {
throw ('Invalid search mode in Zotero.ItemGroup.getSearchObject()');
}
// Create the outer (filter) search
var s2 = new Zotero.Search();
if (this.isTrash()) {
s2.addCondition('deleted', 'true');
}
s2.setScope(s, includeScopeChildren);
if (this.searchText) {

View file

@ -75,12 +75,14 @@ Zotero.Item.prototype._init = function () {
this._changedPrimaryData = false;
this._changedItemData = false;
this._changedCreators = false;
this._changedDeleted = false;
this._changedNote = false;
this._changedSource = false;
this._changedAttachmentData = false;
this._previousData = null;
this._deleted = null;
this._noteTitle = null;
this._noteText = null;
this._noteAccessTime = null;
@ -341,8 +343,9 @@ Zotero.Item.prototype.loadFromRow = function(row, reload) {
Zotero.Item.prototype.hasChanged = function() {
return !!(this._changed
|| this._changedPrimaryData
|| this._changedCreators
|| this._changedItemData
|| this._changedCreators
|| this._changedDeleted
|| this._changedNote
|| this._changedSource
|| this._changedAttachmentData);
@ -919,6 +922,44 @@ Zotero.Item.prototype.removeCreator = function(orderIndex) {
}
Zotero.Item.prototype.__defineGetter__('deleted', function () {
if (this._deleted !== null) {
return this._deleted;
}
if (!this.id) {
return '';
}
var sql = "SELECT COUNT(*) FROM deletedItems WHERE itemID=?";
var deleted = !!Zotero.DB.valueQuery(sql, this.id);
this._deleted = deleted;
return deleted;
});
Zotero.Item.prototype.__defineSetter__('deleted', function (val) {
Zotero.debug('setting deleted');
Zotero.debug(val);
if (!this.id) {
Zotero.debug("Deleted state not set on item without id");
return;
}
var deleted = !!val;
if (this.deleted == deleted) {
Zotero.debug("Deleted state hasn't changed for item " + this.id);
return;
}
if (!this._changedDeleted) {
this._changedDeleted = true;
}
this._deleted = deleted;
});
Zotero.Item.prototype.addRelatedItem = function (itemID) {
var parsedInt = parseInt(itemID);
if (parsedInt != itemID) {
@ -1033,6 +1074,7 @@ Zotero.Item.prototype.save = function() {
Zotero.DB.query("UPDATE itemTags SET itemID=? WHERE itemID=?", params);
Zotero.DB.query("UPDATE fulltextItemWords SET itemID=? WHERE itemID=?", params);
Zotero.DB.query("UPDATE fulltextItems SET itemID=? WHERE itemID=?", params);
Zotero.DB.query("UPDATE deletedItems SET itemID=? WHERE itemID=?", params);
Zotero.DB.query("UPDATE annotations SET itemID=? WHERE itemID=?", params);
Zotero.DB.query("UPDATE highlights SET itemID=? WHERE itemID=?", params);
@ -1237,6 +1279,17 @@ Zotero.Item.prototype.save = function() {
}
if (this._changedDeleted) {
if (this.deleted) {
sql = "REPLACE INTO deletedItems (itemID) VALUES (?)";
}
else {
sql = "DELETE FROM deletedItems WHERE itemID=?";
}
Zotero.DB.query(sql, itemID);
}
// Note
if (this.isNote() || this._changedNote) {
sql = "INSERT INTO itemNotes "
@ -1393,6 +1446,7 @@ Zotero.Item.prototype.save = function() {
Zotero.DB.query(sql, sqlValues);
//
// ItemData
//
@ -1573,6 +1627,17 @@ Zotero.Item.prototype.save = function() {
}
if (this._changedDeleted) {
if (this.deleted) {
sql = "REPLACE INTO deletedItems (itemID) VALUES (?)";
}
else {
sql = "DELETE FROM deletedItems WHERE itemID=?";
}
Zotero.DB.query(sql, this.id);
}
// Note
if (this._changedNote) {
if (this._noteText === null || this._noteTitle === null) {
@ -1790,6 +1855,13 @@ Zotero.Item.prototype.save = function() {
this._key = key;
}
if (this._changedDeleted) {
Zotero.Notifier.trigger('refresh', 'collection', 0);
if (this._deleted) {
Zotero.Notifier.trigger('trash', 'item', this.id);
}
}
Zotero.Items.reload(this.id);
if (isNew) {
@ -3244,6 +3316,7 @@ Zotero.Item.prototype.erase = function(deleteChildren) {
Zotero.DB.query('DELETE FROM annotations WHERE itemID=?', this.id);
Zotero.DB.query('DELETE FROM highlights WHERE itemID=?', this.id);
Zotero.DB.query('DELETE FROM deletedItems WHERE itemID=?', this.id);
Zotero.DB.query('DELETE FROM itemCreators WHERE itemID=?', this.id);
Zotero.DB.query('DELETE FROM itemNotes WHERE itemID=?', this.id);
Zotero.DB.query('DELETE FROM itemAttachments WHERE itemID=?', this.id);
@ -3467,6 +3540,10 @@ Zotero.Item.prototype.serialize = function(mode) {
}
}
// Deleted items flag
if (this.deleted) {
arr.deleted = true;
}
if (this.isRegularItem()) {
// Creators

View file

@ -106,6 +106,24 @@ Zotero.Items = new function() {
}
/**
* Return items marked as deleted
*
* @param {Boolean} asIDs Return itemIDs instead of
* Zotero.Item objects
* @return {Zotero.Item[]|Integer[]}
*/
this.getDeleted = function (asIDs) {
var sql = "SELECT itemID FROM deletedItems";
var ids = Zotero.DB.columnQuery(sql);
if (asIDs) {
return ids;
}
return this.get(ids);
}
/*
* Returns all items in the database
*
@ -320,6 +338,45 @@ Zotero.Items = new function() {
}
this.trash = function (ids) {
ids = Zotero.flattenArguments(ids);
Zotero.UnresponsiveScriptIndicator.disable();
try {
Zotero.DB.beginTransaction();
for each(var id in ids) {
var item = this.get(id);
if (!item) {
Zotero.debug('Item ' + id + ' does not exist in Items.trash()!', 1);
Zotero.Notifier.trigger('delete', 'item', id);
continue;
}
item.deleted = true;
item.save();
}
Zotero.DB.commitTransaction();
}
catch (e) {
Zotero.DB.rollbackTransaction();
throw (e);
}
finally {
Zotero.UnresponsiveScriptIndicator.enable();
}
}
this.emptyTrash = function () {
Zotero.DB.beginTransaction();
var deletedIDs = this.getDeleted(true);
if (deletedIDs) {
this.erase(deletedIDs, true);
}
Zotero.Notifier.trigger('refresh', 'collection', 0);
Zotero.DB.commitTransaction();
}
/**
* Delete item(s) from database and clear from internal array
*

View file

@ -232,7 +232,7 @@ Zotero.ItemTreeView.prototype.refresh = function()
Zotero.ItemTreeView.prototype.__defineGetter__('readOnly', function () {
if (this._itemGroup.isShare()) {
if (this._itemGroup.isTrash() || this._itemGroup.isShare()) {
return true;
}
return false;
@ -309,7 +309,7 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
}
if ((action == 'remove' && !this._itemGroup.isLibrary())
|| action == 'delete' || action == 'id-change') {
|| action == 'delete' || action == 'id-change' || action == 'trash') {
// We only care about the old ids
if (action == 'id-change') {
@ -323,7 +323,7 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
var rows = [];
for(var i=0, len=ids.length; i<len; i++)
{
if (action == 'delete' || action == 'id-change' ||
if (action == 'delete' || action == 'trash' || action == 'id-change' ||
!this._itemGroup.ref.hasItem(ids[i])) {
// Row might already be gone (e.g. if this is a child and
// 'modify' was sent to parent)
@ -350,12 +350,11 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
madeChanges = true;
sort = true;
}
}
else if (action == 'modify')
{
// If saved search, just re-run search
if (this._itemGroup.isSearch())
// If trash or saved search, just re-run search
if (this._itemGroup.isTrash() || this._itemGroup.isSearch())
{
this.refresh();
madeChanges = true;
@ -410,6 +409,11 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
// modify comes in after a delete
continue;
}
// Deleted items get a modify that we have to ignore when
// not viewing the trash
if (item.deleted) {
continue;
}
if(item.isRegularItem() || !item.getSource())
{
//most likely, the note or attachment's parent was removed.
@ -436,9 +440,8 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
}
else if(action == 'add')
{
// If saved search, just re-run search
if (this._itemGroup.isSearch())
{
// If saved search or trash, just re-run search
if (this._itemGroup.isSearch() || this._itemGroup.isTrash()) {
this.refresh();
madeChanges = true;
sort = true;
@ -1167,10 +1170,11 @@ Zotero.ItemTreeView.prototype.getSelectedItems = function(asIDs)
}
/*
/**
* Delete the selection
*
* _force_ deletes item from DB even if removing from a collection
* @param {Boolean} eraseChildren
* @param {Boolean} force Delete item even if removing from a collection
*/
Zotero.ItemTreeView.prototype.deleteSelection = function(eraseChildren, force)
{
@ -1201,11 +1205,14 @@ Zotero.ItemTreeView.prototype.deleteSelection = function(eraseChildren, force)
// Erase item(s) from DB
if (this._itemGroup.isLibrary() || force) {
Zotero.Items.erase(ids, eraseChildren);
Zotero.Items.trash(ids);
}
else if (this._itemGroup.isCollection()) {
this._itemGroup.ref.removeItems(ids);
}
else if (this._itemGroup.isTrash()) {
Zotero.Items.erase(ids, eraseChildren);
}
this._treebox.endUpdateBatch();
}

View file

@ -2146,7 +2146,7 @@ Zotero.Schema = new function(){
}
}
// // 1.5 Sync Preview 3.6
// 1.5 Sync Preview 3.6
if (i==47) {
Zotero.DB.query("ALTER TABLE syncDeleteLog RENAME TO syncDeleteLogOld");
Zotero.DB.query("DROP INDEX syncDeleteLog_timestamp");
@ -2155,6 +2155,11 @@ Zotero.Schema = new function(){
Zotero.DB.query("INSERT OR IGNORE INTO syncDeleteLog SELECT syncObjectTypeID, key, timestamp FROM syncDeleteLogOld ORDER BY timestamp DESC");
Zotero.DB.query("DROP TABLE syncDeleteLogOld");
}
//
if (i==48) {
Zotero.DB.query("CREATE TABLE deletedItems (\n itemID INTEGER PRIMARY KEY,\n dateDeleted DEFAULT CURRENT_TIMESTAMP NOT NULL\n);");
}
}
_updateDBVersion('userdata', toVersion);

View file

@ -928,6 +928,10 @@ Zotero.Search.prototype._buildQuery = function(){
// Handle special conditions
else {
switch (data['name']){
case 'deleted':
var deleted = this._conditions[i].operator == 'true';
continue;
case 'noChildren':
var noChildren = this._conditions[i]['operator']=='true';
continue;
@ -971,20 +975,19 @@ Zotero.Search.prototype._buildQuery = function(){
}
}
// Exclude deleted items by default
sql += " WHERE itemID " + (deleted ? "" : "NOT ") + "IN "
+ "(SELECT itemID FROM deletedItems)";
if (noChildren){
sql += " WHERE (itemID NOT IN (SELECT itemID FROM itemNotes "
sql += " AND (itemID NOT IN (SELECT itemID FROM itemNotes "
+ "WHERE sourceItemID IS NOT NULL) AND itemID NOT IN "
+ "(SELECT itemID FROM itemAttachments "
+ "WHERE sourceItemID IS NOT NULL))";
}
if (this._hasPrimaryConditions) {
if (noChildren){
sql += " AND ";
}
else {
sql += " WHERE ";
}
sql += " AND ";
for each(var condition in conditions){
var skipOperators = false;
@ -1441,12 +1444,10 @@ Zotero.Search.prototype._buildQuery = function(){
}
// Keep non-required conditions separate if in ANY mode
else if (!condition['required'] && joinMode == 'ANY') {
var nonQSConditions = true;
anySQL += condSQL + ' OR ';
anySQLParams = anySQLParams.concat(condSQLParams);
}
else {
var nonQSConditions = true;
condSQL += ' AND ';
sql += condSQL;
sqlParams = sqlParams.concat(condSQLParams);
@ -1460,11 +1461,8 @@ Zotero.Search.prototype._buildQuery = function(){
sql = sql.substring(0, sql.length-4); // remove last ' OR '
sql += ')';
}
else if (nonQSConditions) {
sql = sql.substring(0, sql.length-5); // remove last ' AND '
}
else {
sql = sql.substring(0, sql.length-7); // remove ' WHERE '
sql = sql.substring(0, sql.length-5); // remove last ' AND '
}
// Add on quicksearch conditions
@ -1601,6 +1599,15 @@ Zotero.SearchConditions = new function(){
// Special conditions
//
{
name: 'deleted',
operators: {
true: true,
false: true
}
},
// Don't include child items
{
name: 'noChildren',

View file

@ -2535,6 +2535,11 @@ Zotero.Sync.Server.Data = new function() {
xml.field += newField;
}
// Deleted item flag
if (item.deleted) {
xml.@deleted = '1';
}
if (item.primary.itemType == 'note' || item.primary.itemType == 'attachment') {
if (item.sourceItemID) {
xml.@sourceItemID = item.sourceItemID;
@ -2665,6 +2670,10 @@ Zotero.Sync.Server.Data = new function() {
}
}
// Deleted item flag
var deleted = xmlItem.@deleted.toString();
item.deleted = (deleted == 'true' || deleted == "1");
// Item creators
var i = 0;
for each(var creator in xmlItem.creator) {

View file

@ -61,6 +61,7 @@
<!ENTITY zotero.toolbar.newCollection.label "New Collection...">
<!ENTITY zotero.toolbar.newSubcollection.label "New Subcollection...">
<!ENTITY zotero.toolbar.newSavedSearch.label "New Saved Search...">
<!ENTITY zotero.toolbar.emptyTrash.label "Empty Trash">
<!ENTITY zotero.toolbar.tagSelector.label "Show/Hide Tag Selector">
<!ENTITY zotero.toolbar.actions.label "Actions">
<!ENTITY zotero.toolbar.import.label "Import...">

View file

@ -14,6 +14,7 @@ general.errorHasOccurred = An error has occurred.
general.restartFirefox = Please restart Firefox.
general.restartFirefoxAndTryAgain = Please restart Firefox and try again.
general.checkForUpdate = Check for update
general.actionCannotBeUndone = This action cannot be undone.
general.install = Install
general.updateAvailable = Update Available
general.upgrade = Upgrade
@ -50,12 +51,14 @@ startupError = There was an error starting Zotero.
pane.collections.delete = Are you sure you want to delete the selected collection?
pane.collections.deleteSearch = Are you sure you want to delete the selected search?
pane.collections.emptyTrash = Are you sure you want to permanently remove items in the Trash?
pane.collections.newCollection = New Collection
pane.collections.name = Enter a name for this collection:
pane.collections.newSavedSeach = New Saved Search
pane.collections.savedSearchName = Enter a name for this saved search:
pane.collections.rename = Rename collection:
pane.collections.library = My Library
pane.collections.trash = Trash
pane.collections.untitled = Untitled
pane.collections.menu.rename.collection = Rename Collection...

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

View file

@ -1,4 +1,4 @@
-- 3
-- 4
-- Triggers to validate date field
DROP TRIGGER IF EXISTS insert_date_field;
@ -807,6 +807,41 @@ CREATE TRIGGER fku_savedSearches_savedSearchID_savedSearchConditions_savedSearch
UPDATE savedSearchConditions SET savedSearchID=NEW.savedSearchID WHERE savedSearchID=OLD.savedSearchID;
END;
-- deletedItems/itemID
-- savedSearchConditions/savedSearchID
DROP TRIGGER IF EXISTS fki_deletedItems_itemID_items_itemID;
CREATE TRIGGER fki_deletedItems_itemID_items_itemID
BEFORE INSERT ON deletedItems
FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'insert on table "deletedItems" violates foreign key constraint "fki_deletedItems_itemID_items_itemID"')
WHERE (SELECT COUNT(*) FROM items WHERE itemID = NEW.itemID) = 0;
END;
DROP TRIGGER IF EXISTS fku_deletedItems_itemID_items_itemID;
CREATE TRIGGER fku_deletedItems_itemID_items_itemID
BEFORE UPDATE OF itemID ON deletedItems
FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'update on table "deletedItems" violates foreign key constraint "fku_deletedItems_itemID_items_itemID"')
WHERE (SELECT COUNT(*) FROM items WHERE itemID = NEW.itemID) = 0;
END;
DROP TRIGGER IF EXISTS fkd_deletedItems_itemID_items_itemID;
CREATE TRIGGER fkd_deletedItems_itemID_items_itemID
BEFORE DELETE ON items
FOR EACH ROW BEGIN
SELECT RAISE(ABORT, 'delete on table "items" violates foreign key constraint "fkd_deletedItems_itemID_items_itemID"')
WHERE (SELECT COUNT(*) FROM deletedItems WHERE itemID = OLD.itemID) > 0;
END;
DROP TRIGGER IF EXISTS fku_items_itemID_deletedItems_itemID;
CREATE TRIGGER fku_items_itemID_deletedItems_itemID
AFTER UPDATE OF itemID ON items
FOR EACH ROW BEGIN
UPDATE deletedItems SET itemID=NEW.itemID WHERE itemID=OLD.itemID;
END;
-- syncDeleteLog/syncObjectTypeID
DROP TRIGGER IF EXISTS fki_syncDeleteLog_syncObjectTypeID_syncObjectTypes_syncObjectTypeID;
CREATE TRIGGER fki_syncDeleteLog_syncObjectTypeID_syncObjectTypes_syncObjectTypeID

View file

@ -1,4 +1,4 @@
-- 47
-- 48
-- This file creates tables containing user-specific data -- any changes made
-- here must be mirrored in transition steps in schema.js::_migrateSchema()
@ -171,6 +171,11 @@ CREATE TABLE savedSearchConditions (
FOREIGN KEY (savedSearchID) REFERENCES savedSearches(savedSearchID)
);
CREATE TABLE deletedItems (
itemID INTEGER PRIMARY KEY,
dateDeleted DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE TABLE fulltextItems (
itemID INTEGER PRIMARY KEY,
version INT,