Skip to content

Commit

Permalink
Adds basic administration area with tests
Browse files Browse the repository at this point in the history
** Why are these changes being introduced:

* We want to add the Administrate gem to the application, and use its
  dashboards to manage information within TACOS.

** Relevant ticket(s):

* https://mitlibraries.atlassian.net/browse/TCO-51

** How does this address that need:

This does several things:

* Adds the Administrate gem

* Builds the default dashboards for several resources already in the
  application: Search Events, Terms, and Users

* Defines a separate "Overview" dashboard for summary statistics about
  information that has been compiled by TACOS. This dashboard is the
  default display for the /admin path.

* Adds a nav link to the admin area, visible only to users with the
  admin flag set.

* Starts to define the authorization checks in the admin application
  controller, which should in turn look to CanCan's ability model. The
  ability model itself is so far unchanged.

** Document any side effects to this change:

* There is a conflating of concerns at the moment, because the view
  layer checks the "admin" flag on the User model directly, without
  consulting the CanCan ability model, in order to build the link to the
  admin area. I'm working on refactoring away from this.

* The one resource I most want to expose in the admin area, the new
  Detector::SuggestedResource records, isn't working yet.

* The overview dashboard being the "Stats" controller under the hood
  may need to be adjusted to be consistently named.
  • Loading branch information
matt-bernhardt committed Aug 8, 2024
1 parent 9be8b36 commit 0963da4
Show file tree
Hide file tree
Showing 15 changed files with 476 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,5 @@ group :test do
gem 'vcr'
gem 'webmock'
end

gem "administrate", "~> 0.20.1"
26 changes: 26 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ GEM
tzinfo (~> 2.0)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
administrate (0.20.1)
actionpack (>= 6.0, < 8.0)
actionview (>= 6.0, < 8.0)
activerecord (>= 6.0, < 8.0)
jquery-rails (~> 4.6.0)
kaminari (~> 1.2.2)
sassc-rails (~> 2.1)
selectize-rails (~> 0.6)
aes_key_wrap (1.1.0)
annotate (3.2.0)
activerecord (>= 3.2, < 8.0)
Expand Down Expand Up @@ -178,6 +186,10 @@ GEM
jbuilder (2.12.0)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
jquery-rails (4.6.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.7.2)
json-jwt (1.16.6)
activesupport (>= 4.2)
Expand All @@ -186,6 +198,18 @@ GEM
bindata
faraday (~> 2.0)
faraday-follow_redirects
kaminari (1.2.2)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.2)
kaminari-activerecord (= 1.2.2)
kaminari-core (= 1.2.2)
kaminari-actionview (1.2.2)
actionview
kaminari-core (= 1.2.2)
kaminari-activerecord (1.2.2)
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
language_server-protocol (3.17.0.3)
llhttp-ffi (0.5.0)
ffi-compiler (~> 1.0)
Expand Down Expand Up @@ -349,6 +373,7 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
selectize-rails (0.12.6)
selenium-webdriver (4.23.0)
base64 (~> 0.2)
logger (~> 1.4)
Expand Down Expand Up @@ -429,6 +454,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
administrate (~> 0.20.1)
annotate
bootsnap
cancancan
Expand Down
36 changes: 36 additions & 0 deletions app/controllers/admin/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# All Administrate controllers inherit from this
# `Administrate::ApplicationController`, making it the ideal place to put
# authentication logic or other before_actions.
#
# If you want to add pagination or other controller-level concerns,
# you're free to overwrite the RESTful controller actions.
module Admin
class ApplicationController < Administrate::ApplicationController
before_action :require_user
before_action :authorize_user

private

def authorize_user
return if authorize_action?(resource_name, action_name)

redirect_to root_path, alert: 'Not authorized'
end

def authorize_action?(resource, action)
can? action, resource
end

def require_user
return if current_user

redirect_to root_path, alert: 'Please sign in to continue'
end

