blacklight_folders 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +15 -0
  3. data/README.rdoc +50 -0
  4. data/Rakefile +68 -0
  5. data/app/assets/javascripts/blacklight_folders/catalog/edit.js +28 -0
  6. data/app/assets/javascripts/blacklight_folders/catalog/index.js +65 -0
  7. data/app/assets/javascripts/blacklight_folders/catalog/show.js +27 -0
  8. data/app/assets/javascripts/blacklight_folders/catalog.js +1 -0
  9. data/app/assets/javascripts/blacklight_folders/folder_items.js +36 -0
  10. data/app/assets/javascripts/blacklight_folders/sort.js +10 -0
  11. data/app/assets/javascripts/blacklight_folders.js +8 -0
  12. data/app/assets/stylesheets/blacklight_folders/_blacklight_folders.css.scss +4 -0
  13. data/app/assets/stylesheets/blacklight_folders/_folders.css.scss +56 -0
  14. data/app/assets/stylesheets/blacklight_folders/_forms.css.scss +11 -0
  15. data/app/assets/stylesheets/blacklight_folders/_nestable.css.scss +129 -0
  16. data/app/controllers/blacklight/folders/application_controller.rb +11 -0
  17. data/app/controllers/blacklight/folders/folders_controller.rb +162 -0
  18. data/app/controllers/concerns/blacklight/folders/application_controller_behavior.rb +63 -0
  19. data/app/forms/blacklight/folders/folder_form.rb +74 -0
  20. data/app/helpers/blacklight/folders/application_helper.rb +29 -0
  21. data/app/helpers/blacklight/folders/crud_links_helper.rb +29 -0
  22. data/app/helpers/blacklight/folders/folders_helper.rb +14 -0
  23. data/app/helpers/blacklight/folders/main_app_helper.rb +30 -0
  24. data/app/models/ability.rb +4 -0
  25. data/app/models/blacklight/folders/folder.rb +120 -0
  26. data/app/models/blacklight/folders/folder_item.rb +21 -0
  27. data/app/models/blacklight/folders/solr_response.rb +20 -0
  28. data/app/models/concerns/blacklight/folders/ability.rb +39 -0
  29. data/app/models/concerns/blacklight/folders/solr_document.rb +3 -0
  30. data/app/models/concerns/blacklight/folders/user.rb +20 -0
  31. data/app/views/blacklight/folders/_add_to_folder.html.erb +10 -0
  32. data/app/views/blacklight/folders/_folder_control.html.erb +1 -0
  33. data/app/views/blacklight/folders/folders/_folder.html.erb +22 -0
  34. data/app/views/blacklight/folders/folders/_folder_item.html.erb +2 -0
  35. data/app/views/blacklight/folders/folders/_form.html.erb +78 -0
  36. data/app/views/blacklight/folders/folders/_index_header_default.html.erb +20 -0
  37. data/app/views/blacklight/folders/folders/_marc_tools.html.erb +10 -0
  38. data/app/views/blacklight/folders/folders/_tools.html.erb +11 -0
  39. data/app/views/blacklight/folders/folders/edit.html.erb +2 -0
  40. data/app/views/blacklight/folders/folders/index.html.erb +33 -0
  41. data/app/views/blacklight/folders/folders/new.html.erb +2 -0
  42. data/app/views/blacklight/folders/folders/show.endnote.erb +1 -0
  43. data/app/views/blacklight/folders/folders/show.html.erb +43 -0
  44. data/app/views/blacklight/folders/show/_add_to_folder.html.erb +18 -0
  45. data/app/views/blacklight/folders/show/_folder_controls.html.erb +15 -0
  46. data/app/views/blacklight/nav/_folders.html.erb +14 -0
  47. data/app/views/catalog/_show_sidebar.html.erb +11 -0
  48. data/config/jetty.yml +6 -0
  49. data/config/locales/blacklight_folders.en.yml +73 -0
  50. data/config/routes.rb +5 -0
  51. data/db/migrate/1416883534_create_blacklight_folders_folders.rb +12 -0
  52. data/db/migrate/1416883577_create_blacklight_folders_folder_items.rb +13 -0
  53. data/lib/blacklight_folders/engine.rb +11 -0
  54. data/lib/blacklight_folders/version.rb +5 -0
  55. data/lib/blacklight_folders.rb +4 -0
  56. data/lib/generators/blacklight_folders/install_generator.rb +53 -0
  57. data/lib/generators/blacklight_folders/templates/blacklight_folders.rb +12 -0
  58. data/lib/generators/blacklight_folders/templates/blacklight_folders_helper.rb +3 -0
  59. data/lib/migration/bookmark_migrator.rb +45 -0
  60. data/lib/tasks/blacklight_folders_tasks.rake +12 -0
  61. metadata +269 -0
