mirror of
https://github.com/astuto/astuto.git
synced 2025-12-15 11:17:49 +01:00
Add admin panel and make it work for user resource
This commit is contained in:
4
Gemfile
4
Gemfile
@@ -28,8 +28,12 @@ gem 'jbuilder', '~> 2.7'
|
|||||||
# Reduces boot times through caching; required in config/boot.rb
|
# Reduces boot times through caching; required in config/boot.rb
|
||||||
gem 'bootsnap', '>= 1.4.2', require: false
|
gem 'bootsnap', '>= 1.4.2', require: false
|
||||||
|
|
||||||
|
# Authentication
|
||||||
gem 'devise', git: 'https://github.com/plataformatec/devise'
|
gem 'devise', git: 'https://github.com/plataformatec/devise'
|
||||||
|
|
||||||
|
# Administration panel
|
||||||
|
gem "administrate", git: "https://github.com/thoughtbot/administrate.git"
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
||||||
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
|
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
|
||||||
|
|||||||
50
Gemfile.lock
50
Gemfile.lock
@@ -9,6 +9,23 @@ GIT
|
|||||||
responders
|
responders
|
||||||
warden (~> 1.2.3)
|
warden (~> 1.2.3)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: https://github.com/thoughtbot/administrate.git
|
||||||
|
revision: fcf46ae4e6989ceb1b091791fec754a67da0f32c
|
||||||
|
specs:
|
||||||
|
administrate (0.11.0)
|
||||||
|
actionpack (>= 4.2)
|
||||||
|
actionview (>= 4.2)
|
||||||
|
activejob (>= 4.2)
|
||||||
|
activerecord (>= 4.2)
|
||||||
|
autoprefixer-rails (>= 6.0)
|
||||||
|
datetime_picker_rails (~> 0.0.7)
|
||||||
|
jquery-rails (>= 4.0)
|
||||||
|
kaminari (>= 1.0)
|
||||||
|
momentjs-rails (~> 2.8)
|
||||||
|
sassc-rails (~> 2.1)
|
||||||
|
selectize-rails (~> 0.6)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
@@ -69,6 +86,8 @@ GEM
|
|||||||
zeitwerk (~> 2.1, >= 2.1.8)
|
zeitwerk (~> 2.1, >= 2.1.8)
|
||||||
addressable (2.6.0)
|
addressable (2.6.0)
|
||||||
public_suffix (>= 2.0.2, < 4.0)
|
public_suffix (>= 2.0.2, < 4.0)
|
||||||
|
autoprefixer-rails (9.6.1)
|
||||||
|
execjs
|
||||||
bcrypt (3.1.13)
|
bcrypt (3.1.13)
|
||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
bootsnap (1.4.4)
|
bootsnap (1.4.4)
|
||||||
@@ -87,8 +106,11 @@ GEM
|
|||||||
rake (< 13.0)
|
rake (< 13.0)
|
||||||
concurrent-ruby (1.1.5)
|
concurrent-ruby (1.1.5)
|
||||||
crass (1.0.4)
|
crass (1.0.4)
|
||||||
|
datetime_picker_rails (0.0.7)
|
||||||
|
momentjs-rails (>= 2.8.1)
|
||||||
diff-lcs (1.3)
|
diff-lcs (1.3)
|
||||||
erubi (1.8.0)
|
erubi (1.8.0)
|
||||||
|
execjs (2.7.0)
|
||||||
factory_bot (5.0.2)
|
factory_bot (5.0.2)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
factory_bot_rails (5.0.2)
|
factory_bot_rails (5.0.2)
|
||||||
@@ -101,6 +123,22 @@ GEM
|
|||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
jbuilder (2.9.1)
|
jbuilder (2.9.1)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
|
jquery-rails (4.3.5)
|
||||||
|
rails-dom-testing (>= 1, < 3)
|
||||||
|
railties (>= 4.2.0)
|
||||||
|
thor (>= 0.14, < 2.0)
|
||||||
|
kaminari (1.1.1)
|
||||||
|
activesupport (>= 4.1.0)
|
||||||
|
kaminari-actionview (= 1.1.1)
|
||||||
|
kaminari-activerecord (= 1.1.1)
|
||||||
|
kaminari-core (= 1.1.1)
|
||||||
|
kaminari-actionview (1.1.1)
|
||||||
|
actionview
|
||||||
|
kaminari-core (= 1.1.1)
|
||||||
|
kaminari-activerecord (1.1.1)
|
||||||
|
activerecord
|
||||||
|
kaminari-core (= 1.1.1)
|
||||||
|
kaminari-core (1.1.1)
|
||||||
listen (3.1.5)
|
listen (3.1.5)
|
||||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||||
rb-inotify (~> 0.9, >= 0.9.7)
|
rb-inotify (~> 0.9, >= 0.9.7)
|
||||||
@@ -117,6 +155,8 @@ GEM
|
|||||||
mini_mime (1.0.2)
|
mini_mime (1.0.2)
|
||||||
mini_portile2 (2.4.0)
|
mini_portile2 (2.4.0)
|
||||||
minitest (5.11.3)
|
minitest (5.11.3)
|
||||||
|
momentjs-rails (2.20.1)
|
||||||
|
railties (>= 3.1)
|
||||||
msgpack (1.3.1)
|
msgpack (1.3.1)
|
||||||
nio4r (2.4.0)
|
nio4r (2.4.0)
|
||||||
nokogiri (1.10.4)
|
nokogiri (1.10.4)
|
||||||
@@ -194,6 +234,15 @@ GEM
|
|||||||
sprockets (>= 2.8, < 4.0)
|
sprockets (>= 2.8, < 4.0)
|
||||||
sprockets-rails (>= 2.0, < 4.0)
|
sprockets-rails (>= 2.0, < 4.0)
|
||||||
tilt (>= 1.1, < 3)
|
tilt (>= 1.1, < 3)
|
||||||
|
sassc (2.1.0)
|
||||||
|
ffi (~> 1.9)
|
||||||
|
sassc-rails (2.1.2)
|
||||||
|
railties (>= 4.0.0)
|
||||||
|
sassc (>= 2.0)
|
||||||
|
sprockets (> 3.0)
|
||||||
|
sprockets-rails
|
||||||
|
tilt
|
||||||
|
selectize-rails (0.12.6)
|
||||||
selenium-webdriver (3.142.3)
|
selenium-webdriver (3.142.3)
|
||||||
childprocess (>= 0.5, < 2.0)
|
childprocess (>= 0.5, < 2.0)
|
||||||
rubyzip (~> 1.2, >= 1.2.2)
|
rubyzip (~> 1.2, >= 1.2.2)
|
||||||
@@ -242,6 +291,7 @@ PLATFORMS
|
|||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
administrate!
|
||||||
bootsnap (>= 1.4.2)
|
bootsnap (>= 1.4.2)
|
||||||
byebug
|
byebug
|
||||||
capybara (>= 2.15)
|
capybara (>= 2.15)
|
||||||
|
|||||||
31
app/controllers/admin/application_controller.rb
Normal file
31
app/controllers/admin/application_controller.rb
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# All Administrate controllers inherit from this `Admin::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 :authenticate_admin
|
||||||
|
|
||||||
|
def authenticate_admin
|
||||||
|
unless user_signed_in?
|
||||||
|
flash[:alert] = "You must be logged in to access this page."
|
||||||
|
redirect_to new_user_session_path
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
unless current_user.moderator? || current_user.admin?
|
||||||
|
flash[:alert] = "You do not have the privilegies to access this page."
|
||||||
|
redirect_to root_path
|
||||||
|
return
|
||||||
|
end
|
||||||
|
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
|
||||||
88
app/controllers/admin/users_controller.rb
Normal file
88
app/controllers/admin/users_controller.rb
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
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
|
||||||
|
# foo = Foo.find(params[:id])
|
||||||
|
# foo.update(params[:foo])
|
||||||
|
# send_foo_updated_email
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# See https://administrate-prototype.herokuapp.com/customizing_controller_actions
|
||||||
|
# for more information
|
||||||
|
|
||||||
|
def authenticate_admin
|
||||||
|
super # apply the generic rules for authentication in the admin panel...
|
||||||
|
|
||||||
|
# ...plus this one
|
||||||
|
unless current_user.admin?
|
||||||
|
flash[:alert] = "You do not have the privilegies to access this page."
|
||||||
|
redirect_to root_path
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# overwrite default create
|
||||||
|
def create
|
||||||
|
user = User.new(user_params)
|
||||||
|
user.skip_confirmation! # automatically confirm user email
|
||||||
|
|
||||||
|
if user.save
|
||||||
|
flash[:notice] = translate_with_resource("create.success")
|
||||||
|
redirect_to admin_user_path(user)
|
||||||
|
else
|
||||||
|
render :new, locals: {
|
||||||
|
page: Administrate::Page::Form.new(dashboard, user),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# overwrite default update
|
||||||
|
def update
|
||||||
|
user = User.find(params[:user][:id])
|
||||||
|
|
||||||
|
if params[:user][:password].empty?
|
||||||
|
user.assign_attributes(user_params.except(:password))
|
||||||
|
else
|
||||||
|
user.assign_attributes(user_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
user.skip_reconfirmation! # automatically reconfirm user email
|
||||||
|
|
||||||
|
if user.save
|
||||||
|
flash[:notice] = translate_with_resource("update.success")
|
||||||
|
redirect_to admin_user_path(user)
|
||||||
|
else
|
||||||
|
render :new, locals: {
|
||||||
|
page: Administrate::Page::Form.new(dashboard, user),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def user_params
|
||||||
|
params.require(:user).permit(:full_name, :email, :role, :password)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
83
app/dashboards/user_dashboard.rb
Normal file
83
app/dashboards/user_dashboard.rb
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
require "administrate/base_dashboard"
|
||||||
|
|
||||||
|
class UserDashboard < 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: IdField,
|
||||||
|
email: Field::String,
|
||||||
|
password: Field::Password,
|
||||||
|
encrypted_password: Field::String,
|
||||||
|
reset_password_token: Field::String,
|
||||||
|
reset_password_sent_at: Field::DateTime,
|
||||||
|
remember_created_at: Field::DateTime,
|
||||||
|
confirmation_token: Field::String,
|
||||||
|
confirmed_at: Field::DateTime,
|
||||||
|
confirmation_sent_at: Field::DateTime,
|
||||||
|
unconfirmed_email: Field::String,
|
||||||
|
created_at: Field::DateTime,
|
||||||
|
updated_at: Field::DateTime,
|
||||||
|
role: RoleField,
|
||||||
|
full_name: Field::String,
|
||||||
|
}.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[
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
role
|
||||||
|
].freeze
|
||||||
|
|
||||||
|
# SHOW_PAGE_ATTRIBUTES
|
||||||
|
# an array of attributes that will be displayed on the model's show page.
|
||||||
|
SHOW_PAGE_ATTRIBUTES = %i[
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
role
|
||||||
|
password
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
confirmed_at
|
||||||
|
confirmation_sent_at
|
||||||
|
unconfirmed_email
|
||||||
|
].freeze
|
||||||
|
|
||||||
|
# FORM_ATTRIBUTES
|
||||||
|
# an array of attributes that will be displayed
|
||||||
|
# on the model's form (`new` and `edit`) pages.
|
||||||
|
FORM_ATTRIBUTES = %i[
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
role
|
||||||
|
password
|
||||||
|
].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) { where(open: true) }
|
||||||
|
# }.freeze
|
||||||
|
COLLECTION_FILTERS = {}.freeze
|
||||||
|
|
||||||
|
# Overwrite this method to customize how users are displayed
|
||||||
|
# across all pages of the admin dashboard.
|
||||||
|
#
|
||||||
|
# def display_resource(user)
|
||||||
|
# "User ##{user.id}"
|
||||||
|
# end
|
||||||
|
end
|
||||||
7
app/fields/id_field.rb
Normal file
7
app/fields/id_field.rb
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
require "administrate/field/base"
|
||||||
|
|
||||||
|
class IdField < Administrate::Field::Base
|
||||||
|
def to_s
|
||||||
|
data.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
13
app/fields/role_field.rb
Normal file
13
app/fields/role_field.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
require "administrate/field/base"
|
||||||
|
|
||||||
|
class RoleField < Administrate::Field::Base
|
||||||
|
def to_s
|
||||||
|
data.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_field_values(form_builder)
|
||||||
|
form_builder.object.class.public_send(attribute.to_s.pluralize).keys.map do |v|
|
||||||
|
[v.titleize, v]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
class User < ApplicationRecord
|
class User < ApplicationRecord
|
||||||
|
devise :database_authenticatable, :registerable,
|
||||||
|
:recoverable, :rememberable, :validatable,
|
||||||
|
:confirmable
|
||||||
|
|
||||||
enum role: [:user, :moderator, :admin]
|
enum role: [:user, :moderator, :admin]
|
||||||
after_initialize :set_default_role, if: :new_record?
|
after_initialize :set_default_role, if: :new_record?
|
||||||
|
|
||||||
@@ -12,8 +16,4 @@ class User < ApplicationRecord
|
|||||||
gravatar_id = Digest::MD5::hexdigest(email.downcase)
|
gravatar_id = Digest::MD5::hexdigest(email.downcase)
|
||||||
"https://secure.gravatar.com/avatar/#{gravatar_id}"
|
"https://secure.gravatar.com/avatar/#{gravatar_id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
devise :database_authenticatable, :registerable,
|
|
||||||
:recoverable, :rememberable, :validatable,
|
|
||||||
:confirmable
|
|
||||||
end
|
end
|
||||||
|
|||||||
19
app/views/admin/application/_navigation.html.erb
Normal file
19
app/views/admin/application/_navigation.html.erb
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<%#
|
||||||
|
# Navigation
|
||||||
|
|
||||||
|
This partial is used to display the navigation in Administrate.
|
||||||
|
By default, the navigation contains navigation links
|
||||||
|
for all resources in the admin dashboard,
|
||||||
|
as defined by the routes in the `admin/` namespace
|
||||||
|
%>
|
||||||
|
|
||||||
|
<nav class="navigation" role="navigation">
|
||||||
|
<%= link_to "⇦ Back to site", root_path, class: "navigation__link", "data-turbolinks": "false" %>
|
||||||
|
<% Administrate::Namespace.new(namespace).resources.each do |resource| %>
|
||||||
|
<%= link_to(
|
||||||
|
display_resource_name(resource),
|
||||||
|
[namespace, resource_index_route_key(resource)],
|
||||||
|
class: "navigation__link navigation__link--#{nav_link_state(resource)}"
|
||||||
|
) %>
|
||||||
|
<% end %>
|
||||||
|
</nav>
|
||||||
1
app/views/fields/id_field/_form.html.erb
Normal file
1
app/views/fields/id_field/_form.html.erb
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<%= f.text_field field.attribute, hidden: :true %>
|
||||||
1
app/views/fields/id_field/_index.html.erb
Normal file
1
app/views/fields/id_field/_index.html.erb
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<%= field.to_s %>
|
||||||
1
app/views/fields/id_field/_show.html.erb
Normal file
1
app/views/fields/id_field/_show.html.erb
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<%= field.to_s %>
|
||||||
6
app/views/fields/role_field/_form.html.erb
Normal file
6
app/views/fields/role_field/_form.html.erb
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<div class="field-unit__label">
|
||||||
|
<%= f.label field.attribute %>
|
||||||
|
</div>
|
||||||
|
<div class="field-unit__field">
|
||||||
|
<%= f.select field.attribute, field.select_field_values(f) %>
|
||||||
|
</div>
|
||||||
1
app/views/fields/role_field/_index.html.erb
Normal file
1
app/views/fields/role_field/_index.html.erb
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<%= field.to_s.titleize %>
|
||||||
1
app/views/fields/role_field/_show.html.erb
Normal file
1
app/views/fields/role_field/_show.html.erb
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<%= field.to_s.titleize %>
|
||||||
@@ -8,6 +8,11 @@
|
|||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
<ul class="navbar-nav ml-auto">
|
<ul class="navbar-nav ml-auto">
|
||||||
<% if user_signed_in? %>
|
<% if user_signed_in? %>
|
||||||
|
<% if current_user.moderator? || current_user.admin? %>
|
||||||
|
<li class="nav-item">
|
||||||
|
<%= link_to "Admin Panel", admin_root_path, class: "nav-link", 'data-turbolinks': 'false' %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<%= image_tag(current_user.gravatar_url, class: "gravatar", alt: current_user.full_name, size: 24) %>
|
<%= image_tag(current_user.gravatar_url, class: "gravatar", alt: current_user.full_name, size: 24) %>
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
root to: 'static_pages#home'
|
root to: 'static_pages#home'
|
||||||
|
|
||||||
|
namespace :admin do
|
||||||
|
root to: "users#index"
|
||||||
|
resources :users
|
||||||
|
end
|
||||||
|
|
||||||
devise_for :users
|
devise_for :users
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user