New conference UI

This commit is contained in:
Petko Bordjukov 2014-10-14 18:27:28 +03:00
parent c19154820c
commit c2548fcd48
19 changed files with 322 additions and 32 deletions

View File

@ -19,7 +19,7 @@ gem 'rails-i18n'
gem 'devise' gem 'devise'
gem 'devise-i18n' gem 'devise-i18n'
gem 'simple_form' gem 'simple_form', '~> 3.1.0.rc2'
gem 'simple_form_fancy_uploads', github: 'apeacox/simple_form_fancy_uploads' gem 'simple_form_fancy_uploads', github: 'apeacox/simple_form_fancy_uploads'
# Phone validation # Phone validation
@ -45,6 +45,8 @@ gem 'bootswatch-rails'
gem 'autoprefixer-rails' gem 'autoprefixer-rails'
gem 'font-awesome-rails' gem 'font-awesome-rails'
gem 'nested_form'
group :development do group :development do
gem 'spring' gem 'spring'
gem 'spring-commands-rspec' gem 'spring-commands-rspec'

View File

@ -1,8 +1,8 @@
GIT GIT
remote: git://github.com/apeacox/simple_form_fancy_uploads.git remote: git://github.com/apeacox/simple_form_fancy_uploads.git
revision: a135aad8f17cbf219a30ce5a30709406e277f7e0 revision: bc13fcd62143c1ff4f0ac8e4621c11415b6be6bf
specs: specs:
simple_form_fancy_uploads (0.1.0) simple_form_fancy_uploads (0.1.1)
carrierwave carrierwave
rails (>= 4.0.0) rails (>= 4.0.0)
simple_form (~> 3.0) simple_form (~> 3.0)
@ -44,7 +44,7 @@ GEM
thread_safe (~> 0.1) thread_safe (~> 0.1)
tzinfo (~> 1.1) tzinfo (~> 1.1)
arel (5.0.1.20140414130214) arel (5.0.1.20140414130214)
autoprefixer-rails (3.1.0.20140911) autoprefixer-rails (3.1.1.20141001)
execjs execjs
bcrypt (3.1.7) bcrypt (3.1.7)
bootstrap-sass (3.2.0.2) bootstrap-sass (3.2.0.2)
@ -64,10 +64,10 @@ GEM
capistrano-rails (1.1.2) capistrano-rails (1.1.2)
capistrano (~> 3.1) capistrano (~> 3.1)
capistrano-bundler (~> 1.1) capistrano-bundler (~> 1.1)
capistrano-rvm (0.1.1) capistrano-rvm (0.1.2)
capistrano (~> 3.0) capistrano (~> 3.0)
sshkit (~> 1.2) sshkit (~> 1.2)
capybara (2.4.1) capybara (2.4.4)
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
rack (>= 1.0.0) rack (>= 1.0.0)
@ -82,7 +82,7 @@ GEM
timers (~> 4.0.0) timers (~> 4.0.0)
choice (0.1.6) choice (0.1.6)
coderay (1.1.0) coderay (1.1.0)
coffee-rails (4.0.1) coffee-rails (4.1.0)
coffee-script (>= 2.2.0) coffee-script (>= 2.2.0)
railties (>= 4.0.0, < 5.0) railties (>= 4.0.0, < 5.0)
coffee-script (2.3.0) coffee-script (2.3.0)
@ -93,13 +93,14 @@ GEM
countries (0.9.3) countries (0.9.3)
currencies (~> 0.4.2) currencies (~> 0.4.2)
currencies (0.4.2) currencies (0.4.2)
devise (3.3.0) devise (3.4.0)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5) railties (>= 3.2.6, < 5)
responders
thread_safe (~> 0.1) thread_safe (~> 0.1)
warden (~> 1.2.3) warden (~> 1.2.3)
devise-i18n (0.11.1) devise-i18n (0.11.2)
diff-lcs (1.2.5) diff-lcs (1.2.5)
erubis (2.7.0) erubis (2.7.0)
execjs (2.2.1) execjs (2.2.1)
@ -110,7 +111,7 @@ GEM
railties (>= 3.0.0) railties (>= 3.0.0)
faker (1.4.3) faker (1.4.3)
i18n (~> 0.5) i18n (~> 0.5)
ffi (1.9.3) ffi (1.9.6)
font-awesome-rails (4.2.0.0) font-awesome-rails (4.2.0.0)
railties (>= 3.2, < 5.0) railties (>= 3.2, < 5.0)
formatador (0.2.5) formatador (0.2.5)
@ -134,7 +135,7 @@ GEM
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
json (1.8.1) json (1.8.1)
libv8 (3.16.14.7) libv8 (3.16.14.7)
listen (2.7.9) listen (2.7.11)
celluloid (>= 0.15.2) celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9) rb-inotify (>= 0.9)
@ -142,10 +143,11 @@ GEM
mail (2.6.1) mail (2.6.1)
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
method_source (0.8.2) method_source (0.8.2)
mime-types (2.3) mime-types (2.4.1)
mini_portile (0.6.0) mini_portile (0.6.0)
minitest (5.4.1) minitest (5.4.2)
multi_json (1.10.1) multi_json (1.10.1)
nested_form (0.3.2)
net-scp (1.2.1) net-scp (1.2.1)
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (2.9.1) net-ssh (2.9.1)
@ -153,11 +155,11 @@ GEM
mini_portile (= 0.6.0) mini_portile (= 0.6.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
pg (0.17.1) pg (0.17.1)
phony (2.3.0) phony (2.4.3)
phony_rails (0.6.2) phony_rails (0.7.1)
activesupport (>= 3.0) activesupport (>= 3.0)
countries (>= 0.8.2) countries (>= 0.8.2)
phony (~> 2.1) phony (~> 2.4.3)
pry (0.10.1) pry (0.10.1)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
@ -195,17 +197,19 @@ GEM
rb-inotify (0.9.5) rb-inotify (0.9.5)
ffi (>= 0.5.0) ffi (>= 0.5.0)
ref (1.0.5) ref (1.0.5)
responders (1.1.1)
railties (>= 3.2, < 4.2)
rmagick (2.13.3) rmagick (2.13.3)
rspec (3.1.0) rspec (3.1.0)
rspec-core (~> 3.1.0) rspec-core (~> 3.1.0)
rspec-expectations (~> 3.1.0) rspec-expectations (~> 3.1.0)
rspec-mocks (~> 3.1.0) rspec-mocks (~> 3.1.0)
rspec-core (3.1.4) rspec-core (3.1.7)
rspec-support (~> 3.1.0) rspec-support (~> 3.1.0)
rspec-expectations (3.1.1) rspec-expectations (3.1.2)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.1.0) rspec-support (~> 3.1.0)
rspec-mocks (3.1.1) rspec-mocks (3.1.3)
rspec-support (~> 3.1.0) rspec-support (~> 3.1.0)
rspec-rails (3.1.0) rspec-rails (3.1.0)
actionpack (>= 3.0) actionpack (>= 3.0)
@ -215,7 +219,7 @@ GEM
rspec-expectations (~> 3.1.0) rspec-expectations (~> 3.1.0)
rspec-mocks (~> 3.1.0) rspec-mocks (~> 3.1.0)
rspec-support (~> 3.1.0) rspec-support (~> 3.1.0)
rspec-support (3.1.0) rspec-support (3.1.2)
ruby-graphviz (1.0.9) ruby-graphviz (1.0.9)
sass (3.2.19) sass (3.2.19)
sass-rails (4.0.3) sass-rails (4.0.3)
@ -223,7 +227,7 @@ GEM
sass (~> 3.2.0) sass (~> 3.2.0)
sprockets (~> 2.8, <= 2.11.0) sprockets (~> 2.8, <= 2.11.0)
sprockets-rails (~> 2.0) sprockets-rails (~> 2.0)
simple_form (3.0.2) simple_form (3.1.0.rc2)
actionpack (~> 4.0) actionpack (~> 4.0)
activemodel (~> 4.0) activemodel (~> 4.0)
slim (2.0.3) slim (2.0.3)
@ -243,10 +247,10 @@ GEM
multi_json (~> 1.0) multi_json (~> 1.0)
rack (~> 1.0) rack (~> 1.0)
tilt (~> 1.1, != 1.3.0) tilt (~> 1.1, != 1.3.0)
sprockets-rails (2.1.4) sprockets-rails (2.2.0)
actionpack (>= 3.0) actionpack (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
sprockets (~> 2.8) sprockets (>= 2.8, < 4.0)
sqlite3 (1.3.9) sqlite3 (1.3.9)
sshkit (1.5.1) sshkit (1.5.1)
colorize colorize
@ -293,6 +297,7 @@ DEPENDENCIES
globalize (~> 4.0.2) globalize (~> 4.0.2)
guard-rspec guard-rspec
jquery-rails jquery-rails
nested_form
pg pg
phony_rails phony_rails
puma puma
@ -302,7 +307,7 @@ DEPENDENCIES
rmagick rmagick
rspec-rails rspec-rails
sass-rails (~> 4.0.3) sass-rails (~> 4.0.3)
simple_form simple_form (~> 3.1.0.rc2)
simple_form_fancy_uploads! simple_form_fancy_uploads!
slim-rails slim-rails
spring spring

View File

@ -1,5 +1,6 @@
//= require jquery //= require jquery
//= require jquery_ujs //= require jquery_ujs
//= require jquery_nested_form
//= require bootstrap-sprockets //= require bootstrap-sprockets
//= require metisMenu/metisMenu //= require metisMenu/metisMenu
//= require_directory . //= require_directory .

View File

@ -0,0 +1,10 @@
select.date, select.datetime, select.time {
display: inline;
width: auto;
}
@media(min-width:768px) {
form .text {
height: 180px;
}
}

View File

@ -8,3 +8,4 @@
@import "layout"; @import "layout";
@import "navigation"; @import "navigation";
@import "forms";

View File

@ -0,0 +1,29 @@
module Management
class ConferencesController < ManagementController
before_action :assign_conference, only: [:edit]
def new
@conference = Conference.new
3.times { @conference.tracks.build }
end
def create
@conference = Conference.new conference_params
@conference.save
render :new
end
def edit
end
private
def assign_conference
@conference = Conference.find params[:id]
end
def conference_params
params.require(:conference).permit [:title, :email, :start_date, :end_date, :description, tracks_attributes: [:id, :name, :color, :description, :_destroy]]
end
end
end

View File

@ -0,0 +1,6 @@
class ColorInput < SimpleForm::Inputs::Base
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.color_field(attribute_name, merged_input_options)
end
end

View File

@ -12,6 +12,8 @@ class Conference < ActiveRecord::Base
has_many :events, through: :tracks has_many :events, through: :tracks
has_many :candidate_speakers, through: :events has_many :candidate_speakers, through: :events
accepts_nested_attributes_for :tracks
scope :future, -> { where('start_date >= ?', Date.today).order('start_date ASC') } scope :future, -> { where('start_date >= ?', Date.today).order('start_date ASC') }
def self.current def self.current

View File

@ -3,11 +3,16 @@ class Track < ActiveRecord::Base
has_many :events has_many :events
validates :name, presence: true validates :name, presence: true
validates :color, presence: true, format: {with: /\A[a-f0-9]{6}\z/i} validates :color, presence: true, format: {with: /\A#?[a-f0-9]{6}\z/i}
validates :description, presence: true
translates :name, :description translates :name, :description
def color=(hex_triplet) def color=(hex_triplet)
write_attribute :color, hex_triplet.gsub(/\A#/,'') if hex_triplet write_attribute :color, hex_triplet.gsub(/\A#/,'') if hex_triplet
end end
def color
"##{read_attribute :color}"
end
end end

View File

@ -0,0 +1,20 @@
= simple_nested_form_for [:management, @conference], wrapper: :horizontal_form, html: { class: 'form-horizontal' } do |f|
.panel.panel-primary
.panel-heading
h1.panel-title = t 'views.conference.info'
.panel-body
.row
.col-lg-12
= f.input :title
= f.input :email
= f.input :start_date, as: :date
= f.input :end_date, as: :date
= f.input :description
.row
.col-lg-12
h2 = Track.model_name.human(count: 2).mb_chars.capitalize
.row
= render partial: 'form_tracks', locals: {form: f}
.panel-footer.text-right
= f.submit class: 'btn btn-primary'

View File

@ -0,0 +1,14 @@
div#tracks
= form.simple_fields_for :tracks do |ff|
.col-lg-6
.panel.panel-default
.panel-body
= ff.input :name
= ff.input :color, as: :color
= ff.input :description
.panel-footer.text-right
= ff.link_to_remove t('helpers.destroy', model: Track.model_name.human), class: ['btn', 'btn-danger', ff.object.events.any? ? 'disabled' : nil]
.col-lg-6
.panel.panel-default
.panel-body.text-right
= form.link_to_add t('helpers.submit.create', model: Track.model_name.human), :tracks, data: {target: '#tracks'}, class: 'btn btn-success'

View File

@ -0,0 +1,3 @@
.row
.col-lg-12
= render partial: 'form'

View File

@ -0,0 +1,3 @@
.row
.col-lg-12
= render partial: 'form'

View File

@ -22,9 +22,9 @@ SimpleForm.setup do |config|
b.use :placeholder b.use :placeholder
## Optional extensions ## Optional extensions
# They are disabled unless you pass `f.input EXTENSION_NAME => :lookup` # They are disabled unless you pass `f.input EXTENSION_NAME => true`
# to the input. If so, they will retrieve the values from the model # to the input. If so, they will retrieve the values from the model
# if any exists. If you want to enable the lookup for any of those # if any exists. If you want to enable any of those
# extensions by default, you can change `b.optional` to `b.use`. # extensions by default, you can change `b.optional` to `b.use`.
# Calculates maxlength from length validations for string inputs # Calculates maxlength from length validations for string inputs
@ -43,6 +43,12 @@ SimpleForm.setup do |config|
b.use :label_input b.use :label_input
b.use :hint, wrap_with: { tag: :span, class: :hint } b.use :hint, wrap_with: { tag: :span, class: :hint }
b.use :error, wrap_with: { tag: :span, class: :error } b.use :error, wrap_with: { tag: :span, class: :error }
## full_messages_for
# If you want to display the full error message for the attribute, you can
# use the component :full_error, like:
#
# b.use :full_error, wrap_with: { tag: :span, class: :error }
end end
# The default wrapper to be used by the FormBuilder. # The default wrapper to be used by the FormBuilder.
@ -66,7 +72,7 @@ SimpleForm.setup do |config|
config.error_notification_tag = :div config.error_notification_tag = :div
# CSS class to add for error notification helper. # CSS class to add for error notification helper.
config.error_notification_class = 'alert alert-error' config.error_notification_class = 'error_notification'
# ID to add for error notification helper. # ID to add for error notification helper.
# config.error_notification_id = nil # config.error_notification_id = nil
@ -92,10 +98,10 @@ SimpleForm.setup do |config|
# config.item_wrapper_class = nil # config.item_wrapper_class = nil
# How the label text should be generated altogether with the required text. # How the label text should be generated altogether with the required text.
# config.label_text = lambda { |label, required| "#{required} #{label}" } # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
# You can define the class to use on all labels. Default is nil. # You can define the class to use on all labels. Default is nil.
config.label_class = 'control-label' # config.label_class = nil
# You can define the class to use on all forms. Default is simple_form. # You can define the class to use on all forms. Default is simple_form.
# config.form_class = :simple_form # config.form_class = :simple_form
@ -125,6 +131,10 @@ SimpleForm.setup do |config|
# type as key and the wrapper that will be used for all inputs with specified type. # type as key and the wrapper that will be used for all inputs with specified type.
# config.wrapper_mappings = { string: :prepend } # config.wrapper_mappings = { string: :prepend }
# Namespaces where SimpleForm should look for custom input classes that
# override default inputs.
# config.custom_inputs_namespaces << "CustomInputs"
# Default priority for time_zone inputs. # Default priority for time_zone inputs.
# config.time_zone_priority = nil # config.time_zone_priority = nil
@ -142,4 +152,14 @@ SimpleForm.setup do |config|
# Default class for inputs # Default class for inputs
# config.input_class = nil # config.input_class = nil
# Define the default class of the input wrapper of the boolean input.
config.boolean_label_class = 'checkbox'
# Defines if the default input wrapper class should be included in radio
# collection wrappers.
# config.include_default_input_wrapper_class = true
# Defines which i18n scope will be used in Simple Form.
# config.i18n_scope = 'simple_form'
end end

View File

@ -0,0 +1,136 @@
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
config.error_notification_class = 'alert alert-danger'
config.button_class = 'btn btn-default'
config.boolean_label_class = nil
config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'control-label'
b.use :input, class: 'form-control'
b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
config.wrappers :vertical_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :readonly
b.use :label, class: 'control-label'
b.use :input
b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
config.wrappers :vertical_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.optional :readonly
b.wrapper tag: 'div', class: 'checkbox' do |ba|
ba.use :label_input
end
b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
config.wrappers :vertical_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.optional :readonly
b.use :label_input
b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
config.wrappers :horizontal_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'col-sm-3 control-label'
b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
ba.use :input, class: 'form-control'
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end
config.wrappers :horizontal_file_input, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :readonly
b.use :label, class: 'col-sm-3 control-label'
b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
ba.use :input
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end
config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.optional :readonly
b.wrapper tag: 'div', class: 'col-sm-offset-3 col-sm-9' do |wr|
wr.wrapper tag: 'div', class: 'checkbox' do |ba|
ba.use :label_input, class: 'col-sm-9'
end
wr.use :error, wrap_with: { tag: 'span', class: 'help-block' }
wr.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end
config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'col-sm-3 control-label'
b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
ba.use :input
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end
config.wrappers :horizontal_submit, tag: 'div', class: 'form-group' do |b|
b.optional :readonly
b.wrapper tag: 'div', class: 'col-sm-offset-2 col-sm-10' do |ba|
ba.use :input
end
end
config.wrappers :inline_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'sr-only'
b.use :input, class: 'form-control'
b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
# Wrappers for forms and inputs using the Bootstrap toolkit.
# Check the Bootstrap docs (http://getbootstrap.com)
# to learn about the different styles for forms and inputs,
# buttons and other elements.
# config.default_wrapper = :vertical_form
end

View File

@ -20,6 +20,9 @@
# available at http://guides.rubyonrails.org/i18n.html. # available at http://guides.rubyonrails.org/i18n.html.
bg: bg:
helpers:
destroy: Премахни %{model}
create_new: Създаване на %{model_name}
event_mailer: event_mailer:
acceptance_notification: acceptance_notification:
subject: 'Предложението ви за %{conference} за %{submission_type} „%{title}“ е одобрено' subject: 'Предложението ви за %{conference} за %{submission_type} „%{title}“ е одобрено'
@ -37,8 +40,19 @@ bg:
event: event:
one: Предложение one: Предложение
other: Предложения other: Предложения
track: Поток от лекции track:
one: поток от лекции
other: потоци от лекции
conference:
one: конференция
other: конференции
attributes: attributes:
conference:
email: E-mail
title: Заглавие
start_date: Начална дата
end_date: Крайна дата
description: Описание
user: user:
email: E-mail email: E-mail
current_password: Текуща парола current_password: Текуща парола

View File

@ -8,6 +8,16 @@ bg:
error_notification: error_notification:
default_message: 'Моля, разгледайте посочените грешки във формуляра:' default_message: 'Моля, разгледайте посочените грешки във формуляра:'
hints: hints:
conference:
title: Заглавието на конференцията
start_date: Денят, в който конференцията започва
end_date: Денят, в който конференцията приключва
description: Описание на конференцията
email: Email на организаторския екип
tracks:
name: Име на потокът
description: Кратко описание
color: Цвят
user: user:
email: e-mail адресът Ви. Ще бъде видим само от организаторите email: e-mail адресът Ви. Ще бъде видим само от организаторите
password: Парола с дължина между 8 и 128 символа password: Парола с дължина между 8 и 128 символа

View File

@ -72,3 +72,6 @@ bg:
lecture_was_successfully_confirmed: "Лекцията беше потвърдена успешно" lecture_was_successfully_confirmed: "Лекцията беше потвърдена успешно"
workshop_was_successfully_confirmed: "Уъркшопът беше потвърден успешно" workshop_was_successfully_confirmed: "Уъркшопът беше потвърден успешно"
views:
conference:
info: Информация за конференция

View File

@ -16,6 +16,12 @@ Rails.application.routes.draw do
namespace :management do namespace :management do
get '/', to: 'events#index' get '/', to: 'events#index'
resources :conferences do
resources :events do
patch 'state'
end
end
resources :users do resources :users do
member do member do
post 'toggle_admin' post 'toggle_admin'