curation_concerns 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +1 -1
  4. data/Rakefile +3 -11
  5. data/VERSION +1 -1
  6. data/app/assets/javascripts/curation_concerns/application.js +3 -0
  7. data/app/assets/javascripts/curation_concerns/curation_concerns.js +5 -0
  8. data/app/assets/javascripts/curation_concerns/file_manager/affix.es6 +13 -0
  9. data/app/assets/javascripts/curation_concerns/file_manager/list_toggle.es6 +6 -0
  10. data/app/assets/javascripts/curation_concerns/file_manager/member.es6 +82 -0
  11. data/app/assets/javascripts/curation_concerns/file_manager/save_manager.es6 +70 -0
  12. data/app/assets/javascripts/curation_concerns/file_manager/sorting.es6 +82 -0
  13. data/app/assets/stylesheets/curation_concerns/_modules.scss +1 -1
  14. data/app/assets/stylesheets/curation_concerns/_typography.scss +0 -11
  15. data/app/assets/stylesheets/curation_concerns/modules/file_manager.scss +115 -0
  16. data/app/controllers/concerns/curation_concerns/collections_controller_behavior.rb +1 -1
  17. data/app/controllers/concerns/curation_concerns/curation_concern_controller.rb +5 -1
  18. data/app/forms/curation_concerns/forms/work_form.rb +3 -1
  19. data/app/views/catalog/_document_list.html.erb +1 -1
  20. data/app/views/collections/_search_collection_dashboard_form.html.erb +1 -1
  21. data/app/views/collections/_search_form.html.erb +1 -1
  22. data/app/views/collections/show.html.erb +24 -17
  23. data/app/views/curation_concerns/base/_file_manager_actions.html.erb +3 -0
  24. data/app/views/curation_concerns/base/_file_manager_attributes.html.erb +0 -0
  25. data/app/views/curation_concerns/base/_file_manager_extra_tools.html.erb +0 -0
  26. data/app/views/curation_concerns/base/_file_manager_member.html.erb +31 -0
  27. data/app/views/curation_concerns/base/_file_manager_thumbnail.html.erb +1 -0
  28. data/app/views/curation_concerns/base/_show_actions.html.erb +1 -0
  29. data/app/views/curation_concerns/base/file_manager.html.erb +34 -0
  30. data/app/views/shared/_site_search.html.erb +3 -3
  31. data/config/locales/curation_concerns.en.yml +2 -0
  32. data/curation_concerns.gemspec +2 -0
  33. data/lib/curation_concerns/engine.rb +1 -0
  34. data/lib/curation_concerns/rails/routes.rb +5 -0
  35. data/lib/curation_concerns/version.rb +1 -1
  36. data/spec/actors/curation_concerns/work_actor_spec.rb +21 -5
  37. data/spec/controllers/curation_concerns/collections_controller_spec.rb +9 -0
  38. data/spec/controllers/curation_concerns/generic_works_controller_spec.rb +24 -4
  39. data/spec/javascripts/file_manager_member_spec.coffee +87 -0
  40. data/spec/javascripts/fixtures/.gitkeep +0 -0
  41. data/spec/javascripts/fixtures/file_manager_member.html +40 -0
  42. data/spec/javascripts/fixtures/save_button.html +3 -0
  43. data/spec/javascripts/helpers/jasmine-jquery.js +841 -0
  44. data/spec/javascripts/helpers/mock-ajax.js +736 -0
  45. data/spec/javascripts/helpers/test_responses.js +12 -0
  46. data/spec/javascripts/jasmine_spec.rb +25 -0
  47. data/spec/javascripts/save_manager_spec.coffee +84 -0
  48. data/spec/javascripts/support/jasmine.yml +136 -0
  49. data/spec/javascripts/support/jasmine_helper.rb +15 -0
  50. data/spec/routing/route_spec.rb +4 -0
  51. data/spec/support/rake_support.rb +41 -0
  52. data/spec/views/catalog/index.html.erb_spec.rb +2 -2
  53. data/spec/views/curation_concerns/base/file_manager.html.erb_spec.rb +72 -0
  54. data/tasks/jasmine.rake +18 -0
  55. metadata +71 -4
