Commit graph

133 commits

Author SHA1 Message Date
Dan Stillman
745c67d42f Remove duplicate condition
Fixes #1095
2016-09-19 14:01:13 -04:00
Dan Stillman
0eb6dc9ca5 Merge branch '4.0' 2016-08-09 01:52:21 -04:00
Dan Stillman
6a7e3d0259 Add "is (less|greater) than" to num pages/volumes search conditions
https://forums.zotero.org/discussion/60897/
2016-07-16 17:15:17 -04:00
Dan Stillman
c1f7a188e2 Fix error modifying existing saved search with more than 1 condition
Closes #1056, which wasn't actually the problem
2016-07-07 07:55:15 -04:00
Dan Stillman
451c12513e Remove unused toResponseJSON() overrides for Collection/Search 2016-06-20 01:21:24 -05:00
Dan Stillman
8e0e69332e Fix syncing of search conditions 2016-06-20 01:08:25 -05:00
Dan Stillman
2894e4f462 Fix search by collection 2016-05-06 04:31:49 -04:00
Dan Stillman
72c8711cd3 String::contains() -> indexOf() for Firefox 48 (and 38) compatibility
.contains() was removed in Firefox 48, but .includes() wasn't available until
40, so use indexOf() for now. We can start using .contains() once we no longer
need to support 38 ESR.
2016-05-03 12:08:38 -04:00
Dan Stillman
a05134e903 Fix search by file type
Fixes #966
2016-04-25 00:50:27 -04:00
Dan Stillman
f1af54236e Add Zotero.Notifier.Queue to keep event groups separate, and use for sync
A queue can be created and passed as an option to data layer methods, which
will then queue events on that queue instead of the main internal queue. A
queue or an array of queues can then be passed to Zotero.Notifier.commit() to
commit those events.

Some auxiliary functions don't yet take a queue, so those events will still get
run on DB transaction commit.

Sync data processing now processes notifier events in batches to reduce
repaints, even though individual objects are processed within their own
transactions (so that failures don't roll back other objects' data).

Also remove some unused notifier code
2016-04-22 22:48:58 -04:00
Dan Stillman
fabc2ba6a2 Always start at MAX() + 1 for Zotero.ID.get(), and deasyncify
Instead of getting batches of unused primary key ids, even if they're lower
than other ids, which for some reason seemed like a good idea in 2008, just do
a `MAX()` on the table at startup and return the next available id on each call
to `Zotero.ID.get()`. This is much simpler, and not reusing ids allows them to
be used as a chronological sort field.

While SQLite's `SELECT last_insert_rowid()` could return auto-increment values,
it's unsafe with async DB access, since a second `INSERT` can come in before
the first `last_insert_rowid()` is called. This is true even in a transaction
unless a function that calls it is never called in parallel (e.g., with
`Zotero.Promise.all()`, which can be faster than sequential `yield`s).

Note that the next id is always initialized as MAX() + 1, so if an object is
added and then deleted, after a restart the same id will be given. (This is
equivalent to (though unrelated to) SQLite's `INTEGER PRIMARY KEY` behavior,
as opposed to its `INTEGER PRIMARY KEY AUTOINCREMENT` behavior.)

Closes #993, Feed items out of order
2016-03-30 01:39:43 -04:00
Dan Stillman
b7b246e741 Saved search fixes
- Fix saved search editing
- Refresh items list on search change
- Generate correct conditions array for search JSON
2016-03-26 04:14:56 -04:00
Dan Stillman
a1ce85decb Overhaul object downloading/processing during data syncs
Previously, objects were first downloaded and saved to the sync cache,
which was then processed separately to create/update local objects. This
meant that a server bug could result in invalid data in the sync cache
that would never be processed. Now, objects are saved as they're
downloaded and only added to the sync cache after being successfully
saved. The keys of objects that fail are added to a queue, and those
objects are refetched and retried on a backoff schedule or when a new
client version is installed (in case of a client bug or a client with
outdated data model support).

