projectblacklight/blacklight — pull request #852 by jrochkind (2014-03-24 22:01:39+00:00)
Title: Bookmarks export feature

We add Endnote and Refworks export buttons to the Bookmarks page.

There are a couple weird things, and things I'm not sure about, in here. Feedback welcome.

* In this PR, the export functions just export the currently viewed page of Bookmarks, not all bookmarks. This is consistent with how the similar feature worked in the old Folder/Selected Items area; it makes some aspects of implementation easier; @jkeck though it was good enough, at least for now.

* Blacklight supports diverse documents, as well as hetereogenous document sets -- not all docs can be exported to endnote. In this PR, the "Export to Endnote" button shows up if at least _one_ document on the current page is exportable to Endnote. If some docs are and some aren't, when you export to endnote it might only include the subset that are exportable.
* Same for refworks, and the format we use for refworks export
* This may not be ideal, but I think is prob good enough for now, and is prob consistent with how old Folder worked. Most users to begin with will probably have either all docs in their result set exportable or none, hetereogenous sets are going to be rare, perhaps we can figure out what to do with them as a later stage, when the issue comes up.

* Exporting to Refworks is tricky, because we want to give Refworks a callback URL, but how can a callback url get a particular user's bookmarked docs? We choose to give refworks a callback url that includes a user_id that's _encrypted and signed_. Supported by the new Bookmarks#export action. When Bookmarks#export gets called, it takes an encrypted-and-signed user_id out of params, and if it decrypts and verifies fine, delivers that users bookmarks. Alternate solutions were considered in #847, me and @jcoyne decided this seemed best trade off.
* We use `ActiveSupport::MessageEncryptor` for our encryption. However, *I'm not entirely sure it's entirely secure.* Encryption is hard. I noticed that more recent versions of Rails use `ActiveSupport::MessageEncryptor` in [more complicated ways than we do with regard to key generation](https://github.com/rails/rails/blob/de5ef153984f4fc3229a3346a8a4a1595303afc8/railties/lib/rails/application.rb#L156), we just use a single fixed secret_token instead, like Rails used. I think that's probably good enough for what we're doing though.
* But because it's hard to be sure about this stuff, I did _not_ want to re-use Rails existing secret_token or secret_key_base as our key -- because our use of it might make it easier to attack and discover the key, I didn't want to make the main Rails app secrets vulnerable.
* So we use a separate `config.blacklight_export_secret_token`. The app generator now generates a config/initializer for this.
* If you don't have a `config.blacklight_export_secret_token` defined, it'll define one on the fly at first use that stays in memory for life of process, etc, _and_ print out a deprecation warning telling you to generate and define one. I think this is a decent trade-off to make sure a BL upgrade doesn't introduce an export function that will result in a fatal error, possibly in production, if someone hasn't generated the key yet!

* There are no tests at present, which I acknowledge is not great, and tests may be required for merging.
* I was not sure how to write tests at all for this functionality, it gets confusing. Advice welcome. I _think_ it should be controller tests, rather than 'feature' tests, because it's hard/impossible to do Capybara with non-html responses. But I'm not sure how to mock everything up that needs to be mocked up, without mocking up so much that it's not testing anything.
* I started out planning on writing _some_ tests little by little as I did the features too -- but I couldn't get BL tests to run _at all_ (related to that sprockets_rails stuff, yeah), so temporarily abandoned that to at least write some code. I _think_ I have tests running now, but it required a complicated multi-step dance of first `bundle install`, then `engine_cart:create`, then `bundle update`, I'm honestly not sure exactly what I did or how I'd do it again from scratch. I think something is not right in BL's engine_cart world.