-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrails_activeadmin_notes.txt
1633 lines (1156 loc) · 35.7 KB
/
rails_activeadmin_notes.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
* Active Admin
==========================================================================================
gem 'activeadmin'
# Plus integrations with:
gem 'devise'
gem 'cancancan'
gem 'draper'
gem 'pundit'
> rails g active_admin:install --skip-users
- If you want to use an existing user class, provide it as an argument:
> rails g active_admin:install User
- Otherwise, with no arguments we will create an AdminUser class to use with Devise:
> rails g active_admin:install
The generator adds these core files, among others:
app/admin/dashboard.rb
app/assets/javascripts/active_admin.js
app/assets/stylesheets/active_admin.scss
config/initializers/active_admin.rb
Now, migrate and seed your database before starting the server:
rails db:migrate
rails db:seed
rails server
Visit http://localhost:3000/admin and log in as the default user:
User: [email protected]
Password: password
* To register an existing model with Active Admin:
> rails generate active_admin:resource MyModel
Configurations
============
https://activeadmin.info/1-general-configuration.html
Utility Navigation
ActiveAdmin.setup do |config|
config.namespace :admin do |admin|
admin.build_menu :utility_navigation do |menu|
menu.add label: "ActiveAdmin.info", url: "http://www.activeadmin.info",
html_options: { target: :blank }
admin.add_current_user_to_menu menu
admin.add_logout_button_to_menu menu
end
end
end
--
config.footer = "MyApp Revision v1.3"
config.site_title = 'Administration'.html_safe
config.authentication_method = :authenticate_admin_user!
config.on_unauthorized_access = :access_denied
config.logout_link_path = :destroy_admin_user_session_path
config.current_user_method = :current_admin_user
config.logout_link_method = :get
config.root_to = 'dashboard#index'
config.comments = false
config.show_comments_in_menu = false
config.comments_registration_name = 'AdminComment'
config.comments_order = 'created_at ASC'
config.batch_actions = true
config.localize_format = :long
config.meta_tags = { author: 'My Company' }
meta_tags_options = { viewport: 'width=device-width, initial-scale=1' }
config.meta_tags = meta_tags_options
config.meta_tags_for_logged_out_pages = meta_tags_options
config.meta_tags_for_logged_out_pages = {}
config.register_javascript 'my_javascript.js'
config.register_stylesheet 'my_print_stylesheet.css', media: :print
config.register_stylesheet 'my_stylesheet.css'
config.breadcrumb = false
config.csv_options = { col_sep: ';' }
config.csv_options = { force_quotes: true }
config.namespace :admin do |admin|
admin.build_menu :utility_navigation do |menu|
menu.add label: "Sign out", url: "/users/sign_out", html_options: { target: :blank }
admin.add_logout_button_to_menu menu
end
end
config.namespace :admin do |admin|
# Disable the links entirely
# admin.download_links = false
# Only show XML & PDF options
admin.download_links = [:xml, :pdf]
# Enable/disable the links based on block
# (for example, with cancan)
# admin.download_links = proc { can?(:view_download_links) }
end
config.default_per_page = 30
config.max_per_page = 10_000
config.filters = true
-----
-----
config.site_title = "My Admin Site"
config.site_title_link = "/"
config.site_title_image = "site_image.png"
config.site_title_image = "http://www.google.com/images/logos/google_logo_41.png"
config.site_title_image = ->(context) { context.current_user.company.logo_url }
# config/initializers/kaminari.rb
Kaminari.configure do |config|
config.page_method_name = :per_page_kaminari
end
If you are also using Draper, you may want to make sure per_page_kaminari is delegated correctly:
Draper::CollectionDecorator.send :delegate, :per_page_kaminari
* You can use the same syntax inside registered resource.
- For the integration with existing devise installation:
Add the following in application_controller
def authenticate_admin_user!
authenticate_user!
unless current_user.admin?
flash[:alert] = "This area is restricted to administrators only."
redirect_to root_path
end
end
def current_admin_user
return nil if user_signed_in? && !current_user.admin?
current_user
end
# In Rails 4.2 and above
def verified_request?
super || valid_authenticity_token?(session, request.headers['X-XSRF-TOKEN'])
end
with webpacker
---------------
For new apps starting with Rails 6.0, Webpacker has become the default asset generator. You can opt-in to using Webpacker for ActiveAdmin assets as well by updating your configuration to turn on the use_webpacker option, either at installation time or manually.
at active_admin installation:
rails g active_admin:install --use_webpacker
manually:
ActiveAdmin.setup do |config|
config.use_webpacker = true
end
And run the generator to get default Active Admin assets:
rails g active_admin:webpacker
/--- end of active admin
json editor (activeadmin)
-------------------------
* https://github.com/udacity/activeadmin_json_editor
gem 'activeadmin_json_editor', '~> 0.0.7'
$ bundle
- Include styles in "active_admin" initializer
config.register_stylesheet 'active_admin/json_editor.css'
config.register_javascript 'active_admin/json_editor.js'
- Then
f.input :info, as: :json
- Further reading, create your own field renderer:
* https://lixy.tech/blog/2019/05/active-admin-jsonb-forms.html
Resources :
===========
https://activeadmin.info/2-resource-customization.html
> rails generate active_admin:resource MyModel
This will generate a file in app/admin/mymodel.rb:
ActiveAdmin.register MyModel do
# See permitted parameters documentation:
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
#
# permit_params :list, :of, :attributes, :on, :model --> to add which fields to update
#
# or
#
# permit_params do
# permitted = [:permitted, :attributes]
# permitted << :other if resource.something?
# permitted
# end
end
This will show the minimum index, show and filters .
- For nested asscoaitions
permit_params :title, :content, :publisher_id,
tags_attributes: [:id, :name, :description, :_destroy]
- disable / enable
actions :all, except: [:update, :destroy]
- To rename actions
en:
active_admin:
resources:
offer: # Registered resource
new_model: 'Make an Offer' # new action item
edit_model: 'Change Offer' # edit action item
delete_model: 'Cancel Offer' # delete action item
- To rename resource
ActiveAdmin.register Post, as: "Article"
- name spaces
# Available at /today/posts
ActiveAdmin.register Post, namespace: :today
# Available at /posts
ActiveAdmin.register Post, namespace: false
- disable showing the resource in menu
menu false
.
menu label: "My Posts"
menu priority: 1 # so it's on the very left
menu if: proc{ current_user.can_edit_posts? }
menu parent: "Blog"
- Parent Menu Items
# config/initializers/active_admin.rb
config.namespace :admin do |admin|
admin.build_menu do |menu|
menu.add label: 'Blog', priority: 0
end
end
# app/admin/post.rb
ActiveAdmin.register Post do
menu parent: 'Blog'
end
Scoping
------------
scope_to :current_user # limits the accessible posts to `current_user.posts`
# Finally, you can pass a block to be called:
scope_to do
User.most_popular_posts
end
scope_to :current_user, if: proc{ current_user.limited_access? }
scope_to :current_user, unless: proc{ current_user.admin? }
- Eager loading
ActiveAdmin.register Post do
includes :author, :categories
end
- how to retreive
controller do
def scoped_collection
end_of_association_chain.where(visibility: true)
end
end
- If you need to completely replace the record retrieving code (e.g., you have a custom to_param implementation in your models), override the find_resource method on the controller:
ActiveAdmin.register Post do
controller do
def find_resource
scoped_collection.where(id: params[:id]).first!
end
end
end
- Belongs To
It’s common to want to scope a series of resources to a relationship. For example a Project may have many Milestones and Tickets. To nest the resource within another, you can use the belongs_to method:
ActiveAdmin.register Project
ActiveAdmin.register Ticket do
belongs_to :project
end
# Projects will be available as usual and tickets will be available by visiting /admin/projects/1/tickets assuming that a Project with the id of 1 exists. Active Admin does not add “Tickets” to the global navigation because the routes can only be generated when there is a project id.
ActiveAdmin.register Project do
sidebar "Project Details", only: [:show, :edit] do
ul do
li link_to "Tickets", admin_project_tickets_path(resource)
li link_to "Milestones", admin_project_milestones_path(resource)
end
end
end
ActiveAdmin.register Ticket do
belongs_to :project
end
ActiveAdmin.register Milestone do
belongs_to :project
end
Index Pages
===========
https://activeadmin.info/3-index-pages.html
index do
id_column
column :image_title
actions
end
index as: :grid do |product|
link_to image_tag(product.image_path), admin_product_path(product)
end
--
index do
column :image_title
actions
end
index as: :grid, default: true do |product|
link_to image_tag(product.image_path), admin_product_path(product)
end
--
- Adding new filters
filter :id
filter :description
filter :status, as: :select, collection: Order.statuses
filter :booking_status, as: :select, collection: {'Incomplete' => 0, 'Pending' => 1, 'Complete' => 2}
filter :category
filter :created_at, as: :date_range
filter :author, as: :check_boxes
filter :name_equals
# or
filter :name_contains
filter :author, label: 'Something else'
filter :first_name_or_last_name_cont, as: :string, label: "Name"
- disable filters
ActiveAdmin.register Post do
config.filters = false
end
preserve default ones
preserve_default_filters!
filter :author
remove_filter :id
-Index Scopes
scope :all, default: true
# assumes the model has a scope called ':active'
scope :active
# renames model scope ':leaves' to ':subcategories'
scope "Subcategories", :leaves
# Dynamic scope name
scope ->{ Date.today.strftime '%A' }, :published_today
# custom scope not defined on the model
scope("Inactive") { |scope| scope.where(active: false) }
# conditionally show a custom controller scope
scope "Published", if: -> { current_admin_user.can? :manage, Posts } do |posts|
posts.published
end
- grouping scopes
# a scope in the default group
scope :all
# two scopes used to filter by status
scope :active, group: :status
scope :inactive, group: :status
# two scopes used to filter by date
scope :today, group: :date
scope :tomorrow, group: :date
- Index default sort order
You can define the default sort order for index pages:
ActiveAdmin.register Post do
config.sort_order = 'name_asc'
end
- Index pagination
ActiveAdmin.setup do |config|
config.default_per_page = 30
config.paginate = false
config.per_page = [10, 50, 100]
end
controller do
before_action only: :index do
@per_page = 100
end
end
- Download Links
# Per resource:
ActiveAdmin.register Post do
index download_links: false
index download_links: [:pdf]
index download_links: proc{ current_user.can_view_download_links? }
end
# For the entire application:
ActiveAdmin.setup do |config|
config.download_links = false
config.download_links = [:csv, :xml, :json, :pdf]
config.download_links = proc { current_user.can_view_download_links? }
end
Forms
=====
- Simple
form do |f|
f.semantic_errors # shows errors on :base
f.inputs # builds an input field for every attribute
f.actions # adds the 'Submit' and 'Cancel' buttons
end
- Complex
ActiveAdmin.register Post do
form title: 'A custom title' do |f|
inputs 'Details' do
input :title
input :published_at, label: "Publish Post At"
li "Created at #{f.object.created_at}" unless f.object.new_record?
input :category
end
panel 'Markup' do
"The following can be used in the content below..."
end
inputs 'Content', :body
para "Press cancel to return to the list without saving."
actions
end
end
- Partials
ActiveAdmin.register Post do
form partial: 'form'
end
-->
# app/views/admin/posts/_form.html.arb
insert_tag active_admin_form_for resource do |f|
inputs :title, :body
actions
end
- Nested Resources
ActiveAdmin.register Post do
permit_params :title,
:published_at,
:body,
categories_attributes: [:id, :title, :_destroy],
form do |f|
f.inputs 'Details' do
f.input :title
f.input :published_at, label: 'Publish Post At'
end
f.inputs 'Content', :body
f.inputs do
f.has_many :categories, heading: 'Themes',
allow_destroy: true,
new_record: false do |a|
a.input :title
end
end
f.actions
end
end
- Datepicker
f.input :starts_at, as: :datepicker,
datepicker_options: {
min_date: "2013-10-8",
max_date: "+3D"
}
- Displaying Errors
form do |f|
f.semantic_errors *f.object.errors.keys
# ...
end
- Tabs
form do |f|
tabs do
tab 'Basic' do
f.inputs 'Basic Details' do
f.input :email
f.input :password
f.input :password_confirmation
end
end
tab 'Advanced', html_options: { class: 'specific_css_class' } do
f.inputs 'Advanced Details' do
f.input :role
end
end
end
f.actions
end
- Customize the Create Another checkbox
In order to simplify creating multiple resources you may enable ActiveAdmin to show nice “Create Another” checkbox alongside of Create Model button. It may be enabled for the whole application:
ActiveAdmin.setup do |config|
config.create_another = true
end
or for the particular resource:
ActiveAdmin.register Post do
config.create_another = true
end
Show Pages
==========
ActiveAdmin.register Post do
show do
h3 post.title
div do
simple_format post.body
end
end
end
...
show do
# renders app/views/admin/posts/_some_partial.html.erb
render 'some_partial', { post: post }
end
show do
attributes_table do
row :title
row :image do |ad|
image_tag ad.image.url
end
end
active_admin_comments
end
show title: :name do
# ...
end
- With sidebar
ActiveAdmin.register Book do
show do
panel "Table of Contents" do
table_for book.chapters do
column :number
column :title
column :page
end
end
active_admin_comments
end
sidebar "Details", only: :show do
attributes_table_for book do
row :title
row :author
row :publisher
row('Published?') { |b| status_tag b.published? }
end
end
end
- preserve default one
show do
div do
h3 'Some custom charts about this object'
render partial: 'charts'
end
default_main_content
end
Sidebar
=======
sidebar :help do
"Need help? Email us at [email protected]"
end
sidebar :help, only: :index do
"Need help? Email us at [email protected]"
end
sidebar :help, if: proc{ current_admin_user.super_admin? } do
"Only for super admins!"
end
sidebar :custom, only: :show do
resource.a_method
end
sidebar :help # app/views/admin/posts/_help_sidebar.html.erb
sidebar :help, partial: 'custom' # app/views/admin/posts/_custom.html.erb
sidebar :help, class: 'custom_class'
# will push Help section to the top (above default Filters section)
sidebar :help, priority: 0
# you can use both, resource or resource instance (quote)
sidebar "Customer", only: [:show, :edit] do
render :partial => "/admin/customer", :locals => {:order => resource}
end
# the partial views/admin/_customer.html.arb # ARB
user = order.user
table do
tr do
# the partial can be .html.erb as well
<%
_order = order
.....
%>
sidebar :Subtotal, only: [:show, :edit] do
strong "#{with_currency2(quote, :total_price)}", :class => "right"
end
Custom Controller Actions
=========================
- Collection Actions
ActiveAdmin.register Post do
collection_action :import_csv, method: :post do
# Do some CSV importing work here...
redirect_to collection_path, notice: "CSV imported successfully!"
end
end
- Member Actions
ActiveAdmin.register User do
member_action :lock, method: :put do
resource.lock!
redirect_to resource_path, notice: "Locked!"
end
end
- HTTP Verbs
member_action :foo, method: [:get, :post] do
if request.post?
resource.update_attributes! foo: params[:foo] || {}
head :ok
else
render :foo
end
end
- Rendering
# /admin/posts/:id/comments
member_action :comments do
@comments = resource.comments
# This will render app/views/admin/posts/comments.html.erb
end
- For example, create app/views/admin/posts/comments.html.arb with:
table_for assigns[:post].comments do
column :id
column :author
column :body do |comment|
simple_format comment.body
end
end
- Page Titles
ActiveAdmin.register Post do
member_action :comments do
@comments = resource.comments
@page_title = "#{resource.title}: Comments" # Sets the page title
end
end
- Action Items
# To include your own action items (like the New, Edit and Delete buttons), add an action_item block. The first parameter is just a name to identify the action, and is required. For example, to add a “View on site” button to view a blog post:
action_item :view, only: :show do
link_to 'View on site', post_path(post) if post.published?
end
action_item :super_action,
only: :show,
if: proc{ current_admin_user.super_admin? } do
"Only display this to super admins on the show screen"
end
action_item :help, priority: 0 do
"Display this action to the first position"
end
- Modifying the Controller
ActiveAdmin.register Post do
controller do
# This code is evaluated within the controller class
def define_a_method
# Instance method
end
end
end
Also
before_action do |resource|
end
before_create do |resource|
end
def edit
super
end
def create
create! do |success, failure|
success.html { redirect_to collection_url }
failure.html { redirect_to admin_shipments_new_url, alert: "Pick one item at least"}
end
end
after_create do |resource|
end
def update_resource(object, attributes)
# object.items.update_all(:user_id => object.user_id)
# moved the logic to order model ..
object.update(*attributes)
end
def update(options={}, &block)
# You can put your send email code over here
super do |success, failure|
block.call(success, failure) if block
failure.html { render :edit }
end
end
before_action :populate_collection, :only => [:new, :update]
Batch Actions
===============
# By default, the index page provides you a “Batch Action” to quickly delete records, as well as an API for you to easily create your own. Note that if you override the default index, you must add selectable_column back for batch actions to be usable:
index do
selectable_column
# ...
end
- Creating your own
ActiveAdmin.register Post do
batch_action :flag do |ids|
batch_action_collection.find(ids).each do |post|
post.flag! :hot
end
redirect_to collection_path, alert: "The posts have been flagged."
end
end
- Disabling Batch Actions
# config/initializers/active_admin.rb
ActiveAdmin.setup do |config|
# Application level:
config.batch_actions = false
# Namespace level:
config.namespace :admin do |admin|
admin.batch_actions = false
end
end
# app/admin/post.rb
ActiveAdmin.register Post do
# Resource level:
config.batch_actions = false
end
- Modification
ActiveAdmin.register Post do
batch_action :destroy do |ids|
redirect_to collection_path, alert: "Didn't really delete these!"
end
end
- Removal
ActiveAdmin.register Post do
batch_action :destroy, false
end
- Conditional display
ActiveAdmin.register Post do
batch_action :flag, if: proc{ can? :flag, Post } do |ids|
# ...
end
end
- Priority in the drop-down menu
ActiveAdmin.register Post do
batch_action :destroy, priority: 1 do |ids|
# ...
end
end
- Confirmation prompt
ActiveAdmin.register Post do
batch_action :destroy, confirm: "Are you sure??" do |ids|
# ...
end
end
- Batch Action forms
If you want to capture input from the user as they perform a batch action, Active Admin has just the thing for you:
batch_action :flag, form: {
type: %w[Offensive Spam Other],
reason: :text,
notes: :textarea,
hide: :checkbox,
date: :datepicker
} do |ids, inputs|
# inputs is a hash of all the form fields you requested
redirect_to collection_path, notice: [ids, inputs].to_s
end
- If you pass a nested array, it will behave just like Formtastic would, with the first element being the text displayed and the second element being the value.
batch_action :doit, form: {user: [['Jake',2], ['Mary',3]]} do |ids, inputs|
User.find(inputs[:user])
# ...
end
- When you have dynamic form inputs you can pass a proc instead:
batch_action :doit, form: -> { {user: User.pluck(:name, :id)} } do |ids, inputs|
User.find(inputs[:user])
# ...
end
- Under the covers this is powered by the JS ActiveAdmin.ModalDialog which you can use yourself:
if $('body.admin_users').length
$('a[data-prompt]').click ->
ActiveAdmin.ModalDialog $(@).data('prompt'), comment: 'textarea',
(inputs)=>
$.post "/admin/users/#{$(@).data 'id'}/change_state",
comment: inputs.comment, state: $(@).data('state'),
success: ->
window.location.reload()
- Translation
So this:
ActiveAdmin.register Post do
batch_action :publish do |ids|
# ...
end
end
Can be translated with:
# config/locales/en.yml
en:
active_admin:
batch_actions:
labels: