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.
- checksums.yaml +7 -0
- data/LICENSE +15 -0
- data/README.rdoc +50 -0
- data/Rakefile +68 -0
- data/app/assets/javascripts/blacklight_folders/catalog/edit.js +28 -0
- data/app/assets/javascripts/blacklight_folders/catalog/index.js +65 -0
- data/app/assets/javascripts/blacklight_folders/catalog/show.js +27 -0
- data/app/assets/javascripts/blacklight_folders/catalog.js +1 -0
- data/app/assets/javascripts/blacklight_folders/folder_items.js +36 -0
- data/app/assets/javascripts/blacklight_folders/sort.js +10 -0
- data/app/assets/javascripts/blacklight_folders.js +8 -0
- data/app/assets/stylesheets/blacklight_folders/_blacklight_folders.css.scss +4 -0
- data/app/assets/stylesheets/blacklight_folders/_folders.css.scss +56 -0
- data/app/assets/stylesheets/blacklight_folders/_forms.css.scss +11 -0
- data/app/assets/stylesheets/blacklight_folders/_nestable.css.scss +129 -0
- data/app/controllers/blacklight/folders/application_controller.rb +11 -0
- data/app/controllers/blacklight/folders/folders_controller.rb +162 -0
- data/app/controllers/concerns/blacklight/folders/application_controller_behavior.rb +63 -0
- data/app/forms/blacklight/folders/folder_form.rb +74 -0
- data/app/helpers/blacklight/folders/application_helper.rb +29 -0
- data/app/helpers/blacklight/folders/crud_links_helper.rb +29 -0
- data/app/helpers/blacklight/folders/folders_helper.rb +14 -0
- data/app/helpers/blacklight/folders/main_app_helper.rb +30 -0
- data/app/models/ability.rb +4 -0
- data/app/models/blacklight/folders/folder.rb +120 -0
- data/app/models/blacklight/folders/folder_item.rb +21 -0
- data/app/models/blacklight/folders/solr_response.rb +20 -0
- data/app/models/concerns/blacklight/folders/ability.rb +39 -0
- data/app/models/concerns/blacklight/folders/solr_document.rb +3 -0
- data/app/models/concerns/blacklight/folders/user.rb +20 -0
- data/app/views/blacklight/folders/_add_to_folder.html.erb +10 -0
- data/app/views/blacklight/folders/_folder_control.html.erb +1 -0
- data/app/views/blacklight/folders/folders/_folder.html.erb +22 -0
- data/app/views/blacklight/folders/folders/_folder_item.html.erb +2 -0
- data/app/views/blacklight/folders/folders/_form.html.erb +78 -0
- data/app/views/blacklight/folders/folders/_index_header_default.html.erb +20 -0
- data/app/views/blacklight/folders/folders/_marc_tools.html.erb +10 -0
- data/app/views/blacklight/folders/folders/_tools.html.erb +11 -0
- data/app/views/blacklight/folders/folders/edit.html.erb +2 -0
- data/app/views/blacklight/folders/folders/index.html.erb +33 -0
- data/app/views/blacklight/folders/folders/new.html.erb +2 -0
- data/app/views/blacklight/folders/folders/show.endnote.erb +1 -0
- data/app/views/blacklight/folders/folders/show.html.erb +43 -0
- data/app/views/blacklight/folders/show/_add_to_folder.html.erb +18 -0
- data/app/views/blacklight/folders/show/_folder_controls.html.erb +15 -0
- data/app/views/blacklight/nav/_folders.html.erb +14 -0
- data/app/views/catalog/_show_sidebar.html.erb +11 -0
- data/config/jetty.yml +6 -0
- data/config/locales/blacklight_folders.en.yml +73 -0
- data/config/routes.rb +5 -0
- data/db/migrate/1416883534_create_blacklight_folders_folders.rb +12 -0
- data/db/migrate/1416883577_create_blacklight_folders_folder_items.rb +13 -0
- data/lib/blacklight_folders/engine.rb +11 -0
- data/lib/blacklight_folders/version.rb +5 -0
- data/lib/blacklight_folders.rb +4 -0
- data/lib/generators/blacklight_folders/install_generator.rb +53 -0
- data/lib/generators/blacklight_folders/templates/blacklight_folders.rb +12 -0
- data/lib/generators/blacklight_folders/templates/blacklight_folders_helper.rb +3 -0
- data/lib/migration/bookmark_migrator.rb +45 -0
- data/lib/tasks/blacklight_folders_tasks.rake +12 -0
- 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,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,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
|
+
|