# Override this value to specify the number of elements to display at a time
# on index pages. Defaults to 20.
# def records_per_page
# params[:per_page] || 20
# end
end
end
46 changes: 46 additions & 0 deletions app/controllers/admin/search_events_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module Admin
class SearchEventsController < Admin::ApplicationController
# Overwrite any of the RESTful controller actions to implement custom behavior
# For example, you may want to send an email after a foo is updated.
#
# def update
# super
# send_foo_updated_email(requested_resource)
# end

# Override this method to specify custom lookup behavior.
# This will be used to set the resource for the `show`, `edit`, and `update`
# actions.
#
# def find_resource(param)
# Foo.find_by!(slug: param)
# end

# The result of this lookup will be available as `requested_resource`

# Override this if you have certain roles that require a subset
# this will be used to set the records shown on the `index` action.
#
# def scoped_resource
# if current_user.super_admin?
# resource_class
# else
# resource_class.with_less_stuff
# end
# end

# Override `resource_params` if you want to transform the submitted
# data before it's persisted. For example, the following would turn all
# empty values into nil values. It uses other APIs such as `resource_class`
# and `dashboard`:
#
# def resource_params
# params.require(resource_class.model_name.param_key).
# permit(dashboard.permitted_attributes(action_name)).
# transform_values { |value| value == "" ? nil : value }
# end

# See https://administrate-demo.herokuapp.com/customizing_controller_actions
# for more information
end
end
10 changes: 10 additions & 0 deletions app/controllers/admin/stats_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Admin
class StatsController < Admin::ApplicationController
def index
@stats = {
term_count: Term.count,
search_event_count: SearchEvent.count,
}
end
end
end
46 changes: 46 additions & 0 deletions app/controllers/admin/terms_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module Admin
class TermsController < Admin::ApplicationController
# Overwrite any of the RESTful controller actions to implement custom behavior
# For example, you may want to send an email after a foo is updated.
#
# def update
# super
# send_foo_updated_email(requested_resource)
# end

# Override this method to specify custom lookup behavior.
# This will be used to set the resource for the `show`, `edit`, and `update`
# actions.
#
# def find_resource(param)
# Foo.find_by!(slug: param)
# end

# The result of this lookup will be available as `requested_resource`

# Override this if you have certain roles that require a subset
# this will be used to set the records shown on the `index` action.
#
# def scoped_resource
# if current_user.super_admin?
# resource_class
# else
# resource_class.with_less_stuff
# end
# end

# Override `resource_params` if you want to transform the submitted
# data before it's persisted. For example, the following would turn all
# empty values into nil values. It uses other APIs such as `resource_class`
# and `dashboard`:
#
# def resource_params
# params.require(resource_class.model_name.param_key).
# permit(dashboard.permitted_attributes(action_name)).
# transform_values { |value| value == "" ? nil : value }
# end

# See https://administrate-demo.herokuapp.com/customizing_controller_actions
# for more information
end
end
46 changes: 46 additions & 0 deletions app/controllers/admin/users_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module Admin
class UsersController < Admin::ApplicationController
# Overwrite any of the RESTful controller actions to implement custom behavior
# For example, you may want to send an email after a foo is updated.
#
# def update
# super
# send_foo_updated_email(requested_resource)
# end

# Override this method to specify custom lookup behavior.
# This will be used to set the resource for the `show`, `edit`, and `update`
# actions.
#
# def find_resource(param)
# Foo.find_by!(slug: param)
# end

# The result of this lookup will be available as `requested_resource`

# Override this if you have certain roles that require a subset
# this will be used to set the records shown on the `index` action.
#
# def scoped_resource
# if current_user.super_admin?
# resource_class
# else
# resource_class.with_less_stuff
# end
# end

# Override `resource_params` if you want to transform the submitted
# data before it's persisted. For example, the following would turn all
# empty values into nil values. It uses other APIs such as `resource_class`
# and `dashboard`:
#
# def resource_params
# params.require(resource_class.model_name.param_key).
# permit(dashboard.permitted_attributes(action_name)).
# transform_values { |value| value == "" ? nil : value }
# end

