├── .documentation └── screenshot.png ├── .gitignore ├── .rubocop.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── administration_zero.gemspec └── lib ├── administration-zero.rb ├── administration_zero.rb ├── administration_zero └── version.rb └── generators └── admin ├── install ├── USAGE ├── install_generator.rb └── templates │ ├── controllers │ └── admin │ │ ├── base_controller.rb │ │ ├── home_controller.rb │ │ ├── password_resets_controller.rb │ │ ├── sessions_controller.rb │ │ └── users_controller.rb │ ├── erb │ ├── admin │ │ ├── base │ │ │ ├── _flash_messages.html.erb │ │ │ ├── _footer.html.erb │ │ │ ├── _javascript_tags.html.erb │ │ │ ├── _page_header.html.erb │ │ │ ├── _page_header_actions.html.erb │ │ │ ├── _page_header_breadcrumb.html.erb │ │ │ ├── _primary_navbar.html.erb │ │ │ ├── _secondary_navbar.html.erb │ │ │ └── _stylesheet_link_tags.html.erb │ │ ├── home │ │ │ └── index.html.erb │ │ ├── password_resets │ │ │ ├── edit.html.erb │ │ │ └── new.html.erb │ │ ├── sessions │ │ │ └── new.html.erb │ │ ├── user_mailer │ │ │ └── password_reset.html.erb │ │ └── users │ │ │ ├── _form.html.erb │ │ │ ├── edit.html.erb │ │ │ ├── index.html.erb │ │ │ ├── new.html.erb │ │ │ └── show.html.erb │ └── layouts │ │ └── admin │ │ ├── authentication.html.erb │ │ └── base.html.erb │ ├── helpers │ └── admin │ │ └── application_helper.rb │ ├── images │ └── admin │ │ ├── 000m.jpg │ │ └── logo.svg │ ├── mailers │ └── admin │ │ ├── application_mailer.rb │ │ └── user_mailer.rb │ ├── migrations │ └── create_admin_users.rb.tt │ ├── models │ ├── admin │ │ ├── application_record.rb │ │ ├── current.rb │ │ └── user.rb │ └── application_record.rb │ ├── seeds.rb │ └── test_unit │ ├── admin_users.yml │ ├── controllers │ └── admin │ │ ├── home_controller_test.rb │ │ ├── password_resets_controller_test.rb │ │ ├── sessions_controller_test.rb │ │ └── users_controller_test.rb │ └── test_helper.rb └── scaffold ├── USAGE ├── scaffold_generator.rb └── templates ├── controller.rb.tt ├── erb ├── _form.html.erb.tt ├── edit.html.erb.tt ├── index.html.erb.tt ├── new.html.erb.tt └── show.html.erb.tt └── functional_test.rb.tt /.documentation/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazaronixon/administration-zero/a69b1356f756341c97ff94c3b7b9481da4b04a42/.documentation/screenshot.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: https://raw.githubusercontent.com/rails/rails/master/.rubocop.yml 2 | 3 | Performance: 4 | Exclude: 5 | - 'test/**/*' 6 | 7 | Style/FrozenStringLiteralComment: 8 | Enabled: false 9 | 10 | Style/StringLiterals: 11 | Enabled: true 12 | EnforcedStyle: double_quotes 13 | Include: 14 | - 'app/**/*' 15 | - 'test/**/*' 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | 3 | ## [1.0.6] - 2025-01-17 4 | - Remove admin scaffold system test 5 | 6 | ## [1.0.5] - 2025-01-17 7 | - Added ransackable_attributes to Admin::User 8 | - Updated dependencies 9 | - Fixed scaffold with boolean columns 10 | - Updated authentication 11 | - Removed system tests -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | * Demonstrating empathy and kindness toward other people 14 | * Being respectful of differing opinions, viewpoints, and experiences 15 | * Giving and gracefully accepting constructive feedback 16 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 | * Focusing on what is best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | * The use of sexualized language or imagery, and sexual attention or 22 | advances of any kind 23 | * Trolling, insulting or derogatory comments, and personal or political attacks 24 | * Public or private harassment 25 | * Publishing others' private information, such as a physical or email 26 | address, without their explicit permission 27 | * Other conduct which could reasonably be considered inappropriate in a 28 | professional setting 29 | 30 | ## Enforcement Responsibilities 31 | 32 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 33 | 34 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 35 | 36 | ## Scope 37 | 38 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 39 | 40 | ## Enforcement 41 | 42 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at lazaronixon@hotmail.com. All complaints will be reviewed and investigated promptly and fairly. 43 | 44 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 45 | 46 | ## Enforcement Guidelines 47 | 48 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 49 | 50 | ### 1. Correction 51 | 52 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 53 | 54 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 55 | 56 | ### 2. Warning 57 | 58 | **Community Impact**: A violation through a single incident or series of actions. 59 | 60 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 61 | 62 | ### 3. Temporary Ban 63 | 64 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 65 | 66 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 67 | 68 | ### 4. Permanent Ban 69 | 70 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 71 | 72 | **Consequence**: A permanent ban from any sort of public interaction within the community. 73 | 74 | ## Attribution 75 | 76 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, 77 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 78 | 79 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 80 | 81 | [homepage]: https://www.contributor-covenant.org 82 | 83 | For answers to common questions about this code of conduct, see the FAQ at 84 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 85 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source "https://rubygems.org" 4 | 5 | # Specify your gem's dependencies in administration_zero.gemspec 6 | gemspec 7 | 8 | gem "rake", "~> 13.0" 9 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | administration-zero (1.0.6) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | rake (13.0.6) 10 | 11 | PLATFORMS 12 | x86_64-darwin-21 13 | x86_64-darwin-23 14 | 15 | DEPENDENCIES 16 | administration-zero! 17 | rake (~> 13.0) 18 | 19 | BUNDLED WITH 20 | 2.3.7 21 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Nixon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Administration Zero 2 | 3 | The purpose of administration zero is to generate a pre-built administration panel into a rails application. 4 | 5 | screenshot 6 | 7 | ## Features 8 | 9 | - [Beautiful interface](https://github.com/tabler/tabler) 10 | - [Paginated results](https://github.com/ddnexus/pagy) 11 | - [Sortable and filterable](https://github.com/activerecord-hackery/ransack) 12 | - [Exportable data](https://github.com/westonganger/spreadsheet_architect) 13 | - [Toastr for flash messages](https://getbootstrap.com/docs/5.1/components/toasts) 14 | - Show fields with errors in red 15 | - Determine time zone automatically 16 | - Easy authentication system 17 | - Admin scaffolds 18 | 19 | ## Installation 20 | 21 | ```ruby 22 | bundle add administration-zero 23 | ``` 24 | 25 | ## Usage 26 | 27 | ``` 28 | rails generate admin:install 29 | ``` 30 | 31 | Then run `bundle install` 32 | 33 | Then run `rails db:migrate db:seed` 34 | 35 | You can access the admin panel in `/admin`, using `email: "admin@example.com", password: "Password9957"` 36 | 37 | You'll need to create a model to be administrated, if you don't have one. for this example let's use the following: 38 | 39 | ``` 40 | rails generate model posts title:string body:text published:boolean 41 | ``` 42 | 43 | Now you're ready to generate your admin scaffolds. 44 | 45 | ``` 46 | rails generate admin:scaffold posts title:string body:text published:boolean 47 | ``` 48 | 49 | ## Development 50 | 51 | To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). 52 | 53 | ## Contributing 54 | 55 | Bug reports and pull requests are welcome on GitHub at https://github.com/lazaronixon/administration-zero. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/lazaronixon/administration-zero/blob/master/CODE_OF_CONDUCT.md). 56 | 57 | ## License 58 | 59 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 60 | 61 | ## Code of Conduct 62 | 63 | Everyone interacting in the AdministrationZero project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/lazaronixon/administration-zero/blob/master/CODE_OF_CONDUCT.md). 64 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | task default: %i[] 5 | -------------------------------------------------------------------------------- /administration_zero.gemspec: -------------------------------------------------------------------------------- 1 | require_relative 'lib/administration_zero/version' 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = "administration-zero" 5 | spec.version = AdministrationZero::VERSION 6 | spec.authors = ["Nixon"] 7 | spec.email = ["lazaronixon@hotmail.com"] 8 | 9 | spec.summary = "An administration system generator for Rails applications" 10 | spec.homepage = "https://github.com/lazaronixon/authentication-zero" 11 | spec.license = "MIT" 12 | 13 | spec.metadata["homepage_uri"] = spec.homepage 14 | spec.metadata["source_code_uri"] = "https://github.com/lazaronixon/administration-zero" 15 | spec.metadata["changelog_uri"] = "https://github.com/lazaronixon/administration-zero/blob/main/CHANGELOG.md" 16 | 17 | # Specify which files should be added to the gem when it is released. 18 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 19 | spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do 20 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/administration-zero.rb: -------------------------------------------------------------------------------- 1 | require "administration_zero" 2 | -------------------------------------------------------------------------------- /lib/administration_zero.rb: -------------------------------------------------------------------------------- 1 | require_relative "administration_zero/version" 2 | 3 | module AdministrationZero 4 | end 5 | -------------------------------------------------------------------------------- /lib/administration_zero/version.rb: -------------------------------------------------------------------------------- 1 | module AdministrationZero 2 | VERSION = "1.0.6" 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/admin/install/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Explain the generator 3 | 4 | Example: 5 | bin/rails generate admin:install 6 | -------------------------------------------------------------------------------- /lib/generators/admin/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/active_record" 2 | 3 | module Admin 4 | module Generators 5 | class InstallGenerator < Rails::Generators::Base 6 | include ActiveRecord::Generators::Migration 7 | 8 | source_root File.expand_path("templates", __dir__) 9 | 10 | def add_field_error_proc 11 | field_error_proc_code = <<~RUBY 12 | # Provides an HTML generator for displaying errors that come from Active Model 13 | config.action_view.field_error_proc = Proc.new do |html_tag, instance| 14 | raw Nokogiri::HTML.fragment(html_tag).child.add_class("is-invalid") 15 | end 16 | RUBY 17 | 18 | environment field_error_proc_code 19 | end 20 | 21 | def add_gems 22 | uncomment_lines "Gemfile", /"bcrypt"/ 23 | gem "pagy", comment: "Use Pagy to add paginated results [https://github.com/ddnexus/pagy]" 24 | gem "ransack", comment: "Use Ransack to enable the creation of search forms for your application [https://github.com/activerecord-hackery/ransack]" 25 | gem "spreadsheet_architect", comment: "Spreadsheet Architect is a library that allows you to create XLSX, ODS, or CSV spreadsheets super easily [https://github.com/westonganger/spreadsheet_architect]" 26 | end 27 | 28 | def create_db_files 29 | copy_file "seeds.rb", "db/seeds.rb", force: true 30 | migration_template "migrations/create_admin_users.rb", "#{db_migrate_path}/create_admin_users.rb" 31 | end 32 | 33 | def create_models 34 | directory "models/admin", "app/models/admin" 35 | copy_file "models/application_record.rb", "app/models/application_record.rb", force: true 36 | end 37 | 38 | def create_fixture_file 39 | copy_file "test_unit/admin_users.yml", "test/fixtures/admin/users.yml" 40 | end 41 | 42 | def create_controllers 43 | directory "controllers", "app/controllers" 44 | end 45 | 46 | def create_views 47 | directory "erb", "app/views" 48 | end 49 | 50 | def create_helpers 51 | directory "helpers", "app/helpers" 52 | end 53 | 54 | def create_mailers 55 | directory "mailers", "app/mailers" 56 | end 57 | 58 | def create_images 59 | directory "images", "app/assets/images" 60 | end 61 | 62 | def add_routes 63 | route "resource :password_reset", namespace: :admin 64 | route "resources :users", namespace: :admin 65 | route "delete 'sign_out', to: 'sessions#destroy'", namespace: :admin 66 | route "post 'sign_in', to: 'sessions#create'", namespace: :admin 67 | route "get 'sign_in', to: 'sessions#new'", namespace: :admin 68 | route "get '/', to: 'home#index'", namespace: :admin 69 | end 70 | 71 | def create_test_files 72 | directory "test_unit/controllers", "test/controllers" 73 | copy_file "test_unit/test_helper.rb", "test/test_helper.rb", force: true 74 | end 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/controllers/admin/base_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::BaseController < ActionController::Base 2 | include Pagy::Backend 3 | 4 | around_action :set_time_zone 5 | before_action :authenticate 6 | 7 | private 8 | def set_time_zone 9 | Time.use_zone(cookies[:time_zone]) { yield } 10 | end 11 | 12 | def authenticate 13 | if user = Admin::User.find_by_id(session[:admin_user_id]) 14 | Admin::Current.user = user 15 | else 16 | redirect_to admin_sign_in_path 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/controllers/admin/home_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::HomeController < Admin::BaseController 2 | def index 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/controllers/admin/password_resets_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::PasswordResetsController < Admin::BaseController 2 | skip_before_action :authenticate 3 | 4 | before_action :set_user, only: %i[ edit update ] 5 | 6 | layout "admin/authentication" 7 | 8 | def new 9 | end 10 | 11 | def edit 12 | end 13 | 14 | def create 15 | if @user = Admin::User.find_by(email: params[:email]) 16 | send_password_reset_email 17 | redirect_to admin_sign_in_path, notice: "Check your email for reset instructions" 18 | else 19 | redirect_to new_admin_password_reset_path, alert: "Sorry, we didn't recognize that email address" 20 | end 21 | end 22 | 23 | def update 24 | if @user.update(user_params) 25 | redirect_to admin_sign_in_path, notice: "Your password was reset successfully. Please sign in" 26 | else 27 | redirect_to edit_admin_password_reset_path(token: params[:token]), alert: @user.errors.first.full_message 28 | end 29 | end 30 | 31 | private 32 | def set_user 33 | @user = Admin::User.find_by_token_for!(:password_reset, params[:token]) 34 | rescue StandardError 35 | redirect_to new_admin_password_reset_path, alert: "That password reset link is invalid" 36 | end 37 | 38 | def user_params 39 | params.permit(:password, :password_confirmation) 40 | end 41 | 42 | def send_password_reset_email 43 | Admin::UserMailer.with(user: @user).password_reset.deliver_later 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/controllers/admin/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::SessionsController < Admin::BaseController 2 | skip_before_action :authenticate, only: %i[ new create ] 3 | 4 | layout "admin/authentication" 5 | 6 | def new 7 | @user = Admin::User.new 8 | end 9 | 10 | def create 11 | if user = Admin::User.authenticate_by(email: params[:email], password: params[:password]) 12 | session[:admin_user_id] = user.id; redirect_to(admin_path) 13 | else 14 | redirect_to admin_sign_in_path(email_hint: params[:email]), alert: "That email or password is incorrect" 15 | end 16 | end 17 | 18 | def destroy 19 | session[:admin_user_id] = nil; redirect_to admin_sign_in_path 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/controllers/admin/users_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::UsersController < Admin::BaseController 2 | before_action :set_user, only: %i[ show edit update destroy ] 3 | 4 | def index 5 | @search = Admin::User.all.ransack(params[:q]) 6 | 7 | respond_to do |format| 8 | format.html { @pagy, @users = pagy(@search.result) } 9 | format.csv { render csv: @search.result } 10 | end 11 | end 12 | 13 | def show 14 | end 15 | 16 | def new 17 | @user = Admin::User.new 18 | end 19 | 20 | def edit 21 | end 22 | 23 | def create 24 | @user = Admin::User.new(user_params) 25 | 26 | if @user.save 27 | redirect_to @user, notice: "User was successfully created." 28 | else 29 | render :new, status: :unprocessable_entity 30 | end 31 | end 32 | 33 | def update 34 | if @user.update(user_params) 35 | redirect_to @user, notice: "User was successfully updated." 36 | else 37 | render :edit, status: :unprocessable_entity 38 | end 39 | end 40 | 41 | def destroy 42 | @user.destroy 43 | redirect_to admin_users_url, notice: "User was successfully destroyed." 44 | end 45 | 46 | private 47 | def set_user 48 | @user = Admin::User.find(params[:id]) 49 | end 50 | 51 | def user_params 52 | params.require(:admin_user).permit(:email, :password, :password_confirmation) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/base/_flash_messages.html.erb: -------------------------------------------------------------------------------- 1 | <% message = notice || alert %> 2 | 3 | <% if message.present? %> 4 |
5 |
6 |
7 | <%= message %> 8 |
9 |
10 |
11 | <% end %> 12 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/base/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/base/_javascript_tags.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | 20 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/base/_page_header.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/base/_page_header_actions.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= yield %> 4 |
5 |
6 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/base/_page_header_breadcrumb.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :title, title %> 2 | 3 |
4 |
5 | 8 |
9 |