An alternative would be to save to the sync cache first and evict
objects that fail and add them to the queue, but that requires more
complicated logic, and it probably makes more sense just to buffer a few
downloads ahead so that processing is never waiting for downloads to
finish.
2016-03-23 04:29:04 -04:00
Dan Stillman
8e5016ae4d Load synced settings (incl. tag colors) at startup 2016-03-15 01:18:55 -04:00
Dan Stillman
daf4a8fe4d Deasyncification 🔙 😢
While trying to get translation and citing working with asynchronously
generated data, we realized that drag-and-drop support was going to
be...problematic. Firefox only supports synchronous methods for
providing drag data (unlike, it seems, the DataTransferItem interface
supported by Chrome), which means that we'd need to preload all relevant
data on item selection (bounded by export.quickCopy.dragLimit) and keep
the translate/cite methods synchronous (or maintain two separate
versions).

What we're trying instead is doing what I said in #518 we weren't going
to do: loading most object data on startup and leaving many more
functions synchronous. Essentially, this takes the various load*()
methods described in #518, moves them to startup, and makes them operate
on entire libraries rather than individual objects.

The obvious downside here (other than undoing much of the work of the
last many months) is that it increases startup time, potentially quite a
lot for larger libraries. On my laptop, with a 3,000-item library, this
adds about 3 seconds to startup time. I haven't yet tested with larger
libraries. But I'm hoping that we can optimize this further to reduce
that delay. Among other things, this is loading data for all libraries,
when it should be able to load data only for the library being viewed.
But this is also fundamentally just doing some SELECT queries and
storing the results, so it really shouldn't need to be that slow (though
performance may be bounded a bit here by XPCOM overhead).

If we can make this fast enough, it means that third-party plugins
should be able to remain much closer to their current designs. (Some
things, including saving, will still need to be made asynchronous.)
2016-03-07 17:03:58 -05:00
Dan Stillman
d0d818840a Switch temp table inserts from SELECT...UNION to VALUES (x),(x)...
Supported as of SQLite 3.7.11

