Code Monkey home page Code Monkey logo

ex_admin's Introduction

ExAdmin

Build Status Hex Version License

Note: This version has been updated to support both Ecto 1.1 and Ecto 2.0. See Installation for more information.

ExAdmin is an auto administration package for Elixir and the Phoenix Framework, a port/inspiration of ActiveAdmin for Ruby on Rails.

Checkout the Live Demo. The source code can be found at ExAdmin Demo.

Checkout this Additional Live Demo for examples of many-to-many relationships, nested attributes, and authentication.

See the docs and the Wiki for more information.

Usage

ExAdmin is an add on for an application using the Phoenix Framework to create a CRUD administration tool with little or no code. By running a few mix tasks to define which Ecto Models you want to administer, you will have something that works with no additional code.

Before using ExAdmin, you will need a Phoenix project and an Ecto model created.

ExAdmin

Installation

Add ex_admin to your deps:

Hex

mix.exs

  defp deps do
     ...
     {:ex_admin, "~> 0.8"},
     ...
  end

GitHub with Ecto 2.0

mix.exs

  defp deps do
     ...
     {:ex_admin, github: "smpallen99/ex_admin"},
     ...
  end

Add some admin configuration and the admin modules to the config file

config/config.exs

config :ex_admin,
  repo: MyProject.Repo,
  module: MyProject,    # MyProject.Web for phoenix >= 1.3.0-rc 
  modules: [
    MyProject.ExAdmin.Dashboard,
  ]

Fetch and compile the dependency

mix do deps.get, deps.compile

Configure ExAdmin:

mix admin.install

Add the admin routes

web/router.ex

defmodule MyProject.Router do
  use MyProject.Web, :router
  use ExAdmin.Router
  ...
  scope "/", MyProject do
    ...
  end

  # setup the ExAdmin routes on /admin
  scope "/admin", ExAdmin do
    pipe_through :browser
    admin_routes()
  end

Add the paging configuration

lib/my_project/repo.ex

  defmodule MyProject.Repo do
    use Ecto.Repo, otp_app: :my_project
    use Scrivener, page_size: 10
  end

Edit your brunch-config.js file and follow the instructions that the installer appended to this file. This requires you copy 2 blocks and replace the existing blocks.

Start the application with iex -S mix phoenix.server

Visit http://localhost:4000/admin

You should see the default Dashboard page.

Getting Started

Adding an Ecto Model to ExAdmin

To add a model, use admin.gen.resource mix task:

mix admin.gen.resource MyModel

Add the new module to the config file:

config/config.exs

config :ex_admin,
  repo: MyProject.Repo,
  module: MyProject,
  modules: [
    MyProject.ExAdmin.Dashboard,
    MyProject.ExAdmin.MyModel,
  ]

Start the phoenix server again and browse to http://localhost:4000/admin/my_model

You can now list/add/edit/and delete MyModels.

Changesets

ExAdmin will use your schema's changesets. By default we call the changeset function on your schema, although you can configure the changeset we use for update and create seperately.

custom changeset:

defmodule TestExAdmin.ExAdmin.Simple do
  use ExAdmin.Register

  register_resource TestExAdmin.Simple do
    update_changeset :changeset_update
    create_changeset :changeset_create
  end
end

Relationships

We support many-to-many and has many relationships as provided by Ecto. We recommend using cast_assoc for many-to-many relationships and put_assoc for has-many. You can see example changesets in our test schemas

When passing in results from a form for relationships we do some coercing to make it easier to work with them in your changeset. For collection checkboxes we will pass an array of the selected options ids to your changeset so you can get them and use put_assoc as seen here

In order to support has many deletions you need you to setup a virtual attribute on your schema's. On the related schema you will need to add an _destroy virtual attribute so we can track the destroy property in the form. You will also need to cast this in your changeset. Here is an example changeset. In this scenario a User has many products and products can be deleted. We also have many roles associated.

defmodule TestExAdmin.User do
  import Ecto.Changeset
  use Ecto.Schema
  import Ecto.Query

  schema "users" do
    field :name, :string
    field :email, :string
    field :active, :boolean, default: true
    has_many :products, TestExAdmin.Product, on_replace: :delete
    many_to_many :roles, TestExAdmin.Role, join_through: TestExAdmin.UserRole, on_replace: :delete
  end

  @fields ~w(name active email)

  def changeset(model, params \\ %{}) do
    model
    |> cast(params, @fields)
    |> validate_required([:email, :name])
    |> cast_assoc(:products, required: false)
    |> add_roles(params)
  end

  def add_roles(changeset, params) do
    if Enum.count(Map.get(params, :roles, [])) > 0 do
      ids = params[:roles]
      roles = TestExAdmin.Repo.all(from r in TestExAdmin.Role, where: r.id in ^ids)
      put_assoc(changeset, :roles, roles)
    else
      changeset
    end
  end