10 | <%= title %> 11 |

12 |
13 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/base/_primary_navbar.html.erb: -------------------------------------------------------------------------------- 1 | 68 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/base/_secondary_navbar.html.erb: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/base/_stylesheet_link_tags.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/home/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :title, "Dashboard" %> 2 | 3 |
4 |
5 |

Admin::Home#index

6 |

Find me in app/views/admin/home/index.html.erb

7 |
8 |
9 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/password_resets/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(url: admin_password_reset_path, method: :patch, class: "card card-md") do |form| %> 2 |
3 |

Reset password

4 | 5 | <%= hidden_field_tag :token, params[:token] %> 6 | 7 |
8 | <%= form.label :password, "New password", class: "form-label" %> 9 | <%= form.password_field :password, required: true, autofocus: true, autocomplete: "new-password", class: "form-control" %> 10 |
11 | 12 |
13 | <%= form.label :password_confirmation, "Confirm new password", class: "form-label" %> 14 | <%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", class: "form-control" %> 15 |
16 | 17 | 20 |
21 | <% end %> 22 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/password_resets/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(url: admin_password_reset_path, class: "card card-md") do |form| %> 2 |
3 |

Forgot password

4 |

Enter your email address and your password will be reset and emailed to you.

5 |
6 | <%= form.label :email, class: "form-label" %> 7 | <%= form.email_field :email, placeholder: "Enter email", autofocus: true, required: true, class: "form-control" %> 8 |
9 | 12 |
13 | <% end %> 14 | 15 |
16 | Forget it, <%= link_to "send me back", admin_sign_in_path %> to the sign in screen. 17 |
18 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(url: admin_sign_in_path, class: "card card-md") do |form| %> 2 |
3 |