@@ -0,0 +1,34 @@
1
+ <h1><%= t("file_manager.link_text") %></h1>
2
+ <ul class="breadcrumb">
3
+ <li>
4
+ Back to <%= link_to @presenter.to_s, [main_app, @presenter] %>
5
+ </li>
6
+ </ul>
7
+
8
+ <% if !@presenter.file_presenters.empty? %>
9
+ <div data-action="file-manager">
10
+ <div class="col-md-3" id="file-manager-tools">
11
+ <h2>Toolbar</h2>
12
+ <%= render "file_manager_actions" %>
13
+ </div>
14
+ <div class="col-md-3" id="label-tools-spacer" class="fixed"></div>
15
+ <div class="col-md-9" id="order-grid">
16
+ <div class="btn-group" role="group" data-action="list-toggle">
17
+ <button type="button" class="btn btn-default list" aria-label="List">
18
+ <span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
19
+ </button>
20
+ <button type="button" class="btn btn-default active grid" aria-label="Grid">
21
+ <span class="glyphicon glyphicon-th" aria-hidden="true"></span>
22
+ </button>
23
+ </div>
24
+ <ul id="sortable" data-id="<%= @presenter.id %>" data-class-name="<%= @presenter.model_name.plural %>" data-singular-class-name="<%= @presenter.model_name.singular %>" class="list-unstyled grid">
25
+ <% @presenter.file_presenters.each do |member| %>
26
+ <%= render "file_manager_member", node: member %>
27
+ <% end %>
28
+ </ul>
29
+ </div>
30
+ </div>
31
+ <% end %>
32
+ <div id="file-manager-extra-tools">
33
+ <%= render "file_manager_extra_tools" %>
34
+ </div>
@@ -1,12 +1,12 @@
1
1
  <%= form_tag main_app.search_catalog_path, method: :get, class: "search-form" do %>
2
2
  <fieldset>
3
- <legend class="accessible-hidden">Search <%= t('curation_concerns.product_name') %></legend>
4
- <%= label_tag :catalog_search, t('curation_concerns.search.form.q.label'), class: "accessible-hidden" %>
3
+ <legend class="sr-only">Search <%= t('curation_concerns.product_name') %></legend>
4
+ <%= label_tag :catalog_search, t('curation_concerns.search.form.q.label'), class: "sr-only" %>
5
5
  <%= render_hash_as_hidden_fields(search_state.params_for_search.except(:q, :search_field, :qt, :page, :utf8)) %>
6
6
  <%= text_field_tag(:q, params[:q], class: "q search-query", id: "catalog_search",
7
7
  placeholder: t('curation_concerns.search.form.q.placeholder'), tabindex: "1", type: "search") %>
8
8
  <button type="submit" class="search-submit btn btn-primary" id="keyword-search-submit" tabindex="2">
9
- <i class="glyphicon glyphicon-search"></i><span class="accessible-hidden">Search</span>
9
+ <i class="glyphicon glyphicon-search"></i><span class="sr-only">Search</span>
10
10
  </button>
11
11
  </fieldset>
12
12
  <% end %>
@@ -147,6 +147,8 @@ en:
147
147
  human_readable_type_tesim: "Resource Type"
148
148
  format_tesim: "File Format"
149
149
  identifier_tesim: "Identifier"
150
+ file_manager:
151
+ link_text: 'File Manager'
150
152
  simple_form:
151
153
  hints:
152
154
  defaults:
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_dependency 'hydra-editor', '~> 1.1'
26
26
  spec.add_dependency 'blacklight_advanced_search', '~> 6.0'
27
27
  spec.add_dependency 'rails_autolink'
28
+ spec.add_dependency 'sprockets-es6'
28
29
 
29
30
  spec.add_development_dependency 'solr_wrapper', '~> 0.4'
30
31
  spec.add_development_dependency 'fcrepo_wrapper', '~> 0.1'
@@ -42,4 +43,5 @@ Gem::Specification.new do |spec|
42
43
  spec.add_development_dependency "factory_girl"
43
44
  spec.add_development_dependency "database_cleaner", "< 1.1.0"
44
45
  spec.add_development_dependency 'mida', '~> 0.3.4'
46
+ spec.add_development_dependency 'jasmine'
45
47
  end
@@ -5,6 +5,7 @@ require 'hydra-collections'
5
5
  require 'hydra-editor'
