blacklight_folders 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 %>