Login to your account

4 |
5 | <%= form.label :email, class: "form-label" %> 6 | <%= form.email_field :email, value: params[:email_hint], placeholder: "Enter email", autofocus: true, required: true, autocomplete: "email", class:"form-control" %> 7 |
8 |
9 |
10 | <%= form.label :password, class: "form-label" %> 11 | 12 | <%= link_to "I forgot password", new_admin_password_reset_path %> 13 | 14 |
15 | <%= form.password_field :password, placeholder: "Password", required: true, autocomplete: "current-password", class:"form-control" %> 16 |
17 | 20 |
21 | <% end %> 22 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/user_mailer/password_reset.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Hey there,

8 |

Can't remember your password for <%= @user.email %>? That's OK, it happens. Just hit the link below to set a new one.

9 |

<%= link_to "Reset my password", edit_admin_password_reset_url(token: @signed_id) %>

10 |

If you did not request a password reset you can safely ignore this email, it expires in 20 minutes. Only someone with access to this email account can reset your password.

11 |
12 |

Have questions or need help? Just reply to this email and our support team will help you sort it out.

13 | 14 | 15 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/users/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_with(model: user) do |form| %> 2 |
3 | <%= form.label :email, class: "form-label col-md-3 col-form-label" %> 4 |
5 | <%= form.email_field :email, autocomplete: "email", class: "form-control" %> 6 | <%= tag.div user.errors[:email].first, class: "invalid-feedback" %> 7 |
8 |
9 | 10 |
11 | <%= form.label :password, class: "form-label col-md-3 col-form-label" %> 12 |
13 | <%= form.password_field :password, required: true, autocomplete: "new-password", class: "form-control" %> 14 | <%= tag.div user.errors[:password].first, class: "invalid-feedback" %> 15 |
16 |
17 | 18 |
19 | <%= form.label :password_confirmation, class: "form-label col-md-3 col-form-label" %> 20 |
21 | <%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", class: "form-control" %> 22 | <%= tag.div user.errors[:password_confirmation].first, class: "invalid-feedback" %> 23 |
24 |
25 | 26 | 30 | <% end %> 31 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/users/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "page_header" do %> 2 | <%= render "page_header_breadcrumb", title: "Editing user" do %> 3 | 4 | 5 | 6 | <% end %> 7 | <% end %> 8 | 9 |
10 |
11 |
12 |
13 | <%= render "form", user: @user %> 14 |
15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/users/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "page_header" do %> 2 | <%= render "page_header_breadcrumb", title: "Users" do %> 3 | 4 | <% end %> 5 | 6 | <%= render "page_header_actions" do %> 7 | <%= link_to "Filters", "#offcanvas_filters", "data-bs-toggle": "offcanvas", class: "btn btn-white" %> 8 | <%= link_to "New user", new_admin_user_path, class: "btn btn-primary" %> 9 | <% end %> 10 | <% end %> 11 | 12 |
13 |
14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | <% @users.each do |user| %> 27 | 28 | 29 | 30 | 31 | 36 | 37 | <% end %> 38 | 39 |
<%= sort_link @search, :id %><%= sort_link @search, :email %><%= sort_link @search, :created_at %>
<%= link_to user.id, user %><%= user.email %><%= l(user.created_at, format: :long) %> 32 | <%= link_to "View", user, class: "btn btn-white btn-sm" %> 33 | <%= link_to "Edit", edit_admin_user_path(user), class: "btn btn-white btn-sm" %> 34 | <%= link_to "Delete", user, class: "btn btn-white btn-sm", data: { method: :delete, confirm: "Are you sure?" } %> 35 |
40 |
41 | 50 |
51 |
52 |
53 | 54 |
55 |
56 |