@@ -0,0 +1,162 @@
1
+ require_dependency "blacklight/folders/application_controller"
2
+
3
+ module Blacklight::Folders
4
+ class FoldersController < ApplicationController
5
+ include Blacklight::TokenBasedUser
6
+ include Blacklight::Catalog::SearchContext
7
+
8
+ load_and_authorize_resource class: Blacklight::Folders::Folder, except: [:add_bookmarks, :remove_bookmarks]
9
+ before_filter :load_and_authorize_folder, only: [:add_bookmarks, :remove_bookmarks]
10
+ before_filter :clear_session_search_params, only: [:show]
11
+
12
+ def index
13
+ @folders = if current_or_guest_user.new_record?
14
+ # Just show the temporary folder
15
+ current_or_guest_user.folders
16
+ else
17
+ Blacklight::Folders::Folder.accessible_by(current_ability)
18
+ end
19
+
20
+ if ['created_at', 'updated_at'].include?(params[:order_by])
21
+ @folders = @folders.order(params[:order_by] + ' DESC')
22
+ elsif ['name', 'number_of_members'].include?(params[:order_by])
23
+ @folders = @folders.order(params[:order_by])
24
+ end
25
+ end
26
+
27
+ def show
28
+ @response = @folder.response
29
+ respond_to do |format|
30
+ format.html { }
31
+ document_export_formats(format, @response)
32
+ end
33
+ end
34
+
35
+ def new
36
+ end
37
+
38
+ def edit
39
+ end
40
+
41
+ def create
42
+ @folder.user = current_user
43
+ if @folder.save
44
+ redirect_to @folder
45
+ else
46
+ render :new
47
+ end
48
+ end
49
+
50
+ def update
51
+ form = folder_form_class.new(create_params)
52
+ if form.update(@folder)
53
+ respond_to do |format|
54
+ format.html do
55
+ redirect_to @folder, notice: t(:'helpers.submit.folder.updated')
56
+ end
57
+ format.json do
58
+ render json: @folder
59
+ end
60
+ end
61
+ else
62
+ render :edit
63
+ end
64
+ end
65
+
66
+ def destroy
67
+ @folder.destroy
68
+ redirect_to main_app.root_path, notice: "Folder \"#{@folder.name}\" was successfully deleted."
69
+ end
70
+
71
+ def add_bookmarks
72
+ doc_ids = Array(params['document_ids'].split(',').map(&:strip))
73
+ @folder.add_bookmarks(doc_ids)
74
+
75
+ if @folder.save
76
+ message = doc_ids.size == 1 ? t(:'helpers.submit.folder.added_one', folder_name: @folder.name) : t(:'helpers.submit.folder.added_many', folder_name: @folder.name)
77
+ redirect_to :back, notice: message
78
+ else
79
+ redirect_to :back, alert: 'Unable to save bookmarks.'
80
+ end
81
+ end
82
+
83
+ def remove_bookmarks
84
+ item_ids = Array(params['item_ids'].split(',').map(&:to_i))
85
+ items = @folder.items.select {|x| item_ids.include?(x.id)}
86
+ @folder.remove_bookmarks(items)
87
+ redirect_to :back
88
+ end
89
+
90
+ protected
91
+
92
+ def current_ability
93
+ if token_user
94
+ ::Ability.new(token_user)
95
+ else
96
+ super
97
+ end
98
+ end
99
+
100
+ # These methods are extracted from Blacklight::Catalog and maybe can be extracted to a reusable model.
101
+ def document_export_formats(format, response)
102
+ format.any do
103
+ format_name = params.fetch(:format, '').to_sym
104
+
105
+ if response.export_formats.include? format_name
106
+ render_document_export_format format_name, response
107
+ else
108
+ raise ActionController::UnknownFormat.new
109
+ end
110
+ end
111
+ end
112
+
113
+ ##
114
+ # Render the document export formats for a response
115
+ # First, try to render an appropriate template (e.g. index.endnote.erb)
116
+ # If that fails, just concatenate the document export responses with a newline.
117
+ def render_document_export_format format_name, response
118
+ begin
119
+ render
120
+ rescue ActionView::MissingTemplate
121
+ render text: response.documents.map { |x| x.export_as(format_name) if x.exports_as? format_name }.compact.join("\n"), layout: false
122
+ end
123
+ end
124
+ protected
125
+
126
+ def folder_form_class
127
+ FolderForm
128
+ end
129
+
130
+ private
131
+
132
+ def _prefixes
133
+ @_prefixes ||= super + ['catalog']
134
+ end
135
+
136
+ def create_params
137
+ params.require(:folder).permit(:name, :visibility, items_attributes: [:id, :position, :_destroy, :folder_id])
138
+ end
139
+
140
+ def clear_session_search_params
141
+ # TODO: Is there a blacklight method we can use to do this?
142
+ session['search'] = nil
143
+ end
144
+
145
+ def load_and_authorize_folder
146
+ id = params['id'] || params['folder']['id']
147
+ if id == '0' && current_or_guest_user.new_record?
148
+ # Clear out the temporary folder
149
+ current_or_guest_user.folders = []
150
+ # This should be the only place a real folder is created for a guest user
151
+ @folder = current_or_guest_user.folders.first_or_initialize(name: Folder.default_folder_name)
152
+ # Now that the guest user needs a folder, persist the user.
153
+ current_or_guest_user.save!
154
+ # Reset the cached cancan ability
155
+ @current_ability = nil
156
+ else
157
+ @folder = Folder.find(id)
158
+ end
159
+ authorize! :update_bookmarks, @folder
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,63 @@
1
+ module Blacklight::Folders
2
+ module ApplicationControllerBehavior
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ rescue_from ::CanCan::AccessDenied, with: :unauthorized_access
7
+ before_action :give_guest_a_folder, if: :build_a_folder_for_guests?
8
+
9
+ define_callbacks :logging_in_user
10
+ set_callback :logging_in_user, :before, :transfer_guest_user_bookmarks_to_current_user
11
+
12
+ end
13
+
14
+ # Override cancan to be aware of guest users
15
+ def current_ability
16
+ @current_ability ||= ::Ability.new(current_or_guest_user)
17
+ end
18
+
19
+ # Called when a CanCan error is raised. Redirects to the root page and sets a
20
+ # flash error if the user is signed in, othewise tells the user to sign in.
21
+ def unauthorized_access(exception)
22
+ if current_user
23
+ redirect_to main_app.root_url, alert: exception.message
24
+ else
25
+ redirect_to main_app.new_user_session_path, alert: 'Please sign in to continue.'
26
+ end
27
+ end
28
+
29
+ # Build a folder on the current user so that the nav menu and Folder index page can be displayed
30
+ def give_guest_a_folder
31
+ current_or_guest_user.folders.build id: 0,
32
+ name: Blacklight::Folders::Folder.default_folder_name,
33
+ created_at: Time.now,
34
+ updated_at: Time.now
35
+ end
36
+
37
+ # Test to see if the current user needs to have a folder built
38
+ # Returns true when it's a guest who has no folders
39
+ def build_a_folder_for_guests?
40
+ current_or_guest_user.new_record? && current_or_guest_user.folders.empty?
41
+ end
42
+
43
+ private
44
+
45
+ def transfer_guest_user_bookmarks_to_current_user
46
+ return unless respond_to? :current_user and respond_to? :guest_user and current_user and guest_user
47
+
48
+ if current_user.default_folder
49
+ guest_user.folders.each do |f|
50
+ f.items.each do |i|
51
+ i.update(folder: current_user.default_folder)
52
+ end
53
+ f.delete
54
+ end
55
+ else
56
+ guest_user.folders.each do |f|
57
+ f.update(user: current_user)
58
+ end
59
+ end
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,74 @@
1
+ module Blacklight::Folders
2
+ class FolderForm
3
+ def initialize(params)
4
+ @params = params
5
+ end
6
+
7
+ def update(folder)
8
+ folder.assign_attributes(preprocess_nested_attributes(@params))
9
+ reorder_items(folder)
10
+ folder.save
11
+ end
12
+
13
+ protected
14
+ def preprocess_nested_attributes(in_params)
15
+ return in_params unless in_params.key?(:items_attributes)
16
+ in_params.merge(items_attributes: strip_blank_folders(nested_params_to_array(in_params[:items_attributes])))
17
+ end
18
+
19
+ def nested_params_to_array(attributes_collection)
20
+ if attributes_collection.is_a? Hash
21
+ keys = attributes_collection.keys
22
+ attributes_collection = if keys.include?('id') || keys.include?(:id)
23
+ [attributes_collection]
24
+ else
25
+ attributes_collection.values
26
+ end
27
+ end
28
+ attributes_collection
29
+ end
30
+
31
+ def strip_blank_folders(attributes_collection)
32
+ attributes_collection.each do |record_attributes|
33
+ record_attributes.delete(:folder_id) if record_attributes[:folder_id].blank?
34
+ end
35
+ attributes_collection
36
+ end
37
+
38
+ # This updates the positions of the folder items
39
+ def reorder_items(folder)
40
+ # we have to do a sort_by, not order, because the updated attributes have not been saved.
41
+ changed_folder, new, changed_position, unchanged = folder.items.
42
+ sort_by(&:position).
43
+ group_by do |item|
44
+ if item.folder_id_was != item.folder_id
45
+ :changed_folder
46
+ elsif item.position_was.nil?
47
+ :new
48
+ elsif item.position_was != item.position
49
+ :changed_position
50
+ else
51
+ :unchanged
52
+ end
53
+ end.values_at(:changed_folder, :new, :changed_position, :unchanged).map(&:to_a)
54
+
55
+ # items that will be in this folder
56
+ unmoved_items = unchanged
57
+ # place items whose positions were specified
58
+ changed_position.map {|item| unmoved_items.insert(item.position - 1, item)}
59
+ # add new items at the end
60
+ unmoved_items = unmoved_items + new
61
+ # calculate positions
62
+ unmoved_items.compact.
63
+ select {|item| item.folder_id_was == item.folder_id}.
64
+ each_with_index do |item, position|
65
+ item.position = position + 1
66
+ end
67
+
68
+ # items that have moved to another folder
69
+ changed_folder.select {|item| item.folder_id_was != item.folder_id}.each do |item|
70
+ item.position = nil
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,29 @@
1
+ module Blacklight::Folders
2
+ module ApplicationHelper
3
+
4
+ include CrudLinksHelper
5
+
6
+ # The layout file from the main app might invoke
7
+ # some of the named routes from blacklight, which
8
+ # causes errors on the blacklight_folders views
9
+ # unless you add the main_app prefix.
10
+ def method_missing(method, *args)
11
+ if main_app_url_helper?(method)
12
+ main_app.send(method, *args)
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ def search_action_url(*args)
19
+ main_app.catalog_index_url *args
20
+ end
21
+
22
+ private
23
+
24
+ def main_app_url_helper?(method)
25
+ method.to_s.end_with?('_path', '_url') && main_app.respond_to?(method)
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module Blacklight::Folders
2
+ module CrudLinksHelper
3
+ def action_label model, action=nil
4
+ action_default_value model, action
5
+ end
6
+
7
+ private
8
+ def action_default_value object, key = nil
9
+ object_model = convert_to_model(object)
10
+
11
+ key ||= object_model ? (object_model.persisted? ? :edit : :create) : :view
12
+
13
+ case object_model
14
+ when Symbol, String
15
+ model = object_model
16
+ object_name = object_model
17
+ else
18
+ model = object_model.class.model_name.human
19
+ object_name = object_model.class.model_name.i18n_key
20
+ end
21
+
22
+ defaults = []
23
+ defaults << :"helpers.action.#{object_name}.#{key}"
24
+ defaults << :"helpers.action.#{key}"
25
+ defaults << "#{key.to_s.humanize} #{model}"
26
+ I18n.t(defaults.shift, model: model, default: defaults)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ module Blacklight::Folders
2
+ module FoldersHelper
3
+
4
+ def human_friendly_visibility(visibility)
5
+ icon = visibility == Blacklight::Folders::Folder::PUBLIC ? 'unlock' : 'lock'
6
+ safe_join([content_tag(:span, '', class:"glyphicon glyphicon-#{icon}"),
7
+ t("activerecord.attributes.blacklight/folders/folder.visibility.#{visibility}")], ' ')
8
+ end
9
+
10
+ def folder_export_url(folder, format)
11
+ polymorphic_path([blacklight_folders, folder], format: format, only_path: false, encrypted_user_id: encrypt_user_id(current_or_guest_user.id))
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # This helper is included by the main app, so that Blacklight Catalog pages can use these functions.
2
+ module Blacklight::Folders::MainAppHelper
3
+ # Give a list of folder options for a select drop down
4
+ # @param [Hash] options
5
+ # @option options [SolrDocument] :without only show folders that don't include this document
6
+ def options_for_folder_select(options={})
7
+ collection = if current_or_guest_user.guest?
8
+ current_or_guest_user.folders
9
+ else
10
+ folders = Blacklight::Folders::Folder.for_user(current_or_guest_user)
11
+ folders = folders.without_document(options[:without]) if options.key?(:without)
12
+ sort_folders_with_default_first(folders, current_or_guest_user.default_folder)
13
+ end
14
+
15
+ truncated_options_for_select(collection, :name, :id, length: 25, separator: ' ')
16
+ end
17
+
18
+ def truncated_options_for_select(collection, value, key, opts = {})
19
+ options_for_select(collection.map { |c| [truncate(c[value], opts), c[key]] } )
20
+ end
21
+
22
+ def sort_folders_with_default_first(folders, default)
23
+ if default
24
+ [default] + (folders - [default])
25
+ else
26
+ folders
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,4 @@
1
+ class Ability
2
+ include CanCan::Ability
3
+ include Blacklight::Folders::Ability
4
+ end
@@ -0,0 +1,120 @@
1
+ module Blacklight::Folders
2
+ class Folder < ActiveRecord::Base
3
+ belongs_to :user, polymorphic: true
4
+ validates :user, presence: true
5
+ validates :name, presence: true
6
+
7
+ after_initialize :default_values
8
+
9
+ has_many :items, -> { order('position ASC') }, class_name: 'FolderItem', :dependent => :destroy
10
+ has_many :bookmarks, -> { order('blacklight_folders_folder_items.position ASC') }, through: :items
11
+ accepts_nested_attributes_for :items, allow_destroy: true
12
+
13
+ # visibility
14
+ PUBLIC = 'public'
15
+ PRIVATE = 'private'
16
+ before_save :apply_visibility
17
+
18
+ # How many folders will appear in the drop-down menu
19
+ MENU_LIMIT = 5
20
+
21
+ def default_values
22
+ self.number_of_members ||= 0
23
+ self.visibility ||= Blacklight::Folders::Folder::PRIVATE
24
+ end
25
+
26
+ def recalculate_size
27
+ self.number_of_members = items.count
28
+ end
29
+
30
+ def documents
31
+ response.documents
32
+ end
33
+
34
+ def response
35
+ @response ||= begin
36
+ doc_ids = bookmarks.pluck(:document_id)
37
+ # return [] if doc_ids.empty?
38
+
39
+ rows = doc_ids.count
40
+ query_ids = doc_ids.map{|id| RSolr.escape(id) }
41
+ query_ids = query_ids.join(' OR ')
42
+
43
+ query = query_ids.blank? ? '' : "id:(#{query_ids})"
44
+ solr_repository.search(q: query, qt: 'document', rows: rows).tap do |response|
45
+ response.order = doc_ids
46
+ end
47
+ end
48
+ end
49
+
50
+ def apply_visibility
51
+ self.visibility ||= default_visibility
52
+ end
53
+
54
+ def add_bookmarks(doc_ids=[])
55
+ doc_ids = Array(doc_ids)
56
+ doc_ids.each do |doc_id|
57
+ b = bookmarks.build(document_id: doc_id, user_id: user_id)
58
+ b.document_type = blacklight_config.solr_document_model.to_s
59
+ end
60
+ end
61
+
62
+ def remove_bookmarks(target=[])
63
+ items.delete(target)
64
+ end
65
+
66
+ protected
67
+ def default_visibility
68
+ PRIVATE
69
+ end
70
+
71
+ def solr_repository
72
+ @solr_repo ||= Blacklight::SolrRepository.new(blacklight_config)
73
+ end
74
+
75
+ def blacklight_config
76
+ @blacklight_config ||= begin
77
+ ::CatalogController.blacklight_config.deep_copy.tap do |config|
78
+ config.solr_response_model = Blacklight::Folders::SolrResponse
79
+ end
80
+ end
81
+ end
82
+
83
+ class << self
84
+
85
+ # Find the folders that belong to this user
86
+ def for_user(user)
87
+ if user.new_record?
88
+ user.folders
89
+ else
90
+ accessible_by(user.ability, :update).order(:name)
91
+ end
92
+ end
93
+
94
+ # Find the folders that contain this document
95
+ def with_document(document)
96
+ where("id in (#{membership_query(document)})")
97
+ end
98
+
99
+ # Find the folders that don't contain this document
100
+ def without_document(document)
101
+ where("id not in (#{membership_query(document)})")
102
+ end
103
+
104
+ def most_recent
105
+ order('updated_at DESC')
106
+ end
107
+
108
+ def default_folder_name
109
+ I18n.translate(:'blacklight.folders.default_folder_name')
110
+ end
111
+
112
+ private
113
+
114
+ def membership_query(document)
115
+ Blacklight::Folders::FolderItem.select(:folder_id).joins(:bookmark).where('bookmarks.document_id' => document.id).to_sql
116
+ end
117
+
118
+ end # class << self
119
+ end
120
+ end
@@ -0,0 +1,21 @@
1
+ require 'acts_as_list'
2
+
3
+ module Blacklight::Folders
4
+ class FolderItem < ActiveRecord::Base
5
+ after_save :recount_folders
6
+ belongs_to :folder, touch: true
7
+ validates :folder_id, presence: true
8
+ acts_as_list scope: :folder
9
+
10
+ belongs_to :bookmark, dependent: :destroy
11
+ validates :bookmark_id, presence: true
12
+
13
+ def recount_folders
14
+ Array(changes['folder_id']).compact.each do |folder_id|
15
+ f = Folder.find(folder_id)
16
+ f.recalculate_size
17
+ f.save!
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ module Blacklight::Folders
2
+ class SolrResponse < Blacklight::SolrResponse
3
+ attr_reader :doc_ids
4
+ def docs
5
+ @docs ||= begin
6
+ # Put them into the right order (same order as doc_ids),
7
+ # and cast them to the right model.
8
+ doc_ids.map.with_index {|id, i|
9
+ doc_hash = (response['docs'] || []).find{|doc| doc['id'] == id }
10
+ raise "Couldn't find Solr document for id: `#{id}'" unless doc_hash
11
+ doc_hash
12
+ }
13
+ end
14
+ end
15
+
16
+ def order= doc_ids
17
+ @doc_ids = doc_ids
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ module Blacklight::Folders
2
+ module Ability
3
+ def initialize(user)
4
+ super()
5
+ folder_permissions(user)
6
+ end
7
+
8
+ def folder_permissions(user)
9
+ can_manage_my_own_folders(user)
10
+ can_read_public_folders(user)
11
+ can :index, Blacklight::Folders::Folder, user_id: user.try(:id)
12
+ end
13
+
14
+ def can_manage_my_own_folders(user)
15
+ return unless user
16
+ can [:update_bookmarks], Blacklight::Folders::Folder, user_id: user.id
17
+ if user.guest?
18
+ guest_permissions(user)
19
+ else
20
+ can [:read, :update, :create, :destroy], Blacklight::Folders::Folder, user_id: user.id
21
+ end
22
+
23
+ can [:create, :destroy], Blacklight::Folders::FolderItem, folder: { user_id: user.id }
24
+ end
25
+
26
+ def can_read_public_folders(user)
27
+ can :show, Blacklight::Folders::Folder, visibility: Blacklight::Folders::Folder::PUBLIC
28
+ end
29
+
30
+
31
+ def guest_permissions(user)
32
+ if user.persisted?
33
+ can [:read, :update], Blacklight::Folders::Folder, ['user_id = ? AND id IS NOT NULL', user.id] do |f|
34
+ f.user_id == user.id && f.persisted?
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ module Blacklight::Folders::SolrDocument
2
+ extend ActiveSupport::Concern
3
+ end
@@ -0,0 +1,20 @@
1
+ module Blacklight::Folders::User
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ has_many :folders, class_name: 'Blacklight::Folders::Folder', as: :user, inverse_of: :user
6
+ after_create :create_default_folder
7
+ end
8
+
9
+ def create_default_folder
10
+ folders.create(name: Blacklight::Folders::Folder.default_folder_name) unless guest?
11
+ end
12
+
13
+ def default_folder
14
+ folders.where(name: Blacklight::Folders::Folder.default_folder_name).first
15
+ end
16
+
17
+ def ability
18
+ @ability ||= ::Ability.new(self)
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ <div id="folder-tools">
2
+ <% # This displays the folder dropdown on the search page %>
3
+ <%= form_tag blacklight_folders.add_bookmarks_to_folder_path, method: :patch, data: { behavior: 'move-to-folder' }, style: 'display: none', role: 'form', class: 'form-inline' do %>
4
+ <div class="form-group">
5
+ <%= label_tag :folder_id, t('catalog.search.folder.label', doctype: Blacklight::Folders::FolderItem.model_name.human, model: Blacklight::Folders::Folder.model_name.human), class: 'sr-only' %>
6
+ <%= select_tag :id, options_for_folder_select, prompt: t(:'.select.prompt'), class: "form-control"%>
7
+ <%= hidden_field_tag(:document_ids, '') %>
8
+ </div>
9
+ <% end %>
10
+ </div>
@@ -0,0 +1 @@
1
+ <%= check_box_tag 'folder_ids[]', document.id, nil, style: 'display: none'%>
@@ -0,0 +1,22 @@
1
+ <tr>
2
+ <td>
3
+ <%= can?(:show, folder) ? link_to(folder.name, blacklight_folders.folder_path(folder)) : folder.name %>
4
+ </td>
5
+ <td>
6
+ <%= pluralize(folder.items.count, Blacklight::Folders::FolderItem.model_name.human.downcase) %>
7
+ </td>
8
+ <td>
9
+ <%= human_friendly_visibility(folder.visibility) %>
10
+ </td>
11
+ <td>
12
+ <span title="<%= folder.created_at %>"><%= t('.ago', time: time_ago_in_words(folder.created_at)) %></span>
13
+ </td>
14
+ <td>
15
+ <span title="<%= folder.updated_at %>"><%= t('.ago', time: time_ago_in_words(folder.updated_at)) %></span>
16
+ </td>
17
+
18
+ <td>
19
+ <%= link_to 'Edit', blacklight_folders.edit_folder_path(folder), method: :get, class: 'btn btn-default' if can? :edit, folder %>
20
+ <%= link_to 'Delete', blacklight_folders.folder_path(folder), method: :delete, class: 'btn btn-danger', data: { confirm: "Are you sure you want to delete this folder and all its contents?" } if can? :destroy, folder %> </td>
21
+ </tr>
22
+
@@ -0,0 +1,2 @@
1
+ <% doc = docs[folder_item_counter] %>
2
+ <%= render 'document', document: docs[folder_item_counter], document_counter: folder_item_counter %>