6
6
  require 'jquery-ui-rails'
7
7
  require 'qa'
8
+ require 'sprockets/es6'
8
9
 
9
10
  module CurationConcerns
10
11
  # Ensures that routes to curation_concerns are prefixed with `curation_concerns_`
@@ -12,6 +12,11 @@ module ActionDispatch::Routing
12
12
  namespace :curation_concerns, path: :concern do
13
13
  concerns_to_route.each do |curation_concern_name|
14
14
  namespaced_resources curation_concern_name, except: [:index], &block
15
+ namespaced_resources curation_concern_name, only: [] do
16
+ member do
17
+ get :file_manager
18
+ end
19
+ end
15
20
  end
16
21
 
17
22
  resources :permissions, only: [] do
@@ -1,3 +1,3 @@
1
1
  module CurationConcerns
2
- VERSION = "0.7.0".freeze
2
+ VERSION = "0.8.0".freeze
3
3
  end
@@ -247,15 +247,31 @@ describe CurationConcerns::GenericWorkActor do
247
247
  context 'with multiple file sets' do
248
248
  let(:file_set1) { create(:file_set) }
249
249
  let(:file_set2) { create(:file_set) }
250
- let(:curation_concern) { FactoryGirl.create(:generic_work, user: user, members: [file_set1, file_set2]) }
250
+ let(:curation_concern) { FactoryGirl.create(:generic_work, user: user, ordered_members: [file_set1, file_set2]) }
251
251
  let(:attributes) do
252
- FactoryGirl.attributes_for(:generic_work, members: [file_set2, file_set1])
252
+ FactoryGirl.attributes_for(:generic_work, ordered_member_ids: [file_set2.id, file_set1.id])
253
253
  end
254
- xit 'updates the order of file sets' do
255
- expect(curation_concern.ordered_members).to eq [file_set1, file_set2]
254
+ it 'updates the order of file sets' do
255
+ expect(curation_concern.ordered_members.to_a).to eq [file_set1, file_set2]
256
+
256
257
  expect(subject.update).to be true
258
+
257
259
  curation_concern.reload
258
- expect(curation_concern.ordered_members).to eq [file_set2, file_set1]
260
+ expect(curation_concern.ordered_members.to_a).to eq [file_set2, file_set1]
261
+ end
262
+ ## Is this something we want to support?
263
+ context "when told to stop ordering a file set" do
264
+ let(:attributes) do
265
+ FactoryGirl.attributes_for(:generic_work, ordered_member_ids: [file_set2.id])
266
+ end
267
+ it "works" do
268
+ expect(curation_concern.ordered_members.to_a).to eq [file_set1, file_set2]
269
+
270
+ expect(subject.update).to be true
271
+
272
+ curation_concern.reload
273
+ expect(curation_concern.ordered_members.to_a).to eq [file_set2]
274
+ end
259
275
  end
260
276
  end
261
277
  end
@@ -163,6 +163,15 @@ describe CollectionsController do
163
163
  expect(assigns[:presenter].title).to eq collection.title
164
164
  expect(assigns[:member_docs].map(&:id)).to match_array [asset1, asset2, asset3].map(&:id)
165
165
  end
166
+
167
+ context 'when the q parameter is passed' do
168
+ it 'loads the collection (paying no attention to the q param)' do
169
+ get :show, id: collection, q: 'no matches'
170
+ expect(response).to be_successful
171
+ expect(assigns[:presenter]).to be_kind_of CurationConcerns::CollectionPresenter
172
+ expect(assigns[:presenter].title).to eq collection.title
173
+ end
174
+ end
166
175
  end
167
176
 
168
177
  context 'not signed in' do
@@ -122,7 +122,7 @@ describe CurationConcerns::GenericWorksController do
122
122
  describe '#update' do
123
123
  let(:a_work) { create(:private_generic_work, user: user) }
124
124
  before do
125
- allow(controller).to receive(:actor).and_return(actor)
125
+ allow(CurationConcerns::CurationConcern).to receive(:actor).and_return(actor)
126
126
  end
127
127
  let(:actor) { double(update: true, visibility_changed?: false) }
128
128
 
@@ -131,6 +131,13 @@ describe CurationConcerns::GenericWorksController do
131
131
  expect(response).to redirect_to main_app.curation_concerns_generic_work_path(a_work)