Also use hard-coded values instead of bound params
2016-01-09 16:58:40 -05:00
Dan Stillman
6b8e5bafc6 Don't show deleted items outside of trash 2015-11-01 03:36:23 -05:00
Dan Stillman
3692536770 Fixes #862, Trash Looks Empty
Broken by 3ff1ff88a9
2015-10-31 17:13:51 -04:00
Dan Stillman
7d8a1b2573 Make Zotero.DataObject#fromJSON() synchronous
When called on an identified object (i.e., one with an id or
library/key), loadAllData() must be called first. When called on a new
object (which is more common anyway), fromJSON() can be called
immediately.
2015-10-29 01:19:14 -04:00
Aurimas Vinckevicius
88ab129ffb Add Feed and FeedItem
Also:
* _finalizeErase in Zotero.DataObject is now inheritable
* Call _initErase before starting a DB transaction
* removes Zotero.Libraries.add and Zotero.Libraries.remove (doesn't seem like this is used any more)
2015-09-21 17:08:21 -05:00
Aurimas Vinckevicius
07ca00edd5 Use _canHaveParent property to determine if object can have parent 2015-09-18 03:34:04 -05:00
Dan Stillman
67f4a467ea Consolidate object erase methods into DataObjects::erase() 2015-08-02 03:40:14 -04:00
Dan Stillman
4600318ad7 Support 'successful' property in upload response
Save uploaded data to cache, and update local object if necessary (which
it mostly shouldn't be except for invalid characters and HTML filtering
in notes)

Also add some upload and JSON tests
2015-08-01 05:28:42 -04:00
Dan Stillman
70d9b9870c Fix a few small data layer bugs, and tidy up a little
I don't think these were triggered by any client code, but I found them while
porting code to the server.
2015-07-31 04:09:24 -04:00
Dan Stillman
b4a8083f2f Throw specific errors for missing objects or unknown fields
And add a bit more debugging info to other messages
2015-07-20 17:31:27 -04:00
Dan Stillman
cd4d084dd9 Support for automatically merging collections and searches 2015-07-20 02:12:14 -04:00
Dan Stillman
3ff1ff88a9 A couple search query optimizations
...which make very little difference, but they look better in the SQLite
query plan view.
2015-06-23 15:07:57 -04:00
Dan Stillman
7ba54886a6 Add toJSON() for searches and make Collection::toJSON() async 2015-06-19 04:04:33 -04:00
Dan Stillman
0d59bde186 Clean up DataObject erasing, and fix search unloading 2015-06-16 19:51:21 -04:00
Dan Stillman
1f643c1baa Fix skipNotifier option with DataObject::erase() 2015-06-02 03:51:09 -04:00
Dan Stillman
5a2ec43de1 Add Notifier.queue()
Notifier.trigger() needs to be async, since if it actually runs it waits for
promises returned from observers. But the vast majority of trigger() calls are
in transactions where they just queue and can therefore be synchronous. This
replaces all such calls with Notifier.queue().

This should fix a race condition that was causing the emptyTrash() test to fail
intermittently.
2015-05-29 05:03:05 -04:00
Dan Stillman
3d3b817724 Allow data to be set after save() on a new object without load() calls
After saving a new object and reloading primary data and any changed
data (which we can maybe reconsider at some point), mark all other data
types as loaded, since there's no other data we don't have. For example,
this allows for item.save() to be followed by item.setField() without
needing to call item.loadItemData() first.
2015-05-26 04:08:47 -04:00
Dan Stillman
ea1573e1c3 Don't log warning when localized string for search condition not found
Falls back to item field strings, so not sure why I added logging for
this.
2015-05-26 03:19:30 -04:00
Dan Stillman
432d89af24 Group data layer fixes 2015-05-22 14:41:59 -04:00
Dan Stillman
a3f4fe181f More data layer changes
- Moved ::_get() and _set() from Collection/Search into DataObject, and
  disabled in Item
- Don't disable new items after save. We now put new objects into the
  DataObjects cache from save() so that changes made post-save are
  picked up by other code using .get().
- Added 'skipCache' save() option to avoid reloading data on new objects
  and adding them to the cache. (This will be used in syncing, where
  objects might be in another library where they're not needed right
  away.) Objects created with this option are instead disabled to
  prevent reuse.
- Modified some tests to try to make sure we're reloading everything properly
  after a save.
- Documented save() options
2015-05-21 23:39:00 -04:00
Dan Stillman
4792b7cd48 Data layer fixes
- Fixes some saving and erasing issues with collections and searches
- Adds Zotero.DataObject::eraseTx() to automatically start transaction,
  and have .erase() log a warning like .save()
- Adds createUnsavedDataObject() and createDataObject() helper functions
  for tests
2015-05-20 23:16:18 -04:00
Dan Stillman
3f4eebe51c Set 'synced' to false automatically on save, unless value is changed
And add 'skipSyncedUpdate' option to leave untouched

Also move some save logic into Zotero.DataObject.prototype._saveData(),
and call that first.
2015-05-13 19:32:53 -04:00
Dan Stillman
14d435b8d8 Closes #711, Remove support for nested transactions 2015-05-10 18:32:10 -04:00
Dan Stillman
18714a4fcb Default to user library for saved searches
For consistency with collections and items
2015-05-05 03:17:51 -04:00
Dan Stillman
aa512f0f8d Miscellaneous data layer fixes and tweaks 2015-05-04 03:19:58 -04:00
Dan Stillman
53e40bb0f4 Miscellaneous tweaks 2015-05-04 02:46:40 -04:00
Dan Stillman
bdd44e9a44 DB isolation changes and item selection tweaks
- Add an 'exclusive' option to transactions that causes them to block other
  transactions and wait for other transactions to finish before starting,
  instead of nesting
- Resolve Zotero.DB.waitForTransaction() promise before returning from
  executeTransaction()
- A side effect of the above: wait for a newly created item to be selected in
  the middle pane and rendered in the right-hand pane before returning from
  executeTransaction()
- Don't save items multiple times when adding/removing a non-final creator in
  the Info pane
- Use a simpler, non-recursive method for focusing the next field in the Info
  pane; this prevents "too much recursion" errors if something causes the
  right-hand pane not to be rendered when expected
2015-05-04 02:45:55 -04:00
Dan Stillman
285995807d fromJSON() methods for data objects
Tests needed
2015-05-04 02:45:25 -04:00
Dan Stillman
afe0412c58 Collection/item tree view updates
- Pass .skipSelect option to data object .save() to prevent new objects
  from being selected
- Fix miscellaneous bugs
- Selection-related tests
2015-05-04 02:43:32 -04:00
Dan Stillman
fbef911cb7 Add wizard for My Publications
Show a wizard after items are dragged to My Publications choosing
whether to include files and notes and choosing sharing settings for the
items. Sharing options are Creative Commons licenses, CC0, "All rights
reserved", or keeping the existing Rights field if available.

Also blocks collections, searches, linked file attachments, and
top-level attachments/notes from My Publications at the data layer, but
not yet from the UI in all places (so it can crash if you try).

Todo:

- Block certain UI actions with nice messages
- Show a nice scrollable list of items in the wizard to allow selecting
  specific files/notes, instead of just having checkboxes for files and
  notes that apply to all dragged items
- Show an explanation of My Publications in the right-hand pane when no
  items are selected
- Maybe adjust handling when no attached files/notes, since it might be
  a bit alarming at the moment to see sharing options for metadata
  entries
2015-04-26 19:41:45 -04:00
Dan Stillman
f8ac21d891 Return object instead of array from Zotero.DataObjects.getLibraryAndKeyFromID()
Object contains 'libraryID' and 'key' properties

This is due to changed array destructuring behavior in Firefox. Previously,
`var [foo, bar] = maybeArrayMaybeFalse()` always worked, leaving foo and bar
undefined if the function returned false. Now (with ES6, I assume), if the
function returns false it results in a "false[Symbol.iterator] is not a
function" error. But `var {libraryID, key} = false` works as expected, leaving
both values undefined, so instead we can just return an object with those
properties from getLibraryAndKeyFromID(). To assign to different variables, use
`var {libraryID, key: parentItemKey} = ...`.
2015-04-25 03:17:41 -04:00
Dan Stillman
4b040c78a7 Fix various saved search bugs, and add tests
Search condition ids are now indexed from 0, and always saved
contiguously (no more 'fixGaps' option), since they're just in an array
in the API. (They're still returned as an object from
Zotero.Search.prototype.getConditions() because it's easier for the
advanced search window to not have to deal with shifting ids between
saves.)
2015-04-17 19:29:37 -04:00
Dan Stillman
d9c32a8e90 Fix search saving, and add some unit tests
Also:

- Return an object from `Zotero.Search.prototype.getConditions()`
  instead of an array.
- Add support function `getPromiseError(promise)` to return the error
  thrown from a chain of promises, or false if none. (We could make an
  `assert.throwsAsync()`, but this allows testing of various properties
  such as `.name`, which even the built-in `assert.throws()` can't
  test.)
- Clarify some search save errors
2015-04-17 00:25:09 -04:00
Dan Stillman
59773f3f6d Zotero.Search.getSearchCondition(s) -> getCondition(s) 2015-04-17 00:25:09 -04:00
Dan Stillman
293f7c6dd4 Zotero.Search.prototype.addCondition() doesn't need to be async 2015-04-16 20:48:59 -04:00