blacklight_folders 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|