Filters

57 | 58 |
59 | <%= search_form_for @search, class: "offcanvas-body" do |f| %> 60 | <%= f.label :email_cont, class: "form-label" %> 61 | <%= f.text_field :email_cont, class: "form-control mb-3" %> 62 | 63 | <%= f.label :created_at, class: "form-label" %> 64 |
65 | <%= f.date_field :created_at_gteq, class: "form-control" %> 66 | <%= f.date_field :created_at_lteq, class: "form-control" %> 67 |
68 | 69 | <%= f.submit "Filter", class: "btn btn-primary" %> 70 | <%= link_to "Clear Filter", admin_users_path, class: "btn btn-white" %> 71 | <% end %> 72 |
73 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/users/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "page_header" do %> 2 | <%= render "page_header_breadcrumb", title: "New user" do %> 3 | 4 | 5 | <% end %> 6 | <% end %> 7 | 8 |
9 |
10 |
11 |
12 | <%= render "form", user: @user %> 13 |
14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/admin/users/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= render "page_header" do %> 2 | <%= render "page_header_breadcrumb", title: @user.email do %> 3 | 4 | 5 | <% end %> 6 | 7 | <%= render "page_header_actions" do %> 8 | <%= link_to "Edit user", edit_admin_user_path(@user), class: "btn btn-primary" %> 9 | <%= link_to "Delete user", @user, class: "btn btn-white", data: { method: :delete, confirm: "Are you sure?" } %> 10 | <% end %> 11 | <% end %> 12 | 13 |
14 |
15 |
16 |
17 |