end

defmodule TestExAdmin.Role do
  use Ecto.Schema
  import Ecto.Changeset
  alias TestExAdmin.Repo

  schema "roles" do
    field :name, :string
    has_many :uses_roles, TestExAdmin.UserRole
    many_to_many :users, TestExAdmin.User, join_through: TestExAdmin.UserRole
  end

  @fields ~w(name)

  def changeset(model, params \\ %{}) do
    model
    |> cast(params, @fields)
  end
end


defmodule TestExAdmin.Product do
  use Ecto.Schema
  import Ecto.Changeset

  schema "products" do
    field :_destroy, :boolean, virtual: true
    field :title, :string
    field :price, :decimal
    belongs_to :user, TestExAdmin.User
  end

  def changeset(schema, params \\ %{}) do
    schema
    |> cast(params, ~w(title price user_id))
    |> validate_required(~w(title price))
    |> mark_for_deletion
  end

  defp mark_for_deletion(changeset) do
    # If delete was set and it is true, let's change the action
    if get_change(changeset, :_destroy) do
      %{changeset | action: :delete}
    else
      changeset
    end
  end
end

A good blog post exisits on the Platformatec blog describing how these relationships work: http://blog.plataformatec.com.br/2015/08/working-with-ecto-associations-and-embeds/

Customizing the index page

Use the index do command to define the fields to be displayed.

admin/my_model.ex

defmodule MyProject.ExAdmin.MyModel do
  use ExAdmin.Register
  register_resource MyProject.MyModel do

    index do
      selectable_column()

      column :id
      column :name
      actions()     # display the default actions column
    end
  end
end

Customizing the form

The following example shows how to customize the form with the form macro:

defmodule MyProject.ExAdmin.Contact do
  use ExAdmin.Register

  register_resource MyProject.Contact do
    form contact do
      inputs do
        input contact, :first_name
        input contact, :last_name
        input contact, :email
        input contact, :register_date, type: Date # if you use Ecto :date type in your schema
        input contact, :category, collection: MyProject.Category.all
      end

      inputs "Groups" do
        inputs :groups, as: :check_boxes, collection: MyProject.Group.all
      end
    end
  end
end

Customizing the show page

The following example illustrates how to modify the show page.

defmodule MyProject.ExAdmin.Question do
  use ExAdmin.Register

  register_resource MyProject.Question do
    menu priority: 3

    show question do

      attributes_table   # display the defaults attributes

      # create a panel to list the question's choices
      panel "Choices" do
        table_for(question.choices) do
          column :key
          column :name
        end
      end
    end
  end
end

Custom Types

Support for custom field types is done in two areas, rendering fields, and input controls.

Rendering Custom Types

Use the ExAdmin.Render.to_string/ protocol for rendering types that are not supported by ExAdmin.

For example, to support rendering a tuple, add the following file to your project:

# lib/render.ex
defimpl ExAdmin.Render, for: Tuple do
  def to_string(tuple), do: inspect(tuple)
end

Input Type

Use the :field_type_matching config item to set the input type.

For example, given the following project:

defmodule ElixirLangMoscow.SpeakerSlug do
  use EctoAutoslugField.Slug, from: [:name, :company], to: :slug
end

defmodule ElixirLangMoscow.Speaker do
  use ElixirLangMoscow.Web, :model
  use Arc.Ecto.Model

  alias ElixirLangMoscow.SpeakerSlug
  schema "speakers" do
    field :slug, SpeakerSlug.Type
    field :avatar, ElixirLangMoscow.Avatar.Type
  end
end

Add the following to your project's configuration:

config :ex_admin,
  # ...
  field_type_matching: %{
    ElixirLangMoscow.SpeakerSlug.Type => :string,
    ElixirLangMoscow.Avatar.Type => :file
  }

Theme Support

ExAdmin supports 2 themes. The new AdminLte2 theme is enabled by default. The old ActiveAdmin theme is also supported for those that want backward compatibility.

Changing the Theme

To change the theme to ActiveAdmin, at the following to your config/config.exs file:

config/config.exs

config :ex_admin,
  theme: ExAdmin.Theme.ActiveAdmin,
  ...

Changing the AdminLte2 Skin Color

The AdminLte2 theme has a number of different skin colors including blue, black, purple, green, red, yellow, blue-light, black-light, purple-light, green-light, red-light, and yellow-light

To change the skin color to, for example, purple:

config/config.exs

config :ex_admin,
  skin_color: :purple,
  ...

Enable Theme Selector

You can add a theme selector on the top right of the menu bar by adding the following to your config/config.exs file:

config/config.exs