132
132
  end
133
133
 
134
+ it "can update file membership" do
135
+ file = create(:file_set, user: user)
136
+
137
+ patch :update, id: a_work, generic_work: { ordered_member_ids: [file.id] }
138
+ expect(CurationConcerns::CurationConcern).to have_received(:actor).with(anything, anything, ordered_member_ids: [file.id])
139
+ end
140
+
134
141
  describe 'changing rights' do
135
142
  let(:actor) { double(update: true, visibility_changed?: true) }
136
143
 
@@ -138,14 +145,14 @@ describe CurationConcerns::GenericWorksController do
138
145
  let(:a_work) { create(:work_with_one_file, user: user) }
139
146
 
140
147
  it 'prompts to change the files access' do
141
- patch :update, id: a_work
148
+ patch :update, id: a_work, generic_work: {}
142
149
  expect(response).to redirect_to main_app.confirm_curation_concerns_permission_path(controller.curation_concern)
143
150
  end
144
151
  end
145
152
 
146
153
  context 'without children' do
147
154
  it "doesn't prompt to change the files access" do
148
- patch :update, id: a_work
155
+ patch :update, id: a_work, generic_work: {}
149
156
  expect(response).to redirect_to main_app.curation_concerns_generic_work_path(a_work)
150
157
  end
151
158
  end
@@ -155,7 +162,7 @@ describe CurationConcerns::GenericWorksController do
155
162
  let(:actor) { double(update: false, visibility_changed?: false) }
156
163
 
157
164
  it 'renders the form' do
158
- patch :update, id: a_work
165
+ patch :update, id: a_work, generic_work: {}
159
166
  expect(assigns[:form]).to be_kind_of CurationConcerns::GenericWorkForm
160
167
  expect(response).to render_template('edit')
161
168
  end
@@ -207,4 +214,17 @@ describe CurationConcerns::GenericWorksController do
207
214
  end
208
215
  end
209
216
  end
217
+
218
+ describe '#file_manager' do
219
+ let(:work) { create(:private_generic_work, user: user) }
220
+ before do
221
+ sign_in user
222
+ end
223
+ it "is successful" do
224
+ get :file_manager, id: work.id
225
+
226
+ expect(response).to be_success
227
+ expect(assigns(:presenter)).not_to be_blank
228
+ end
229
+ end
210
230
  end
