From ee0abe761128da7cb758a4710d25e6bf0acbba93 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Sun, 28 Nov 2021 07:17:59 -0800 Subject: [PATCH] Move docs from wiki to the project repository itself Moving the docs allows them to more easily be developed in a first-class manner through pull requests. The docs can stay aligned with code changes as they happen within the same commit. For example, as the code gets refactored, docs may become incorrect or out of date. With the docs in the repository, this can be caught during development and review. Contributors can provide reviewable documentaiton-only changes, even small ones, thus improving the community experience. An up to date version of the docs were cloned and copied using: git clone https://github.com/railsadminteam/rails_admin.wiki.git The docs were left as-is as much as possible. If there are desired changes, these can now be opened as future pull requests. Articles that are linked to internally are not included. Changes to the wiki articles: - All previous wiki links have been converted to be compatible with GitHub flavored Markdown so links continue to work as before. - Small formatting changes to be compliant with Prettier. - References to *.coffee files were updated to *.js. - Renamed index.md to home.md to follow conventions. - Added an H1 header to all articles which was previously implicitly added by the wiki engine. Once this change lands, the wiki can be removed so as to avoid confusion for new users. --- README.md | 10 +- docs/actions.md | 208 +++++ docs/actiontext.md | 61 ++ docs/activestorage.md | 102 +++ docs/associations-basics.md | 119 +++ docs/associations-scoping.md | 116 +++ docs/associations-validation.md | 21 + docs/auditing.md | 15 + docs/authentication.md | 14 + docs/authorization.md | 36 + docs/base-action.md | 41 + docs/base-configuration.md | 62 ++ docs/base-field.md | 5 + docs/base.md | 20 + docs/belongs-to-association.md | 16 + docs/boolean.md | 5 + docs/bulk-delete-action.md | 18 + ...move-associated-action-buttons-in-forms.md | 102 +++ docs/cancan.md | 3 + docs/cancancan.md | 123 +++ docs/carrierwave.md | 104 +++ docs/changing-locale.md | 27 + docs/ckeditor.md | 58 ++ docs/codemirror.md | 37 + docs/create.md | 7 + docs/custom-action.md | 78 ++ docs/custom-field.md | 22 + docs/customized-authorization.md | 13 + docs/dashboard-action.md | 39 + docs/decimal.md | 3 + docs/declarative-authorization.md | 35 + docs/delete-action.md | 20 + docs/devise.md | 42 + docs/dragonfly.md | 19 + docs/edit-action.md | 18 + docs/edit.md | 9 + docs/enumeration.md | 187 +++++ docs/examples.md | 5 + docs/excluding-railsadmin-from-newrelic.md | 21 + docs/export-action.md | 13 + docs/export.md | 35 + docs/fields.md | 537 ++++++++++++ docs/file-upload.md | 22 + docs/float.md | 3 + docs/froala-wysiwyg-html-editor.md | 76 ++ docs/groups.md | 129 +++ docs/has-and-belongs-to-many-association.md | 46 + docs/has-many-association.md | 32 + docs/has-many-through-association.md | 132 +++ docs/has-one-association.md | 39 + docs/hidden.md | 5 + docs/history-index-action.md | 13 + docs/history-show-action.md | 13 + ...-table-with-frozen-columns-in-list-view.md | 220 +++++ ...loading-railsadmin-config-automatically.md | 790 ++++++++++++++++++ docs/how-to-set-default-values.md | 85 ++ docs/index-action.md | 15 + docs/index.md | 171 ++++ docs/integer.md | 3 + docs/internal-audit-plugin.md | 36 + docs/list-view-table-styling.md | 52 ++ docs/list.md | 263 ++++++ docs/manually.md | 64 ++ docs/mass-assignments-protection.md | 10 + docs/modal.md | 7 + docs/models.md | 171 ++++ docs/navigation.md | 200 +++++ docs/nested.md | 7 + docs/new-action.md | 18 + docs/paperclip.md | 33 + docs/papertrail.md | 47 ++ docs/password.md | 5 + docs/performance.md | 35 + docs/polymorphic-belongs-to-association.md | 22 + docs/routing-problems.md | 21 + docs/rspec-with-capybara-examples.md | 41 + docs/show-action.md | 15 + docs/show-in-app-action.md | 13 + docs/show.md | 30 + docs/sorcery.md | 43 + docs/string.md | 5 + docs/text.md | 8 + docs/theming-and-customization.md | 88 ++ docs/timestamp-date-datetime-time.md | 9 + docs/translations.md | 51 ++ docs/troubleshoot.md | 127 +++ docs/update.md | 7 + docs/using-railsadmin-routes.md | 38 + docs/wysihtml5.md | 35 + .../rails_admin/templates/initializer.erb | 2 +- lib/rails_admin/config.rb | 2 +- 91 files changed, 5718 insertions(+), 7 deletions(-) create mode 100644 docs/actions.md create mode 100644 docs/actiontext.md create mode 100644 docs/activestorage.md create mode 100644 docs/associations-basics.md create mode 100644 docs/associations-scoping.md create mode 100644 docs/associations-validation.md create mode 100644 docs/auditing.md create mode 100644 docs/authentication.md create mode 100644 docs/authorization.md create mode 100644 docs/base-action.md create mode 100644 docs/base-configuration.md create mode 100644 docs/base-field.md create mode 100644 docs/base.md create mode 100644 docs/belongs-to-association.md create mode 100644 docs/boolean.md create mode 100644 docs/bulk-delete-action.md create mode 100644 docs/cancan-remove-associated-action-buttons-in-forms.md create mode 100644 docs/cancan.md create mode 100644 docs/cancancan.md create mode 100644 docs/carrierwave.md create mode 100644 docs/changing-locale.md create mode 100644 docs/ckeditor.md create mode 100644 docs/codemirror.md create mode 100644 docs/create.md create mode 100644 docs/custom-action.md create mode 100644 docs/custom-field.md create mode 100644 docs/customized-authorization.md create mode 100644 docs/dashboard-action.md create mode 100644 docs/decimal.md create mode 100644 docs/declarative-authorization.md create mode 100644 docs/delete-action.md create mode 100644 docs/devise.md create mode 100644 docs/dragonfly.md create mode 100644 docs/edit-action.md create mode 100644 docs/edit.md create mode 100644 docs/enumeration.md create mode 100644 docs/examples.md create mode 100644 docs/excluding-railsadmin-from-newrelic.md create mode 100644 docs/export-action.md create mode 100644 docs/export.md create mode 100644 docs/fields.md create mode 100644 docs/file-upload.md create mode 100644 docs/float.md create mode 100644 docs/froala-wysiwyg-html-editor.md create mode 100644 docs/groups.md create mode 100644 docs/has-and-belongs-to-many-association.md create mode 100644 docs/has-many-association.md create mode 100644 docs/has-many-through-association.md create mode 100644 docs/has-one-association.md create mode 100644 docs/hidden.md create mode 100644 docs/history-index-action.md create mode 100644 docs/history-show-action.md create mode 100644 docs/horizontally-scrolling-table-with-frozen-columns-in-list-view.md create mode 100644 docs/how-to-reloading-railsadmin-config-automatically.md create mode 100644 docs/how-to-set-default-values.md create mode 100644 docs/index-action.md create mode 100644 docs/index.md create mode 100644 docs/integer.md create mode 100644 docs/internal-audit-plugin.md create mode 100644 docs/list-view-table-styling.md create mode 100644 docs/list.md create mode 100644 docs/manually.md create mode 100644 docs/mass-assignments-protection.md create mode 100644 docs/modal.md create mode 100644 docs/models.md create mode 100644 docs/navigation.md create mode 100644 docs/nested.md create mode 100644 docs/new-action.md create mode 100644 docs/paperclip.md create mode 100644 docs/papertrail.md create mode 100644 docs/password.md create mode 100644 docs/performance.md create mode 100644 docs/polymorphic-belongs-to-association.md create mode 100644 docs/routing-problems.md create mode 100644 docs/rspec-with-capybara-examples.md create mode 100644 docs/show-action.md create mode 100644 docs/show-in-app-action.md create mode 100644 docs/show.md create mode 100644 docs/sorcery.md create mode 100644 docs/string.md create mode 100644 docs/text.md create mode 100644 docs/theming-and-customization.md create mode 100644 docs/timestamp-date-datetime-time.md create mode 100644 docs/translations.md create mode 100644 docs/troubleshoot.md create mode 100644 docs/update.md create mode 100644 docs/using-railsadmin-routes.md create mode 100644 docs/wysihtml5.md diff --git a/README.md b/README.md index 7164a816dc..c6efd6307d 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ RailsAdmin is a Rails engine that provides an easy-to-use interface for managing [demo]: http://rails-admin-tb.herokuapp.com/ [dummy_app]: https://github.com/bbenezech/dummy_app -[docs]: https://github.com/railsadminteam/rails_admin/wiki +[docs]: docs/index.md ## Features @@ -59,9 +59,9 @@ RailsAdmin is a Rails engine that provides an easy-to-use interface for managing In `config/initializers/rails_admin.rb`: -[Details](https://github.com/railsadminteam/rails_admin/wiki/Base-configuration) +[Details](docs/base-configuration.md) -To begin with, you may be interested in setting up [Devise](https://github.com/railsadminteam/rails_admin/wiki/Devise), [CanCanCan](https://github.com/railsadminteam/rails_admin/wiki/Cancancan) or [Papertrail](https://github.com/railsadminteam/rails_admin/wiki/Papertrail)! +To begin with, you may be interested in setting up [Devise](docs/devise.md), [CanCanCan](docs/cancancan.md) or [Papertrail](docs/papertrail.md)! ### Per model @@ -78,14 +78,14 @@ class Ball < ActiveRecord::Base end ``` -Details: [Models](https://github.com/railsadminteam/rails_admin/wiki/Models), [Groups](https://github.com/railsadminteam/rails_admin/wiki/Groups), [Fields](https://github.com/railsadminteam/rails_admin/wiki/Fields) +Details: [Models](docs/models.md), [Groups](docs/groups.md), [Fields](docs/fields.md) ## Support If you have a question, please check this README, the wiki, and the [list of known issues][troubleshoot]. -[troubleshoot]: https://github.com/railsadminteam/rails_admin/wiki/Troubleshoot +[troubleshoot]: docs/troubleshoot.md If you still have a question, you can ask the [official RailsAdmin mailing list][list]. diff --git a/docs/actions.md b/docs/actions.md new file mode 100644 index 0000000000..a6ad8a8284 --- /dev/null +++ b/docs/actions.md @@ -0,0 +1,208 @@ +## Default + +Actions used to be static and hard-coded. A community request was that they could be added/removed/customized. + +This is now possible. + +By default, to keep existing installation safe, all actions are added as they used to be. + +Default is equivalent to: + +```ruby +# config/initializers/rails_admin.rb +RailsAdmin.config do |config| + config.actions do + # root actions + dashboard # mandatory + # collection actions + index # mandatory + new + export + history_index + bulk_delete + # member actions + show + edit + delete + history_show + show_in_app + end +end +``` + +## Use existing actions and customize them + +Simply list them and pass an optional block, like so: + +```ruby +config.actions do + dashboard do + i18n_key :dash + end + index + new +end +``` + +Please note that `dashboard` and `index` are mandatory for the moment, but this may change in the future. + +## Define actions + +- `root` defines root level actions (Dashboard, etc.) +- `collection` defines collection level actions (Index, New, etc.) +- `member` defines member level actions (Show, Edit, etc.) + +First argument is the key of the action. +It will be the `i18n_key`, the `route_fragment`, the `action_name`, the `authorization_key`, etc. +You can override each of these individually. See the respective class and the [Base Action class](../lib/rails_admin/config/actions/base.rb) to get the list of these options. + +Second (optional) argument is the key of the parent class. It can be any existing Action class. If none given, it will be `Base`. + +Then you can pass the configuration block. + +Then add `app/views/rails_admin/main/my_action.html.` in your application, where you will be able to access: + +- `@abstract_model` (except for root actions, give the RailsAdmin representation of the model. Use .model to have your ActiveRecord original model) +- `@model_config` (except for root actions, give the RailsAdmin configuration of the model) +- `@objects = list_entries` (for collection actions, list the entries as specified in params, see the :index action and template) +- `@object` (member actions only, ActiveRecord object) + +```ruby +config.actions do + root :my_dashboard, :dashboard # subclass Dashboard. Accessible at /admin/my_dashboard + collection :my_collection_action do # subclass Base. Accessible at /admin//my_collection_action + ... + end + member :my_member_action do # subclass Base. Accessible at /admin///my_member_action + i18n_key :edit # will have the same menu/title labels as the Edit action. + end +end + +``` + +## Create a reusable action + +```ruby +# somewhere in your lib/ directory: + +require 'rails_admin/config/actions' +require 'rails_admin/config/actions/base' + +module RailsAdmin + module Config + module Actions + class MyAction < RailsAdmin::Config::Actions::Base + RailsAdmin::Config::Actions.register(self) + register_instance_option :my_option do + :default_value + end + end + end + end +end + +# use it like this: + +config.actions do + my_action do + my_option :another_value + end +end +``` + +If you want to share it as a gem, see [custom action](custom-action.md). + +## Action wording for title, menu, breadcrumb and links + +Default I18n key is action name underscored. You can change it like so: + +```ruby +config.actions do + dashboard do + i18n_key :customized + end + ... +end +``` + +Then head for your `config/locales/rails_admin.xx.yml` file: + +```yaml +xx: + admin: + actions: + : + title: "..." + menu: "..." + breadcrumb: "..." + link: "..." +``` + +See [rails_admin.en.yml](../config/locales/rails_admin.en.yml) to get an idea. + +Actions can provide specific option configuration, check their respective wiki page. + +## Controlling visibility + +### Through authorization + +Authorization is done automatically before any link is displayed, any page accessed, etc. +Check [CanCanCan](cancancan.md) for the list of key used by RailsAdmin default actions. + +You can change the authorization key with: + +```ruby +config.actions do + dashboard do + authorization_key :customized + end + ... +end +``` + +### Per-model basis + +```ruby +config.actions do + edit do + only ['Player'] + end + delete do + except ['Team', 'Ball'] + end +end +``` + +### Visible block + +You can use these 3 bindings to decide whereas the action should be visible or not: + +- `bindings[:controller]` is current controller instance +- `bindings[:abstract_model]` is checked abstract model (except root actions) +- `bindings[:object]` is checked instance object (member actions only) + +For instance, if you want to allow editing of games, but only if they haven't yet started: + +```ruby +config.actions do + edit do + visible do + object = bindings[:object] + case object + when Game then !object.started? # only allow editing games if they haven't started + when Player then true # allow editing of Players any time + else false # don't allow editing anything else + end + end + end +end +``` + +Have a look at [Show in App implementation](../lib/rails_admin/config/actions/show_in_app.rb) for a better idea of how you can take advantage of this. + +Important: at some point of the application lifecycle, bindings can be nil: + +- when RailsAdmin creates the route +- when RailsAdmin defines the action in its controller + +These bindings are available in all options. diff --git a/docs/actiontext.md b/docs/actiontext.md new file mode 100644 index 0000000000..62e97d9d34 --- /dev/null +++ b/docs/actiontext.md @@ -0,0 +1,61 @@ +# ActionText + +Rails 6 is shipped with a new rich text editor, called ActionText. RailsAdmin can make use of it in two ways. + +## Easy-to-use setup + +Follow [the official documentation](https://edgeguides.rubyonrails.org/action_text_overview.html#installation) to configure ActionText. + +```bash +$ rails action_text:install +``` + +```ruby +class Message < ApplicationRecord + has_rich_text :content +end +``` + +That's it, RailsAdmin will show the rich text editor in the edit form of Message model. + +Please note that this setup makes use of CDN-delivered assets for simplicity, hence it lacks some features like uploading attachments. + +## Full-featured setup using Webpacker + +First, you have to setup [Webpacker](https://github.com/rails/webpacker#installation) and [ActionText](https://edgeguides.rubyonrails.org/action_text_overview.html#installation) correctly. Watch out those issues if you're an early adopter: + +- https://github.com/rails/webpacker/issues/2109 +- https://github.com/rails/rails/issues/36368 + +Create `app/javascript/packs/actiontext.js` with following content: + +```javascript +require("trix"); +require("@rails/actiontext"); +``` + +Configure your rich text field to pick up Webpacker-built asset: + +```ruby + config.model 'Message' do + field :content do + js_location { bindings[:view].asset_pack_path 'actiontext.js' } + end + end +``` + +ActiveStorage Blob view generated by ActionText shows wrong image URL when used inside engine's path, so apply this patch to `app/views/active_storage/blobs/_blob.html.erb`: + +```diff +
attachment--<%= blob.filename.extension %>"> + <% if blob.representable? %> +- <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %> ++ <%= image_tag polymorphic_url(blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]), script_name: nil) %> + <% end %> + +
+``` + +Drag and drop an image to the editor and you can now see the image uploaded and stored into ActiveStorage. + +[More here](../lib/rails_admin/config/fields/types/action_text.rb) diff --git a/docs/activestorage.md b/docs/activestorage.md new file mode 100644 index 0000000000..87b017922f --- /dev/null +++ b/docs/activestorage.md @@ -0,0 +1,102 @@ +# ActiveStorage + +Install and configure according to [the official instruction](https://github.com/rails/rails/tree/master/activestorage#installation), first. + +Your model should look like this: + +## Simple upload + +```ruby +class Article < ActiveRecord::Base + has_one_attached :asset +end +``` + +You can specify the field as an 'active_storage' type if not detected: + +```ruby +field :asset, :active_storage +``` + +### Deleting an attachment + +You need to define a delete method if you want to delete attachment: + +```ruby +class Article < ActiveRecord::Base + has_one_attached :asset + attr_accessor :remove_asset + after_save { asset.purge if remove_asset == '1' } +end +``` + +The method name is `remove_#{name}` by default, but you can configure it using `delete_method` option: + +```ruby +field :asset, :active_storage do + delete_method :remove_asset +end +``` + +### show correct file name of attachment + +```ruby +field :asset, :active_storage do + delete_method :remove_asset + pretty_value do + if value + path = Rails.application.routes.url_helpers.rails_blob_path(value, only_path: true) + bindings[:view].content_tag(:a, value.filename, href: path) + end + end +end +``` + +## Multiple uploads + +Support for multiple uploads works the same way: + +```ruby +class Article < ActiveRecord::Base + has_many_attached :assets +end +``` + +### Deleting Multiple attachments + +```ruby +class Article < ActiveRecord::Base + has_many_attached :assets + # for deletion + attr_accessor :remove_assets + after_save do + Array(remove_assets).each { |id| assets.find_by_id(id).try(:purge) } + end +end +``` + + + +```ruby +field :assets, :multiple_active_storage do + delete_method :remove_assets +end +``` + +### show correct file name for Multiple attachments + +```ruby +#TODO + +``` + +### show thumbnails on show and edit views + +Showing thumbnails may require additional setup. + +For images, Rails 6 recommend installing the [`image_processing`](https://github.com/janko/image_processing) gem: + +```ruby +# add to your gemfile +gem 'image_processing', '~> 1.2' +``` diff --git a/docs/associations-basics.md b/docs/associations-basics.md new file mode 100644 index 0000000000..2210c828ed --- /dev/null +++ b/docs/associations-basics.md @@ -0,0 +1,119 @@ +### Select/Multiselect widget + +#### Ordered associations + +For `has_many/has_and_belongs_to_many/has_many :through` + +Orderable can be enabled on filtering multiselect fields, allowing selected options to be moved up/down. + +RailsAdmin will handle ordering in and out of the form. + +```ruby +RailsAdmin.config do |config| + config.model Player do + edit do + field :fans do + orderable true + end + end + end +end +``` + +You'll need to handle ordering in your model with a position column for example. See [here](has-many-through-association.md) for a comprehensive ActiveRecord example with a `has_many :through` association. + +#### Important + +You must specify attr_accessible for the singular-form \_ids setter method of your associated model, e.g., a `has_one` association. This setter method comes automatically with ActiveRecord when you create a `has_many` association, but not for a `has_one` association. + +For Rails 3, in the example above you would specify this at the top of your model: + +``` +attr_accessible :fan_ids +``` + +For Rails 4 and above you need to [define setters and getters](has-one-association.md) for `has_one` associations. + +If you fail to do this, the multiselect widget will simply not appear on your page. + +### Editing records + +You can edit related objects by double-clicking on any visible item in the widget. + +### Querying and searching by association columns + +You can configure which columns from associated records show up as available filters or get searched in the general search box. See [fields-searching](list.md#fields-searching) for more details. + +### Limit/filter associated records + +See [associations scoping](associations-scoping.md) for more informations on how to limit and filter proposed associated records. + +### Inverse_of: Avoiding edit association spaghetti issues + +If you set the `:inverse_of` option on your relations, RailsAdmin will automatically populate the inverse relationship +in the modal creation window. (link next to :belongs_to and :has_many multiselect widgets) + +It will also hide the inverse relation on nested forms. As a good practice, you should always set `:inverse_of` options to all your associations, even if these are useless to ActiveRecord (in combination with :through and :as). RailsAdmin will take advantage of them. But it will bomb 'Unknown key: inverse_of' on HABTMs, you'll need to set them manually: + +Simply set it directly into your configuration: + +```ruby +config.model Team do + field :categories do + inverse_of :teams + end +end +``` + +### Readonly + +`:readonly` options are automatically inferred from associations and won't be editable in forms. + +### Nested form + +If you have `accepts_nested_attributes_for` set up in your model but don't want the association to be a nested form in your model: + +```ruby +config.model Team do + field :players do + nested_form false + end +end +``` + +[More here](../lib/rails_admin/config/fields/association.rb) + +### Disabling inline creation and edition + +In some cases you may want to disable the addition or edition of an association, the simplest way to do so is by using the `inline_add` and `inline_edit` options: + +```ruby +config.model Player do + field :team do + inline_add false + inline_edit false + end +end +``` + +### Eager load + +Associations can be configured to be eager-loaded to prevent N+1 queries. `belongs_to` associations are eager-loaded by default, but you can configure additional eager-loads like: + +```ruby +config.model Team do + field :players do + eager_load true + end +end +``` + +or passing a custom value: + +```ruby +config.model Team do + field :players do + eager_load players: :draft + end +end +``` diff --git a/docs/associations-scoping.md b/docs/associations-scoping.md new file mode 100644 index 0000000000..dfdb69b753 --- /dev/null +++ b/docs/associations-scoping.md @@ -0,0 +1,116 @@ +### Scoping, ordering and limiting associable records in filtering selects/multiselects + +You may have business rules where you want to limit the members of a collection that are available for association with a particular record. For example, a Player might be a member of a League. When selecting Players for a Team, we wouldn't want to see all the Players we know about, just the ones in the same League as the Team. + +For all associations types (other than polymorphics at the moment) you can scope associable records with: + +```ruby +config.model Team do + field :players do + associated_collection_cache_all false # REQUIRED if you want to SORT the list as below + associated_collection_scope do + # bindings[:object] & bindings[:controller] are available, but not in scope's block! + team = bindings[:object] + Proc.new { |scope| + # scoping all Players currently, let's limit them to the team's league + # Be sure to limit if there are a lot of Players and order them by position + scope = scope.where(league_id: team.league_id) if team.present? + scope = scope.limit(30) # 'order' does not work here + } + end + end +end +``` + +Use `associated_collection_cache_all true` if you want all associated records preloaded. +Defaults to true if there are less than 100 records in the associated collection. +The scope will default to limit records to 30, unless cache_all is true (no limit). + +**bindings[:object] can be null for new parent records!** Also note that the scope takes in to account the saved version of the record, not considering any unsaved changes you may have made in the edit form. If you change the team's league, you'll still see the players from the old league until you save. + +Validating associations is up to your models, and you'll certainly want to set the up properly. This functionality is basically a filter--that allows you to scope in on the records you're likely to want. It does not enforce what gets mapped in your database. If the Team knows the id of a Player in another League, he can associate it. Use `authorization` or association conditions, and a validation, to prevent that. + +### Scoping the relation itself with conditions + +This is good and all but it doesn't ensure anything about security and sanity! + +Now let's see the relation itself: + +```ruby + class Team + has_many :number_time_players, :conditions => proc { { :position => Time.now.to_i } }, :class_name => 'Player' + end +``` + +console: + +``` + > Team.first.number_time_players.build +=> # position: 1320166460, > + > Team.first.number_time_players.build +=> # position: 1320166461, > + > Team.first.number_three_players +Player Load (1.2ms) SELECT "players".* FROM "players" WHERE "players"."team_id" = 1 AND "players"."position" = 1320167057 +``` + +Note that position changes at each request, you can use lambdas. +You can use `:after_add` hook to reject records you don't want (sanity check). +More on ActiveRecord's API pages. + +RailsAdmin doesn't know about :conditions in your association, so you'll need to use `authorization` or `associated_collection_scope` to scope visible records (in the select box) + +### Restricting records based on association scope + +You can configure RailsAdmin to use the scope as defined on your association (also works on through associations). The following code will result in only showing `active` players for the Players field on the Team form: + +```ruby +class Team < ApplicationRecord + has_many :memberships, inverse_of: :team + has_many :players, through: :memberships +end + +class Membership < ApplicationRecord + belongs_to :team, inverse_of: :memberships + belongs_to :player, -> { active }, inverse_of: :memberships +end + +class Player < ApplicationRecord + has_many :memberships, inverse_of: :player + scope :active, -> { where(retired: false) } +end + +config.model Team do + edit do + include_fields :policy_section, :title, :description, :players + configure :players do + associated_collection_scope do + resource_scope = bindings[:object].class.reflect_on_association(:players).source_reflection.scope + + proc do |scope| + resource_scope ? scope.merge(resource_scope) : scope + end + end + end + end +end +``` + +### Restricting records through authorization + +Another way to scope potential records is to use authorization, through Cancan: + +```ruby +class Ability + include CanCan::Ability + def initialize(user) + can :manage, Contact, :email => user.email + end +end +``` + +The advantage here is that user will never be able to see 'wrong' contacts and he won't be able to set a wrong email. +More on cancan's own page. + +### Restricting fields through roles or some condition + +You can also restrict some field through role or some condition, [take a look at this post](https://glaucocustodio.github.io/2013/11/28/conditional-fields-in-rails-admin/) diff --git a/docs/associations-validation.md b/docs/associations-validation.md new file mode 100644 index 0000000000..08fe53cf21 --- /dev/null +++ b/docs/associations-validation.md @@ -0,0 +1,21 @@ +# Associations validation + +`:validates_presence_of` works for `belongs_to`, `has_one` and `has_many` associations in Rails3, so you can safely have: + +```ruby +belongs_to :draft +validates :draft, :presence => true # you will need one associated object +# validates :draft_id, :presence => true would also work, but is not recommended (validation of foreign key). + +has_one :team +validates :team, :presence => true # you will need one associated object + +has_many :comments +validates :comments, :presence => true # you will need at least one associated object +``` + +RailsAdmin will look on both `:draft` and `:draft_id` for validation when checking for `:required?` and for displaying errors messages. + +Polymorphic `belongs_to` will work as well, the same way. + +`validates_associated` is a different thing: it validates the opposite object(s), and nil (no associated object) is valid for `belongs_to` associations. diff --git a/docs/auditing.md b/docs/auditing.md new file mode 100644 index 0000000000..a4534e9485 --- /dev/null +++ b/docs/auditing.md @@ -0,0 +1,15 @@ +# Auditing + +You can display an historic of all actions done on any model/instance with the help of one of the provided plugin integration below: + +- [PaperTrail](papertrail.md) (ActiveRecord) +- [mongoid_audit](https://github.com/rs-pro/mongoid-audit) (Mongoid) +- [Internal audit plugin](internal-audit-plugin.md) (ActiveRecord - legacy) + +If you wish to rollback any changes recorded by PaperTrail, use [RailsAdminHistoryRollback](https://github.com/rikkipitt/rails_admin_history_rollback). It provides a visual diff of the model changes with the ability to revert. See below screenshots. + +## Screenshots + +![History view](https://github.com/rikkipitt/rails_admin_history_rollback/raw/master/screenshots/history.png "history view") + +![Modal view](https://github.com/rikkipitt/rails_admin_history_rollback/raw/master/screenshots/modal.png "modal view") diff --git a/docs/authentication.md b/docs/authentication.md new file mode 100644 index 0000000000..b399ffd6dd --- /dev/null +++ b/docs/authentication.md @@ -0,0 +1,14 @@ +# Authentication + +You can (each is optional) provide 2 things: + +1. An `authenticate_with` block that will trigger your authentication logic before any action in RailsAdmin. +2. A `current_user_method` block that will yield a user model (for UI purposes) + +If your application responds to a route helper named `logout_path` then Rails Admin will add a "Log out" button to the top-right corner of your app. _[Needs expansion: There's also a Devise integration but I don't know how Devise works. See [#1386](https://github.com/railsadminteam/rails_admin/issues/1386) for details.]_ + +Popular authentication gems examples: + +- [Devise](devise.md) +- [Sorcery](sorcery.md) +- [Manually](manually.md) diff --git a/docs/authorization.md b/docs/authorization.md new file mode 100644 index 0000000000..d14aea5f3c --- /dev/null +++ b/docs/authorization.md @@ -0,0 +1,36 @@ +# Authorization + +Authorization can be added using the `authorize_with` method. If you pass a block +it will be triggered through a `before_action` on every action in Rails Admin. + +For example: + +```ruby +RailsAdmin.config do |config| + config.authorize_with do + redirect_to main_app.root_path unless current_user.is_admin? + end +end +``` + +To use an authorization adapter, pass the name of the adapter. For example, to use +with [CanCanCan](https://github.com/CanCanCommunity/cancancan), pass it like this. + +```ruby +RailsAdmin.config do |config| + config.authorize_with :cancancan +end +``` + +Also, there's built-in support for Pundit: + +```ruby +RailsAdmin.config do |config| + config.authorize_with :pundit +end +``` + +- [CanCanCan (recommended)](cancancan.md) +- [CanCan with relation to current Model](cancan-remove-associated-action-buttons-in-forms.md) +- [Declarative Authorization (possible)](declarative-authorization.md) +- [Manually](customized-authorization.md) diff --git a/docs/base-action.md b/docs/base-action.md new file mode 100644 index 0000000000..5cb5fca263 --- /dev/null +++ b/docs/base-action.md @@ -0,0 +1,41 @@ +# Base action + +_All actions inherit from this one._ + +### #only + +Restrict action to one/more specified model(s): + +```ruby +actions do + index do + only Player # no other model will have the `index` action visible. + end + + new do + only [Player, Comment] # no other model will have the `new` action visible. Note the extra brackets '[]' when there is more than one model. + end +end +``` + +Relevant only for model/instance actions, not base actions (like `dashboard`). + +### #except + +Restrict action from one/more specified model(s): + +```ruby +actions do + index do + except Player # all other models will have the `index` action visible. + end + + new do + except [Player, Comment] # all other models will have the `new` action visible. Note the extra brackets '[]' when there is more than one model. + end +end +``` + +Relevant only for model/instance actions, not base actions (like `dashboard`). + +[More here](../lib/rails_admin/config/actions/base.rb) diff --git a/docs/base-configuration.md b/docs/base-configuration.md new file mode 100644 index 0000000000..10157a12f3 --- /dev/null +++ b/docs/base-configuration.md @@ -0,0 +1,62 @@ +# Base configuration + +RailsAdmin provides its out of the box administrative interface by inspecting your application's +models and following some Rails conventions. For a more tailored experience, it also provides a +configuration DSL which allows you to customize many aspects of the interface. + +**Set the application name:** + +```ruby +RailsAdmin.config do |config| + config.main_app_name = ["Cool app", "BackOffice"] + # or something more dynamic + config.main_app_name = Proc.new { |controller| [ "Cool app", "BackOffice - #{controller.params[:action].try(:titleize)}" ] } +end +``` + +**Locale** + +If your default_locale is different from :en, set your default locale at the beginning of the file: + +```ruby +require 'i18n' +I18n.default_locale = :de +``` + +**Authentication integration (Devise, Sorcery, Manual)** + +[Authentication](authentication.md) + +**Authorization (Cancan)** + +[Authorization](authorization.md) + +**ActiveModel's :attr_accessible :attr_protected** + +Default is :default (default for ActiveModel) + +```ruby +config.attr_accessible_role { :default } +``` + +`_current_user` is accessible in the block if you need to make it user specific: + +```ruby +config.attr_accessible_role { _current_user.role.to_sym } +``` + +**Instance labels** + +```ruby +config.label_methods << :description # Default is [:name, :title] +``` + +**Browser validations** + +```ruby +config.browser_validations = false # Default is true +``` + +**Next** + +Then you can start adding [actions](actions.md), configuring [models](models.md), [sections](base.md) and [fields](fields.md). diff --git a/docs/base-field.md b/docs/base-field.md new file mode 100644 index 0000000000..9ec2094d46 --- /dev/null +++ b/docs/base-field.md @@ -0,0 +1,5 @@ +# Base field + +Parent class for all fields, should not be used directly. + +[More here](../lib/rails_admin/config/fields/base.rb) diff --git a/docs/base.md b/docs/base.md new file mode 100644 index 0000000000..72476b271c --- /dev/null +++ b/docs/base.md @@ -0,0 +1,20 @@ +# Base + +Base section. + +All other sections inherits from it. +It's the section used in configuration when no section is specified (you can also explicitly use the `base` section name). + +Example: + +```ruby +RailsAdmin.config do |config| + config.model Team do + configure :name do + label "Team's name" + end + end +end +``` + +[More here](../lib/rails_admin/config/sections/base.rb) diff --git a/docs/belongs-to-association.md b/docs/belongs-to-association.md new file mode 100644 index 0000000000..ec5248d868 --- /dev/null +++ b/docs/belongs-to-association.md @@ -0,0 +1,16 @@ +# `belongs_to` association + +```ruby +class Player < ActiveRecord::Base + belongs_to :team, :inverse_of => :players # dropdown select: belongs_to association + # or for nested fields: + accepts_nested_attributes_for :team, :allow_destroy => true +end + +# for info +class Team < ActiveRecord::Base + has_many :players, :inverse_of => :team +end +``` + +[More here](../lib/rails_admin/config/fields/types/belongs_to_association.rb) diff --git a/docs/boolean.md b/docs/boolean.md new file mode 100644 index 0000000000..c7023fdec3 --- /dev/null +++ b/docs/boolean.md @@ -0,0 +1,5 @@ +# Boolean + +Default view_helper is a checkbox - when checked, the value of the attribute is the `String`, `"1"`. + +[More here](../lib/rails_admin/config/fields/types/boolean.rb) diff --git a/docs/bulk-delete-action.md b/docs/bulk-delete-action.md new file mode 100644 index 0000000000..63786d82be --- /dev/null +++ b/docs/bulk-delete-action.md @@ -0,0 +1,18 @@ +### Example authorizations for cancan: + +```ruby + # with + alias_action :update, :destroy, :create, :to => :write + + can :manage, :all + # includes + can :write, :all + # includes + can :destroy, Model + # equals + can :delete, Model + # includes + can :destroy, Model, { conditions } +``` + +[More here](../lib/rails_admin/config/actions/bulk_delete.rb) diff --git a/docs/cancan-remove-associated-action-buttons-in-forms.md b/docs/cancan-remove-associated-action-buttons-in-forms.md new file mode 100644 index 0000000000..009dcb85e4 --- /dev/null +++ b/docs/cancan-remove-associated-action-buttons-in-forms.md @@ -0,0 +1,102 @@ +# CanCan remove associated action buttons in forms + +**Help: I dont feel too confident about my ruby-skills. Please help improving this article by improving the source where needed, thanks** + +In order to remove the "Add new"/"Edit" buttons for associated Models in the form without removing the ability to manage those associated models, cancan helps out well. + +**Example: a User can :manage Authors and Books, but must not be able to :manage authors while in Books-Edit form** + +The idea is pretty simple: + +```ruby + cannot [:create,:update], Model unless current_model == Model +``` + +The default CanCan AuthorizationAdapter however does not inform the Ability-Class about the current view/model, that is why it needs to be overwritten. + +--- + +### In your Rails-App root: + +
mkdir -p lib/rails_admin/extensions/cancan/
+cd  lib/rails_admin/extensions/cancan/;
+wget https://raw.github.com/gist/3302673/7d013f23bba67a7c7317003c56117b2a88503d39/authorization_adapter.rb
+
+ +This file, contains the ControllerExtension-Module, cloned from RailsAdmins original CanCan AuthorizationAdapter, except for the 2nd Parameter in @ability.new, which represents the current displayed model you want to edit. + +This gist contains: + +```ruby +module RailsAdmin + module Extensions + module CanCan + class AuthorizationAdapter + private + module ControllerExtension + def current_ability + @current_ability ||= @ability.new(_current_user, self.params["model_name"]) + end + end + end + end + end +end +``` + +--- + +### config/initializers/rails_admin.rb + +the CanCan configuration will remain the same, except for loading the updated authorization_adapter.rb. + +```ruby + require Rails.root.join('lib/rails_admin/extensions/cancan/authorization_adapter') + RailsAdmin.config do |config| + .... +``` + +I load the authorization_adapter here, since i find it belongs to rails_admins configuration, which i dont want to have "all over the app". + +--- + +### Ability-Class + +The only mandatory update is to add a further parameter to the initialize method, named like request_model. It is the Model-Class you are viewing,editing in RailsAdmin. + +My Example from above was: "a User can :manage Authors and Books, but should not be able to manage Autors while in Books-Edit form" + +this can result in something like: + +```ruby +class Ability + include CanCan::Ability + + def initialize(user, request_model) + + if user + can :access, :rails_admin + can :dashboard + can :manage, :all + + # For all Models we have, forbid create and update + # but dont forbid :manage, in order to keep it in + # navigation if not excluded by rails_admin config + ActiveRecord::Base.descendants.each do |m| + cannot [:create, :update], m unless request_model == m.name.underscore + end if request_model + + # a manual way can look like this : + # cannot [:create, :update], Author if request_model == "books" + # cannot [:create, :update], Book if request_model == "author" + + else + cannot :access, :rails_admin + end + end +end + +``` + +Thats it, now the Edit/Create associated Model-buttons are gone, but the Model remains in the Navigation. +This is quite usefull if a User may create associations with models which are excluded via rails_admin config diff --git a/docs/cancan.md b/docs/cancan.md new file mode 100644 index 0000000000..1794de4565 --- /dev/null +++ b/docs/cancan.md @@ -0,0 +1,3 @@ +# CanCan + +See [CanCanCan](cancancan.md). diff --git a/docs/cancancan.md b/docs/cancancan.md new file mode 100644 index 0000000000..70ae0b235b --- /dev/null +++ b/docs/cancancan.md @@ -0,0 +1,123 @@ +# CanCanCan + +Rails Admin is fully compatible with [CanCanCan](https://github.com/CanCanCommunity/cancancan). + +### Configuration + +Add this to RailsAdmin initializer. + +```ruby +# config/initializers/rails_admin.rb + +RailsAdmin.config do |config| + config.authorize_with :cancancan +end +``` + +At this point, all authorization will fail and no one will be able to access the admin pages. To grant access, add this to `Ability#initialize`. +You must also grant access to the `dashboard`, or the login will fail there. + +```ruby +can :access, :rails_admin # grant access to rails_admin +can :read, :dashboard # grant access to the dashboard +``` + +Then, you will need to grant access on each of the models. Here's a complete example of an `Ability` class which defines different permissions depending upon the user's role. + +```ruby +class Ability + include CanCan::Ability + def initialize(user) + can :read, :all # allow everyone to read everything + return unless user && user.admin? + can :access, :rails_admin # only allow admin users to access Rails Admin + can :read, :dashboard # allow access to dashboard + if user.role? :superadmin + can :manage, :all # allow superadmins to do anything + elsif user.role? :manager + can :manage, [User, Product] # allow managers to do anything to products and users + elsif user.role? :sales + can :update, Product, hidden: false # allow sales to only update visible products + end + end +end +``` + +How you define the user roles is completely up to you. See the [CanCanCan Documentation](https://github.com/CanCanCommunity/cancancan/wiki) for more information. + +### Use different Ability classes for front-end and admin + +If you use CanCanCan in your project, there are chances that abilities for RailsAdmin will conflict with your project ones. In that case, you will want to define a specific Ability class for admin section (e.g. `AdminAbility`). + +You just have to add your admin ability class as a second parameter to `authorize_with`: + +```ruby +# in config/initializers/rails_admin.rb + +RailsAdmin.config do |config| + config.authorize_with :cancancan, AdminAbility +end +``` + +With `AdminAbility`: + +```ruby +# in models/admin_ability.rb +class AdminAbility + include CanCan::Ability + def initialize(user) + return unless user && user.admin? + can :access, :rails_admin + can :manage, :all + end +end +``` + +### Handle Unauthorized Access + +If the user authorization fails, a CanCan::AccessDenied exception will be raised. You can catch this and modify its behavior in the ApplicationController. + +```ruby +class ApplicationController < ActionController::Base + rescue_from CanCan::AccessDenied do |exception| + redirect_to main_app.root_path, alert: exception.message + end +end +``` + +Also make sure RailsAdmin is inheriting from ApplicationController: + +```ruby +# in config/initializers/rails_admin.rb + +config.parent_controller = 'ApplicationController' +``` + +### RailsAdmin verbs + +Each action in RailsAdmin checks for authorization if adapter is present. +Usually the name of the action gives the name of the verb used. + +Here are the checks used by default in RailsAdmin: + +```ruby +# Always performed +can :access, :rails_admin # needed to access RailsAdmin + +# Performed checks for `root` level actions: +can :read, :dashboard # dashboard access + +# Performed checks for `collection` scoped actions: +can :index, Model # included in :read +can :new, Model # included in :create +can :export, Model +can :history, Model # for HistoryIndex +can :destroy, Model # for BulkDelete + +# Performed checks for `member` scoped actions: +can :show, Model, object # included in :read +can :edit, Model, object # included in :update +can :destroy, Model, object # for Delete +can :history, Model, object # for HistoryShow +can :show_in_app, Model, object +``` diff --git a/docs/carrierwave.md b/docs/carrierwave.md new file mode 100644 index 0000000000..ac7d0b9802 --- /dev/null +++ b/docs/carrierwave.md @@ -0,0 +1,104 @@ +# Carrierwave + +Once added to your Gemfile, and after `bundle install` has been run, Carrierwave is ready to be used with rails admin. No further configuration is required to integrate it. + +Your model should look like this: + +```ruby +class Article < ActiveRecord::Base + mount_uploader :asset, AssetUploader +end +``` + +You can specify the field as a 'carrierwave' type if not detected: + +```ruby +field :asset, :carrierwave +``` + +Now a file upload field will be added to your model's form. + +## Polymorphic association + +If you have an Asset model class as polymorphic and you want a thumbnail in you Article model. + +Create the association: + +```ruby +class Article < ActiveRecord::Base + has_one :post_thumbnail, :as => :assetable, :class_name => Asset +end +``` + +In rails_admin.rb initializer indicate :post_thumbnail should be a file tag. + +```ruby +config.model Asset do + edit do + field :asset, :carrierwave + end +end +config.model Article do + nested do + field :post_thumbnail + end +end +``` + +## Multiple upload + +RailsAdmin also work with CarrierWave's native multiple upload feature, but it has a little quirkiness and needs some work depending on your usage. + +Simple setup goes like: + +```ruby +class Article < ActiveRecord::Base + mount_uploaders :assets, AssetUploader +end +``` + +```ruby +field :assets, :multiple_carrierwave +``` + +### Want to delete attachments + +CarrierWave decides whether the attachments to be deleted or not by looking `remove_#{name}` attribute. But when activated, it removes all attachments, so RailsAdmin decided not to use it. + +Instead, you need to implement alternative way in your model like this: + +```ruby +class Article < ActiveRecord::Base + mount_uploaders :assets, CarrierwaveUploader + attr_accessor :delete_assets + after_validation do + uploaders = assets.delete_if do |uploader| + if Array(delete_assets).include?(uploader.file.filename) + uploader.remove! + true + end + end + write_attribute(:assets, uploaders.map { |uploader| uploader.file.filename }) + end +end +``` + +### Want to preserve existing attachments when uploading more + +By default, CarrierWave's multiple upload feature discards existing ones when new files are uploaded. Here's a workaround: + +```ruby +class Article < ActiveRecord::Base + mount_uploaders :assets, CarrierwaveUploader + def assets=(files) + appended = files.map do |file| + uploader = _mounter(:assets).blank_uploader + uploader.cache! file + uploader + end + super(assets + appended) + end +end +``` + +[More here](../lib/rails_admin/config/fields/types/carrierwave.rb) diff --git a/docs/changing-locale.md b/docs/changing-locale.md new file mode 100644 index 0000000000..c800d5d7b9 --- /dev/null +++ b/docs/changing-locale.md @@ -0,0 +1,27 @@ +# Changing locale + +Creating a separate controller (inherit from ActionController::Base not ApplicationController) with I18n logic and inherit rails_admin controller from it in a config file will do the job. + +```ruby +class RailsAdminAbstractController < ActionController::Base + around_action :switch_locale + + private + + def switch_locale(&action) + I18n.with_locale(:en, &action) # or anything you like + end +end +``` + +`config/initializers/rails_admin.rb` + +```ruby +... +RailsAdmin.config do |config| + ... + config.parent_controller = '::RailsAdminAbstractController' + ... +``` + +See also [this](https://github.com/railsadminteam/rails_admin/pull/2805) and never forget about [this](https://guides.rubyonrails.org/i18n.html#managing-the-locale-across-requests). diff --git a/docs/ckeditor.md b/docs/ckeditor.md new file mode 100644 index 0000000000..7ac117bbcb --- /dev/null +++ b/docs/ckeditor.md @@ -0,0 +1,58 @@ +# CKEditor + +To use the CKEditor with Upload function, add [Rails-CKEditor](https://github.com/galetahub/ckeditor) to your Gemfile (`gem 'ckeditor'`) and follow [Rails-CKEditor](https://github.com/galetahub/ckeditor) installation instructions. + +You can configure more options of CKEditor "config.js" file following the [Api Documentation](http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html) . + +```ruby +RailsAdmin.config do |config| + config.model Team do + edit do + # For RailsAdmin >= 0.5.0 + field :description, :ck_editor + # For RailsAdmin < 0.5.0 + # field :description do + # ckeditor true + # end + end + end +end +``` + +## Configuration + +CKEditor accepts [various options](https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_config.html), you can configure them in following way: + +```ruby +field :description, :ck_editor do + config_js ActionController::Base.helpers.asset_path('your_ckeditor_config.js') +end +``` + +## Misc + +Although ckeditor is loaded in modal windows (e.g. twb), by default changes are not saved by submitting the modal. This is because the underlying textarea gets not updated before the ajax post request is issued. To prevent this you can use the following code: + +```coffeescript +$(document).ready -> + $(document).on 'mousedown', '.save-action', (e) -> # triggers also when submitting form with enter + for instance of CKEDITOR.instances + editor = CKEDITOR.instances[instance] + if editor.checkDirty() + editor.updateElement(); + return true; +``` + +Add this code to assets/javascripts/rails_admin/custom/ckeditor_ajax.js.coffee and create a file assets/javascripts/rails_admin/custom/ui.js that loads this coffee file into asset pipeline: + +``` +//= require rails_admin/custom/ckeditor_ajax +``` + +Reminder: It's necessary to configure ckeditor in asset precompile: + +``` +Rails.application.config.assets.precompile += ['ckeditor/*'] +``` + +[More here](../lib/rails_admin/config/fields/types/ck_editor.rb) diff --git a/docs/codemirror.md b/docs/codemirror.md new file mode 100644 index 0000000000..df100e728d --- /dev/null +++ b/docs/codemirror.md @@ -0,0 +1,37 @@ +# CodeMirror + +Add + +```ruby +gem 'codemirror-rails' +``` + +to your Gemfile. + +```ruby +RailsAdmin.config do |config| + config.model Team do + edit do + # For RailsAdmin >= 0.5.0 + field :description, :code_mirror + # For RailsAdmin < 0.5.0 + # field :description do + # codemirror true + # end + end + end +end +``` + +This code that you see upper hide all other columns and left only description. +Ruby 2.1.0 Rails 4.0.2 - this configuration helped me. + +```ruby +RailsAdmin.config do |config| + config.model Team do + configure :code, :code_mirror + end +end +``` + +[More here](../lib/rails_admin/config/fields/types/code_mirror.rb) diff --git a/docs/create.md b/docs/create.md new file mode 100644 index 0000000000..48130c55df --- /dev/null +++ b/docs/create.md @@ -0,0 +1,7 @@ +# Create + +Section used for the create (new) view. + +It inherits its configuration from the `base` section and the `edit` section. + +[More here](../lib/rails_admin/config/sections/create.rb) diff --git a/docs/custom-action.md b/docs/custom-action.md new file mode 100644 index 0000000000..34035c63e3 --- /dev/null +++ b/docs/custom-action.md @@ -0,0 +1,78 @@ +## Create a reusable action `ActionName` + +```bash +rails plugin new rails_admin_ -m https://gist.github.com/bbenezech/1621146/raw/5268788e715397bf476c83d76d335f152095e659/rails_admin_action_creator --skip-gemfile --skip-bundle -T -O -S -J --full +``` + +## Add it to your project + +```ruby +# Gemfile + +# if uploaded to github with a valid .gemspec (remove TODOS and change owner credentials) +gem 'rails_admin_', :git => 'git://github.com//rails_admin_.git' +# or in development mode +gem 'rails_admin_', :path => '../rails_admin_' +``` + +## Development documentation + +See the Base class your `ActionName` will inherit from: + +https://github.com/railsadminteam/rails_admin/blob/master/lib/rails_admin/config/actions/base.rb + +It is also possible to inherit from any other action class. + +## Resources + +How to create custom action in Rails admin, as a plugin: + +http://blog.endpoint.com/2012/03/railsadmin-custom-action-case-study.html + +Add a custom action without needing to add it as a plugin: + +https://web.archive.org/web/20180828051240/http://blog.paulrugelhiatt.com/ruby/rails/2014/10/27/rails-admin-custom-action-example.html + +Create a custom action: + +https://blog.codeminer42.com/writing-custom-railsadmin-actions-e0799aadc8ae + +## Double pjax + +If you're seeing a double call to your new action, try disabling pjax. + +```ruby +register_instance_option :pjax? do + false +end +``` + +## Automatically add custom action to rails_admin available action + +The order in the menu (if it's a custom action, is the position in the main navigation sidapanel) is given by the requiring it befor or after other custom actions but all custom actions, loaded this way, are placed the end of the default actions. + +In an initializer, say `[ENGINE_NAME]\config\initializers\my_action_initializer.rb` + +```ruby +RailsAdmin.config do |config| + config.actions do + thecore_record_analysis + end +end +``` + +## Manage custom action's visibility via CanCanCan + +### Root Actions + +Example (in case the root action is called my_root_action and rails admin ~> 1): + +```ruby +can :my_root_action, :all if user.admin? +``` + +For rails admin ~> 2 + +```ruby +can :read, :my_root_action if user.admin? +``` diff --git a/docs/custom-field.md b/docs/custom-field.md new file mode 100644 index 0000000000..81b392ba56 --- /dev/null +++ b/docs/custom-field.md @@ -0,0 +1,22 @@ +## Create a reusable field `FieldName` + +``` +rails plugin new rails_admin_ -m https://gist.github.com/bbenezech/1626605/raw/8f67e4eb05418a92a20649fc551fcd1bacb481d1/rails_admin_field_creator --skip-gemfile --skip-bundle -T -O -S -J --full +``` + +## Add it to your project + +```ruby +# Gemfile + +# if uploaded to github with a valid .gemspec (remove TODOS and change owner credentials) +gem 'rails_admin_', :git => 'git://github.com//rails_admin_.git' +# or in development mode +gem 'rails_admin_', :path => '../rails_admin_' +``` + +## Development documentation + +See the Base class your `FieldName` will inherit from: + +https://github.com/railsadminteam/rails_admin/blob/master/lib/rails_admin/config/fields/base.rb diff --git a/docs/customized-authorization.md b/docs/customized-authorization.md new file mode 100644 index 0000000000..cc598664dc --- /dev/null +++ b/docs/customized-authorization.md @@ -0,0 +1,13 @@ +# Customized authorization + +You have access to the controller though `self` or with a block variable. You can decide whether the user should or should not be allowed to continue with something like: + +```ruby +# in config/initializer/rails_admin.rb + +RailsAdmin.config do |config| + config.authorize_with do |controller| + redirect_to main_app.root_path unless current_user.try(:admin?) + end +end +``` diff --git a/docs/dashboard-action.md b/docs/dashboard-action.md new file mode 100644 index 0000000000..280c12a15d --- /dev/null +++ b/docs/dashboard-action.md @@ -0,0 +1,39 @@ +### Example authorizations for cancan: + +```ruby + can :manage, :all + # includes + can :dashboard +``` + +### Disabling record count bars: + +You can hide dashboard statistics graphs via the [actions](actions.md) configuration. This is useful when working with huge datasets that take a long time to be queried. + +Note that once you start configuring actions, it will only load the ones you specify, so if you want to disable statistics while keeping everything else at the default setting, then you need to include all the actions, like so: + +```ruby +RailsAdmin.config do |c| + c.actions do + dashboard do + statistics false + end + # collection actions + index # mandatory + new + export + history_index + bulk_delete + # member actions + show + edit + delete + history_show + show_in_app + end +end +``` + +Note that disabling statistics removes the entire dashboard table, not just the colored graphs. + +[More here](../lib/rails_admin/config/actions/dashboard.rb) diff --git a/docs/decimal.md b/docs/decimal.md new file mode 100644 index 0000000000..c9fe70a3c2 --- /dev/null +++ b/docs/decimal.md @@ -0,0 +1,3 @@ +# Decimal + +[More here](../lib/rails_admin/config/fields/types/decimal.rb) diff --git a/docs/declarative-authorization.md b/docs/declarative-authorization.md new file mode 100644 index 0000000000..bb3bef778e --- /dev/null +++ b/docs/declarative-authorization.md @@ -0,0 +1,35 @@ +# Declarative authorization + +[Declarative Authorization](https://github.com/stffn/declarative_authorization) is not fully integrated into Rails Admin, so it is only possible to add permissions to the controller actions. Currently if you want more integrated authorization, consider [CanCan](cancan.md). + +You can hook declarative_authorization into Rails Admin using code like this in an initializer (e.g., config/initializers/rails_admin.rb): + +```ruby +require "rails_admin/application_controller" + +module RailsAdmin + class ApplicationController < ::ApplicationController + filter_access_to :all + end +end +``` + +By default, access to the controllers will be denied to all users, so +you need to write some authz rules so that the appropriate users can +get access. These rules will vary, but here's an example: + +```ruby +authorization do + role :admin do + has_permission_on :rails_admin_history, :to => [:list, :slider, :for_model, :for_object] + has_permission_on :rails_admin_main, :to => [:index, :show, :new, :edit, :create, :update, :destroy, :list, :delete, :bulk_delete, :bulk_destroy, :get_pages, :show_history] + end +end +``` + +This will allow the :admin role to do everything, and will prevent all +other roles from doing anything. + +# Authorization Adapter + +If you would like better support for Declarative Authorization in Rails Admin, consider making an authorization adapter for it. See the [CanCanCan Adapter](../lib/rails_admin/extensions/cancancan/authorization_adapter.rb) for an example. Fork the project, add the adapter, and send a pull request. diff --git a/docs/delete-action.md b/docs/delete-action.md new file mode 100644 index 0000000000..3b482c68ee --- /dev/null +++ b/docs/delete-action.md @@ -0,0 +1,20 @@ +### Example authorizations for cancan: + +```ruby + # with + alias_action :update, :destroy, :create, :to => :write + + can :manage, :all + # includes + can :write, :all + # includes + can :write, Model + # includes + can :destroy, Model + # includes + can :destroy, Model, { conditions } + # equals + can :delete, Model, { conditions } +``` + +[More here](../lib/rails_admin/config/actions/delete.rb) diff --git a/docs/devise.md b/docs/devise.md new file mode 100644 index 0000000000..ef08c17de3 --- /dev/null +++ b/docs/devise.md @@ -0,0 +1,42 @@ +# Devise + +Example for Warden/Devise with an 'user' scope: + +In `config/initializers/rails_admin.rb` + +```ruby +RailsAdmin.config do |config| + config.authenticate_with do + warden.authenticate! scope: :user + end + config.current_user_method(&:current_user) +end +``` + +Or for an 'admin' scope: + +In `config/initializers/rails_admin.rb` + +```ruby +RailsAdmin.config do |config| + config.authenticate_with do + warden.authenticate! scope: :admin + end + config.current_user_method(&:current_admin) +end +``` + +Remember to add the routes for the scope you are working with: + +```ruby +devise_for :users +``` + +Or if you are using on :admin + +```ruby +devise_for :admins +``` + +~~Currently there is a bug in devise https://github.com/plataformatec/devise/issues/3321 +It is recommended to copy the devise view and modify as the issue said.~~ Fixed here: https://github.com/ryanmccarthypdx/retain_me/commit/b7534807c477f94c8d9a78d262513ad8c00d3623 diff --git a/docs/dragonfly.md b/docs/dragonfly.md new file mode 100644 index 0000000000..2c0d9c1811 --- /dev/null +++ b/docs/dragonfly.md @@ -0,0 +1,19 @@ +# Dragonfly + +If a `asset_uid` column is found, it will be hidden (along with the optional `asset_name` column) and a `field :asset, :dragonfly` will be created. + +Due to the 'fire and forget' nature of Dragonfly and the obfuscated uid, RailsAdmin cannot always infer if asset is an image or not (show thumbnails or links?). + +It will try to read `asset_name` to see the extension for a smart guess. If absent, it will suppose it is an image. + +See the #image option in [File Upload](file-upload.md) if you need to override this behavior. + +```ruby +class Article < ActiveRecord::Base + dragonfly_accessor :asset + # don't forget those if you use :attr_accessible (delete method and form caching method are provided by Dragonfly and used by RailsAdmin) + attr_accessible :asset, :remove_asset, :retained_asset +end +``` + +[More here](../lib/rails_admin/config/fields/types/dragonfly.rb) diff --git a/docs/edit-action.md b/docs/edit-action.md new file mode 100644 index 0000000000..a90a0d721d --- /dev/null +++ b/docs/edit-action.md @@ -0,0 +1,18 @@ +### Example authorizations for cancan: + +```ruby + # with + alias_action :update, :destroy, :create, :to => :write + + can :manage, :all + # includes + can :write, :all + # includes + can :write, Model + # includes + can :update, Model + # includes + can :update, Model, { conditions_and_default_attributes } +``` + +[More here](../lib/rails_admin/config/actions/edit.rb) diff --git a/docs/edit.md b/docs/edit.md new file mode 100644 index 0000000000..845326dc5d --- /dev/null +++ b/docs/edit.md @@ -0,0 +1,9 @@ +# Edit + +Section used for the edit views: `create` and `update`, and when the model is `nested`. + +It inherits its configuration from the `base` section. + +Its configuration can be overridden by `create`, `update` and `nested` section's configuration. + +[More here](../lib/rails_admin/config/sections/edit.rb) diff --git a/docs/enumeration.md b/docs/enumeration.md new file mode 100644 index 0000000000..f0d9def744 --- /dev/null +++ b/docs/enumeration.md @@ -0,0 +1,187 @@ +## ActiveRecord Enums(Recommended) + +RailsAdmin has built-in support for [ActiveRecord's Enum feature](https://api.rubyonrails.org/v5.2.3/classes/ActiveRecord/Enum.html). All you have to do is setting up enums + +```ruby +class Player < ActiveRecord::Base + # keys are string + enum formation: {start: 'start', substitute: 'substitute'} +end +``` + +```ruby +class Team < ActiveRecord::Base + # keys are integer + enum main_sponsor: [:no_sponsor, :food_factory, :transportation_company, :bank, :energy_producer] +end +``` + +then RailsAdmin will automatically recognize it as ActiveRecord Enum. Filter feature is also supported. + +You can omit type for the fields detected automatically, but if you want to be explicit for field type in configuration, be sure to use `:active_record_enum`. + +```ruby +config.model Player do + field :formation +end +config.model Team do + field :main_sponsor, :active_record_enum +end +``` + +`:enum` is the different thing(see the section below) and do not work with ActiveRecord Enum columns. + +[More here](../lib/rails_admin/config/fields/types/active_record_enum.rb) + +## RailsAdmin Enums(Legacy) + +The `:enum` field type is for when you need to display a list of potential values. It will be rendered with a select box in forms. + +Other advantage, a **filter** with a select box will be added too. + +As usual with RailsAdmin, there are two ways to do this. + +### Using the smart default approach + +If you have a `:color` column in your Team model, RailsAdmin will check if Team#color_enum exists. +If it does, then you're done. + +The result call will be sent to `FormOptionsHelper#options_for_select` to fill the select box. +See [this](http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-options_for_select) for possible output (hash, array) + +```ruby +class Team < ActiveRecord::Base + def color_enum + # Do not select any value, or add any blank field. RailsAdmin will do it for you. + ['green', 'white'] + # alternatively + # { green: 0, white: 1 } + # [ %w(Green 0), %w(White 1)] + end +end +``` + +### Using the configuration approach + +```ruby +# you need to tell RailsAdmin that you want to use an `:enum` field +field :color, :enum do + # if your model has a method that sends back the options: + enum_method do + :my_color_enum_instance_method + end + + # or doing it directly inline + enum do + ['green', 'white'] + # alternatively, + # { green: 0, white: 1 } + # [ %w(Green 0), %w(White 1)] + end + + # if you need select the default value + default_value 'green' + + enum do + # ActiveRecord querys + except = bindings[:view]._current_user.team_id + Team.where('id != ?', except).map { |c| [ c.name, c.id ] } + end +end +``` + +\*\*\* If you are using enumerator in `has_one` association field, be aware to place `_id` after field's name in RailsAdmin initializer, otherwise you can have problems. + +```ruby +field :responsible_id, :enum do + enum do + # ... + end +end + +# instead + +field :responsible, :enum do + enum do + # ... + end +end +``` + +### Integration with enum plugins + +- The [Enumerize gem](https://github.com/brainspec/enumerize) will automatically generate the appropriate \_enum methods. + +- The [bitmask_attributes gem](https://github.com/joelmoss/bitmask_attributes) can be configured this way: + +```ruby + configure :my_mask, :enum do + enum_method do + name + end + + enum do + Hash[abstract_model.model.bitmasks[name].map { |k,v| [k.humanize.titleize, k] }] + end + + pretty_value do + bindings[:object].send(name).map{|v| v.to_s.humanize.titleize }.join(', ') + end + + def form_value + bindings[:object].send(name) + end + + # set this to true for multi-select + multiple do + false + end + end +``` + +- The [mongoid-enum gem](https://github.com/thetron/mongoid-enum) needs a bit of help to play with rails_admin: + +```ruby +class Person + include Mongoid::Document + include Mongoid::Enum + + # This typical mongoid-enum field definition will add a constant for you. In this case, Person::STATUS + enum :status, [:living, :dead, :undead] + + rails_admin do + list do + # mongoid stores enum fields in a column named with an underscore prefix + configure :_status, :enum do + enum { STATUS } + end + end + end +end +``` + +### Multi-select ENUM example using User.roles as example... + +During Create/Update, display a Multi-Select Widget for :roles field. +Stores/Retrieves the selected options as array into a single db string field as serialized array. + +#### model/user.rb + +```ruby +class User < ActiveRecord::Base + serialize :roles, Array + def roles_enum + [ [ 'role one', '1' ], [ 'role 2', '2' ], [ 'role 3', '3' ] ] + end + def has_role?( role ) + # example called from cancan's app/models/ability.rb + # if user.has_role?( :ADMIN + + # for roles array stored in db... take each value, see if it matches the second column in the roles_enum array, if so, return the 1st col of the enum as a uprcase,space_to_underscore,symbol . + assigned_roles = self.roles.map { |r| self.roles_enum.rassoc(r)[0].gsub(/ /, '_').upcase.to_sym } + assigned_roles.include?( role ) + end +end +``` + +[More here](../lib/rails_admin/config/fields/types/enum.rb) diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000000..e6da3b2ca4 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,5 @@ +#### Drop your examples here, eh? + +##### chadical's examples + +- [Burly, dynamically reloading RailsAdmin.config, enjoy. (Some goofy stuff going on in there but. hey it works for me.)](how-to-reloading-railsadmin-config-automatically.md) diff --git a/docs/excluding-railsadmin-from-newrelic.md b/docs/excluding-railsadmin-from-newrelic.md new file mode 100644 index 0000000000..1448ef0b29 --- /dev/null +++ b/docs/excluding-railsadmin-from-newrelic.md @@ -0,0 +1,21 @@ +# Excluding RailsAdmin from NewRelic + +You may want to ignore RailsAdmin controllers and actions from metrics gathering in NewRelic by default. There are a couple ways to do so. + +1. Monkey patch RailsAdmin to call `newrelic_ignore`: + +```ruby +# in an initializer +module RailsAdmin + class ApplicationController + newrelic_ignore if defined?(NewRelic) + end +end +``` + +2. Ignore your RailsAdmin endpoints via url blacklist. NewRelic has some information on the topic here: https://docs.newrelic.com/docs/agents/ruby-agent/installation-configuration/ignoring-specific-transactions#config-ignoring. Essentially, you just need to add a rule to your newrelic.yml like (assuming you've mounted RailsAdmin under `/admin`): + +``` +rules: + ignore_url_regexes: ["^/admin"] +``` diff --git a/docs/export-action.md b/docs/export-action.md new file mode 100644 index 0000000000..f69707550c --- /dev/null +++ b/docs/export-action.md @@ -0,0 +1,13 @@ +### Example authorizations for cancan: + +```ruby + can :manage, :all + # includes + can :export, :all + # includes + can :export, Model + # includes + can :export, Model, { conditions } +``` + +[More here](../lib/rails_admin/config/actions/export.rb) diff --git a/docs/export.md b/docs/export.md new file mode 100644 index 0000000000..257ec0ba54 --- /dev/null +++ b/docs/export.md @@ -0,0 +1,35 @@ +# Export + +Section used for the export view. + +It inherits its configuration from the `base` section. + +**Specify fields to export** + +```ruby +RailsAdmin.config do |config| + config.model 'Highway' do + export do + field :number_of_lanes + end + end +end +``` + +**Specify field Value to export with 'export_value'** + +```ruby + RailsAdmin.config do |config| + config.model 'Lesson' do + export do + field :teacher, :string do + export_value do + value.name if value #value is an instance of Teacher + end + end + end + end + end +``` + +[More here](../lib/rails_admin/config/sections/export.rb) diff --git a/docs/fields.md b/docs/fields.md new file mode 100644 index 0000000000..2702992994 --- /dev/null +++ b/docs/fields.md @@ -0,0 +1,537 @@ +## Visibility and ordering + +By default all fields are visible, but they are not presented in any particular +order. If you specifically declare fields, only defined fields will be visible +and they will be presented in the order defined: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + field :name + field :created_at + end + end +end +``` + +This would show only "name" and "created at" columns in the list view. + +If you would like to configure fields in the default group without changing the other +fields already included in the default group, you can use the `configure` block like this: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + configure :name do + hide + end + end + end +end +``` + +This would hide the name field on the team list page, but it would not affect +any of the other field defaults. + +### Virtual Fields + +It is possible to configure Rails Admin to display "virtual" fields--fields that are not database attributes on the model. Just define them as methods on your model, then configure a field of the same name. + +In your model add a method with any name, like: + +```ruby +class Address < ApplicationRecord + belongs_to :state + belongs_to :city + + # Virtual field method + def full_address + [self.street, self.number, self.city.name, self.state.name].compact.join(', ') + end +end +``` + +And + +```ruby +RailsAdmin.config do |config| + config.model Address do + list do + # virtual field + configure :full_address do + # any configuration + end + fields :full_address, :street, :number #, ... + end + end +end +``` + +### Controlling by logic + +If you need to hide fields based on some logic on runtime (for instance +authorization to view field) you can pass a block for the `visible` option +(including its `hide` and `show` accessors): + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + field :name + field :created_at + field :revenue do + visible do + bindings[:view]._current_user.roles.include?(:accounting) + end + end + end + end +end +``` + +Note that above example's authorization conditional is not runnable code, just +an imaginary example. You need to provide RailsAdmin with your own +authorization scheme for which you can find a guide at the end of this file. + +### Exclusion + +By default _all_ fields found on your model will be added to list/edit/export views, if no field is found for the section and model. + +But after you specify your _first_ field with `field(field_name, field_type = found_column_type, &conf_block)` or `include_field` or `fields`, this behaviour will be canceled. + +_Only_ the specified fields will be added. +If you don't want that very behavior, use `configure` instead of `field` (same signature). +That way, that field won't be added to the section, just configured. + +Once in `add specified fields` mode, you can exclude some specific fields with exclude_fields & exclude_fields_if: + +Example: + +```ruby +RailsAdmin.config do |config| + config.model 'League' do + list do + exclude_fields_if do + type == :datetime + end + + exclude_fields :id, :name + end + end +end +``` + +Be careful, if you exclude fields before anything is added, this will instead add all other fields, which might not be what you expect (especially since fields ordering will be frozen). See https://github.com/railsadminteam/rails_admin/issues/859 for an example. + +You can use include_all_fields to add all default fields: + +Example: + +```ruby +RailsAdmin.config do |config| + config.model 'League' do + list do + field :name do + # snipped specific configuration for name attribute + end + include_all_fields # all other default fields will be added after, conveniently + exclude_fields :created_at # but you still can remove fields + end + end +end +``` + +### Inclusion + +It is also possible to add fields by group and configure them by group: + +Example: + +```ruby +RailsAdmin.config do |config| + config.model 'League' do + list do + # all selected fields will be added, but you can't configure them. + # If you need to select them by type, see *fields_of_type* + include_fields_if do + name =~ /displayed/ + end + + include_fields :name, :title # simply adding fields by their names (order will be maintained) + fields :created_at, :updated_at do # adding and configuring + label do + "#{label} (timestamp)" + end + end + end + end +end +``` + +Note that some fields are hidden by default (source fields for belongs_to associations) and that you can display them to the list view by manually setting them to visible: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + field :league_id do + visible true + end + end + end +end +``` + +## Label + +The header of a list view column can be changed with the familiar label method: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + field :name do + label "Title" + end + field :created_at do + label "Created on" + end + end + end +end +``` + +As in the previous example this would show only columns for fields "name" and +"created at" and their headers would have been renamed to "Title" and +"Created on". + +Also applies to an edit form, which will change the html label element associated with +field's input element. + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :name do + label "Title" + end + end + end +end +``` + +## Output formatting + +The field's output can be modified: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + field :name do + formatted_value do # used in form views + value.to_s.upcase + end + + pretty_value do # used in list view columns and show views, defaults to formatted_value for non-association fields + value.titleize + end + + export_value do + value.camelize # used in exports, where no html/data is allowed + end + end + field :created_at + end + end +end +``` + +This would render all the teams' names uppercased. + +The field declarations also have access to a bindings hash which contains the +current record instance in key :object and the view instance in key :view. +Via :object we can access other columns' values and via :view we can access our +application's view helpers: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + field :name do + formatted_value do + bindings[:view].tag(:img, { :src => bindings[:object].logo_url }) << value + end + end + field :created_at + end + end +end +``` + +This would output the name column prepended with team's logo using the `tag` +view helper. This example uses `value` method to access the name field's value, +but that could be written more verbosely as `bindings[:object].name`. + +### Render a link (a tag, href) + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + show do + field :name do + read_only true + pretty_value do + v = bindings[:view] + team = bindings[:object] + url = team.ticket_page + # value will point to bindings[:object].name + v.link_to(value, url, target: '_blank', rel: 'noopener noreferrer') + end + end + end + end +end +``` + +Fields of different date types (date, datetime, time, timestamp) have two extra +options to set the time formatting: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + field :name + field :created_at do + date_format :short + end + field :updated_at do + strftime_format "%Y-%m-%d" + end + end + end +end +``` + +This would render all the teams' "created at" dates in the short format of your +application's locale and "updated at" dates in format YYYY-MM-DD. If both +options are defined for a single field, `strftime_format` has precedence over +`date_format` option. For more information about localizing Rails see +[Rails Internationalization API](http://edgeguides.rubyonrails.org/i18n.html#adding-date-time-formats) +and [Rails I18n repository](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale). + +## Form rendering + +The edit view's fields are rendered using partials. Each field type has its own +partial per default, but that can be overridden: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :name do + partial "my_awesome_partial" + end + end + end +end +``` + +The partial should be placed in your applications template folder, such as +`app/views/rails_admin/main/_my_awesome_partial.html.erb`. + +The object is available from the partial with `form.object`. + +One can also completely override the rendering logic: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :name do + render do + bindings[:view].render :partial => partial.to_s, :locals => {:field => self, :form => bindings[:form]} + end + end + end + end +end +``` + +You can flag a field as read only, and if necessary fine-tune the output with pretty_value: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :published do + read_only true + pretty_value do + bindings[:object].published? ? "Yes, it's live!" : "No, in the loop..." + end + end + end + end +end +``` + +## Help + +Every field is accompanied by a hint/text help based on model's validations. +Everything can be overridden with `help`: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :name + field :email do + help 'Required - popular webmail addresses not allowed' + end + end + end +end +``` + +Since v0.6 you can also override your fields help text based on rails i18n functionality, using your locale files: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :name + field :email + end + end +end +``` + +```yml +en: + admin: + help: + team: + email: "%{help}. Popular webmail addresses not allowed" +``` + +%{help} will be replaced by the rails_admin default generated help message. + +## Overriding field type + +If you'd like to override the type of the field that gets instantiated, the +field method provides second parameter which is field type as a symbol. For +instance, if we have a column that's a text column in the database, but we'd +like to have it as a string type we could accomplish that like this: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :description, :string do + # configuration here + end + end + end +end +``` + +If no configuration needs to take place the configuration block could have been +left out: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :description, :string + end + end +end +``` + +A word of warning, if you make field declarations for the same field a number +of times with a type defining second argument in place, the type definition +will ditch the old field configuration and load a new field instance in place. + +## Creating a custom field type + +If you have a reusable field you can define a custom class extending +`RailsAdmin::Config::Fields::Base` and register it for RailsAdmin: + +```ruby +RailsAdmin::Config::Fields::Types::register(:my_awesome_type, MyAwesomeFieldClass) +``` + +Then you can use your custom class in a field: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :name, :my_awesome_type do + # configuration here + end + end + end +end +``` + +### Existing custom field type (color picker field) + +Did you know Rails Admin comes with useful custom color picker field type? You can use it for editing but also for listing. + +```ruby +rails_admin do + field :color, :color +end +RailsAdmin.config do |config| + config.model 'Team' do + edit do + field :name, :color + end + list do + field :name, :color + end + # Or simply as follow if you want to use it everywhere + field :name, :color + end +end +``` + +## Creating a custom field factory + +Type guessing can be overridden by registering a custom field "factory", but +for now you need to study `lib/rails_admin/config/fields/factories/*` for +examples if you want to use that mechanism. + +## Making all fields readonly by default + +Borrowing from [Configuring models all at once](models.md#configuring-models-all-at-once, you can use the following to make fields read-only by default: + +```rb +RailsAdmin.config do |config| + ActiveRecord::Base.descendants.each do |imodel| + config.model "#{imodel.name}" do + base do + fields do + read_only true + + # If you want rules about inclusion or exclusion of fields to only apply at the model level, + # you should include the following two lines. (This code should be in the initializer in order + # to run first and not clobber the `order` and `defined` attributes from your model config.) + # + # See also: + # - https://github.com/railsadminteam/rails_admin/blob/v1.1.1/lib/rails_admin/config/has_fields.rb#L93-L94 + # - https://github.com/railsadminteam/rails_admin/blob/v1.1.1/lib/rails_admin/config/lazy_model.rb#L26-L47 + # + self.defined = false + self.order = nil + end + end + end + end +end +``` + +Note: this requires rails_admin >= 1.0.0.rc (depends on [#2670](https://github.com/railsadminteam/rails_admin/pull/2670)). diff --git a/docs/file-upload.md b/docs/file-upload.md new file mode 100644 index 0000000000..ba2ae2ac5c --- /dev/null +++ b/docs/file-upload.md @@ -0,0 +1,22 @@ +# File upload + +File upload is a 'virtual' field type not meant to be used directly, but through vendor implementations ([Paperclip](paperclip.md)/[Dragonfly](dragonfly.md)/[Carrierwave](carrierwave.md)/[ActiveStorage](activestorage.md)) + +Those implementation share common characteristics. + +If defaults don't fit, fine-tune with: + +```ruby +field :asset do + # set a method available to your asset (defaults to :thumb, :thumbnail or '100x100>' for Dragonfly) + thumb_method :large + + # for delete checkbox in forms + delete_method :asset_delete # don't forget to whitelist if you use :attr_accessible + + # in case of a validation failure, to retain asset in the form (not available for Paperclip) + cache_method :asset_cache # don't forget to whitelist if you use :attr_accessible +end +``` + +[More here](../lib/rails_admin/config/fields/types/file_upload.rb) diff --git a/docs/float.md b/docs/float.md new file mode 100644 index 0000000000..e140ea75e1 --- /dev/null +++ b/docs/float.md @@ -0,0 +1,3 @@ +# Float + +[More here](../lib/rails_admin/config/fields/types/float.rb) diff --git a/docs/froala-wysiwyg-html-editor.md b/docs/froala-wysiwyg-html-editor.md new file mode 100644 index 0000000000..5cc843249e --- /dev/null +++ b/docs/froala-wysiwyg-html-editor.md @@ -0,0 +1,76 @@ +# Froala WYSIWYG HTML editor + +This assume you want to use the FREE version of Froala WYSIWYG HTML Editor (which shows their badge in the editor). For more information, please see: http://www.froala.com/wysiwyg-editor + +### How to use: + +1. Add Froala's assets gem. + + ``` + gem "rails_admin", "~> 0.6.8" + gem "wysiwyg-rails", "~> 1.2.7" + ``` + +2. Enabling froala editor for your field is easy + +``` +edit do + field :content, :froala +end + +# Optionally providing froala options (see https://froala.com/wysiwyg-editor/docs/options/) +edit do + field :content, :froala do + config_options do + { + inlineMode: false, + paragraphy: false + } + end + end +end +``` + +[More here](../lib/rails_admin/config/fields/types/froala.rb) and [here](../app/assets/javascripts/rails_admin/ra.widgets.js) + +### Using Plugins + +Some additional configuration is necessary in order to use [Froala plugins]: + +1. In `app/assets/javascripts/rails_admin/custom/ui.js`: + + ``` + //= require froala_editor.min + // Include the plugins you want: + //= require plugins/block_styles.min + //= require plugins/colors.min + //= require plugins/media_manager.min + //= require plugins/tables.min + //= require plugins/video.min + //= require plugins/font_family.min + //= require plugins/font_size.min + //= require plugins/file_upload.min + //= require plugins/lists.min + //= require plugins/char_counter.min + //= require plugins/fullscreen.min + //= require plugins/urls.min + //= require plugins/inline_styles.min + ``` + +2. In `app/assets/stylesheets/rails_admin/custom/theming.scss`: + + ``` + @import "froala_editor.min"; + @import "froala_style.min"; + @import "font-awesome"; + ``` + +3. If you're deploying to Heroku, you may need to add these files to Rails' asset precompilation list. + + In `config/initializers/assets.rb`: + + ``` + Rails.application.config.assets.precompile += %w(rails_admin/custom/theming.css rails_admin/custom/ui.js) + ``` + +[froala plugins]: https://www.froala.com/wysiwyg-editor/docs/plugins diff --git a/docs/groups.md b/docs/groups.md new file mode 100644 index 0000000000..7eb2167f4d --- /dev/null +++ b/docs/groups.md @@ -0,0 +1,129 @@ +# Groups + +By default RailsAdmin groups fields in the edit views (create and update views) +by including all database columns and associations to the `:default` group. + +The configuration accessors are `edit`, `create` and `update`. First one is a +batch accessor which configures both create and update views. For consistency, +these examples only include the batch accessor `edit`, but if you need differing +create and update views just replace `edit` with `create` or `update`. If you need +to configure the form when it is displayed as a modal, replace `edit` with `modal`. +Attention: If you lose some fields after using `groupings`, add `include_all_fields` to your configuration. + +## Visibility + +Field groups can be hidden: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + group :default do + hide + end + end + end +end +``` + +This would hide the default group which is accessed by the symbol :default. +The hide method is just a shortcut for the actual `visible` +option which was mentioned in the beginning of the navigation section. + +## Labels + +Field groups can be renamed: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + group :default do + label "Team information" + end + end + end +end +``` + +This would render "Team information" instead of "Basic info" as the groups label. + +## Help + +Field groups can have a set of instructions which is displayed under the label: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + group :default do + label "Team information" + help "Please fill all information related to your team..." + end + end + end +end +``` + +This content is mostly useful when the admin doing the data entry is not familiar with the system or as a way to display inline documentation. + +## Syntax + +As in the list view, the edit views' configuration blocks can directly +contain field configurations, but in edit views those configurations can +also be nested within group configurations. Below examples result an +equal configuration: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + group :default do + label "Default group" + end + field :name do + label "Title" + group :default + end + end + end +end + +RailsAdmin.config do |config| + config.model 'Team' do + edit do + group :default do + label "Default group" + field :name do + label "Title" + end + end + end + end +end +``` + +### Important note on label - I18n + +Use association name as translation key for label for association fields. +If you have :user_id field with a user association, use :user as the attribute + +In fact the first examples `group :default` configuration is unnecessary +as the default group has already initialized all fields and +associations for itself. + +## Toggles + +By default, all field groups (other than :default) will have a toggle and start off active. To change the default and have a field group start off with the toggle inactive, use 'active false' + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + edit do + group :advanced do + active false + end + end + end +end +``` diff --git a/docs/has-and-belongs-to-many-association.md b/docs/has-and-belongs-to-many-association.md new file mode 100644 index 0000000000..d1e53b997e --- /dev/null +++ b/docs/has-and-belongs-to-many-association.md @@ -0,0 +1,46 @@ +# `has_and_belongs_to_many` association + +Synopsys: + +For a multiselect widget: (natural choice for n-n associations) + +```ruby +class Team < ActiveRecord::Base + has_and_belongs_to_many :fans + + attr_accessible :fan_ids +end +``` + +Or for a nested form: + +```ruby +class Team < ActiveRecord::Base + has_and_belongs_to_many :fans + + accepts_nested_attributes_for :fans, :allow_destroy => true + attr_accessible :fans_attributes + + rails_admin do + configure :fans do + inverse_of :teams + # configuration here + end + end +end +``` + +The other side of the association is as usual: + +```ruby +# for info +class Fan < ActiveRecord::Base + has_and_belongs_to_many :teams +end +``` + +This will work for regular many-to-many relationships and self-referential many-to-manys. + +For Rails 4+ don't need to use `attr_accessible` + +[More here](../lib/rails_admin/config/fields/types/has_and_belongs_to_many_association.rb) diff --git a/docs/has-many-association.md b/docs/has-many-association.md new file mode 100644 index 0000000000..2d68bb3faf --- /dev/null +++ b/docs/has-many-association.md @@ -0,0 +1,32 @@ +# `has_many` association + +Synopsys: + +```ruby +class Team < ActiveRecord::Base + has_many :players, :dependent => :destroy, :inverse_of => :team + + # for a nested form: (natural choice for 1-n associations) + accepts_nested_attributes_for :players, :allow_destroy => true + + # uncomment if you don't use strong parameters + # attr_accessible :players_attributes, :allow_destroy => true + + # for a multiselect widget: + # uncomment if you don't use strong parameters + # attr_accessible :player_ids + + rails_admin do + configure :players do + # configuration here + end + end +end + +# for info +class Player < ActiveRecord::Base + belongs_to :team, :inverse_of => :players +end +``` + +[More here](../lib/rails_admin/config/fields/types/has_many_association.rb) diff --git a/docs/has-many-through-association.md b/docs/has-many-through-association.md new file mode 100644 index 0000000000..6db43198dd --- /dev/null +++ b/docs/has-many-through-association.md @@ -0,0 +1,132 @@ +# `has_many` `:through` association + +Synopsis: + +```ruby + +class Grid < ActiveRecord::Base + + has_many :block_grid_associations, :dependent => :delete_all, :autosave => true, :include => :block + has_many :blocks, :through => :block_grid_associations + + # for a multiselect widget: (natural choice for n-n associations) + + attr_accessible :block_ids + # if you need ordered blocks inside each grid (assuming a position column in block_grid_associations table) + def block_ids=(ids) + unless (ids = ids.map(&:to_i).select{|i|i>0}) == (current_ids = block_grid_associations.map(&:block_id)) + (current_ids - ids).each { |id| block_grid_associations.select{|b|b.block_id == id}.first.mark_for_destruction } + self.blocks = ids.each_with_index.map do |id, index| + if current_ids.include?(id) + (block_association = block_grid_associations.select{|b|b.block_id == id}.first).position = (index+1) + block_association + else + block_grid_associations.build({:block_id => id, :position => (index+1)}) + end + end.map(&:block) + end + end + + # for a nested form: (no reordering) + + accepts_nested_attributes_for :blocks, :allow_destroy => true + attr_accessible :blocks_attributes + + rails_admin do + configure :block_grid_associations do + visible(false) + end + + configure :blocks do + orderable(true) # only for multiselect widget currently. Will add the possibility to order blocks + # configuration here + end + end +end + +# for info + +class Block < ActiveRecord::Base + has_many :block_grid_associations, :dependent => :delete_all + has_many :grids, :through => :block_grid_associations +end + +class BlockGridAssociation < ActiveRecord::Base + belongs_to :block + belongs_to :grid + default_scope order(:position) # if you need ordered blocks +end +``` + +Note: `has_many :through` associations are not considered any differently from vanilla `has_many` association. In particular, no special help is provided to edit join table attributes; you can edit indifferently the join-table or the target table. + +### Alternative + +The code above didn't work for me. There were some join models created with the position attribute set, and then the self.blocks assignment created new join models with a nil position. I don't know if the code is equivalent to the former, but it works for me and it's a lot simpler. + +```ruby +def block_ids=(ids) + unless (ids = ids.map(&:to_i).select { |i| i>0 }) == (current_ids = block_grid_associations.map(&:block_id)) + (current_ids - ids).each { |id| block_grid_associations.select{|b|b.block_id == id}.first.mark_for_destruction } + ids.each_with_index do |id, index| + if current_ids.include? (id) + block_grid_associations.select { |b| b.block_id == id }.first.position = (index+1) + else + block_grid_associations.build({:block_id => id, :position => (index+1)}) + end + end + end +end +``` + +### Cleaner Alternative + +The code above didn't destroy the outdated associations for me. I've refactored the code to fix the bugs and make it easier to read here. + +```ruby + #This is used to order blocks from the grids + def block_ids=(ids) + ids = ids.reject{|i| i == "" || i == nil}.map{|i| i.to_i} + current_ids = block_grid_associations.map(&:block_id) + if current_ids != ids + destroy_outdated_block_associations(current_ids, ids) + ids.each_with_index do |id, index| + if current_ids.include? (id) + update_block_association_position(id, index+1) + else + grid_block_associations.build({:block_id => id, :position => (index+1)}) + end + end + end + end + + private + + def destroy_outdated_block_associations(current_ids, ids) + (current_ids - ids).each { |id| block_grid_associations.select{|b| b.block_id == id}.first.destroy } + end + + def update_block_association_position(id, position) + block_grid_associations.select { |b| b.block_id == id }.first.update_attributes(position: position) + end + +``` + +### Another Alternative + +I wasn't able to use any of the examples above with a validate_presence_of validation. So I came up with my own version. Take note that the position column must absolutely have a default value in the db. + +```ruby + #This is used to order blocks from the grids + def block_ids=(ids) + super(ids) + ids = ids.reject(&:blank?).map(&:to_i) # Flush empty id + ids.each_with_index do |id, index| + block = block_grid_associations.detect { |b| b.block_id == id } + block.update_column :position, index + 1 + end + end + +``` + +[More here (has_many)](../lib/rails_admin/config/fields/types/has_many_association.rb) diff --git a/docs/has-one-association.md b/docs/has-one-association.md new file mode 100644 index 0000000000..ae4cc07b5d --- /dev/null +++ b/docs/has-one-association.md @@ -0,0 +1,39 @@ +# `has_one` association + +Synopsis: + +```ruby +class Player < ActiveRecord::Base + has_one :draft, :dependent => :destroy, :inverse_of => :player + + # for nested fields: (natural choice for a has_one association) + + attr_accessible :draft_attributes + accepts_nested_attributes_for :draft, :allow_destroy => true + + # or if you want a dropdown select: + + attr_accessible :draft_id + + # Since ActiveRecord does not create setters/getters for has_one associations (why is beyond me), diy: + def draft_id + self.draft.try :id + end + def draft_id=(id) + self.draft = Draft.find_by_id(id) + end + + rails_admin do + configure :draft do + # configuration here + end + end +end + +# for info +class Draft < ActiveRecord::Base + belongs_to :player, :inverse_of => :draft +end +``` + +[More here](../lib/rails_admin/config/fields/types/has_one_association.rb) diff --git a/docs/hidden.md b/docs/hidden.md new file mode 100644 index 0000000000..c3e717606b --- /dev/null +++ b/docs/hidden.md @@ -0,0 +1,5 @@ +# Hidden + +[Example here](how-to-set-default-values.md) + +[More here](../lib/rails_admin/config/fields/types/hidden.rb) diff --git a/docs/history-index-action.md b/docs/history-index-action.md new file mode 100644 index 0000000000..deaf6cab28 --- /dev/null +++ b/docs/history-index-action.md @@ -0,0 +1,13 @@ +### Example authorizations for cancan: + +```ruby + can :manage, :all + # includes + can :history, :all + # includes + can :history, Model + # includes + can :history, Model, { conditions } +``` + +[More here](../lib/rails_admin/config/actions/history_index.rb) diff --git a/docs/history-show-action.md b/docs/history-show-action.md new file mode 100644 index 0000000000..ed3450d8dd --- /dev/null +++ b/docs/history-show-action.md @@ -0,0 +1,13 @@ +### Example authorizations for cancan: + +```ruby + can :manage, :all + # includes + can :history, :all + # includes + can :history, Model + # includes + can :history, Model, { conditions } +``` + +[More here](../lib/rails_admin/config/actions/history_show.rb) diff --git a/docs/horizontally-scrolling-table-with-frozen-columns-in-list-view.md b/docs/horizontally-scrolling-table-with-frozen-columns-in-list-view.md new file mode 100644 index 0000000000..8caa93fbeb --- /dev/null +++ b/docs/horizontally-scrolling-table-with-frozen-columns-in-list-view.md @@ -0,0 +1,220 @@ +# Horizontally scrolling table with frozen columns in list view + +Here's a technique that you can use to make the list view table show all of the columns on a single page, with horizontal scrolling and frozen header columns in the table. [PR #3017](https://github.com/railsadminteam/rails_admin/pull/3017) adds this feature with the config setting `sidescroll`, which is used as follows: + +```ruby +RailsAdmin.config do |config| + ... + # Use default horizontal scroll settings of 3 frozen columns (checkboxes, links/actions, ID) with a border on the right: + config.sidescroll = true + + # Use horizontal scrolling, but without any frozen columns: + config.sidescroll = {num_frozen_columns: 0} + + # Freeze more or fewer columns (col 1 = checkboxes, 2 = links/actions): + config.sidescroll = {num_frozen_columns: 4} + config.sidescroll = {num_frozen_columns: 1} + + # Turn off horizontal scrolling for a specific model: + config.model 'Team' do + list do + sidescroll false # "nil" doesn't work, it must be explicitly false + end + end + + # Use custom settings of horizontal scrolling for a specific model: + config.model 'Team' do + list do + checkboxes false + sidescroll(num_frozen_columns: 3) # per-model config does not account for checkboxes + end + end + + # Use horizontal scrolling only for a specific model: + config.sidescroll = nil + config.model 'Team' do + list do + sidescroll true + end + end + ... +end +``` + +This feature uses the CSS `position: sticky` value. It is designed to degrade gracefully on browsers that do not support `sticky`: users of those browsers will still have a horizontally-scrolling table, but the first few columns will not be frozen. + +Here are examples of what you'll get, using RailsAdmin's `spec/dummy_app` for an example. + +Default - `config.sidescroll` unset: +![](https://user-images.githubusercontent.com/1115369/39540385-5804f2b8-4df7-11e8-93c4-3c1b77b647be.png) + +`config.sidescroll = true` (some scrolling shown): +![](https://user-images.githubusercontent.com/1115369/39540539-c3e8fa06-4df7-11e8-958c-730ce13c22f7.png) + +`config.sidescroll = {num_frozen_columns: 0}` (some scrolling shown): +![](https://user-images.githubusercontent.com/1115369/39540666-28615bc2-4df8-11e8-97fb-1f0bc2c246b6.png) + +The following instructions allow you to add this feature to rails_admin before the PR is accepted/released. + +First, make all of your columns show up on a single page by editing `config/initializers/rails_admin.rb`: + +```ruby +RailsAdmin.config do |config| + ... + config.total_columns_width = 9999999 + ... +end +``` + +Next, add some custom javascript by creating `app/assets/javascripts/rails_admin/custom/ui.js`: + +```javascript +(function () { + var horizontalScrollList = function () { + var $table = $("#bulk_form").find("table"); + var table = $table[0]; + + // Abort if there's nothing to do. Don't repeat ourselves, either. + if (!table || $table.hasClass("js-horiz-scroll")) { + return; + } + + // Add our indicator class. Also some enhancements. + $table.addClass("js-horiz-scroll table-hover"); + + //// + // Make the table horizontally scrollable. + // Inspiration from bootstrap's table-responsive. + //// + var tableWrapper = document.createElement("DIV"); + tableWrapper.style.overflowX = "auto"; + tableWrapper.style.marginBottom = "20px"; + table.style.marginBottom = "0"; + table.parentElement.insertBefore(tableWrapper, table); + tableWrapper.appendChild(table); + + // Move the links column to the left. + $table.find("th.last,td.last").each(function (index, td) { + var tr = td.parentElement; + tr.insertBefore(td, tr.children[1]); + }); + + // Allow a render before calculating positions. + setTimeout(function () { + // Freeze the left columns. + var numFrozen = 3; + var $trs = $("#bulk_form").find("table tr"); + var $headerTds = $trs.first().children("th,td"); + var i, bgColor; + var offsets = []; + for (i = 0; i < numFrozen; i++) { + offsets.push($($headerTds[i]).position().left); + } + $trs.each(function (index, tr) { + for (i = 0; i < numFrozen; i++) { + tr.children[i].style.position = "sticky"; + tr.children[i].style.left = offsets[i] - offsets[0] + "px"; + if (i === numFrozen - 1) { + tr.children[i].style.boxShadow = "-1px 0 0 0 #ddd inset"; + tr.children[i].style.paddingRight = "6px"; + } + if (index % 2 === 0) { + bgColor = "#fff"; + if ( + index === 0 && + tr.children[i].className.indexOf("headerSort") > -1 + ) { + bgColor = "#e2eff6"; + } + tr.children[i].style.backgroundColor = bgColor; + } + } + }); + }, 0); + }; + + $(window).on("load", function () { + // on 'load' to allow link icons to load. + horizontalScrollList(); + $(document).on("rails_admin.dom_ready", horizontalScrollList); + }); +})(); +``` + +Eslinted version (minus some no-parm reassign errors) + +https://eslint.org/ + +```javascript +/* eslint-env jquery */ + +const horizontalScrollList = () => { + const $table = $("#bulk_form").find("table"); + const table = $table[0]; + + // Abort if there's nothing to do. Don't repeat ourselves, either. + if (!table || $table.hasClass("js-horiz-scroll")) { + return; + } + + // Add our indicator class. Also some enhancements. + $table.addClass("js-horiz-scroll table-hover"); + + // Make the table horizontally scrollable. + // Inspiration from bootstrap's table-responsive. + const tableWrapper = document.createElement("DIV"); + tableWrapper.style.overflowX = "auto"; + tableWrapper.style.marginBottom = "20px"; + table.style.marginBottom = "0"; + table.parentElement.insertBefore(tableWrapper, table); + tableWrapper.appendChild(table); + + // Move the links column to the left. + $table.find("th.last,td.last").each((index, td) => { + const tr = td.parentElement; + tr.insertBefore(td, tr.children[1]); + }); + + // Allow a render before calculating positions. + setTimeout(() => { + // Freeze the left columns. + const numFrozen = 3; + const $trs = $("#bulk_form").find("table tr"); + const $headerTds = $trs.first().children("th,td"); + let i; + let bgColor; + const offsets = []; + for (i = 0; i < numFrozen; i += 1) { + offsets.push($($headerTds[i]).position().left); + } + $trs.each((index, tr) => { + for (i = 0; i < numFrozen; i += 1) { + tr.children[i].style.position = "sticky"; + tr.children[i].style.left = `${offsets[i] - offsets[0]}px`; + if (i === numFrozen - 1) { + tr.children[i].style.boxShadow = "-1px 0 0 0 #ddd inset"; + tr.children[i].style.paddingRight = "6px"; + } + if (index % 2 === 0) { + bgColor = "#fff"; + if ( + index === 0 && + tr.children[i].className.indexOf("headerSort") > -1 + ) { + bgColor = "#e2eff6"; + } + tr.children[i].style.backgroundColor = bgColor; + } + } + }); + }, 0); +}; + +$(window).on("load", () => { + // on 'load' to allow link icons to load. + horizontalScrollList(); + $(document).on("rails_admin.dom_ready", horizontalScrollList); +}); +``` + +Now be sure to do whatever else might be required in order to get the custom javascript included in your compiled assets, such as bumping your `Rails.application.config.assets.version` - https://github.com/railsadminteam/rails_admin/issues/738#issuecomment-68483204 - or other things mentioned in that issue. diff --git a/docs/how-to-reloading-railsadmin-config-automatically.md b/docs/how-to-reloading-railsadmin-config-automatically.md new file mode 100644 index 0000000000..26d373663a --- /dev/null +++ b/docs/how-to-reloading-railsadmin-config-automatically.md @@ -0,0 +1,790 @@ +## Automatically reload rails_admin configuration when in development mode + +In your `/config/initializers/rails_admin.rb` you should add: + +```ruby +config.parent_controller = ApplicationController.to_s +``` + +And then in `ApplicationController`: + +```ruby +class ApplicationController < ActionController::Base + protect_from_forgery + + before_action :reload_rails_admin, if: :rails_admin_path? + + private + + def reload_rails_admin + models = %W(User UserProfile) + models.each do |m| + RailsAdmin::Config.reset_model(m) + end + RailsAdmin::Config::Actions.reset + + load("#{Rails.root}/config/initializers/rails_admin.rb") + end + + def rails_admin_path? + controller_path =~ /rails_admin/ && Rails.env.development? + end + +end +``` + +This will clear any RailsAdmin configuration in the individual model files. Use it only if you do all your configuration in `rails_admin.rb`. + +[rails_admin_import](https://github.com/stephskardal/rails_admin_import) gem will not work if you add the above code. + +## Automatic Model List + +#### To avoid having to maintain a list of models manually you can extend your ORM to collect them upon initialization. + +```ruby +# config/initializers/mongoid.rb +module Mongoid::Document + @@models = [] + + def self.included base + @@models << base + end + + def self.models + @@models + end +end +``` + +```ruby +# config/initializers/active_record.rb +class ActiveRecord::Base + @@models = [] + + def self.inherited sub_class + @@models << sub_class + end + + def self.models + @@models + end +end +``` + +Then instead of `%W(User UserProfile)` you can do `Mongoid::Document.models` or `ActiveRecord::Base.models` + +If the above throws the following error when accessing an index page while using ActiveRecord : + + undefined method `page' for # + +then delete `config/initializer/active_record.rb` and replace the model loading in the controller (the first line of `reload_rails_admin`) with : + + models = ActiveRecord::Base.descendants + +When using Rails 3/4: + +If `config.cache_classes = false` (by default it's off in development, but on in production) enable your applications eager loading. + +```ruby +Rails.application.configure do + config.eager_load = true +``` + +or + +``` +Rails.application.eager_load! +``` + +## Alternative method for the RailsAdmin versions that does not extend ApplicationController + +Create an initializer file named : rails_admin_reload.rb + +```ruby +Rails.application.config.to_prepare do + RailsAdmin::ApplicationController.class_eval do + before_action :reload_rails_admin, if: :reload_rails_admin? # Reloading RailsAdmin Config Automatically + + def reload_rails_admin + RailsAdmin::Config.reset + + # Use this if you have a single RailsAdmin configuration file (default) + load("#{Rails.root}/config/initializers/rails_admin.rb") + + # Use this if you have a folder with your RailsAdmin configuration files inside, or comment if not + Dir.foreach("#{Rails.root}/config/initializers/rails_admin") do |item| + next if item == '.' or item == '..' + load("#{Rails.root}/config/initializers/rails_admin/#{item}") + end + end + + def reload_rails_admin? + Rails.env.development? + end + end +end +``` + +## Less Desirable Method ("Dirty" just sounds so... dirty.) + +#### Move your RailsAdmin.config block into the app controller for dynamic reloading of changes in dev, etc + +#### Then stare at the various examples below... + +```ruby +class ApplicationController < ActionController::Base + protect_from_forgery + + helper RailsAdmin::Engine.helpers + + before_filter :rails_admin_dynamic_config + + def rails_admin_dynamic_config + + model_list = [ TestScenario, TestCase, TestScript, TestBlock, TestScenarioExecute, TestSeleniumUpload, TestRunResult, QaBamTask, MdmProduct, MdmProductFamily, MdmProductFeature, MdmProductLine, MdmProductSection, MdmStatus, User ] + + model_list.each do |m| + RailsAdmin::Config.reset_model( m ) + end + + RailsAdmin.config do |config| + + config.default_items_per_page = 25 + config.total_columns_width = 1000 + + # LIST TestScenario + config.model TestScenario do + list do + sort_by :name + field :name do + formatted_value do + bindings[:view].content_tag(:a, "#{bindings[:object].name}" , :href => "/qabam/app/test_scenario/#{bindings[:object].id}/edit") + end + end + field :mdm_status do + sortable :position + label "STATUS" + pretty_value do + bindings[:view].content_tag(:span, value.name ) + end + end + field :mdm_priority do + label "PRIORITY" + pretty_value do + bindings[:view].content_tag(:span, value.name ) + end + end + end + end + + # LIST TestCase + config.model TestCase do + list do + #columns_width = 15 + sort_by :case_number + sort_reverse false + #filters [:test_scenario, :case_number, :mdm_status] + field :case_number do + label "CASE NUMBER" + searchable :case_number + formatted_value do + bindings[:view].content_tag(:a, "#{bindings[:object].case_number}" , :href => "/qabam/app/test_case/#{bindings[:object].id}/edit") + end + end + field :test_scenario do + label "TEST SCENARIO" + pretty_value do + bindings[:view].content_tag(:span, value.name ) + end + end + field :mdm_status do + sortable :position + label "STATUS" + pretty_value do + bindings[:view].content_tag(:span, value.name ) + end + end + end + end + + # LIST TestScript + config.model TestScript do + list do + sort_by :test_case_java_class_method + field :test_case_java_class_method do + label "TEST SUITE JAVA METHOD NAME" + formatted_value do + bindings[:view].content_tag(:a, "#{bindings[:object].test_case_java_class_method}" , :href => "/qabam/app/test_script/#{bindings[:object].id}/edit") + end + end + field :mdm_status do + sortable :position + label "STATUS" + pretty_value do + bindings[:view].content_tag(:span, value.name ) + end + end + end + end + + # BULK Adjust labels + config.models do + fields do + label do + label.sub!('Mdm ', '') if label.include? ('Mdm ') + if label.include? ('hours') + label.sub!(' hours', '') + help 'In hours.' + end + if label.include? ('minutes') + label.sub!(' minutes', '') + help 'In minutes.' + end + label.upcase + end + end + end + + # CORE TestScenario + config.model TestScenario do + label 'Scenario' + navigation_label "Tests" + weight 0 + configure :test_suite_java_class_base do + label "TEST SUITE JAVA CLASS BASE NAME" + help "Required. Example: com.taleo.monarch.automation.functionaltests.csw" + end + #configure :test_suite_java_svn_path do + # help "Path to Java Class in SVN codeline." + #end + + # TODO scenario-actual date fields, read only, calculate based on min/max dates for cases/scripts (OR HIDE) + # TODO scenario-jquery file path selector for java svn path + # TODO scenario-have drop downs for product data populate dynamically based on previous field + + include_all_fields + exclude_fields :mdm_product_line, :mdm_product_family, :test_suite_java_svn_path + edit do + field :name do + label "TEST SCENARIO NAME" + end + field :test_suite_java_class_base + #field :test_suite_java_svn_path + field :summary + field :mdm_product + field :assigned_to + field :mdm_priority + field :comment + field :mdm_status do + associated_collection_cache_all false + associated_collection_scope do + Proc.new { |scope| scope = scope.where(status_type: 'Test Scenario').reorder( 'mdm_statuses.position desc, mdm_statuses.name asc' ) } + end + end + field :planned_start_date + field :planned_end_date + field :actual_start_date + field :actual_end_date + field :actual_effort_hours #, :decimal + field :date_completed + field :test_cases do + partial "child_multiselect_clearonsave" + help "Double-Click Items To Edit." + associated_collection_cache_all false + associated_collection_scope do + ts = bindings[:object] + Proc.new { |scope| scope = scope.where("test_cases.test_scenario_id = #{ts.id || 0} or test_cases.test_scenario_id is null") } + end + active true + end + field :test_scripts do + partial "child_multiselect_clearonsave" + help "Double-Click Items To Edit." + associated_collection_cache_all false + associated_collection_scope do + ts = bindings[:object] + Proc.new { |scope| scope = scope.where("test_scripts.test_scenario_id = #{ts.id || 0} or test_scripts.test_scenario_id is null") } + end + end + field :test_blocks do + partial "child_multiselect_clearonsave" + help "Double-Click Items To Edit." + associated_collection_cache_all false + associated_collection_scope do + ts = bindings[:object] + Proc.new { |scope| + # had to override original sql to exclude available blockers that closed.... + scope = scope.joins( 'LEFT OUTER JOIN test_blocks_test_scenarios ON test_blocks.id = test_blocks_test_scenarios.test_block_id' ) + scope = scope.where("test_blocks_test_scenarios.test_scenario_id = #{ts.id || 0} or test_blocks.mdm_status_id = #{MdmStatus.where( status_type: 'Test Blocker', status_state: 'open' ).first.id}" ) + scope = scope.uniq + } + end + # TODO try to hide nested_forms on edit, apply to others + end + field :test_selenium_uploads do + partial "child_multiselect_clearonsave" + help "Double-Click Items To Edit." + associated_collection_cache_all false + associated_collection_scope do + ts = bindings[:object] + Proc.new { |scope| scope = scope.where("test_scenario_id = #{ts.id || 0} or test_scenario_id is null") } + end + end + end + create do + exclude_fields :test_cases, :test_scripts, :test_blocks, :test_selenium_uploads + end + end + + # CORE TestCase + config.model TestCase do + label 'Case' + navigation_label 'Tests' + weight 5 + # TODO case-file/url picker case requirement file + # TODO case-hide actual effort? + configure :case_number do + #label "Case Number / Java Class Name" + #help "(Case # = Java METHOD Name) - Required" + end + configure :test_case_java_class_method do + label "TEST CASE JAVA CLASS+METHOD NAME" + help "Required. Example: UITest_CSW_01_Admin.test_CSW_01" + end + configure :case_requirement_file_path do + help "Url/Path to Requirements Document for this test case." + end + configure :mdm_status do + associated_collection_cache_all false + associated_collection_scope do + Proc.new { |scope| scope = scope.where(status_type: 'Test Case').reorder( 'mdm_statuses.position desc, mdm_statuses.name asc' ) } + end + end + field :created_date + field :closed_date + exclude_fields :case_requirement_file_path + field :test_blocks do + partial "child_multiselect_clearonsave" + help "Double-Click Items To Edit." + associated_collection_cache_all false + associated_collection_scope do + ts = bindings[:object] + Proc.new { |scope| + # had to override original sql to exclude available blockers that closed.... + scope = scope.joins( 'LEFT OUTER JOIN test_blocks_test_cases ON test_blocks.id = test_blocks_test_cases.test_block_id' ) + scope = scope.where("test_blocks_test_cases.test_case_id = #{ts.id || 0} or test_blocks.mdm_status_id = #{MdmStatus.where( status_type: 'Test Blocker', status_state: 'open' ).first.id}" ) + scope = scope.uniq + } + end + # TODO try to hide nested_forms on edit, apply to others + end + end + + # CORE TestScript + config.model TestScript do + label 'Script' + navigation_label "Tests" + #parent TestScenario + weight 10 + include_all_fields + # TODO script-default status + # TODO script-file picker svn java file loc + # TODO script-allow 1 script to have many cases + configure :test_case_java_class_method do + label "TEST CASE JAVA CLASS+METHOD NAME" + help "Required. Example: UITest_CSW_01_Admin.test_CSW_01" + end + configure :mdm_status do + associated_collection_cache_all false + associated_collection_scope do + Proc.new { |scope| scope = scope.where(status_type: 'Test Script').reorder( 'mdm_statuses.position desc, mdm_statuses.name asc' ) } + end + end + field :created_date + field :closed_date + exclude_fields :svn_java_file_location + + field :test_blocks do + partial "child_multiselect_clearonsave" + help "Double-Click Items To Edit." + associated_collection_cache_all false + associated_collection_scope do + ts = bindings[:object] + Proc.new { |scope| + # had to override original sql to exclude available blockers that closed.... + scope = scope.joins( 'LEFT OUTER JOIN test_blocks_test_scripts ON test_blocks.id = test_blocks_test_scripts.test_block_id' ) + scope = scope.where("test_blocks_test_scripts.test_script_id = #{ts.id || 0} or test_blocks.mdm_status_id = #{MdmStatus.where( status_type: 'Test Blocker', status_state: 'open' ).first.id}" ) + scope = scope.uniq + } + end + # TODO try to hide nested_forms on edit, apply to others + end + create do + configure :test_case do + visible false + end + end + update do + configure :test_case do + associated_collection_scope do + #associated_collection_cache_all false + ts = bindings[:object] + Proc.new { |scope| scope = scope.where( test_scenario_id: ts.test_scenario_id) if ts.present? } + end + end + end + end + + # CORE TestBlock + config.model TestBlock do + label 'Blocker' + navigation_label "Tests" + weight 20 + # TODO block-priority to drop-down + configure :mdm_status do + associated_collection_cache_all false + associated_collection_scope do + Proc.new { |scope| scope = scope.where(status_type: 'Test Blocker').reorder( 'mdm_statuses.position desc, mdm_statuses.name asc' ) } + end + end + configure :priority, :belongs_to_association do + partial "form_filtering_select" + end + field :test_scenarios do + partial "child_multiselect_clearonsave" + end + field :test_cases do + partial "child_multiselect_clearonsave" + end + field :test_scripts do + partial "child_multiselect_clearonsave" + end + end + + # CORE TestSeleniumUpload + config.model TestSeleniumUpload do + label 'Upload Selenium Result' + navigation_label "Test Results" + #parent TestScenario + weight 10 + #include_all_fields + #field :xml_result, :paperclip + # TODO script-default upload date + end + + # CORE TestRunResult + config.model TestRunResult do + label 'Test Run Result' + navigation_label "Test Results" + weight 15 + end + + # CORE TestScenarioExecute + config.model TestScenarioExecute do + visible false + label 'Execution' + navigation_label "Test Results" + weight 15 + end + + # CORE QaBamTask + config.model QaBamTask do + label 'QaBAM Task' + navigation_label 'Chad' + weight 850 + configure :mdm_status do + associated_collection_cache_all false + associated_collection_scope do + Proc.new { |scope| + scope = scope.where(status_type: 'QaBam Task').reorder( 'mdm_statuses.position desc, mdm_statuses.name asc' ) + } + end + end + end + + # CORE MdmProductLine + config.model MdmProductLine do + label 'Product Line' + navigation_label 'Setup' + weight 810 + end + + # CORE MdmProductFamily + config.model MdmProductFamily do + label 'Product Family' + navigation_label 'Setup' + weight 811 + end + + # CORE MdmProduct + config.model MdmProduct do + label 'Product' + visible TRUE + navigation_label 'Setup' + weight 812 + list do + sort_by :name + end + field :id do + sortable false + end + end + + # CORE MdmProductFeature + config.model MdmProductFeature do + label 'Product Feature' + visible true + navigation_label 'Setup' + weight 813 + list do + sort_by :name + end + field :id do + sortable false + end + end + + # CORE MdmProductSection + config.model MdmProductSection do + label 'Product Section' + visible true + navigation_label 'Setup' + weight 814 + list do + sort_by :name + end + field :id do + sortable false + end + end + + # CORE MdmStatus + config.model MdmStatus do + label 'Status' + label_plural 'Statuses' + navigation_label 'Setup' + weight 850 + list do + sort_by :position + end + field :id do + sortable false + end + end + + # CORE MdmPriority + config.model MdmPriority do + label 'Priority' + label_plural 'Priorities' + navigation_label 'Setup' + weight 850 + list do + sort_by :name + end + field :id do + sortable false + end + field :name + end + + # CORE User + config.model User do + label 'User' + weight 900 + navigation_label 'Setup' + field :id do + sortable false + end + list do + sort_by :name + field :name do + formatted_value do + bindings[:view].content_tag(:a, "#{bindings[:object].name}" , :href => "/qabam/app/user/#{bindings[:object].id}/edit") + end + end + field :email do + sortable :position + label "EMAIL" + pretty_value do + bindings[:view].content_tag(:span, value ) + end + end + field :timezone do + label "TIME ZONE" + pretty_value do + bindings[:view].content_tag(:span, value ) + end + end + end + edit do + field :notification_email_types do + render do + bindings[:form].select( "notification_email_types", bindings[:object].notification_email_types_enum, {}, { :multiple => true }) + end + end + end + #bindings[:controller].current_user.role == :admin + end + + # *************************** + # BEGIN SET USER ID's + # TODO repair/relogic the 'nothing to see here' hack + config.model TestScenario do + create do + configure :created_by do + visible false + help "" + end + field :created_by_id, :hidden do + visible true + help "" + default_value do + bindings[:view]._current_user.id + end + end + configure :mdm_priority do + default_value MdmPriority.where( default_priority: true ).first.id + end + configure :mdm_product do + default_value MdmProduct.where( name: 'Recruiting' ).first.id + end + configure :mdm_status do + default_value MdmStatus.where( status_type: 'Test Scenario', default_status: true ).first.id + end + end + end + config.model TestCase do + create do + configure :authored_by do + visible false + help "" + end + field :authored_by_id, :hidden do + visible true + help "" + default_value do + bindings[:view]._current_user.id + end + end + configure :mdm_priority do + default_value MdmPriority.where( default_priority: true ).first.id + end + configure :mdm_status do + default_value MdmStatus.where( status_type: 'Test Case', default_status: true ).first.id + end + end + end + config.model TestScript do + create do + configure :scripted_by do + visible false + help "" + end + field :scripted_by_id, :hidden do + visible true + default_value do + bindings[:view]._current_user.id + end + end + configure :mdm_status do + default_value MdmStatus.where( status_type: 'Test Script', default_status: true ).first.id + end + end + end + + config.model TestSeleniumUpload do + create do + configure :uploaded_by do + visible false + help "" + end + field :uploaded_by_id, :hidden do + visible true + default_value do + bindings[:view]._current_user.id + end + end + # TODO default date value... + # D:\apps\java\jruby\lib\ruby\gems\1.8\bundler\gems\rails_admin-7a4fdd931f95\app\views\rails_admin\main\_form_datetime.html.haml + # D:\apps\java\jruby\lib\ruby\gems\1.8\bundler\gems\rails_admin-7a4fdd931f95\app\assets\javascripts\rails_admin\ra.datetimepicker.js + #configure :executed_on do + # value = 'March 23, 2012' + #end + end + end + + config.model TestBlock do + create do + configure :created_by do + visible false + help "" + end + field :created_by_id, :hidden do + visible true + default_value do + bindings[:view]._current_user.id + end + end + configure :mdm_priority do + default_value MdmPriority.where( default_priority: true ).first.id + end + configure :priority_id do + default_value MdmPriority.where( default_priority: true ).first.id + end + configure :mdm_status do + default_value MdmStatus.where( status_type: 'Test Blocker', default_status: true ).first.id + end + end + end + + config.model QaBamTask do + create do + configure :created_by do + visible false + help "" + end + field :created_by_id, :hidden do + visible true + default_value do + bindings[:view]._current_user.id + end + end + configure :mdm_priority do + default_value MdmPriority.where( default_priority: true ).first.id + end + configure :mdm_status do + default_value MdmStatus.where( status_type: 'QaBam Task', default_status: true ).first.id + end + end + end + # END SET USER ID's + # *************************** + + # Hide columns + model_list.each do |m| + config.model m do + create do + configure :lock_version, :hidden do + visible true + end + end + update do + configure :lock_version, :hidden do + visible true + end + end + list do + #exclude_fields :id + exclude_fields :lock_version + end + show do + #exclude_fields :id + exclude_fields :lock_version + end + end + end + end + + end +end +``` diff --git a/docs/how-to-set-default-values.md b/docs/how-to-set-default-values.md new file mode 100644 index 0000000000..60257581ee --- /dev/null +++ b/docs/how-to-set-default-values.md @@ -0,0 +1,85 @@ +# How to set default values + +RailsAdmin always adds a blank entry in drop-downs, so if you are adding a new record using Rails Admin, it will be blank by default. To pre-select a default value however, e.g.please do it in your model definition, at initialization time: + +```ruby +class Team < ActiveRecord::Base + .... + after_initialize do + if new_record? + self.color ||= 'red' # be VERY careful with ||= and False values + end + end + + def color_enum + ['white', 'black', 'red', 'green', 'blue'] + end + ... +end +``` + +The "after_initialize" hook (or callback) triggers after a model record is instantiated. It will set the color value to 'red' for a new record and will leave it to its current value for an existing record (in the edit view). + +## Url Params + +If you want to set a default value based on a URL param you can access the `bindings` object. The following example shows how to add a default message using the "message" URL param. + +```ruby +config.model Post do + edit do + field :message do + def value + bindings[:view].params["message"] + end + end + end +end +``` + +## current_user + +Adding current_user as a default value adds an extra challenge. The controller layer knows about current_user, this information is not typically available in the model layer for use by after_initialize. And this is an intentional implication of the MVC architecture. + +In RailsAdmin, you can assign a default value of current_user like this: + +```ruby +config.model Post do + edit do + field :user_id, :hidden do + default_value do + bindings[:view]._current_user.id + end + end + end +end +``` + +This was taken from these discussion threads: + +- http://groups.google.com/group/rails_admin/msg/fe588202e4401dc4 +- http://groups.google.com/group/rails_admin/msg/5338518c540f9151 + +One thing to be aware of, RailsAdmin hides :user_id when creating the :user belongs_to association. To get around this issue: + +```ruby +config.model Post do + edit do + configure :user do + visible false + end + + field :user_id, :hidden do + visible true + default_value do + bindings[:view]._current_user.id + end + end + end +end +``` + +See the response by bbenezech in this issue for a detailed explanation: + +https://github.com/railsadminteam/rails_admin/issues/963 + +Some refinement may be required to avoid resetting the user of an existing item, if that's not desired. diff --git a/docs/index-action.md b/docs/index-action.md new file mode 100644 index 0000000000..f8ddd43661 --- /dev/null +++ b/docs/index-action.md @@ -0,0 +1,15 @@ +### Example authorizations for CanCanCan: + +```ruby + can :manage, :all + # includes + can :read, :all + # includes + can :read, Model + # includes + can :index, Model + # includes + can :index, Model, { conditions } +``` + +[More here](../lib/rails_admin/config/actions/index.rb) diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000000..b450b3fd02 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,171 @@ +# Index + +[Troubleshoot? check me first](troubleshoot.md) + +### Overview + +1. [Introduction to RailsAdmin](http://www.slideshare.net/benoitbenezech/rails-admin-overbest-practices) + +### Configuration + +1. [Base RailsAdmin configuration](base-configuration.md) +2. [Actions](actions.md) +3. [Navigation](navigation.md) +4. [Models](models.md) +5. [Groups](groups.md) +6. [Fields](fields.md) +7. [Translations](translations.md) + +### Integration Testing + +[Rspec with Capybara examples](rspec-with-capybara-examples.md) + +### Actions + +[Base](base-action.md) + +- Root actions + - [Dashboard](dashboard-action.md) +- Collection actions + - [Index](index-action.md) + - [New](new-action.md) + - [Export](export-action.md) + - [HistoryIndex](history-index-action.md) + - [BulkDelete](bulk-delete-action.md) +- Member actions + - [Show](show-action.md) + - [Edit](edit-action.md) + - [Delete](delete-action.md) + - [HistoryShow](history-show-action.md) + - [ShowInApp](show-in-app-action.md) + +### Field types + +[Base](base-field.md) + +- [FileUpload](file-upload.md) + - [ActiveStorage](activestorage.md) + - [Carrierwave](carrierwave.md) + - [Dragonfly](dragonfly.md) + - [Paperclip](paperclip.md) +- [Enumeration](enumeration.md) +- [Boolean](boolean.md) +- [Decimal](decimal.md) +- [Integer](integer.md) +- [Float](float.md) +- [Hidden](hidden.md) +- [String](string.md) + - [Password](password.md) +- [Text](text.md) + - [ActionText](actiontext.md) + - [CKEditor](ckeditor.md) + - [CodeMirror](codemirror.md) + - [Froala WYSIWYG HTML Editor](froala-wysiwyg-html-editor.md) + - [Wysihtml5](wysihtml5.md) +- [Timestamp - Date - Datetime - Time](timestamp-date-datetime-time.md) + +### Associations + +- [Associations basics](associations-basics.md) +- [Associations scoping](associations-scoping.md) +- [Associations validation](associations-validation.md) + +### Associations types + +- [belongs_to association](belongs-to-association.md) + - [polymorphic belongs_to association](polymorphic-belongs-to-association.md) +- [has_one association](has-one-association.md) +- [has_many association](has-many-association.md) + - [has_and_belongs_to_many association](has-and-belongs-to-many-association.md) + - [has_many :through association](has-many-through-association.md) + +### Sections + +[Base](base.md) + +- [List](list.md) +- [Show](show.md) +- [Export](export.md) +- [Edit](edit.md) + - [Create](create.md) + - [Update](update.md) + - [Nested](nested.md) + - [Modal](modal.md) + +### User integration + +- [Authentication](authentication.md) +- [Authorization](authorization.md) +- [Mass-assignments protection](mass-assignments-protection.md) +- [Auditing changes](auditing.md) + +### Extend RailsAdmin (RailsAdmin API) + +- [Theming and customization](theming-and-customization.md) +- [Custom action](custom-action.md) +- [Custom field](custom-field.md) + +### Styling + +- [List view table styling](list-view-table-styling.md) +- [Horizontally scrolling table with frozen columns in list view](horizontally-scrolling-table-with-frozen-columns-in-list-view.md) + +### Routing + +- [Routing Problems](routing-problems.md) +- [Using RailsAdmin routes](using-railsadmin-routes.md) + +### Recipes + +- [How to set default values](how-to-set-default-values.md) +- [Excluding RailsAdmin from NewRelic](excluding-railsadmin-from-newrelic.md) For when you don't want timing and performance information about RailsAdmin to show up in your NewRelic dashboards. +- [Performance](performance.md) - how to make Rails Admin snappier with large databases +- [Changing locale](changing-locale.md). + +### Plugins + +#### Authorization/Auditing + +- [RailsAdminAuthorizedFields](https://github.com/xronos-i-am/rails_admin_authorized_fields): Simplified authorization rules for models' fields in rails_admin +- [RailsAdminHisteroid](https://github.com/franc/rails_admin_histeroid): Mongoid 3.1 history audit support +- [RailsAdminHistoryRollback](https://github.com/rikkipitt/rails_admin_history_rollback): PaperTrail history rollback +- [RailsAdminPundit](https://github.com/sudosu/rails_admin_pundit): Integration with Pundit authorization system (this gem is for users of Pundit 1.x - the Pundit 2.x API has built-in support in RailsAdmin) + +#### Field extension + +- [Enumerize](https://github.com/brainspec/enumerize): Enumerated attributes with I18n and ActiveRecord/Mongoid support +- [RailsAdminActiontext](https://github.com/jemcode/rails_admin_actiontext): Rails 6 ActionText (Trix editor) support +- [RailsAdminCharts](https://github.com/pgeraghty/rails_admin_charts): Charts using Highcharts +- [RailsAdminDynamicCharts](https://github.com/openjaf/rails_admin_dynamic_charts): Dynamic Charts +- [RailsAdminGlobalizeField](https://github.com/scarfaceDeb/rails_admin_globalize_field): Tabbed interface and custom field type for globalize3 translations +- [RailsAdminJcrop](https://github.com/janx/rails_admin_jcrop): Image cropping with rails_admin_jcrop +- [RailsAdminMapField](https://github.com/trademobile/rails_admin_map_field): Coordinates with Google Maps +- [RailsAdminGoogleMap](https://github.com/nicovak/rails_admin_google_map): Google Maps integration +- [RailsAdminMongoidGeospatialField](https://github.com/sudosu/rails_admin_mongoid_geospatial_field): Support for setting geospatial information with Google Maps into Mongoid's GEO2D field +- [RailsAdminMongoidLocalizeField](https://github.com/sudosu/rails_admin_mongoid_localize_field): Support for mongoid localized fields +- [RailsAdminNestable](https://github.com/dalpo/rails_admin_nestable): Drag and drop tree view for Ancestry gem +- [RailsAdminNestedSet](https://github.com/rs-pro/rails_admin_nested_set): Drag and drop tree view for Awesome Nested Set / mongoid nested set +- [RailsAdminPlaceField](https://github.com/thinkclay/rails_admin_place_field): Google Maps with Places and Foursquare Venues +- [RailsAdminRedactor](https://github.com/anarchocurious/rails-admin-redactor): Adds support for the [Redactor](http://imperavi.com/redactor/) wysiwyg editor +- [RailsAdminTagList](https://github.com/kryzhovnik/rails_admin_tag_list): ActsAsTaggableOn tag_list field (DEPRECATED) +- [RailsUploader](https://github.com/glebtv/rails-uploader): Nice mass file uploads with jQuery File Upload for rails_admin and mongoid +- [Rich](https://github.com/bastiaanterhorst/rich): an opinionated CKEditor implementation with file uploads +- [RailsAdminCountries](https://github.com/xronos-i-am/rails_admin_countries): Add [countries](https://github.com/hexorx/countries) gem support +- [RailsAdminDropzone](https://github.com/luizpicolo/rails_admin_dropzone): Easy to use integration of drag and drop files upload via [dropzone.js](http://www.dropzonejs.com) for rails_admin +- [RailsAdminContentBuilder](https://github.com/luizpicolo/rails_admin_content_builder): Easy way for create contents using rails_admin + +#### Custom action + +- [RailsAdminClone](https://github.com/dalpo/rails_admin_clone): Clone records +- [RailsAdminImport](https://github.com/stephskardal/rails_admin_import): Import data from a CSV or JSON file +- [RailsAdminState](https://github.com/rs-pro/rails_admin_state): Manage state_machine states with rails_admin +- [RailsAdminAasm](https://github.com/zcpdog/rails_admin_aasm): Manage aasm states with rails_admin +- [RailsAdminToggleable](https://github.com/rs-pro/rails_admin_toggleable): Toggle boolean fields in index view +- [RailsAdminSelectable](https://github.com/jesson/rails_admin_selectable): Select value in association fields on index view (Beta) + +#### Misc. + +- [RailsAdminSettings](https://github.com/rs-pro/rails_admin_settings): Application setting for rails_admin and mongoid +- [RailsAdminGrid](https://github.com/colavitam/rails_admin_grid): Custom collection (index) action that displays objects in a grid with thumbnails +- [RailsAdminLiveEdit](https://github.com/blocknotes/rails_admin_live_edit): A plugin which allow to edit site content from the frontend (showing Rails Admin views in dialogs) +- [RailsAdminMydash](https://github.com/blocknotes/rails_admin_mydash): An alternative version of the dashboard with admin messages and the ability to access to Google Analytics data diff --git a/docs/integer.md b/docs/integer.md new file mode 100644 index 0000000000..be9d6fd398 --- /dev/null +++ b/docs/integer.md @@ -0,0 +1,3 @@ +# Integer + +[More here](../lib/rails_admin/config/fields/types/integer.rb) diff --git a/docs/internal-audit-plugin.md b/docs/internal-audit-plugin.md new file mode 100644 index 0000000000..65f7c1c2ae --- /dev/null +++ b/docs/internal-audit-plugin.md @@ -0,0 +1,36 @@ +# Internal audit plugin + +The old history internal plugin installer has been removed. Papertrail integration is recommended instead. + +Please note this is ActiveRecord only. + +Until old history plugin get moved to its own gem, you can still install it manually with this migration: + +```ruby +class CreateRailsAdminHistoriesTable < ActiveRecord::Migration + def self.up + create_table :rails_admin_histories do |t| + t.text :message # title, name, or object_id + t.string :username + t.integer :item + t.string :table + t.integer :month, :limit => 2 + t.integer :year, :limit => 5 + t.timestamps + end + add_index(:rails_admin_histories, [:item, :table, :month, :year], :name => 'index_rails_admin_histories' ) + end + + def self.down + drop_table :rails_admin_histories + end +end +``` + +Then you can activate it with: + +```ruby +RailsAdmin.config do |config| + config.audit_with :history +end +``` diff --git a/docs/list-view-table-styling.md b/docs/list-view-table-styling.md new file mode 100644 index 0000000000..3c2f80b7a8 --- /dev/null +++ b/docs/list-view-table-styling.md @@ -0,0 +1,52 @@ +## Fields - Column CSS class + +By default each column has a CSS class set according to field's data type. +`_field` + +In addition, each column has a CSS class based on its name +`_field` + +Field name CSS class can customized with: + +```ruby +RailsAdmin.config do |config| + config.model Team do + list do + field :created_at do + css_class "customClass" + end + end + end +end +``` + +This classes will be shown on list, show and edit views. They are part of the API and should not change. +In list views, both header th and td data will receive both classes. + +## Fields - Column width + +If you want to set a fixed width for a column in the list view: + +```ruby +RailsAdmin.config do |config| + config.model Team do + list do + field :name do + column_width 200 + end + end + end +end +``` + +Use this to configure lists table width: + +```ruby +RailsAdmin.config do |config| + config.total_columns_width = 1000 +end +``` + +It will use field's name CSS class to set a width for header and data columns. + +Also see how to [show all fields on one page in a horizontally-scrolling table](horizontally-scrolling-table-with-frozen-columns-in-list-view.md). diff --git a/docs/list.md b/docs/list.md new file mode 100644 index 0000000000..22645c90fd --- /dev/null +++ b/docs/list.md @@ -0,0 +1,263 @@ +# List + +Section used for the index view. + +It inherits its configuration from the `base` section. + +### Width of the list table + +```ruby +RailsAdmin.config do |config| + config.total_columns_width = 1000 +end +``` + +Also see how to [show all columns on one page in a horizontally-scrolling table](horizontally-scrolling-table-with-frozen-columns-in-list-view.md). + +### Width of individual columns + +By default, columns have a `max-width` of 120px, and no `min-width`. While there does not appear to be fine-grained controls for max-/min-width, you can specify a fixed width in pixels for individual columns: + +```ruby +RailsAdmin.config do |config| + config.model 'Player' do + list do + field :created_at do # (1) + column_width 300 + end + end + end +end +``` + +### Number of items per page + +You can configure the default number of rows rendered per page: + +```ruby +RailsAdmin.config do |config| + config.default_items_per_page = 50 +end +``` + +### Number of items per page per model + +You can also configure it per model: + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + items_per_page 100 + end + end +end +``` + +### Default sorting + +By default, rows are sorted by the field `id` in reverse order + +You can change default behavior with use two options: `sort_by` and `sort_reverse` + +```ruby +RailsAdmin.config do |config| + config.model 'Player' do + list do + sort_by :name + field :name do + sort_reverse false + end + end + end +end +``` + +### Filters + +Default visible filters. Must be a list of fields name. + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + filters [:name, :manager] + # Manually enable/disable per field + field :name do + filterable true + end + field :manager do + filterable true + end + end + end +end +``` + +### Fields sorting + +- You can make a column non-sortable by setting the sortable option to false (1) +- You can change the column that the field will actually sort on (2) + +`belongs_to` associations: + +- will be sorted on their label if label is not virtual (:name, :title, etc.) +- otherwise on the foreign_key (:team_id) +- you can also specify a column on the targetted table (see example) (3) + +```ruby +RailsAdmin.config do |config| + config.model 'Player' do + list do + field :created_at do # (1) + sortable false + end + field :name do # (2) + sortable :last_name # imagine there is a :last_name column and that :name is virtual + end + field :team do # (3) + # Will order by players playing with the best teams, + # rather than the team name (by default), + # or the team id (dull but default if object_label_method is not a column name) + + sortable :win_percentage + + # if you need to specify the join association name: + # (See #526 and http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Table+Aliasing) + sortable {Team => :win_percentage} + # or + sortable {:teams => :win_percentage} + # or + sortable "teams.win_percentage" + end + end + end +end +``` + +By default, dates and serial ids are reversed when first-sorted ('desc' instead of 'asc' in SQL). +If you want to reverse (or cancel it) the default sort order (first column click or the default sort column): + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + field :id do + sort_reverse false # will sort id increasing ('asc') first ones first (default is last ones first) + end + field :created_at do + sort_reverse false # will sort dates increasing ('asc') first ones first (default is last ones first) + end + field :name do + sort_reverse true # will sort name decreasing ('dec') z->a (default is a->z) + end + end + end +end +``` + +### Fields searching + +- You can make a column non-searchable by setting the searchable option to false (1) +- You can change the column that the field will actually search on (2) +- You can specify a list of column that will be searched over (3) + +Belongs_to associations: + +- will be searched on their foreign_key (:team_id) +- or on their label if label is not virtual (:name, :title, etc.) +- you can also specify columns on the targeted table or the source table (see example) (4) +- will not be searched unless `queryable` is set to `true` + +```ruby +RailsAdmin.config do |config| + config.model 'Player' do + list do + field :created_at do # (1) + searchable false + end + + field :name do # (2) + searchable :last_name + end + # OR + field :name do # (3) + searchable [:first_name, :last_name] + end + + field :team do # (4) + queryable true + searchable [:name, :id] + # eq. to [Team => :name, Team => :id] + # or even [:name, Player => :team_id] will search on teams.name and players.team_id + # if you need to specify the join association name: + # (See #526 and http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html => table_aliasing) + searchable [{:teams => :name}, {:teams => :id}] + # or + searchable ["teams.name", "teams.id"] + end + end + end +end +``` + +For associations with multiple filterable columns configured, a record with _any_ filterable column matching the filter string will be returned. There is not currently a way to split out association columns into separate filters. (E.g. if you filter players by "Team: 5", then any players on team #5 _plus_ any players whose team name contains a "5" will be returned.) + +Searchable definitions will be used for searches and filters. +You can independently deactivate querying (search) or filtering for each field with: + +```ruby +field :team do + searchable [:name, :color] + queryable true # default except for associations + filterable false +end +``` + +### Scopes + +This allows to display a configurable scoped list view. + +First will be the default, nil means no scope (also it is possible to disable non-scoped index) + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + scopes [:not_cart, :cart, nil] + end + end +end +``` + +This can also be used for achieving `unscoped` list view(i.e. ignoring `default_scope`): + +```ruby +RailsAdmin.config do |config| + config.model 'Player' do + list do + scopes [:unscoped] + end + end +end +``` + +### Limited pagination + +It allows to avoid `count(*)` query which take a lot of resources on big tables. +Pagination includes only: `next`/`prev` buttons. +Default value is `false`. + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + list do + limited_pagination true + end + end +end +``` + +[More here](../lib/rails_admin/config/sections/list.rb) + +[List view table styling](list-view-table-styling.md) diff --git a/docs/manually.md b/docs/manually.md new file mode 100644 index 0000000000..cfb3eb1e7e --- /dev/null +++ b/docs/manually.md @@ -0,0 +1,64 @@ +# Using encrypted passwords + +### Add the bcrypt gem to Your Gemfile + +`gem 'bcrypt'` + +and run bundler + +`bundle install` + +### Create the User model + +``` +rails g model user name:string password_digest:string +``` + +Add `has_secure_password` to the model + +``` +class User < ApplicationRecord + has_secure_password +end +``` + +### Edit config + +Edit the `config/initializers/rails_admin.rb` file and add the authentication. + +``` + config.authenticate_with do + authenticate_or_request_with_http_basic('Login required') do |username, password| + user = User.where(name: username).first + user.authenticate(password) if user + end + end +``` + +### Authenitcating for User Roles/Priviliges + +``` + config.authorize_with do |controller| + if current_user.nil? + redirect_to main_app.new_account_session_path, flash: {error: 'Please Login to Continue..'} + elsif !current_user.admin? + redirect_to main_app.root_path, flash: {error: 'You are not Admin bro!'} + end + end +``` + +# Using unencrypted passwords 💣 + +👉**Saving plain text passwords could get you in trouble with GDPR, you have been warned** 👈 + +In config/initializers/rails_admin.rb, you can add the following lines of code: + + config.authenticate_with do + authenticate_or_request_with_http_basic('Login required') do |username, password| + user = User.where(email: username, password: password, admin: true).first + user + end + end + +This will call your User object from the database and check if it exists, +If yes, it will login else it won't. diff --git a/docs/mass-assignments-protection.md b/docs/mass-assignments-protection.md new file mode 100644 index 0000000000..fed4d03f88 --- /dev/null +++ b/docs/mass-assignments-protection.md @@ -0,0 +1,10 @@ +# Mass-assignment protection + +Only visible fields are editable in RailsAdmin. Others will get caught and sanitized. + +Basically you don't need to protect your attributes in RailsAdmin. + +However, `:attr_accessible` and `:attr_protected` are taken into account: restricted fields are not editable (read_only). +If you whitelist attributes, don't forget to whitelist accessible associations' 'id' methods as well : `division_id`, `player_ids`, `commentable_type`, `commentable_id`, etc. + +`:attr_accessible` specifies a list of accessible methods for mass-assignment in your ActiveModel models. diff --git a/docs/modal.md b/docs/modal.md new file mode 100644 index 0000000000..b9e339cde9 --- /dev/null +++ b/docs/modal.md @@ -0,0 +1,7 @@ +# Modal + +Section used for the edit views (create/update) when the object is edited via the AJAX modal. + +It inherits its configuration from the `base` section and the `edit` section. + +[More here](../lib/rails_admin/config/sections/modal.rb) diff --git a/docs/models.md b/docs/models.md new file mode 100644 index 0000000000..efd1e15053 --- /dev/null +++ b/docs/models.md @@ -0,0 +1,171 @@ +## Configuration location + +A model configuration can be done in any of the places below: + +- inside the RailsAdmin initializer: + + ```ruby + # config/initializers/rails_admin.rb + + RailsAdmin.config do |config| + config.model 'ModelName' do + # ... + end + end + ``` + +- in the model definition file: + + ```ruby + # app/models/model_name.rb + + class ModelName < ActiveRecord::Base + rails_admin do + # ... + end + end + ``` + +- in a separate concern file: + + ```ruby + module ModelNameAdmin + extend ActiveSupport::Concern + + included do + rails_admin do + # ... + end + end + end + ``` + + Don't forget to include the concern module in the model file itself by doing: + + ```ruby + include ModelNameAdmin + ``` + +This is your choice to make: + +- The initializer is loaded once at startup (modifications will show up when restarting the application) and may slow down your application startup, but all RailsAdmin configuration will stay in one place. +- Models are reloaded at each request in development mode (when modified), which may smooth your RailsAdmin development workflow. + +## The `object_label_method` method + +The model configuration option `object_label_method` configures +the title display of a single database record, i.e. an instance of a model. + +By default it tries to call "name" or "title" methods on the record in question. If the object responds to neither, +then the label will be constructed from the model's classname appended with its +database identifier. You can add label methods (or replace the default [:name, :title]) with: + +```ruby +RailsAdmin.config {|c| c.label_methods << :description} +``` + +This `object_label_method` value is used in a number of places in RailsAdmin--for instance for the +output of belongs to associations in the listing views of related models, for +the option labels of the relational fields' input widgets in the edit views of +related models and for part of the audit information stored in the history +records--so keep in mind that this configuration option has widespread +effects. + +```ruby +RailsAdmin.config do |config| + config.model 'Team' do + object_label_method do + :custom_label_method + end + end + + def custom_label_method + "Team #{self.name}" + end +end +``` + +_Edit: The above has been reported not to work properly (see https://github.com/railsadminteam/rails_admin/issues/2030). If you're encountering such a problem try this workaround:_ + +```ruby +# config/initializers/rails_admin.rb +RailsAdmin.config do |config| + config.model 'Team' do + object_label_method do + :custom_label_method + end + end + Team.class_eval do + def custom_label_method + "Team #{self.name}" + end + end +end +``` + +_Edit_: Solution 2: Add `object_label_method` to `ApplicationRecord` then send `.decorate` method to use Drapper Decorator. + +Add `object_label_method` method. + +```ruby + def object_label_method + klass = "#{self.class.name}Decorator".classify + + if Object.const_defined? klass + object = klass.constantize.send(:decorate, self) + if object.respond_to? :object_label + object.object_label + else + id + end + end + end +``` + +Then add + +```ruby +class UserDecorator < Draper::Decorator + def object_label + #... + end +end +``` + +or use `alias_method` + +```ruby +class UserDecorator < Draper::Decorator + def display_name + ["(#{id})", first_name, last_name].join(' ') + end + alias_method :object_label, :display_name +end +``` + +### Difference between `label` and `object_label` + +`label` and `object_label` are both model configuration options. `label` is used +whenever Rails Admin refers to a model class, while `object_label` is used whenever +Rails Admin refers to an instance of a model class (representing a single database record). + +## Configuring models all at once + +If you want to use `config.model` for all models, you may find it in `ActiveRecord::Base.descendants` + +If `cache_classes` is off (by default it's off in development, but on in production): +Call `Rails.application.eager_load!` before the following: + +```ruby +RailsAdmin.config do |config| + ActiveRecord::Base.descendants.each do |imodel| + config.model "#{imodel.name}" do + list do + exclude_fields :created_at, :updated_at + end + end + end +end +``` + +**Note**: In versions prior to `v1.0.0.rc`, successive invocations of `config.model` to the same model would _replace_ the configuration block rather than adding to it--meaning that the above block gets ignored for any model that also has a config block in the model class. (This was fixed by [#2670](https://github.com/railsadminteam/rails_admin/pull/2670).) diff --git a/docs/navigation.md b/docs/navigation.md new file mode 100644 index 0000000000..03ead4335c --- /dev/null +++ b/docs/navigation.md @@ -0,0 +1,200 @@ +# Navigation + +You can include/exclude models totally. They won't appear in RailsAdmin at all. + +By default, RailsAdmin automatically discovers all the models in the system and adds them to its list of models to +be accessible through RailsAdmin. + +**Blacklist Approach** + +The `excluded_models` configuration above permits the blacklisting of individual model classes. + +```ruby +config.excluded_models << "ClassName" +``` + +**Whitelist Approach** + +If you prefer a whitelist approach, then you can use the `included_models` configuration option instead: + +```ruby +config.included_models = ["Class1", "Class2", "Class3"] +``` + +Only the models explicitly listed will be put under RailsAdmin access, and the auto-discovery of models is skipped. + +The blacklist is effective on top of that, still, so that if you also have: + +```ruby +config.excluded_models = ["Class1"] +``` + +then only `Class2` and `Class3` would be made available to RailsAdmin. + +The whitelist approach may be useful if RailsAdmin is used only for a part of the application and you want to make +sure that new models are not automatically added to RailsAdmin, e.g. because of security concerns. + +Once done with the choice of model, you can customize the way they appear in the navigation. + +**Static links** + +Static links can be appended to the main navigation: + +```ruby +config.navigation_static_links = { + 'Google' => 'http://www.google.com' +} +``` + +They are displayed in a separate group with default name 'Links', but you can change it: + +```ruby +config.navigation_static_label = "My Links" +``` + +**Setting the model's label** + +RailsAdmin will use ActiveModel I18n API by default, so this shouldn't be needed. Still, you can configure label, and its plural, if needed: + +```ruby +config.model 'Box' do + label "Beautiful box" + label_plural "Beautiful boxen" +end +``` + +But again, this is **way better** to do it in `config/locale/en.yml`: + +```yml +en: + ... +  activerecord: +    models: +      box: + one: Beautiful box + other: Beautiful boxen +    attributes: +      box: +        color: "Shade of grey" + ... +``` + +This label will be used anywhere the model name is shown, e.g. on the navigation tabs, +Dashboard page, list pages, etc. + +**Setting the navigation icon** + +You can set the navigation icon from the bootstrap theme this way : + +```ruby +config.model 'User' do + navigation_icon 'icon-user' +end +``` + +**Hiding a model** + +You can hide a model from the top navigation by marking its `visible` option +as false: + +By passing the value as an argument: + +```ruby +config.model 'Team' do + visible false +end +``` + +Or by passing a block that will be lazy evaluated each time the option is read: + +```ruby +config.model 'Team' do + visible do + # controller bindings is available here. Example: + bindings[:controller].current_user.role == :admin + end +end +``` + +These two examples also work as a generic example of how most of the +configuration options function within RailsAdmin. You can pass a value as an +argument `option_name value`, or you can pass in a block which will be +evaluated each time the option is read. Notable is that boolean options' reader +accessors will be appended with ? whereas the writers will not be. That is, if +you want to get the Team model's visibility, you use +`RailsAdmin.config(Team).visible?`. + +**Treeview List** + +```ruby +# Given there are the following models: League, Team and Division + +config.model 'Team' do + parent League +end + +config.model 'Division' do + parent League +end +``` + +Obtained navigation: + + Dashboard + ... + League + Division + Team + ... + +**Create a navigation_label in navigation** + +You probably want to change the name of the navigation_label. +This can be easily achieved with the 'navigation_label' method of the parent model. + +Added to previous example: + +```ruby +config.model 'League' do + navigation_label 'League related' +end +``` + +Obtained navigation: + + Dashboard + ... + League related # (non-clickable) + League + Division + Team + ... + +**Change models order in navigation** + +By default, they are ordered by alphabetical order. If you need to override this, specify +a weight attribute. Default is 0. Lower values will bubble items to the top, higher values +will move them to the bottom. Items with same weight will still be ordered by alphabetical order. +The mechanism is fully compatible with navigation labels. Items will be ordered within their own +menu subset (but the parent item will always be first inside this submenu). + +Example: + +```ruby +config.model 'League' do + navigation_label 'League related' + weight -1 +end +``` + +The 'League related' navigation label will move to the topmost position. + +**Method for instances label** + +Set the method name for instances' label. Will default to the first `Config.label_methods` (see [base configuration](base-configuration.md)) that instances respond to. You can set it explicitly: + +```ruby +config.model 'Team' do + object_label_method :custom_name_method +end +``` diff --git a/docs/nested.md b/docs/nested.md new file mode 100644 index 0000000000..67d6228ab1 --- /dev/null +++ b/docs/nested.md @@ -0,0 +1,7 @@ +# Nested + +Section used for the edit views (create/update) when the object is nested. + +It inherits its configuration from the `base` section and the `edit` section. + +[More here](../lib/rails_admin/config/sections/nested.rb) diff --git a/docs/new-action.md b/docs/new-action.md new file mode 100644 index 0000000000..175eea7298 --- /dev/null +++ b/docs/new-action.md @@ -0,0 +1,18 @@ +### Example authorizations for cancan: + +```ruby + # with + alias_action :update, :destroy, :create, :to => :write + + can :manage, :all + # includes + can :write, :all + # includes + can :create, Model + # equals + can :new, Model + # includes + can :create, Model, { default_attributes } +``` + +[More here](../lib/rails_admin/config/actions/new.rb) diff --git a/docs/paperclip.md b/docs/paperclip.md new file mode 100644 index 0000000000..0c09dd2bab --- /dev/null +++ b/docs/paperclip.md @@ -0,0 +1,33 @@ +# Paperclip + +Install and read https://github.com/thoughtbot/paperclip and https://github.com/thoughtbot/paperclip/wiki/Thumbnail-Generation first. + +```bash +$ rails generate paperclip product asset +``` + +Automatically detected. + +Considering a `has_attached_file :asset` declaration, all :asset_file_name, :asset_content_type.. columns will be hidden. A file upload field will be created and accessible for customization in the DSL: `field :asset, :paperclip`. + +One thing you may need is a delete method in your model. +Paperclip does not include it, you'll need to add it manually. +RailsAdmin will detect it and add a checkbox. + +```ruby +class Product < ActiveRecord::Base + has_attached_file :asset, + :styles => { + :thumb => "100x100#", + :small => "150x150>", + :medium => "200x200" } + validates_attachment_content_type :asset, :content_type => /\Aimage\/.*\Z/ + # add a delete_ method: + attr_accessor :delete_asset + before_validation { self.asset.clear if self.delete_asset == '1' } +end +``` + +If you use a `attr_accessible` strategy, don't forget to add `delete_asset` to the whitelist. + +[More here](../lib/rails_admin/config/fields/types/paperclip.rb) diff --git a/docs/papertrail.md b/docs/papertrail.md new file mode 100644 index 0000000000..68ce88d0ed --- /dev/null +++ b/docs/papertrail.md @@ -0,0 +1,47 @@ +# PaperTrail + +- First install [PaperTrail](https://github.com/paper-trail-gem/paper_trail) and [PaperTrail-AssociationTracking](https://github.com/westonganger/paper_trail-association_tracking) + +_Note: you should use the `--with-changes` option when creating the version table +to ensure that history messages are recorded in a `object_changes` column._ + +```bash +$ bundle exec rails generate paper_trail:install --with-changes +$ bundle exec rails generate paper_trail_association_tracking:install --with-associations +``` + +- add the `has_paper_trail` statements to the tracked models +- add this to your `rails_admin.rb` initializer: + +```ruby +config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0 +config.audit_with :paper_trail, 'User', 'Version' # PaperTrail < 3.0.0 +``` + +User should be your 'whodunnit' model. + +To show the history: + +```ruby +PAPER_TRAIL_AUDIT_MODEL = ['Order', 'Payment'] +config.actions do + history_index do + only PAPER_TRAIL_AUDIT_MODEL + end + history_show do + only PAPER_TRAIL_AUDIT_MODEL + end +end +``` + +To hide the Papertrail links from the side navigation add this to your `rails_admin.rb` initializer: + +```ruby + config.model "PaperTrail::Version" do + visible false + end + + config.model "PaperTrail::VersionAssociation" do + visible false + end +``` diff --git a/docs/password.md b/docs/password.md new file mode 100644 index 0000000000..d5108f8ac8 --- /dev/null +++ b/docs/password.md @@ -0,0 +1,5 @@ +# Password + +Initializes automatically when field =~ password + +[More here](../lib/rails_admin/config/fields/types/password.rb) diff --git a/docs/performance.md b/docs/performance.md new file mode 100644 index 0000000000..b10fc1f556 --- /dev/null +++ b/docs/performance.md @@ -0,0 +1,35 @@ +# Performance + +The following are some tips and tricks to improve performance of Rails Admin with larger databases. + +## Edit Page Performance + +A common source of slow-loading edit pages for me is fields that are associations to large tables being editable. + +For example, if I have a User model which `has_many :comments`, the `comments` field is editable (the default), and I have millions of rows in my `comments` table, then when I attempt to load the user edit page, Rails Admin appears to issue a `select * from comments`. + +Marking associations to large tables as `readonly` has often been the culprit behind poor edit page performance for me. + +### Making all fields readonly by default + +If you're expecting large tables, it could be beneficial to make all fields of all models readonly by default from the start, and then manually set fields to `readonly true` within individual models as needed. + +See [Making all fields readonly by default](fields.md#making-all-fields-readonly-by-default) for how to do this. + +## History Tab performance + +I've also managed to speed up loads on index pages and the history tab with the following. (Note: Only do this if you don't use Kaminari in your actual app.) + +```rb +module Kaminari + module ActiveRecordRelationMethods + def total_count(column_name = :all, _options = nil) + limit(5000).count(column_name) + end + end +end +``` + +This can also help performance if you are using the [`rails_admin_history_rollback`](https://github.com/rikkipitt/rails_admin_history_rollback) gem, as the "View Changes" button also issues a full count on the versions table. + +See [#2808](https://github.com/railsadminteam/rails_admin/issues/2808) for further discussion. diff --git a/docs/polymorphic-belongs-to-association.md b/docs/polymorphic-belongs-to-association.md new file mode 100644 index 0000000000..31dd7d9353 --- /dev/null +++ b/docs/polymorphic-belongs-to-association.md @@ -0,0 +1,22 @@ +# Polymorphic `belongs_to` association + +Synopsis: + +```ruby +class Comment < ActiveRecord::Base + belongs_to :commentable, :polymorphic => true, :inverse_of => :comments + + rails_admin do + configure :commentable do + # configuration here + end + end +end + +# for info +class Team < ActiveRecord::Base + has_many :comments, :as => :commentable, :inverse_of => :commentable +end +``` + +[More here](../lib/rails_admin/config/fields/types/polymorphic_association.rb) diff --git a/docs/routing-problems.md b/docs/routing-problems.md new file mode 100644 index 0000000000..d0f4dc7079 --- /dev/null +++ b/docs/routing-problems.md @@ -0,0 +1,21 @@ +# Routing problems + +Your routes.rb should look like this one: + +```ruby +DummyApp::Application.routes.draw do + mount RailsAdmin::Engine => '/admin', :as => 'rails_admin' + devise_for :users + root :to => "home#index" + + resources :articles + resources :pages + match ':controller(/:action(/:id(.:format)))' +end +``` + +Note 3 things: + +- `mount RailsAdmin::Engine => '/admin'` will catch all `/admin` urls, including `/administrator`, which would then blow up in RailsAdmin! +- if you choose to put a catch-up root route before `mount RailsAdmin::Engine`, make sure it doesn't catch '/admin/' +- a root url is necessary for Devise, and will be used in RailsAdmin (home button) diff --git a/docs/rspec-with-capybara-examples.md b/docs/rspec-with-capybara-examples.md new file mode 100644 index 0000000000..d409750cb8 --- /dev/null +++ b/docs/rspec-with-capybara-examples.md @@ -0,0 +1,41 @@ +### Go somewhere + +```ruby +visit rails_admin.dashboard_path +visit rails_admin.index_path(model_name: 'blog~post') +visit rails_admin.new_path(model_name: 'blog~post') +visit rails_admin.edit_path(model_name: 'blog~post', id: post.id) +``` + +### Assert you landed somewhere + +```ruby +expect(current_path).to eq rails_admin.dashboard_path +expect(current_path).to eq rails_admin.index_path(model_name: 'blog~post') +expect(current_path).to eq rails_admin.new_path(model_name: 'blog~post') +expect(current_path).to eq rails_admin.edit_path(model_name: 'blog~post', id: post.id) +``` + +### Click links + +```ruby +# From anywhere +page.find('.dashboard_root_link a').click # to a root action +page.find('[data-model=blog~post] a').click # to any model index +# From any collection action +page.find('.new_collection_link a').click # to another collection action +# From any member action +page.find('.edit_member_link a').click # to another member action +# From the dashboard +page.find('.blog_post_links .index_collection_link a').click # to any collection action +# From the index page of a model +page.find('.blog_post_row[1] .show_member_link a').click # to any row's member action +``` + +### Assert the content + +```ruby +expect(page.find('#blog_post_title')).to have_content 'Blog Post Title' # of an edit/new form input +expect(page.find('.blog_post_row[1] .title_field').to have_content 'Blog Post Title' # of an index table row cell +expect(page.find('.alert')).to have_content 'Post successfully created' # of a flash message +``` diff --git a/docs/show-action.md b/docs/show-action.md new file mode 100644 index 0000000000..f94f2caca1 --- /dev/null +++ b/docs/show-action.md @@ -0,0 +1,15 @@ +### Example authorizations for cancan: + +```ruby + can :manage, :all + # includes + can :read, :all + # includes + can :read, Model + # includes + can :show, Model + # includes + can :show, Model, { conditions } +``` + +[More here](../lib/rails_admin/config/actions/show.rb) diff --git a/docs/show-in-app-action.md b/docs/show-in-app-action.md new file mode 100644 index 0000000000..7813043f13 --- /dev/null +++ b/docs/show-in-app-action.md @@ -0,0 +1,13 @@ +### Example authorizations for cancan: + +```ruby + can :manage, :all + # includes + can :show_in_app, :all + # includes + can :show_in_app, Model + # includes + can :show_in_app, Model, { conditions } +``` + +[More here](../lib/rails_admin/config/actions/show_in_app.rb) diff --git a/docs/show.md b/docs/show.md new file mode 100644 index 0000000000..9e6ccd2c99 --- /dev/null +++ b/docs/show.md @@ -0,0 +1,30 @@ +# Show + +Section used for the show view. + +It inherits its configuration from the `base` section. + +To configure which fields are shown in the "Show" view of a model, use the `show` method in the config block: + +```ruby +RailsAdmin.config do |config| + config.model 'BlogPage' do + show do + field :title + field :author_name + end + end +end +``` + +# + +You can display empty fields in show view with: + +```ruby +RailsAdmin.config do |config| + config.compact_show_view = false +end +``` + +[More here](../lib/rails_admin/config/sections/show.rb) diff --git a/docs/sorcery.md b/docs/sorcery.md new file mode 100644 index 0000000000..4d7aeb6c75 --- /dev/null +++ b/docs/sorcery.md @@ -0,0 +1,43 @@ +# Sorcery + +Here are listed community proposed solutions to solve [Sorcery](https://github.com/Sorcery/sorcery) and RailsAdmin compatibility: + +### Authentication + +This was proposed by David Tuite on [SO](http://stackoverflow.com/questions/9815062/rails-admin-with-sorcery/9834837) + +Authentication with Sorcery requires manual tweaking of `rails_admin.rb` initializer. + +```ruby +RailsAdmin.config do |config| + config.authenticate_with do + # Use sorcery's before filter to auth users + require_login + end + config.current_user_method(&:current_user) +end + +``` + +You will also need to update the `not_authenticated` in your `application_controller.rb`: + +```ruby +class ApplicationController + # Overwrite the method sorcery calls when it + # detects a non-authenticated request. + def not_authenticated + # Make sure that we reference the route from the main app. + redirect_to main_app.login_path + end +end +``` + +### Solving incompatibility with authentication in production + +There's a solution proposed by [@adamkangas](https://github.com/adamkangas), for context, see [#147](https://github.com/NoamB/sorcery/issues/147) + +This requires forcing `Sorcery::Controller` inclusion at the end of `sorcery.rb` initializer: + +```ruby +ActionController::Base.send(:include, Sorcery::Controller) +``` diff --git a/docs/string.md b/docs/string.md new file mode 100644 index 0000000000..a7325cb78a --- /dev/null +++ b/docs/string.md @@ -0,0 +1,5 @@ +# String + +Options: html_attributes (maxlength and size) + +[More here](../lib/rails_admin/config/fields/types/string.rb) diff --git a/docs/text.md b/docs/text.md new file mode 100644 index 0000000000..d3e3abb011 --- /dev/null +++ b/docs/text.md @@ -0,0 +1,8 @@ +### Optional external editors integration + +- [CKEditor](ckeditor.md) +- [CodeMirror](codemirror.md) (added [recently](https://github.com/railsadminteam/rails_admin/commit/61a7e0e7ec21e4183777aa8944cc4f6cc89b9bdc)) +- [Wysihtml5](wysihtml5.md) +- [Froala-WYSIWYG-HTML-Editor](froala-wysiwyg-html-editor.md) + +[More here](../lib/rails_admin/config/fields/types/text.rb) diff --git a/docs/theming-and-customization.md b/docs/theming-and-customization.md new file mode 100644 index 0000000000..a2fae69def --- /dev/null +++ b/docs/theming-and-customization.md @@ -0,0 +1,88 @@ +# Theming and customization + +RailsAdmin uses a sass release of bootstrap for CSS, and bootstrap/jquery-ui for JS. + +### For custom theming (application scoped), simply override these files in your app: + +``` +app/assets/stylesheets/rails_admin/custom/mixins.scss +app/assets/stylesheets/rails_admin/custom/theming.scss +app/assets/stylesheets/rails_admin/custom/variables.scss +app/assets/javascripts/rails_admin/custom/ui.js +``` + +Don't forget to re-compile your assets or simply delete the content of your `tmp/cache` folder. Some additional steps might be required, as others reported here: https://github.com/railsadminteam/rails_admin/issues/738#issuecomment-26615578 + +RailsAdmin uses jquery-pjax (https://github.com/defunkt/jquery-pjax) to load pages instead normal HTTP requests, use `$(document).on('rails_admin.dom_ready', function(){ /* your js code here */ });` instead jQuery's default on ready function `$(function(){ /* your js code here */ });` to check if page is loaded. It will work to both normal and pjax requests. + +### To create a distributable theme + +``` +rails plugin new rails_admin_<__THEME_NAME__> -m https://gist.githubusercontent.com/bbenezech/1523639/raw/42579a263c219d111c03936f93ff25a7d8999bda/rails_admin_theme_creator --skip-gemfile --skip-bundle -T -O --full +``` + +Then add to your application `Gemfile` (before RailsAdmin): + +```ruby +gem 'rails_admin_<__THEME_NAME__>', :path => '../rails_admin_<__THEME_NAME__>' +``` + +Inside your rails_admin application `config/application.rb`, just after `Bundler.require`: + +```ruby +ENV['RAILS_ADMIN_THEME'] = '<__THEME_NAME__>' +``` + +This will allow for convenient live development testing. + +Please follow the convention: `rails_admin_` prefix for all RailsAdmin related gems. + +Once done, upload it on Github with a valid gemspec (change authors, email and project descriptions) to share your work. + +### CSS + +Put all the real theming in `theming.css.scss`. It can be regular CSS, LESS or [SCSS](http://sass-lang.com/) + +Note that if you choose to use SCSS, you can: + +- modify all the mixins provided by rails_admin and bootstrap and add others for you to use in `mixins.scss`. (available mixins [here](https://github.com/twbs/bootstrap-sass/blob/master/assets/stylesheets/bootstrap/_mixins.scss)) +- modify all the variables provided by rails*admin and bootstrap and add others for you to use in `variables.scss`. Note that the variables in `variables.scss` are imported before Bootstrap's variables which all have set the [!default](http://sass-lang.com/documentation/file.SASS_REFERENCE.html#variable_defaults*) flag. This effectively means that you can customize chained variables by just assigning a custom value to the first one instead of the need to override each single one. E.g. you do not have to override `$btn-success-bg`, `$label-succes-bg` and `$progress-bar-success-bg` but only assign a custom value to `$brand-success`. (available variables [here](https://github.com/twbs/bootstrap-sass/blob/master/assets/stylesheets/bootstrap/_variables.scss)) +- In `theming.scss`: + - use all mixins and variables. (your owns, Bootstrap's and RailsAdmin's) + - include any other .scss file with `@import rails_admin/themes/__THEME_NAME__/my_scss_file` and organize your the rest of your theme the way you want. + +### JS + +Use anything you want that the asset pipeline supports: regular JS, includes, Coffee, .. + +### Images + +You can use any image available to the asset pipeline. + +### Use a theme + +In your `Gemfile`: + +```ruby +gem 'rails_admin_example_theme', :git => 'git://github.com/bbenezech/rails_admin_example_theme.git' +``` + +Inside `config/application.rb`, just after `Bundler.require`: + +```ruby +ENV['RAILS_ADMIN_THEME'] = 'example_theme' +``` + +### Resources: + +- [Bootstrap](http://twitter.github.com/bootstrap/) +- [Bootstrap Sass](https://github.com/thomas-mcdonald/bootstrap-sass) + +### Existing themes: + +- [Example theme](https://github.com/bbenezech/rails_admin_example_theme): technical stub you can use for bootstrapping. Everything should look painfully greenish. +- [Flatly theme](https://github.com/konjoot/rails_admin_flatly_theme): Bootstrap 2 flatly theme. +- [Rails Admin Material](https://github.com/blocknotes/rails_admin_material): A Material design theme. +- [Rollincode theme](https://github.com/rollincode/rails_admin_theme): Bootstrap 3 flat theme. +- [SoftwareBrothers theme](https://github.com/SoftwareBrothers/rails_admin_softwarebrothers_theme): SoftwareBrothers theme +- Designer, or feel like one? Add your own. diff --git a/docs/timestamp-date-datetime-time.md b/docs/timestamp-date-datetime-time.md new file mode 100644 index 0000000000..2e0a844454 --- /dev/null +++ b/docs/timestamp-date-datetime-time.md @@ -0,0 +1,9 @@ +# Timestamp Date Datetime time + +[Datetime](../lib/rails_admin/config/fields/types/datetime.rb) (parent class) + +[Timestamp](../lib/rails_admin/config/fields/types/timestamp.rb) + +[Date](../lib/rails_admin/config/fields/types/date.rb) + +[Time](../lib/rails_admin/config/fields/types/time.rb) diff --git a/docs/translations.md b/docs/translations.md new file mode 100644 index 0000000000..3b76550c73 --- /dev/null +++ b/docs/translations.md @@ -0,0 +1,51 @@ +## Rails + +Pick [your locale here](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) + +## Devise + +See https://github.com/plataformatec/devise/wiki/I18n and https://github.com/tigrish/devise-i18n + +## RailsAdmin + +Copy https://github.com/railsadminteam/rails_admin/blob/master/config/locales/rails_admin.en.yml to your `config/locales` directory and adapt it to your needs. + +## Translation Missing? + +**You can pick your locales or contribute in translation here: https://www.localeapp.com/projects/905** + +Make sure your existing locales do not have admin: key under :en, :es, :de etc. You will get translation missing warning then + +Community sourced translations: + +- [Arabic: ar](https://gist.github.com/yamanaltereh/6874413) +- [Catalan: ca](https://gist.github.com/1764593] +- [Chinese (Traditional): zh-TW](https://gist.github.com/2001808] +- [Chinese (Simplified): zh-CN](https://gist.github.com/x-ji/d3be402de194a152a859] +- [Czech: cs](https://gist.github.com/4618569] +- [Danish: da](https://gist.github.com/1780778] +- [Dutch: nl](https://gist.github.com/1909240] +- [English: en](../config/locales/rails_admin.en.yml] +- [French: fr](https://gist.github.com/pacohigh/80a96616423859c521e4c7f7a19cbd87] +- [German: de](https://gist.github.com/3188219#file_rails_admin.de.yml] +- [Italian (Italy): it](https://gist.github.com/1700678#file_rails_admin.it.yml] +- [Japanese: ja](https://gist.github.com/1662352#file_rails_admin.ja.yml] +- [Korean: ko](https://gist.github.com/YoonjaeYoo/787eb279e5d46c7e96dc] +- [Thai: th](https://gist.github.com/wittawasw/2a7d6c62d28613702753] +- [Polish (Poland): pl](https://gist.github.com/1717477#file_rails_admin.pl.yml] +- [Portuguese (Brazil): pt-BR](https://gist.github.com/1844723#file_rails_admin.pt_br.yml] +- [Portuguese (Portugal): pt-PT](https://gist.github.com/1751856#file_rails_admin.pt-PT.yml] +- [Russian (Russia): ru](https://gist.github.com/serghost/9832270cbddc5678a3ca] +- [Slovenian (Slovenia): sl](https://gist.github.com/3290930#file_rails_admin.sl.yml] +- [Spanish (Spain): es](https://gist.github.com/1647597#file_rails_admin.es.yml] +- [Spanish (Latin American): es](https://gist.github.com/hernanvicente/8d7f8d92ae4c88e9b2d877fe63e9d4e0] +- [Swedish: sv](https://gist.github.com/4433235] +- [Norwegian: no](https://gist.github.com/2844960#file_rails_admin.nb.yml] +- [Turkish: tr](https://gist.github.com/2888924#file_rails_admin.tr.yml] +- [Ukrainian: uk](https://gist.github.com/nazarok/a6bf4186154f27b850350fa79e31f288#file-rails_admin-uk-yml] + +- Add your own (create a gist first) + +You can find old included translations here: + +https://github.com/railsadminteam/rails_admin/tree/df631d6d4ed49a5417d8135000611c37f6a3ed9b/config/locales diff --git a/docs/troubleshoot.md b/docs/troubleshoot.md new file mode 100644 index 0000000000..4288469a90 --- /dev/null +++ b/docs/troubleshoot.md @@ -0,0 +1,127 @@ +## Known incompatibilities, from namespacing issues: + +- various [Twitter Bootstrap](https://github.com/twitter/bootstrap) Asset-Pipeline vendoring libraries (use RailsAdmin dependency [bootstrap-sass](https://github.com/thomas-mcdonald/bootstrap-sass)) +- [devise_invitable](https://github.com/scambra/devise_invitable) can result in an issue. See [this](http://stackoverflow.com/questions/6012792/devise-invitable-rails-admin-conflict) Stack Overflow question for more info. +- [~~will_paginate~~](https://github.com/mislav/will_paginate) (Now we have a way to avoid method name collision. See ['Conflict between will_paginate and kaminari'](#conflict-between-will_paginate-and-kaminari) section below) + +## Known fix for twitter-bootstrap-rails: + +Just copy the `rails_admin/app/assets/javascripts/rails_admin/rails_admin.js` file to `/app/assets/javascripts/rails_admin/rails_admin.js`. +Then replace "require_asset 'bootstrap'" with "require_asset 'twitter/bootstrap'". +Now twitter-bootstrap-rails works with rails_admin. + +## Known fix for anjlab-bootstrap-rails: + +I am using gem [anjlab-bootstrap-rails](https://github.com/anjlab/bootstrap-rails), the fix steps for javascript as the above one, but we also need to fix the stylesheets import. Not sure if it's missing or not required for the above fix, we do need the additonal step if you are using this gem. + +First, Just copy the rails_admin/app/assets/javascripts/rails_admin/rails_admin.js.erb file to /app/assets/javascripts/rails_admin/rails_admin.js.erb. +Then replace "require_asset 'bootstrap'" with "require_asset 'twitter/bootstrap'". + +Second, Just copy the rails_admin/app/assets/stylesheets/rails_admin/imports.css.scss.erb file to /app/assets/stylesheets/rails_admin/imports.css.scss.erb. +Then replace "@import "bootstrap" with "@import "twitter/bootstrap". +Now twitter-bootstrap-rails works with rails_admin. + +## Other issues: + +### Locale is being forced to `:en` whereas config.i18n.default_locale = `:de` + +Reason: RailsAdmin DSL needs access to locale before default_locale being set by application.rb + +See: https://github.com/railsadminteam/rails_admin/issues/746 + +Workaround: add `I18n.default_locale = :de` inside RailsAdmin's initializer (before model configs) + +--- + +### Asset pipeline + +The master branch currently targets Rails >= 6.0. `rails_admin@2.x.x` (see the [`2.x-stable` branch](https://github.com/railsadminteam/rails_admin/blob/2.x-stable/rails_admin.gemspec#L15)) targets Rails >= 5.0. Older Rails versions may work, but are not actively maintained. + +If you are updating from a Rails 3.0 application, you will no longer need to +update your assets, they will be served from the engine (through Sprockets). +You can delete all RailsAdmin related assets in your public directory. +RailsAdmin needs the asset pipeline. Activate it in `application.rb`: + +```ruby +config.assets.enabled = true +``` + +Please note that `initializer/rails_admin.rb` is very likely to require access to your DB. +Thus, if you don't need access to your application at asset compilation time, + +```ruby +config.assets.initialize_on_precompile = false +``` + +will reduce your compilation time and is recommended. +Note that this is needed on **Heroku** if you set `compile = false` and don't version `public/assets`. +More here: http://devcenter.heroku.com/articles/rails31_heroku_cedar + +Also, as of version 0.0.4, you have to add this to successfully precompile assets. This is also needed if you're deploying in **Heroku**. +(See [#1192](https://github.com/railsadminteam/rails_admin/issues/1192) for the issue report and [#1046](https://github.com/railsadminteam/rails_admin/issues/1046) for the fix.) + +```ruby +config.assets.precompile += ['rails_admin/rails_admin.css', 'rails_admin/rails_admin.js'] +``` + +If you still have issue with the asset pipeline: + +- make sure you didn't commit your assets in public/assets +- Some css/js assets are not meant to be compiled alone: +- make sure you don't have any catch-all \*.(css|js) in `config.assets.precompile` +- make sure you don't have any catch-all `require_tree .` in application.(css|js) +- copy all asset related configuration from application.rb and environment/\*.rb files from a fresh (`rails new dummy`) rails app +- remove old assets with `bundle exec rake assets:clean` when in development +- read thoroughly the [Rails Guide](http://guides.rubyonrails.org/asset_pipeline.html) + +--- + +### Using model name AdminUser results in infinite redirection + +This happens because Rails engine router is greedy. It matches `/admin_users/sign_in` with `RailsAdmin::Engine`'s `_users/sign_in` which one is not authorized to see. + +You can use a different URL scope for `RailsAdmin` by changing `mount RailsAdmin::Engine => '/admin', :as => 'rails_admin'` in your `config/routes.rb`. e.g. You could do `mount RailsAdmin::Engine => '/foo_admin', ...`. + +--- + +### Double insertion of NestedFields + +jquery_nested_form is evaluated twice. Check your assets. Don't commit your assets to public/assets. See [#924](https://github.com/railsadminteam/rails_admin/issues/924) + +--- + +### Conflict between will_paginate and kaminari + +will_paginate is known to cause problem when used with kaminari, to which rails_admin has dependency. +To work around this issue, create `config/initializers/kaminari.rb` with following content: + +```ruby +Kaminari.configure do |config| + config.page_method_name = :per_page_kaminari +end +``` + +to make kaminari to use different paginating method from will_paginate's. + +--- + +### Redirect loop when visiting /admin + +In `config/routes.rb` switch lines for devise and RA so they are in this order: + +``` +devise_for :admins +mount RailsAdmin::Engine => '/admin', :as => 'rails_admin' +``` + +--- + +### No route matches [POST] for delete and update + +The problem was in missing middleware. I have added + +``` +config.middleware.use Rack::MethodOverride +``` + +to `/config/application.rb` diff --git a/docs/update.md b/docs/update.md new file mode 100644 index 0000000000..30ba088560 --- /dev/null +++ b/docs/update.md @@ -0,0 +1,7 @@ +# Update + +Section used for the update (edit) view. + +It inherits its configuration from the `base` section and the `edit` section. + +[More here](../lib/rails_admin/config/sections/update.rb) diff --git a/docs/using-railsadmin-routes.md b/docs/using-railsadmin-routes.md new file mode 100644 index 0000000000..4715ef0e7d --- /dev/null +++ b/docs/using-railsadmin-routes.md @@ -0,0 +1,38 @@ +### Link to RailsAdmin resources from your application : + +```ruby +rails_admin.dashboard_path +... + +rails_admin.index_path('module~class_name') +rails_admin.new_path('module~class_name') +... + +rails_admin.show_path(model_name: 'blog~post', id: post.id) +rails_admin.edit_path(model_name: 'blog~post', id: post.id) +... +``` + +Namespaced models must use a '~' between modules and class name. + +### Link to your application from RailsAdmin (usually from a config block) + +```ruby +main_app.article_path(my_article) +# from a config block: +bindings[:view].main_app.article_path(my_article) +``` + +### Link to RailsAdmin from RailsAdmin + +```ruby +# from a config block: +bindings[:view].link_to('new record', bindings[:view].rails_admin.new_path('module~class_name')) + +# Eg. +field :custom_action do + formatted_value do + bindings[:view].link_to('new record', bindings[:view].rails_admin.new_path('module~class_name', key: 'value')) + end +end +``` diff --git a/docs/wysihtml5.md b/docs/wysihtml5.md new file mode 100644 index 0000000000..79da18d90c --- /dev/null +++ b/docs/wysihtml5.md @@ -0,0 +1,35 @@ +# bootstrap-wysihtml5 + +http://jhollingworth.github.com/bootstrap-wysihtml5/ + +- your rails_admin <= 0.6.5 +- Add `gem 'bootstrap-wysihtml5-rails', '0.3.1.24'` to your Gemfile that is the last version that supports Bootstrap 2. +- your rails_admin >= 0.6.6 +- Add `gem 'bootstrap-wysihtml5-rails', '> 0.3.1.24'` to your Gemfile that supports Bootstrap 3. + +Then update config file `config/initializers/rails_admin.rb` + +```ruby +RailsAdmin.config do |config| + config.model Team do + edit do + # For RailsAdmin >= 0.5.0 + field :description, :wysihtml5 + # For RailsAdmin < 0.5.0 + # field :description do + # bootstrap_wysihtml5 true + # end + end + end +end + +# To configure the editor bar or the parser rules pass a hash of options: +# For RailsAdmin >= 0.5.0 +field :description, :wysihtml5 do + config_options toolbar: { fa: true }, # use font-awesome instead of glyphicon + html: true, # enables html editor + parserRules: { tags: { p:1 } } # support for