config :ex_admin,
  theme_selector: [
    {"AdminLte",  ExAdmin.Theme.AdminLte2},
    {"ActiveAdmin", ExAdmin.Theme.ActiveAdmin}
  ],
  ...

Overriding the model name

You can override the name of a model by defining a model_name/0 function on the module. This is useful if you want to use a different module for some of your actions.

admin/my_model.ex

def model_name do
  "custom_name"
end

Authentication

ExAdmin leaves the job of authentication to 3rd party packages. For an example of using Coherence checkout the Contact Demo Project.

Visit the Wiki for more information on adding Authentication.

Contributing

We appreciate any contribution to ExAdmin. Check our CODE_OF_CONDUCT.md and CONTRIBUTING.md guides for more information. We usually keep a list of features and bugs in the issue tracker.

References

License

ex_admin is Copyright (c) 2015-2016 E-MetroTel

The source code is released under the MIT License.

Check LICENSE for more information.

ex_admin's People

Contributors

alexanderchen1989 avatar bitflorist avatar craigp avatar gazler avatar gwincr11 avatar jwarlander avatar kenta-aktsk avatar kociamber avatar lpil avatar matsu911 avatar mspanc avatar naps62 avatar nashby avatar neerfri avatar nkezhaya avatar praveenperera avatar rakr avatar rhnonose avatar robinclowers avatar romul avatar samhamilton avatar san650 avatar sandisk avatar semenovdl avatar smpallen99 avatar sobolevn avatar tmock12 avatar tsutsu avatar volgar1x avatar zayec77 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ex_admin's Issues

Model w/ out 'id' field breaks by default, because of Sorting

I have a table w/out a autoincrementing id field, and it fails w/ the following error:

deps/ex_queb/lib/ex_queb.ex:120: field App.MyModel.id in order_by does not exist in the model source in query

Is there a place to override the default order by field by any chance?

ex_admin has issue accessing models with fields that end in _id

The log of a test session is:

[info] GET /admin/user_sessions
[debug] Processing by ExAdmin.AdminController.index/2
  Parameters: %{"resource" => "user_sessions"}
  Pipelines: [:browser]