User details

18 |
19 |
20 |
21 |
Email
22 |
<%= @user.email %>
23 | 24 |
Created at
25 |
<%= l(@user.created_at, format: :long) %>
26 | 27 |
Updated at
28 |
<%= l(@user.updated_at, format: :long) %>
29 |
30 |
31 |
32 |
33 |
34 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/layouts/admin/authentication.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Authentication Zero 8 | 9 | <%= csrf_meta_tags %> 10 | <%= csp_meta_tag %> 11 | 12 | <%= render "stylesheet_link_tags" %> 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 | <%= link_to admin_sign_in_path, class: "navbar-brand navbar-brand-autodark" do %> 21 | <%= image_tag "admin/logo.svg", height: 36 %> 22 | <% end %> 23 |
24 | <%= yield %> 25 |
26 |
27 |
28 | 29 | <%= render "flash_messages" %> 30 | <%= render "javascript_tags" %> 31 | 32 | 33 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/erb/layouts/admin/base.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= title %> 8 | 9 | <%= csrf_meta_tags %> 10 | <%= csp_meta_tag %> 11 | 12 | <%= render "stylesheet_link_tags" %> 13 | 14 | 15 | 16 | <%= render "primary_navbar" %> 17 | <%= render "secondary_navbar" %> 18 | 19 |
20 |
21 | <%= yield %> 22 | <%= render "footer" %> 23 |
24 |
25 | 26 | <%= render "flash_messages" %> 27 | <%= render "javascript_tags" %> 28 | 29 | 30 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/helpers/admin/application_helper.rb: -------------------------------------------------------------------------------- 1 | require "pagy/extras/bootstrap" 2 | 3 | module Admin::ApplicationHelper 4 | include Pagy::Frontend 5 | 6 | def title 7 | content_for(:title) || Rails.application.class.to_s.split("::").first 8 | end 9 | 10 | def active_nav_item(*names) 11 | names.include?(controller_path) ? "active" : "" 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/images/admin/000m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lazaronixon/administration-zero/a69b1356f756341c97ff94c3b7b9481da4b04a42/lib/generators/admin/install/templates/images/admin/000m.jpg -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/images/admin/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/mailers/admin/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class Admin::ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/mailers/admin/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class Admin::UserMailer < Admin::ApplicationMailer 2 | def password_reset 3 | @user = params[:user] 4 | @signed_id = @user.generate_token_for(:password_reset) 5 | 6 | mail to: @user.email, subject: "Reset your password" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/migrations/create_admin_users.rb.tt: -------------------------------------------------------------------------------- 1 | class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] 2 | def change 3 | create_table :admin_users do |t| 4 | t.string :email, null: false 5 | t.string :password_digest, null: false 6 | 7 | t.timestamps 8 | end 9 | 10 | add_index :admin_users, :email, unique: true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/models/admin/application_record.rb: -------------------------------------------------------------------------------- 1 | class Admin::ApplicationRecord < ActiveRecord::Base 2 | include SpreadsheetArchitect; self.abstract_class = true; self.table_name_prefix = "admin_" 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/models/admin/current.rb: -------------------------------------------------------------------------------- 1 | class Admin::Current < ActiveSupport::CurrentAttributes 2 | attribute :user 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/models/admin/user.rb: -------------------------------------------------------------------------------- 1 | class Admin::User < Admin::ApplicationRecord 2 | has_secure_password 3 | 4 | generates_token_for :password_reset, expires_in: 20.minutes do 5 | password_salt.last(10) 6 | end 7 | 8 | validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP } 9 | validates :password, allow_nil: true, length: { minimum: 12 }, format: { with: /(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/ } 10 | 11 | normalizes :email, with: -> { _1.strip.downcase } 12 | 13 | def self.ransackable_attributes(auth_object = nil) 14 | %w[email created_at] 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | include SpreadsheetArchitect; primary_abstract_class 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: "Star Wars" }, { name: "Lord of the Rings" }]) 7 | # Character.create(name: "Luke", movie: movies.first) 8 | 9 | Admin::User.create(email: "admin@example.com", password: "Password9957", password_confirmation: "Password9957") 10 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/test_unit/admin_users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | lazaro_nixon: 4 | email: lazaronixon@hotmail.com 5 | password_digest: <%= BCrypt::Password.create("Secret1*3*5*") %> 6 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/test_unit/controllers/admin/home_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class Admin::HomeControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @user = sign_in_admin_as(admin_users(:lazaro_nixon)) 6 | end 7 | 8 | test "should get index" do 9 | get admin_url 10 | assert_response :success 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/test_unit/controllers/admin/password_resets_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class Admin::PasswordResetsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @user = admin_users(:lazaro_nixon) 6 | @sid = @user.generate_token_for(:password_reset) 7 | end 8 | 9 | test "should get new" do 10 | get new_admin_password_reset_url 11 | assert_response :success 12 | end 13 | 14 | test "should get edit" do 15 | get edit_admin_password_reset_url(token: @sid) 16 | assert_response :success 17 | end 18 | 19 | test "should send a password reset email" do 20 | assert_enqueued_email_with Admin::UserMailer, :password_reset, params: { user: @user } do 21 | post admin_password_reset_url, params: { email: @user.email } 22 | end 23 | 24 | assert_redirected_to admin_sign_in_url 25 | end 26 | 27 | test "should not send a password reset email to a nonexistent email" do 28 | assert_no_enqueued_emails do 29 | post admin_password_reset_url, params: { email: "invalid_email@hey.com" } 30 | end 31 | 32 | assert_redirected_to new_admin_password_reset_url 33 | assert_equal "Sorry, we didn't recognize that email address", flash[:alert] 34 | end 35 | 36 | test "should update password" do 37 | patch admin_password_reset_url, params: { token: @sid, password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" } 38 | assert_redirected_to admin_sign_in_url 39 | end 40 | 41 | test "should not update password with expired token" do 42 | travel 30.minutes 43 | patch admin_password_reset_url, params: { token: @sid, password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" } 44 | 45 | assert_redirected_to new_admin_password_reset_url 46 | assert_equal "That password reset link is invalid", flash[:alert] 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/test_unit/controllers/admin/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class Admin::SessionsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @user = admin_users(:lazaro_nixon) 6 | end 7 | 8 | test "should get new" do 9 | get admin_sign_in_url 10 | assert_response :success 11 | end 12 | 13 | test "should sign in" do 14 | post admin_sign_in_url, params: { email: @user.email, password: "Secret1*3*5*" } 15 | assert_redirected_to admin_url 16 | 17 | get admin_url 18 | assert_response :success 19 | end 20 | 21 | test "should not sign in with wrong credentials" do 22 | post admin_sign_in_url, params: { email: @user.email, password: "SecretWrong1*3" } 23 | assert_redirected_to admin_sign_in_url(email_hint: @user.email) 24 | assert_equal "That email or password is incorrect", flash[:alert] 25 | 26 | get admin_url 27 | assert_redirected_to admin_sign_in_url 28 | end 29 | 30 | test "should sign out" do 31 | sign_in_admin_as @user 32 | 33 | delete admin_sign_out_url 34 | assert_redirected_to admin_sign_in_url 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/test_unit/controllers/admin/users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class Admin::UsersControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @user = sign_in_admin_as(admin_users(:lazaro_nixon)) 6 | end 7 | 8 | test "should get index" do 9 | get admin_users_url 10 | assert_response :success 11 | end 12 | 13 | test "should get new" do 14 | get new_admin_user_url 15 | assert_response :success 16 | end 17 | 18 | test "should create admin_user" do 19 | assert_difference("Admin::User.count") do 20 | post admin_users_url, params: { admin_user: { email: "lazaronixon@hey.com", password: "Secret1*3*5*", password_confirmation: "Secret1*3*5*" } } 21 | end 22 | 23 | assert_redirected_to admin_user_url(Admin::User.last) 24 | end 25 | 26 | test "should show admin_user" do 27 | get admin_user_url(@user) 28 | assert_response :success 29 | end 30 | 31 | test "should get edit" do 32 | get edit_admin_user_url(@user) 33 | assert_response :success 34 | end 35 | 36 | test "should update admin_user" do 37 | patch admin_user_url(@user), params: { admin_user: { email: @user.email, password: "NewSecret1*3*5*", password_confirmation: "NewSecret1*3*5*" } } 38 | assert_redirected_to admin_user_url(@user) 39 | end 40 | 41 | test "should destroy admin_user" do 42 | assert_difference("Admin::User.count", -1) do 43 | delete admin_user_url(@user) 44 | end 45 | 46 | assert_redirected_to admin_users_url 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/generators/admin/install/templates/test_unit/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV["RAILS_ENV"] ||= "test" 2 | require_relative "../config/environment" 3 | require "rails/test_help" 4 | 5 | class ActiveSupport::TestCase 6 | # Run tests in parallel with specified workers 7 | parallelize(workers: :number_of_processors) 8 | 9 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | def sign_in_admin_as(user) 14 | post(admin_sign_in_url, params: { email: user.email, password: "Secret1*3*5*" }); user 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/generators/admin/scaffold/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Explain the generator 3 | 4 | Example: 5 | bin/rails generate admin:scaffold model field:type field:type 6 | -------------------------------------------------------------------------------- /lib/generators/admin/scaffold/scaffold_generator.rb: -------------------------------------------------------------------------------- 1 | require "rails/generators/resource_helpers" 2 | 3 | module Admin 4 | module Generators 5 | class ScaffoldGenerator < Rails::Generators::NamedBase 6 | include Rails::Generators::ResourceHelpers 7 | 8 | class_option :orm, banner: "NAME", type: :string, required: true, desc: "ORM to generate the controller for" 9 | 10 | argument :attributes, type: :array, default: [], banner: "field:type field:type" 11 | 12 | source_root File.expand_path("templates", __dir__) 13 | 14 | def copy_files 15 | directory "erb", "app/views/admin/#{file_name.pluralize}" 16 | 17 | template "controller.rb", "app/controllers/admin/#{controller_file_name}_controller.rb" 18 | 19 | template "functional_test.rb", "test/controllers/admin/#{controller_file_name}_controller_test.rb" 20 | template "system_test.rb", "test/system/admin/#{file_name.pluralize}_test.rb" 21 | end 22 | 23 | def create_routes 24 | route "resources :#{file_name.pluralize}", namespace: :admin 25 | end 26 | 27 | private 28 | def controller_class_path 29 | [ "admin" ] 30 | end 31 | 32 | def singular_route_name 33 | "#{controller_class_path.join('_')}_#{singular_table_name}" 34 | end 35 | 36 | def plural_route_name 37 | "#{controller_class_path.join('_')}_#{plural_table_name}" 38 | end 39 | 40 | def model_resource_name(base_name = singular_table_name, prefix: "") 41 | "[#{controller_class_path.map { |name| ":" + name }.join(", ")}, #{prefix}#{base_name}]" 42 | end 43 | 44 | def permitted_params 45 | attachments, others = attributes_names.partition { |name| attachments?(name) } 46 | params = others.map { |name| ":#{name}" } 47 | params += attachments.map { |name| "#{name}: []" } 48 | params.join(", ") 49 | end 50 | 51 | def attachments?(name) 52 | attribute = attributes.find { |attr| attr.name == name } 53 | attribute&.attachments? 54 | end 55 | 56 | def fixture_name 57 | table_name 58 | end 59 | 60 | def attributes_string 61 | attributes_hash.map { |k, v| "#{k}: #{v}" }.join(", ") 62 | end 63 | 64 | def attributes_hash 65 | return {} if attributes_names.empty? 66 | 67 | attributes_names.filter_map do |name| 68 | if %w(password password_confirmation).include?(name) && attributes.any?(&:password_digest?) 69 | ["#{name}", '"secret"'] 70 | elsif !virtual?(name) 71 | ["#{name}", "@#{singular_table_name}.#{name}"] 72 | end 73 | end.sort.to_h 74 | end 75 | 76 | def boolean?(name) 77 | attribute = attributes.find { |attr| attr.name == name } 78 | attribute&.type == :boolean 79 | end 80 | 81 | def virtual?(name) 82 | attribute = attributes.find { |attr| attr.name == name } 83 | attribute&.virtual? 84 | end 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /lib/generators/admin/scaffold/templates/controller.rb.tt: -------------------------------------------------------------------------------- 1 | class <%= controller_class_name %>Controller < Admin::BaseController 2 | before_action :set_<%= singular_table_name %>, only: %i[ show edit update destroy ] 3 | 4 | def index 5 | @search = <%= orm_class.all(class_name) %>.ransack(params[:q]) 6 | 7 | respond_to do |format| 8 | format.html { @pagy, @<%= plural_table_name %> = pagy(@search.result) } 9 | format.csv { render csv: @search.result } 10 | end 11 | end 12 | 13 | def show 14 | end 15 | 16 | def new 17 | @<%= singular_table_name %> = <%= orm_class.build(class_name) %> 18 | end 19 | 20 | def edit 21 | end 22 | 23 | def create 24 | @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> 25 | 26 | if @<%= orm_instance.save %> 27 | redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully created.") %> 28 | else 29 | render :new, status: :unprocessable_entity 30 | end 31 | end 32 | 33 | def update 34 | if @<%= orm_instance.update("#{singular_table_name}_params") %> 35 | redirect_to <%= redirect_resource_name %>, notice: <%= %("#{human_name} was successfully updated.") %> 36 | else 37 | render :edit, status: :unprocessable_entity 38 | end 39 | end 40 | 41 | def destroy 42 | @<%= orm_instance.destroy %> 43 | redirect_to <%= index_helper %>_url, notice: <%= %("#{human_name} was successfully destroyed.") %> 44 | end 45 | 46 | private 47 | def set_<%= singular_table_name %> 48 | @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> 49 | end 50 | 51 | def <%= "#{singular_table_name}_params" %> 52 | <%- if attributes_names.empty? -%> 53 | params.fetch(:<%= singular_table_name %>, {}) 54 | <%- else -%> 55 | params.require(:<%= singular_table_name %>).permit(<%= permitted_params %>) 56 | <%- end -%> 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/generators/admin/scaffold/templates/erb/_form.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= form_with(model: <%= model_resource_name %>) do |form| %> 2 | <% attributes.each do |attribute| -%> 3 |
4 | <% if attribute.password_digest? -%> 5 | <%%= form.label :password, class: "form-label col-md-3 col-form-label" %> 6 |
7 | <%%= form.password_field :password, required: true, autocomplete: "new-password", class: "form-control" %> 8 | <%%= tag.div <%= singular_name %>.errors[:password].first, class: "invalid-feedback" %> 9 |
10 |
11 | 12 |
13 | <%%= form.password_field :password_confirmation, required: true, autocomplete: "new-password", class: "form-control" %> 14 | <%%= tag.div <%= singular_name %>.errors[:password_confirmation].first, class: "invalid-feedback" %> 15 | <% elsif attribute.attachments? -%> 16 | <%%= form.label :<%= attribute.column_name %>, class: "form-label col-md-3 col-form-label" %> 17 |
18 | <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, multiple: true, class: "form-control" %> 19 | <%%= tag.div <%= singular_name %>.errors[:<%= attribute.column_name %>].first, class: "invalid-feedback" %> 20 |
21 | <% elsif attribute.field_type == :checkbox -%> 22 | <%%= form.label :<%= attribute.column_name %>, class: "form-check-label col-md-3 col-form-label" %> 23 |
24 | <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "form-check-input" %> 25 | <%%= tag.div <%= singular_name %>.errors[:<%= attribute.column_name %>].first, class: "invalid-feedback" %> 26 |
27 | <% else -%> 28 | <%%= form.label :<%= attribute.column_name %>, class: "form-label col-md-3 col-form-label" %> 29 |
30 | <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "form-control" %> 31 | <%%= tag.div <%= singular_name %>.errors[:<%= attribute.column_name %>].first, class: "invalid-feedback" %> 32 |
33 | <% end -%> 34 |
35 | 36 | <% end -%> 37 | 41 | <%% end %> 42 | -------------------------------------------------------------------------------- /lib/generators/admin/scaffold/templates/erb/edit.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= render "page_header" do %> 2 | <%%= render "page_header_breadcrumb", title: "Editing <%= human_name.downcase %>" do %> 3 | 4 | 5 | 6 | <%% end %> 7 | <%% end %> 8 | 9 |
10 |
11 |
12 |
13 | <%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %> 14 |
15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /lib/generators/admin/scaffold/templates/erb/index.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= render "page_header" do %> 2 | <%%= render "page_header_breadcrumb", title: "<%= human_name.pluralize %>" do %> 3 | 4 | <%% end %> 5 | 6 | <%%= render "page_header_actions" do %> 7 | <%%= link_to "Filters", "#offcanvas_filters", "data-bs-toggle": "offcanvas", class: "btn btn-white" %> 8 | <%%= link_to "New <%= human_name.downcase %>", <%= new_helper(type: :path) %>, class: "btn btn-primary" %> 9 | <%% end %> 10 | <%% end %> 11 | 12 |
13 |
14 |
15 |
16 | 17 | 18 | 19 | <% attributes.each do |attribute| -%> 20 | 21 | <% end -%> 22 | 23 | 24 | 25 | 26 | <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> 27 | 28 | <% attributes.reject(&:password_digest?).each do |attribute| -%> 29 | <% if attribute.attachment? -%> 30 | 31 | <% elsif attribute.attachments? -%> 32 | <%% <%= singular_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %> 33 | 34 | <%% end %> 35 | <% else -%> 36 | 37 | <% end -%> 38 | <% end -%> 39 | 44 | 45 | <%% end %> 46 | 47 |
<%%= sort_link @search, :<%= attribute.column_name %> %>
<%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %> if <%= singular_name %>.<%= attribute.column_name %>.attached? %><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %><%%= <%= singular_name %>.<%= attribute.column_name %> %> 40 | <%%= link_to "View", <%= model_resource_name %>, class: "btn btn-white btn-sm" %> 41 | <%%= link_to "Edit", <%= edit_helper(singular_table_name, type: :path) %>, class: "btn btn-white btn-sm" %> 42 | <%%= link_to "Delete", <%= model_resource_name %>, class: "btn btn-white btn-sm", data: { method: :delete, confirm: "Are you sure?" } %> 43 |
48 |
49 | 58 |
59 |
60 |
61 | 62 |
63 |
64 |

Filters

65 | 66 |
67 | <%%= search_form_for [:admin, @search], class: "offcanvas-body" do |f| %> 68 | <%%# f.label :name_cont, class: "form-label" %> 69 | <%%# f.text_field :name_cont, class: "form-control mb-3" %> 70 | 71 | <%%= f.submit "Filter", class: "btn btn-primary" %> 72 | <%%= link_to "Clear Filter", <%= index_helper(type: :path) %>, class: "btn btn-white" %> 73 | <%% end %> 74 |
75 | -------------------------------------------------------------------------------- /lib/generators/admin/scaffold/templates/erb/new.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= render "page_header" do %> 2 | <%%= render "page_header_breadcrumb", title: "New <%= human_name.downcase %>" do %> 3 | 4 | 5 | <%% end %> 6 | <%% end %> 7 | 8 |
9 |
10 |
11 |
12 | <%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %> 13 |
14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /lib/generators/admin/scaffold/templates/erb/show.html.erb.tt: -------------------------------------------------------------------------------- 1 | <%%= render "page_header" do %> 2 | <%%= render "page_header_breadcrumb", title: "Showing <%= human_name.downcase %>" do %> 3 | 4 | 5 | <%% end %> 6 | 7 | <%%= render "page_header_actions" do %> 8 | <%%= link_to "Edit <%= human_name.downcase %>", <%= edit_helper(type: :path) %>, class: "btn btn-primary" %> 9 | <%%= link_to "Delete <%= human_name.downcase %>", <%= model_resource_name(prefix: "@") %>, class: "btn btn-white", data: { method: :delete, confirm: "Are you sure?" } %> 10 | <%% end %> 11 | <%% end %> 12 | 13 |
14 |
15 |
16 |
17 |