in html mode +end +``` + +[More here](../lib/rails_admin/config/fields/types/wysihtml5.rb) diff --git a/lib/generators/rails_admin/templates/initializer.erb b/lib/generators/rails_admin/templates/initializer.erb index 1104271044..392e99f77a 100644 --- a/lib/generators/rails_admin/templates/initializer.erb +++ b/lib/generators/rails_admin/templates/initializer.erb @@ -17,7 +17,7 @@ RailsAdmin.config do |config| ## == PaperTrail == # config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0 - ### More at https://github.com/railsadminteam/rails_admin/wiki/Base-configuration + ### More at https://github.com/railsadminteam/rails_admin/docs/base-configuration.md ## == Gravatar integration == ## To disable Gravatar integration in Navigation Bar set to false diff --git a/lib/rails_admin/config.rb b/lib/rails_admin/config.rb index e1b34a182b..f553d33838 100644 --- a/lib/rails_admin/config.rb +++ b/lib/rails_admin/config.rb @@ -165,7 +165,7 @@ def audit_with(*args, &block) # config.authorize_with :cancancan # end # - # See the wiki[https://github.com/railsadminteam/rails_admin/wiki] for more on authorization. + # See the docs[https://github.com/railsadminteam/rails_admin/docs/index.md] for more on authorization. # # @see RailsAdmin::Config::DEFAULT_AUTHORIZE def authorize_with(*args, &block)