# See https://administrate-demo.herokuapp.com/customizing_controller_actions
# for more information
end
end
66 changes: 66 additions & 0 deletions app/dashboards/search_event_dashboard.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require "administrate/base_dashboard"

class SearchEventDashboard < Administrate::BaseDashboard
# ATTRIBUTE_TYPES
# a hash that describes the type of each of the model's fields.
#
# Each different type represents an Administrate::Field object,
# which determines how the attribute is displayed
# on pages throughout the dashboard.
ATTRIBUTE_TYPES = {
id: Field::Number,
source: Field::String,
term: Field::BelongsTo,
created_at: Field::DateTime,
updated_at: Field::DateTime,
}.freeze

# COLLECTION_ATTRIBUTES
# an array of attributes that will be displayed on the model's index page.
#
# By default, it's limited to four items to reduce clutter on index pages.
# Feel free to add, remove, or rearrange items.
COLLECTION_ATTRIBUTES = %i[
id
source
term
created_at
].freeze

# SHOW_PAGE_ATTRIBUTES
# an array of attributes that will be displayed on the model's show page.
SHOW_PAGE_ATTRIBUTES = %i[
id
source
term
created_at
updated_at
].freeze

# FORM_ATTRIBUTES
# an array of attributes that will be displayed
# on the model's form (`new` and `edit`) pages.
FORM_ATTRIBUTES = %i[
source
term
].freeze

# COLLECTION_FILTERS
# a hash that defines filters that can be used while searching via the search
# field of the dashboard.
#
# For example to add an option to search for open resources by typing "open:"
# in the search field:
#
# COLLECTION_FILTERS = {
# open: ->(resources) { resources.where(open: true) }
# }.freeze
COLLECTION_FILTERS = {}.freeze

# Overwrite this method to customize how search events are displayed
# across all pages of the admin dashboard.
#
# def display_resource(search_event)
# "SearchEvent ##{search_event.id}"
# end
end
5 changes: 5 additions & 0 deletions app/dashboards/stat_dashboard.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require 'administrate/custom_dashboard'

class StatDashboard < Administrate::CustomDashboard
resource "Overview"
end
66 changes: 66 additions & 0 deletions app/dashboards/term_dashboard.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require "administrate/base_dashboard"

class TermDashboard < Administrate::BaseDashboard
# ATTRIBUTE_TYPES
# a hash that describes the type of each of the model's fields.
#
# Each different type represents an Administrate::Field object,
# which determines how the attribute is displayed
# on pages throughout the dashboard.
ATTRIBUTE_TYPES = {
id: Field::Number,
phrase: Field::String,
search_events: Field::HasMany,
created_at: Field::DateTime,
updated_at: Field::DateTime,
}.freeze

# COLLECTION_ATTRIBUTES
# an array of attributes that will be displayed on the model's index page.
#
# By default, it's limited to four items to reduce clutter on index pages.
# Feel free to add, remove, or rearrange items.
COLLECTION_ATTRIBUTES = %i[
id
phrase
search_events
created_at
].freeze

# SHOW_PAGE_ATTRIBUTES
# an array of attributes that will be displayed on the model's show page.
SHOW_PAGE_ATTRIBUTES = %i[
id
phrase
search_events
created_at
updated_at
].freeze

# FORM_ATTRIBUTES
# an array of attributes that will be displayed
# on the model's form (`new` and `edit`) pages.
FORM_ATTRIBUTES = %i[
phrase
search_events
].freeze

# COLLECTION_FILTERS
# a hash that defines filters that can be used while searching via the search
# field of the dashboard.
#
# For example to add an option to search for open resources by typing "open:"
# in the search field:
#
# COLLECTION_FILTERS = {
# open: ->(resources) { resources.where(open: true) }
# }.freeze
COLLECTION_FILTERS = {}.freeze

# Overwrite this method to customize how terms are displayed
# across all pages of the admin dashboard.
#
# def display_resource(term)
# "Term ##{term.id}"
# end
end
Loading

0 comments on commit 0963da4

Please sign in to comment.