@@ -0,0 +1,87 @@
1
+ describe "FileManagerMember", ->
2
+ file_manager_member = null
3
+ save_manager = null
4
+ beforeEach () ->
5
+ loadFixtures('file_manager_member.html')
6
+ save_manager = {
7
+ push_changed: () -> {},
8
+ mark_unchanged: () -> {}
9
+ }
10
+ file_manager_member = new FileManagerMember($("li"), save_manager)
11
+ describe "#is_changed", ->
12
+ it "is true when the form's label input is changed", ->
13
+ $("#file_set_title").val("testing")
14
+ $("#file_set_title").change()
15
+
16
+ expect(file_manager_member.is_changed).toEqual(true)
17
+ it "is false when the form's label input isn't changed", ->
18
+ $("#file_set_title").change()
19
+
20
+ expect(file_manager_member.is_changed).toEqual(false)
21
+ it "is false when the form's label input returns", ->
22
+ initial_val = $("#file_set_title").val()
23
+ $("#file_set_title").val("testing")
24
+ $("#file_set_title").change()
25
+ $("#file_set_title").val(initial_val)
26
+ $("#file_set_title").change()
27
+
28
+ expect(file_manager_member.is_changed).toEqual(false)
29
+ it "triggers save_manager's push_changed when changed", ->
30
+ spyOn(save_manager, "push_changed")
31
+ $("#file_set_title").val("testing")
32
+ $("#file_set_title").change()
33
+
34
+ expect(save_manager.push_changed).toHaveBeenCalledWith(file_manager_member)
35
+ it "triggers save_manager's mark_unchanged when no longer changed", ->
36
+ spyOn(save_manager, "mark_unchanged")
37
+ initial_val = $("#file_set_title").val()
38
+ $("#file_set_title").val("testing")
39
+ $("#file_set_title").change()
40
+ $("#file_set_title").val(initial_val)
41
+ $("#file_set_title").change()
42
+
43
+ expect(save_manager.mark_unchanged).toHaveBeenCalledWith(file_manager_member)
44
+ it "doesn't trigger save_manager's mark_unchanged when there are still changed elements", ->
45
+ spyOn(save_manager, "mark_unchanged")
46
+ file_manager_member.elements.push {}
47
+ initial_val = $("#file_set_title").val()
48
+ $("#file_set_title").val("testing")
49
+ $("#file_set_title").change()
50
+ $("#file_set_title").val(initial_val)
51
+ $("#file_set_title").change()
52
+
53
+ expect(save_manager.mark_unchanged).not.toHaveBeenCalled()
54
+ describe "#persist", ->
55
+ describe "when nothing has changed", ->
56
+ it "returns a resolved deferred object", ->
57
+ expect(file_manager_member.persist().state()).toEqual("resolved")
58
+ describe "when updates need to be sent", ->
59
+ request = null
60
+ beforeEach () ->
61
+ jasmine.Ajax.install()
62
+ afterEach () ->
63
+ jasmine.Ajax.uninstall()
64
+ it "returns a deferred object which is resolved with the ajax request", ->
65
+ $("#file_set_title").val("testing")
66
+ $("#file_set_title").change()
67
+ result = file_manager_member.persist()
68
+ request = jasmine.Ajax.requests.mostRecent()
69
+
70
+ expect(result.state()).toEqual("pending")
71
+
72
+ request.respondWith(TestResponses.file_manager_member.success)
73
+
74
+ expect(result.state()).toEqual("resolved")
75
+ expect(file_manager_member.is_changed).toEqual(false)
76
+ it "rejects the deferred object when the ajax request fails", ->
77
+ $("#file_set_title").val("testing")
78
+ $("#file_set_title").change()
79
+ result = file_manager_member.persist()
80
+ request = jasmine.Ajax.requests.mostRecent()
81
+
82
+ expect(result.state()).toEqual("pending")
83
+
84
+ request.respondWith(TestResponses.file_manager_member.failure)
85
+
86
+ expect(result.state()).toEqual("rejected")
87
+ expect(file_manager_member.is_changed).toEqual(true)
File without changes
@@ -0,0 +1,40 @@
1
+ <li data-reorder-id="j38606956">
2
+ <form accept-charset="UTF-8" action="/concern/file_sets/j38606956" class=
3
+ "simple_form edit_file_set" data-remote="true" id="edit_file_set_j38606956"
4
+ method="post" name="edit_file_set_j38606956">
5
+ <input name="utf8" type="hidden" value="✓"><input name="_method" type=
6
+ "hidden" value="patch">
7
+ <div class="panel panel-default">
8
+ <div class="panel-heading ui-sortable-handle">
9
+ <div class="order-title">
10
+ <div class="form-group string required file_set_title">
11
+ <div>
12
+ <input aria-required="true" class=
13
+ "string required title form-control" id="file_set_title" name=
14
+ "file_set[title][]" required="required" type="text" value=
15
+ "01.tif">
16
+ </div>
17
+ </div>
18
+ </div>
19
+ <div class="file-set-link pull-right">
20
+ <a href="/concern/file_sets/j38606956" title=
21
+ "Edit file"><span aria-hidden="true" class=
22
+ "glyphicon glyphicon-edit"></span></a>
23
+ </div>
24
+ <div class="order-filename">
25
+ <em title="01.tif">(01.tif)</em>
26
+ </div>
27
+ </div>
28
+ <div class="panel-body">
29
+ <div class="text-center thumbnail">
30
+ <a data-context-href="/catalog/j38606956/track?search_id=1" href=
31
+ "/concern/file_sets/j38606956"><img alt="J38606956?file=thumbnail"
32
+ class="thumbnail-inner" src=
33
+ "/downloads/j38606956?file=thumbnail"></a>
34
+ </div>
35
+ <div class="attributes"></div>
36
+ <div class="spacer"></div>
37
+ </div>
38
+ </div>
39
+ </form>
40
+ </li>
@@ -0,0 +1,3 @@
1
+ <div class="actions form-horizontal panel panel-default">
2
+ <button name="button" type="submit" class="btn btn-primary disabled" data-action="save-actions">Save</button>
3
+ </div>