[debug] SELECT count('*') FROM "user_sessions" AS u0 [] OK query=69.3ms queue=19.2ms
[debug] SELECT u0."id", u0."session_id", u0."user_id", u0."inserted_at", u0."updated_at" FROM "user_sessions" AS u0 ORDER BY u0."id" DESC LIMIT $1 OFFSET $2 [10, 0] OK query=3.6ms
[debug] SELECT c0."id", c0."cmd", c0."user_session_id", c0."inserted_at", c0."updated_at" FROM "cmds" AS c0 WHERE (c0."user_session_id" IN ($1)) ORDER BY c0."user_session_id" [<<111, 91, 24, 99, 183, 37, 74, 61, 174, 242, 237, 33, 60, 195, 68, 208>>] OK query=0.9ms
[debug] SELECT e0."id", e0."data", e0."user_session_id", e0."inserted_at", e0."updated_at" FROM "extradatas" AS e0 WHERE (e0."user_session_id" IN ($1)) ORDER BY e0."user_session_id" [<<111, 91, 24, 99, 183, 37, 74, 61, 174, 242, 237, 33, 60, 195, 68, 208>>] OK query=2.9ms
[error] %ExAdmin.RuntimeError{message: "Could not find field :session in %ForgeStats.Stat.UserSession{__meta__: #Ecto.Schema.Metadata<:loaded>, cmds: [%ForgeStats.Stat.UserSession.Cmd{__meta__: #Ecto.Schema.Metadata<:loaded>, cmd: 2, id: \"37caf3d9-fbb9-416b-a9a3-64933ef9572a\", inserted_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, updated_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, user_session: #Ecto.Association.NotLoaded<association :user_session is not loaded>, user_session_id: \"6f5b1863-b725-4a3d-aef2-ed213cc344d0\"}], extradatas: [%ForgeStats.Stat.UserSession.ExtraData{__meta__: #Ecto.Schema.Metadata<:loaded>, data: %{\"extra_test_data\" => 42}, id: \"bdf0e6af-6571-47f5-9df1-97892b9ad228\", inserted_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, updated_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, user_session: #Ecto.Association.NotLoaded<association :user_session is not loaded>, user_session_id: \"6f5b1863-b725-4a3d-aef2-ed213cc344d0\"}], id: \"6f5b1863-b725-4a3d-aef2-ed213cc344d0\", inserted_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, session_id: \"00000000-0000-0000-0000-000000000000\", updated_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, user_id: \"00000000-0000-0000-0000-000000000000\"}"}
[error] [{ExAdmin.Helpers, :get_resource_field, 3, [file: 'lib/ex_admin/helpers.ex', line: 194]}, {ExAdmin.Helpers, :build_single_field, 4, [file: 'lib/ex_admin/helpers.ex', line: 147]}, {ExAdmin.Helpers, :build_field, 4, [file: 'lib/ex_admin/helpers.ex', line: 121]}, {ExAdmin.Index, :"-build_table_body/4-fun-1-", 4, [file: 'lib/ex_admin/index.ex', line: 319]}, {Enum, :"-reduce/3-lists^foldl/2-0-", 3, [file: 'lib/enum.ex', line: 1473]}, {ExAdmin.Index, :"-build_table_body/4-fun-2-", 5, [file: 'lib/ex_admin/index.ex', line: 318]}, {Enum, :"-map/2-lists^map/1-0-", 2, [file: 'lib/enum.ex', line: 1088]}, {ExAdmin.Index, :build_table_body, 4, [file: 'lib/ex_admin/index.ex', line: 305]}, {ExAdmin.Index, :"-render_index_table/4-fun-2-", 12, [file: 'lib/ex_admin/index.ex', line: 219]}, {ExAdmin.Index, :batch_action_form, 3, [file: 'lib/ex_admin/index.ex', line: 270]}, {ExAdmin.Index, :default_index_view, 2, [file: 'lib/ex_admin/index.ex', line: 158]}, {ExAdmin.AdminController, :index, 2, [file: 'web/controllers/admin_controller.ex', line: 131]}, {ExAdmin.AdminController, :action, 2, [file: 'web/controllers/admin_controller.ex', line: 1]}, {ExAdmin.AdminController, :phoenix_controller_pipeline, 2, [file: 'web/controllers/admin_controller.ex', line: 1]}, {ForgeStats.Router, :dispatch, 2, [file: 'lib/phoenix/router.ex', line: 261]}, {ForgeStats.Router, :do_call, 2, [file: 'web/router.ex', line: 1]}, {ForgeStats.Endpoint, :phoenix_pipeline, 1, [file: 'lib/forgestats_app/endpoint.ex', line: 1]}, {ForgeStats.Endpoint, :call, 2, [file: 'lib/phoenix/endpoint/render_errors.ex', line: 34]}, {Plug.Adapters.Cowboy.Handler, :upgrade, 4, [file: 'lib/plug/adapters/cowboy/handler.ex', line: 15]}, {:cowboy_protocol, :execute, 4, [file: 'src/cowboy_protocol.erl', line: 442]}]
[info] Sent 500 in 154ms
[error] #PID<0.591.0> running ForgeStats.Endpoint terminated
Server: files.minecraftforge.net:4001 (http)
Request: GET /admin/user_sessions
** (exit) an exception was raised:
    ** (ExAdmin.RuntimeError) Could not find field :session in %ForgeStats.Stat.UserSession{__meta__: #Ecto.Schema.Metadata<:loaded>, cmds: [%ForgeStats.Stat.UserSession.Cmd{__meta__: #Ecto.Schema.Metadata<:loaded>, cmd: 2, id: "37caf3d9-fbb9-416b-a9a3-64933ef9572a", inserted_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, updated_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, user_session: #Ecto.Association.NotLoaded<association :user_session is not loaded>, user_session_id: "6f5b1863-b725-4a3d-aef2-ed213cc344d0"}], extradatas: [%ForgeStats.Stat.UserSession.ExtraData{__meta__: #Ecto.Schema.Metadata<:loaded>, data: %{"extra_test_data" => 42}, id: "bdf0e6af-6571-47f5-9df1-97892b9ad228", inserted_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, updated_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, user_session: #Ecto.Association.NotLoaded<association :user_session is not loaded>, user_session_id: "6f5b1863-b725-4a3d-aef2-ed213cc344d0"}], id: "6f5b1863-b725-4a3d-aef2-ed213cc344d0", inserted_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, session_id: "00000000-0000-0000-0000-000000000000", updated_at: #Ecto.DateTime<2016-03-24T21:04:59Z>, user_id: "00000000-0000-0000-0000-000000000000"}
        lib/ex_admin/helpers.ex:194: ExAdmin.Helpers.get_resource_field/3
        lib/ex_admin/helpers.ex:147: ExAdmin.Helpers.build_single_field/4
        lib/ex_admin/helpers.ex:121: ExAdmin.Helpers.build_field/4
        lib/ex_admin/index.ex:319: anonymous fn/4 in ExAdmin.Index.build_table_body/4
        (elixir) lib/enum.ex:1473: Enum."-reduce/3-lists^foldl/2-0-"/3
        lib/ex_admin/index.ex:318: anonymous fn/5 in ExAdmin.Index.build_table_body/4
        (elixir) lib/enum.ex:1088: Enum."-map/2-lists^map/1-0-"/2
        lib/ex_admin/index.ex:305: ExAdmin.Index.build_table_body/4
        lib/ex_admin/index.ex:219: anonymous fn/12 in ExAdmin.Index.render_index_table/4
        lib/ex_admin/index.ex:270: ExAdmin.Index.batch_action_form/3
        lib/ex_admin/index.ex:158: ExAdmin.Index.default_index_view/2
        web/controllers/admin_controller.ex:131: ExAdmin.AdminController.index/2
        web/controllers/admin_controller.ex:1: ExAdmin.AdminController.action/2
        web/controllers/admin_controller.ex:1: ExAdmin.AdminController.phoenix_controller_pipeline/2
        (forgestats_app) lib/phoenix/router.ex:261: ForgeStats.Router.dispatch/2
        (forgestats_app) web/router.ex:1: ForgeStats.Router.do_call/2
        (forgestats_app) lib/forgestats_app/endpoint.ex:1: ForgeStats.Endpoint.phoenix_pipeline/1
        (forgestats_app) lib/phoenix/endpoint/render_errors.ex:34: ForgeStats.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

The Model schemas:

  schema "user_sessions" do
    field :session_id, Ecto.UUID
    field :user_id, Ecto.UUID

    has_many :cmds, ForgeStats.Stat.UserSession.Cmd
    has_many :extradatas, ForgeStats.Stat.UserSession.ExtraData

    timestamps
  end

  schema "cmds" do
    field :cmd, :integer
    belongs_to :user_session, ForgeStats.Stat.UserSession

    timestamps
  end

  schema "extradatas" do
    field :data, :map
    belongs_to :user_session, ForgeStats.Stats.UserSession

    timestamps
  end

And similar errors on the other two models. There is no :session field in the UserSession, only a :session_id field, the _id seems to be getting trimmed off for unknown reason, causing ex_admin to crash.

Could not render "admin.html"

I am getting the below error

Could not render "admin.html" for ExAdmin.AdminView, please define a matching clause for render/2 or define a template at "web/templates/admin". No templates were compiled for this module.
Assigns:

My env is:
[{:phoenix, "> 1.0.3"},
{:phoenix_ecto, "
> 1.1"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "> 2.1"},
{:phoenix_live_reload, "
> 1.0", only: :dev},
{:cowboy, "> 1.0"},
{:comeonin, "
> 1.3"},
{:ex_admin, github: "smpallen99/ex_admin"}]

Am not sure if its related to the latest Phoenix version.

Authentication support

Can you give some guideline on how to add the admin interface under authentication?
When i tried to put it under my app's '/auth' pipeline, i am getting the following error:

UndefinedFunctionError at GET /auth/admin
undefined function: Myapp.ExAdmin.AdminController.init/1 (module Myapp.ExAdmin.AdminController is not available)
scope "/auth" do
  pipe_through Myapp.Plug.Authenticate

  use ExAdmin.Router
  admin_routes :admin
  ...
end

Upgrade to latest Phoenix?

$ mix deps.get
Running dependency resolution
Looking up alternatives for conflicting requirements on phoenix
Activated version: 1.1.2
From mix.exs: ~> 1.1.2
From deps/ex_admin/mix.exs: ~> 1.0.2

** (Mix) Hex dependency resolution failed, relax the version requirements or unlock dependencies

Map datatype crashed

As mentioned in #42 you had not used the Ecto Map datatype yet, and although ex_admin displays it fine it crashes when attempting to edit it:

[info] GET /admin/extra_datas/bdf0e6af-6571-47f5-9df1-97892b9ad228/edit
[debug] Processing by ExAdmin.AdminController.edit/2
  Parameters: %{"id" => "bdf0e6af-6571-47f5-9df1-97892b9ad228", "resource" => "extra_datas"}
  Pipelines: [:browser]
[debug] SELECT e0."id", e0."data", e0."user_session_id", e0."inserted_at", e0."updated_at" FROM "extradatas" AS e0 WHERE (e0."id" = $1) [<<189, 240, 230, 175, 101, 113, 71, 245, 157, 241, 151, 137, 43, 154, 210, 40>>] OK query=1.1ms
[debug] SELECT u0."id", u0."session_id", u0."user_id", u0."inserted_at", u0."updated_at" FROM "user_sessions" AS u0 WHERE (u0."id" IN ($1)) [<<111, 91, 24, 99, 183, 37, 74, 61, 174, 242, 237, 33, 60, 195, 68, 208>>] OK query=0.9ms
[debug] SELECT u0."id", u0."session_id", u0."user_id", u0."inserted_at", u0."updated_at" FROM "user_sessions" AS u0 [] OK query=0.8ms
[error] %Protocol.UndefinedError{description: nil, protocol: String.Chars, value: %{"extra_test_data" => 42}}
[error] [{String.Chars, :impl_for!, 1, [file: 'lib/string/chars.ex', line: 3]}, {String.Chars, :to_string, 1, [file: 'lib/string/chars.ex', line: 17]}, {Xain, :"-open_tag/3-fun-0-", 2, [file: 'lib/xain.ex', line: 88]}, {Enum, :"-reduce/3-lists^foldl/2-0-", 3, [file: 'lib/enum.ex', line: 1473]}, {Xain, :open_tag, 3, [file: 'lib/xain.ex', line: 88]}, {ExAdmin.Form, :build_control, 7, [file: 'lib/ex_admin/form.ex', line: 870]}, {ExAdmin.Form, :_wrap_item, 7, [file: 'lib/ex_admin/form.ex', line: 617]}, {ExAdmin.Form, :wrap_item, 8, [file: 'lib/ex_admin/form.ex', line: 580]}, {ExAdmin.Form, :"-build_item/5-fun-9-", 6, [file: 'lib/ex_admin/form.ex', line: 804]}, {Enum, :"-reduce/3-lists^foldl/2-0-", 3, [file: 'lib/enum.ex', line: 1473]}, {ExAdmin.Form, :build_item, 5, [file: 'lib/ex_admin/form.ex', line: 803]}, {ExAdmin.Form, :"-build_main_block/4-fun-0-", 6, [file: 'lib/ex_admin/form.ex', line: 532]}, {Enum, :"-reduce/3-lists^foldl/2-0-", 3, [file: 'lib/enum.ex', line: 1473]}, {ExAdmin.Form, :build_main_block, 4, [file: 'lib/ex_admin/form.ex', line: 531]}, {ExAdmin.Form, :build_form, 5, [file: 'lib/ex_admin/form.ex', line: 464]}, {ExAdmin.AdminController, :edit, 2, [file: 'web/controllers/admin_controller.ex', line: 178]}, {ExAdmin.AdminController, :action, 2, [file: 'web/controllers/admin_controller.ex', line: 1]}, {ExAdmin.AdminController, :phoenix_controller_pipeline, 2, [file: 'web/controllers/admin_controller.ex', line: 1]}, {ForgeStats.Router, :dispatch, 2, [file: 'lib/phoenix/router.ex', line: 261]}, {ForgeStats.Router, :do_call, 2, [file: 'web/router.ex', line: 1]}]
[info] Sent 500 in 65ms
[error] #PID<0.476.0> running ForgeStats.Endpoint terminated
Server: files.minecraftforge.net:4001 (http)
Request: GET /admin/extra_datas/bdf0e6af-6571-47f5-9df1-97892b9ad228/edit
** (exit) an exception was raised:
    ** (Protocol.UndefinedError) protocol String.Chars not implemented for %{"extra_test_data" => 42}
        (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
        (elixir) lib/string/chars.ex:17: String.Chars.to_string/1
        lib/xain.ex:88: anonymous fn/2 in Xain.open_tag/3
        (elixir) lib/enum.ex:1473: Enum."-reduce/3-lists^foldl/2-0-"/3
        lib/xain.ex:88: Xain.open_tag/3
        lib/ex_admin/form.ex:870: ExAdmin.Form.build_control/7
        lib/ex_admin/form.ex:617: ExAdmin.Form._wrap_item/7
        lib/ex_admin/form.ex:580: ExAdmin.Form.wrap_item/8
        lib/ex_admin/form.ex:804: anonymous fn/6 in ExAdmin.Form.build_item/5
        (elixir) lib/enum.ex:1473: Enum."-reduce/3-lists^foldl/2-0-"/3
        lib/ex_admin/form.ex:803: ExAdmin.Form.build_item/5
        lib/ex_admin/form.ex:532: anonymous fn/6 in ExAdmin.Form.build_main_block/4
        (elixir) lib/enum.ex:1473: Enum."-reduce/3-lists^foldl/2-0-"/3
        lib/ex_admin/form.ex:531: ExAdmin.Form.build_main_block/4
        lib/ex_admin/form.ex:464: ExAdmin.Form.build_form/5
        web/controllers/admin_controller.ex:178: ExAdmin.AdminController.edit/2
        web/controllers/admin_controller.ex:1: ExAdmin.AdminController.action/2
        web/controllers/admin_controller.ex:1: ExAdmin.AdminController.phoenix_controller_pipeline/2
        (forgestats_app) lib/phoenix/router.ex:261: ForgeStats.Router.dispatch/2
        (forgestats_app) web/router.ex:1: ForgeStats.Router.do_call/2

For note, the Map datatype maps to the PostgreSQL JSONB datatype, a fully indexable on any query binary JSON database, so displaying it in a JSON editor would probably make the most sense. Also filtering queries on it can be very advanced by being able to query about any part of the structure very efficiently, so it has quite good uses. In MariaDB it maps to a binary I think, still JSON though, just lacking the advanced querying abilities and no field indexing.

Test URL: http://files.minecraftforge.net:4001/admin/extra_datas/bdf0e6af-6571-47f5-9df1-97892b9ad228/edit

Hex Package

Hello. Thank you for your superb package. I have managed to get it to work with less than 10 minutes. And I will use it in production.

Everything is fine except the installation process. I don't really trust installing my deps from github. As there's no guaranty for it to work straight from the master branch. So the question is:

Why don't you create a hex package?

Index rendering association xain error

I'm attempting to render an index view in ex_admin an it returns the following error:
FunctionClauseError no function clause matching in Xain.Helpers.id_and_class_shortcuts/2

Including this full stack trace:

    ** (FunctionClauseError) no function clause matching in Xain.Helpers.id_and_class_shortcuts/2
        lib/xain/helpers.ex:41: Xain.Helpers.id_and_class_shortcuts(nil, [value: "1"])
        lib/ex_admin/filter.ex:99: anonymous fn/3 in ExAdmin.Filter.build_field/3
        (elixir) lib/enum.ex:1473: Enum."-reduce/3-lists^foldl/2-0-"/3
        lib/ex_admin/filter.ex:97: ExAdmin.Filter.build_field/3
        lib/ex_admin/filter.ex:131: anonymous fn/4 in ExAdmin.Filter.check_and_build_association/3
        (elixir) lib/enum.ex:2462: Enum.do_find/3
        lib/ex_admin/filter.ex:67: ExAdmin.Filter.build_field/3
        lib/ex_admin/filter.ex:18: anonymous fn/4 in ExAdmin.Filter.filter_view/3
        (elixir) lib/enum.ex:1473: Enum."-reduce/3-lists^foldl/2-0-"/3
        lib/ex_admin/filter.ex:18: ExAdmin.Filter.filter_view/3
        web/templates/layout/admin.html.eex:40: ExAdmin.LayoutView."admin.html"/1
        (phoenix) lib/phoenix/view.ex:344: Phoenix.View.render_to_iodata/3
        (phoenix) lib/phoenix/controller.ex:633: Phoenix.Controller.do_render/4
        web/controllers/admin_controller.ex:1: ExAdmin.AdminController.action/2
        web/controllers/admin_controller.ex:1: ExAdmin.AdminController.phoenix_controller_pipeline/2
        (myapp) lib/phoenix/router.ex:261: Myapp.Router.dispatch/2
        (myapp) web/router.ex:1: Myapp.Router.do_call/2
        (myapp) lib/myapp/endpoint.ex:1: Myapp.Endpoint.phoenix_pipeline/1
        (myapp) lib/plug/debugger.ex:93: Myapp.Endpoint."call (overridable 3)"/2
        (myapp) lib/phoenix/endpoint/render_errors.ex:34: Myapp.Endpoint.call/2

It looks like it's related to a model association I've built and or the data involved in a particular instance of that model, but I'm unsure what other data would be most helpful in diagnosing the issue.

How to setup many to many?

Hello,

I have a user model and a site model. Users can belong to many sites and a sites can have many users. So, i have a user_site model. When i add a user, i need to associate him/her with a site. Can you suggest how to do it? The following did not work for me.

register_resource Myapp.User do
  form user do
    inputs do
      input user, :name
      input user, :email
    end

    inputs :user_sites do
      input user, :site_id
    end
  end
end

user_site.ex

defmodule Myapp.UserSite do
  use Myapp.Web, :model

  schema "user_sites" do
    timestamps

    belongs_to :user, Myapp.User
    belongs_to :site, Myapp.Site
  end
end

Fields of type Ecto.UUID show error in Filter sidebar

Get an error message on the filter page for type Ecto.UUID. Would like the filter page to show a drop down of all the possible uuids for filtering. Note that we can't do a text entry and db like since postgres does not support this. Furthermore, I don't think it makes sense anyway.

Inline validation messages not displayed

Submit a form with a blank belongs to does not display the inline error message. Furthermore, the form returns with the first item selected, which it shouldn't.

Support multiple index page types

Would like to support multiple index macros for the same resource, each with a different type. When multiple are found, a control should be displayed to select the type. For example:

    index do
      column :title
      column :description
      actions
    end

    index as: :grid, default: true do
      cell fn(p) -> 
        div do
          a href: get_route_path(conn, :show, p.id) do
            img(src: ExAdminDemo.Image.url({p.image_file_name, p}, :thumb), height: 100)
          end
        end
        a truncate(p.title), href: get_route_path(conn, :show, p.id)
      end
    end

Nested tags don't work properly inside row macro

  attributes_table do
    row :image, [], fn(s) ->
      img(src: "https://goo.gl/bq40XY", height: 64)
    end
  end

produces

<div class="attributes_table">
  <img src="https://goo.gl/bq40XY" height="64">
  <table border="0" cellspacing="0" cellpadding="0"><tbody>
     <tr><th>Image</th><td class="image"></td></tr>
  </tbody></table>
</div>

instead of

<div class="attributes_table">
  <table border="0" cellspacing="0" cellpadding="0"><tbody>
    <tr><th>Image</th><td class="image"><img src="https://goo.gl/bq40XY" height="64"></td></tr>
  </tbody></table>
</div>

Need test suite

The library does not have a very comprehensive test suite. Need to add lots more tests.

Default pages can't render association without :name field in schema

Throws an exception saying that :name field cannot be found. I would like solution to:

  • Look for a :name field in the association
  • Look for a display_name/1 function in the Admin Resource Module
  • Look for a display_name/1 function in the Model's Module
  • Use the 2nd field in the Model's schema

Add support and docs for installing css and js files when using brunch.

Currently, ExAdmin copies the css and js files into your project's priv/static/ directory. This is an issue if you are using brunch, and don't have the priv/static tracked in git.

To manually fix this, run mix admin.install. Then move the active_admin.css.css, jquery-ujs.js.js and jquery.js files to your project's web/static/vendor directory. Then edit your brunch-config.js like this.

    javascripts: {
      joinTo: {
       "js/app.js": [/^(web\/static\/js)/,
       "deps/phoenix/web/static/js/phoenix.js",
       "deps/phoenix_html/web/static/js/phoenix_html.js"],
       "js/jquery-ujs.js.js": ["web/static/vendor/jquery-ujs.js.js"],
       "js/jquery.js": ["web/static/vendor/jquery.js"],
     }
  },
    stylesheets: {
      joinTo: {
        "css/app.css": [/^(web\/static\/css)/],
        "css/active_admin.css.css": ["web/static/vendor/active_admin.css.css"],
      }
    },

Image file upload

Do you plan to add "Image file upload" ability to ex_admin?

We are looking forward to it.

Support themes

Replace the activeadmin UI with a new UI skin. At that same time, allow for different add-on themes.

Advancing page clears filters

On the index page, apply a filter. Then click the page 2 link. Filter is removed. I should advance to the next page with the filter still applied.

Demo/screenshots wanted

Are you guys considering adding access to demo page or some screenshot section to increase early adopters engagement?

`column` label with spaces error

This is easy to fix, I guess.

I have a code like this:

panel "Registrations" do
    table_for event.registrations do
      column "Full name", fn(item) -> text full_name(item) end
      column "Email", fn(item) -> text item.email end
    end
  end

Which produces this broken output:

2016-04-27 15 19 19

And when I change it to Fullname, like this:

panel "Registrations" do
    table_for event.registrations do
      column "Full name", fn(item) -> text full_name(item) end
      column "Email", fn(item) -> text item.email end
    end
  end

It works normally:

2016-04-27 15 19 39

admin.html seems to be missing?

Following the installation guides, I get the error message:

Could not render "admin.html" for ExAdmin.AdminView, please define a matching clause for render/2 or define a template at "web/templates/admin". No templates were compiled for this module.

Which I just happened to notice is the same message @smpallen99 got at ElixirConf. Do you know what would cause this and how I should fix it?
screen shot 2015-10-20 at 2 41 05 pm

Add sortable option to column in table_for

I would like to add sortable columns like this:

    show customer do
      panel "Order History" do
        table_for customer.orders do
          column "Order", [sortable: :id], fn(order) -> a " ##{order.id}", href: get_route_path(order, :show, order.id) end
          column "State", fn(order) -> status_tag(ExAdminDemo.Order.state order) end
          column :checked_out_at, label: "Date", sortable: true
          column "Total", fn(order) -> text decimal_to_currency(order.total_price) end
        end
      end   
    end

Delete record fails CSRF

Using default admin_routes, deleting a record from the index page, and the view page fails CSRF.

binary_id issue

There's an issue when working with binary_id. I guess it happens when trying to convert in to int somewhere in the code. Here's the exception message:

Postgrex expected an integer in -2147483648..2147483647 that can be encoded/cast to type "int4", got <<190, 171, 222, 121, 147, 77, 77, 185, 186, 87, 17, 70, 193, 192, 41, 66>>. Please make sure the value you are passing matches the definition in your table or in your query or convert the value accordingly.

Full trace:

2016-04-23 13 37 04

My thoughts:

2016-04-23 13 37 28

I guess, that it receives the <option>'s value and expects it to be int. It fails when it is binary.

Date and boolean fields are always nil.

Is there a default format for dates? Is there any way to show a boolean as a check box?
Right now both are shown as text inputs but when I submit the form the value is always nil (for booleans)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.