<%= human_name %> details

18 |
19 |
20 |
21 | <% attributes.reject(&:password_digest?).each do |attribute| -%> 22 |
<%= attribute.human_name %>
23 | <% if attribute.attachment? -%> 24 |
<%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %> if <%= singular_name %>.<%= attribute.column_name %>.attached? %>
25 | <% elsif attribute.attachments? -%> 26 | <%% <%= singular_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %> 27 |
<%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %>
28 | <%% end %> 29 | <% else -%> 30 |
<%%= @<%= singular_name %>.<%= attribute.column_name %> %>
31 | <% end -%> 32 | <% end -%> 33 |
34 |
35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /lib/generators/admin/scaffold/templates/functional_test.rb.tt: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class <%= controller_class_name %>ControllerTest < ActionDispatch::IntegrationTest 4 | <%- if mountable_engine? -%> 5 | include Engine.routes.url_helpers 6 | 7 | <%- end -%> 8 | setup do 9 | @admin_user = sign_in_admin_as admin_users(:lazaro_nixon) 10 | @<%= singular_table_name %> = <%= fixture_name %>(:one) 11 | end 12 | 13 | test "should get index" do 14 | get <%= index_helper(type: :url) %> 15 | assert_response :success 16 | end 17 | 18 | test "should get new" do 19 | get <%= new_helper %> 20 | assert_response :success 21 | end 22 | 23 | test "should create <%= singular_table_name %>" do 24 | assert_difference("<%= class_name %>.count") do 25 | post <%= index_helper(type: :url) %>, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> } 26 | end 27 | 28 | assert_redirected_to <%= show_helper("#{class_name}.last") %> 29 | end 30 | 31 | test "should show <%= singular_table_name %>" do 32 | get <%= show_helper %> 33 | assert_response :success 34 | end 35 | 36 | test "should get edit" do 37 | get <%= edit_helper %> 38 | assert_response :success 39 | end 40 | 41 | test "should update <%= singular_table_name %>" do 42 | patch <%= show_helper %>, params: { <%= "#{singular_table_name}: { #{attributes_string} }" %> } 43 | assert_redirected_to <%= show_helper %> 44 | end 45 | 46 | test "should destroy <%= singular_table_name %>" do 47 | assert_difference("<%= class_name %>.count", -1) do 48 | delete <%= show_helper %> 49 | end 50 | 51 | assert_redirected_to <%= index_helper(type: :url) %> 52 | end 53 | end 54 | --------------------------------------------------------------------------------