diff --git a/lib/kaffy/resource_callbacks.ex b/lib/kaffy/resource_callbacks.ex index 551ed0ad..962ea7ed 100644 --- a/lib/kaffy/resource_callbacks.ex +++ b/lib/kaffy/resource_callbacks.ex @@ -5,7 +5,7 @@ defmodule Kaffy.ResourceCallbacks do def create_callbacks(conn, resource, changes) do changeset = Kaffy.ResourceAdmin.create_changeset(resource, changes) - repo = Kaffy.Utils.repo() + repo = Kaffy.Utils.repo(resource) repo.transaction(fn -> result = @@ -28,7 +28,7 @@ defmodule Kaffy.ResourceCallbacks do {:ok, entry} else {:error, :not_found} -> - Kaffy.Utils.repo().insert(changeset) + Kaffy.Utils.repo(resource).insert(changeset) unexpected_error -> {:error, unexpected_error} @@ -37,7 +37,7 @@ defmodule Kaffy.ResourceCallbacks do def update_callbacks(conn, resource, entry, changes) do changeset = Kaffy.ResourceAdmin.update_changeset(resource, entry, changes) - repo = Kaffy.Utils.repo() + repo = Kaffy.Utils.repo(resource) repo.transaction(fn -> result = @@ -60,7 +60,7 @@ defmodule Kaffy.ResourceCallbacks do {:ok, entry} else {:error, :not_found} -> - Kaffy.Utils.repo().update(changeset) + Kaffy.Utils.repo(resource).update(changeset) unexpected_error -> {:error, unexpected_error} @@ -68,7 +68,7 @@ defmodule Kaffy.ResourceCallbacks do end def delete_callbacks(conn, resource, entry) do - repo = Kaffy.Utils.repo() + repo = Kaffy.Utils.repo(resource) repo.transaction(fn -> result = @@ -163,7 +163,7 @@ defmodule Kaffy.ResourceCallbacks do {:ok, entry} else {:error, :not_found} -> - Kaffy.Utils.repo().delete(changeset) + Kaffy.Utils.repo(resource).delete(changeset) unexpected_error -> {:error, unexpected_error} diff --git a/lib/kaffy/resource_form.ex b/lib/kaffy/resource_form.ex index dfbe4d2d..2955a52a 100644 --- a/lib/kaffy/resource_form.ex +++ b/lib/kaffy/resource_form.ex @@ -29,13 +29,13 @@ defmodule Kaffy.ResourceForm do ) true -> - build_html_input(resource[:schema], form, field, type, []) + build_html_input(resource, resource[:schema], form, field, type, []) end end - def form_field(changeset, form, field, opts \\ []) + def form_field(resource, changeset, form, field, opts \\ []) - def form_field(changeset, form, {field, options}, opts) do + def form_field(resource, changeset, form, {field, options}, opts) do options = options || %{} type = @@ -50,7 +50,7 @@ defmodule Kaffy.ResourceForm do end permission = - case is_nil(changeset.data.id) do + case !Map.has_key?(changeset.data, :id) || is_nil(changeset.data.id) do true -> Map.get(options, :create, :editable) false -> Map.get(options, :update, :editable) end @@ -62,16 +62,24 @@ defmodule Kaffy.ResourceForm do select(form, field, choices, class: "custom-select") true -> - build_html_input(changeset.data, form, field, type, opts, permission == :readonly) + build_html_input( + resource, + changeset.data, + form, + field, + type, + opts, + permission == :readonly + ) end end - def form_field(changeset, form, field, opts) do + def form_field(resource, changeset, form, field, opts) do type = Kaffy.ResourceSchema.field_type(changeset.data.__struct__, field) - build_html_input(changeset.data, form, field, type, opts) + build_html_input(resource, changeset.data, form, field, type, opts) end - defp build_html_input(schema, form, field, type, opts, readonly \\ false) do + defp build_html_input(resource, schema, form, field, type, opts, readonly \\ false) do data = schema {conn, opts} = Keyword.pop(opts, :conn) opts = Keyword.put(opts, :readonly, readonly) @@ -91,7 +99,7 @@ defmodule Kaffy.ResourceForm do [ [ form_label(fp, f), - form_field(embed_changeset, fp, f, class: "form-control") + form_field(resource, embed_changeset, fp, f, class: "form-control") ] | all ] @@ -104,13 +112,13 @@ defmodule Kaffy.ResourceForm do :id -> case Kaffy.ResourceSchema.primary_key(schema) == [field] do true -> text_input(form, field, opts) - false -> text_or_assoc(conn, schema, form, field, opts) + false -> text_or_assoc(conn, resource, form, field, opts) end :binary_id -> case Kaffy.ResourceSchema.primary_key(schema) == [field] do true -> text_input(form, field, opts) - false -> text_or_assoc(conn, schema, form, field, opts) + false -> text_or_assoc(conn, resource, form, field, opts) end :string -> @@ -240,7 +248,9 @@ defmodule Kaffy.ResourceForm do ] end - defp text_or_assoc(conn, schema, form, field, opts) do + defp text_or_assoc(conn, resource, form, field, opts) do + schema = resource[:schema] + actual_assoc = Enum.filter(Kaffy.ResourceSchema.associations(schema), fn a -> Kaffy.ResourceSchema.association(schema, a).owner_key == field @@ -256,7 +266,8 @@ defmodule Kaffy.ResourceForm do case field_no_id in Kaffy.ResourceSchema.associations(schema) do true -> assoc = Kaffy.ResourceSchema.association_schema(schema, field_no_id) - option_count = Kaffy.ResourceQuery.cached_total_count(assoc, true, assoc) + assoc_resource = Kaffy.Utils.get_resource_from_schema(conn, assoc) + option_count = Kaffy.ResourceQuery.cached_total_count(assoc_resource, true, assoc) case option_count > 100 do true -> @@ -291,7 +302,7 @@ defmodule Kaffy.ResourceForm do end false -> - options = Kaffy.Utils.repo().all(assoc) + options = Kaffy.Utils.repo(resource).all(assoc) fields = Kaffy.ResourceSchema.fields(assoc) @@ -335,6 +346,11 @@ defmodule Kaffy.ResourceForm do nil -> {nil, ""} + errors when is_map(errors) -> + error_msg = Kaffy.ResourceAdmin.humanize_term(field) <> " has multiple errors!" + + {error_msg, "is-invalid"} + messages -> error_msg = Kaffy.ResourceAdmin.humanize_term(field) <> " " <> Enum.join(messages, ", ") <> "!" @@ -356,7 +372,7 @@ defmodule Kaffy.ResourceForm do defp build_changeset_value(value), do: to_string(value) - def kaffy_input(conn, changeset, form, field, options) do + def kaffy_input(conn, resource, changeset, form, field, options) do ft = Kaffy.ResourceSchema.field_type(changeset.data.__struct__, field) case Kaffy.Utils.is_module(ft) && Keyword.has_key?(ft.__info__(:functions), :render_form) do @@ -371,7 +387,7 @@ defmodule Kaffy.ResourceForm do label_tag = if ft != :boolean, do: form_label(form, {field, options}), else: "" field_tag = - form_field(changeset, form, {field, options}, + form_field(resource, changeset, form, {field, options}, class: "form-control #{error_class}", conn: conn ) diff --git a/lib/kaffy/resource_query.ex b/lib/kaffy/resource_query.ex index e2a3995e..56c57f83 100644 --- a/lib/kaffy/resource_query.ex +++ b/lib/kaffy/resource_query.ex @@ -26,10 +26,10 @@ defmodule Kaffy.ResourceQuery do ) custom_query = Kaffy.ResourceAdmin.custom_index_query(conn, resource, paged) - current_page = Kaffy.Utils.repo().all(custom_query) + current_page = Kaffy.Utils.repo(resource).all(custom_query) do_cache = if search == "" and Enum.empty?(filtered_fields), do: true, else: false - all_count = cached_total_count(schema, do_cache, all) + all_count = cached_total_count(resource, do_cache, all) {all_count, current_page} end @@ -48,7 +48,7 @@ defmodule Kaffy.ResourceQuery do schema = resource[:schema] query = from(s in schema, where: s.id == ^id) custom_query = Kaffy.ResourceAdmin.custom_show_query(conn, resource, query) - Kaffy.Utils.repo().one(custom_query) + Kaffy.Utils.repo(resource).one(custom_query) end def fetch_list(_, [""]), do: [] @@ -57,13 +57,15 @@ defmodule Kaffy.ResourceQuery do schema = resource[:schema] from(s in schema, where: s.id in ^ids) - |> Kaffy.Utils.repo().all() + |> Kaffy.Utils.repo(resource).all() end - def total_count(schema, do_cache, query) do + def total_count(resource, do_cache, query) do + schema = resource[:schema] + result = from(s in query, select: fragment("count(*)")) - |> Kaffy.Utils.repo().one() + |> Kaffy.Utils.repo(resource).one() if do_cache and result > 100_000 do Kaffy.Cache.Client.add_cache(schema, "count", result, 600) @@ -72,10 +74,11 @@ defmodule Kaffy.ResourceQuery do result end - def cached_total_count(schema, false, query), do: total_count(schema, false, query) + def cached_total_count(resource, false, query), do: total_count(resource, false, query) - def cached_total_count(schema, do_cache, query) do - Kaffy.Cache.Client.get_cache(schema, "count") || total_count(schema, do_cache, query) + def cached_total_count(resource, do_cache, query) do + Kaffy.Cache.Client.get_cache(resource[:schema], "count") || + total_count(resource, do_cache, query) end defp get_filter_fields(params, resource) do diff --git a/lib/kaffy/utils.ex b/lib/kaffy/utils.ex index 5542273e..b729455f 100644 --- a/lib/kaffy/utils.ex +++ b/lib/kaffy/utils.ex @@ -50,6 +50,14 @@ defmodule Kaffy.Utils do end end + @doc """ + Returns the repo module specified by the resource + """ + @spec repo(String.t()) :: atom() + def repo(resource) do + resource[:repo] || repo() + end + @doc """ Returns the version of the provided app. @@ -121,6 +129,14 @@ defmodule Kaffy.Utils do end end + @spec set_dynamic_repo(Plug.Conn.t()) :: atom() | pid() | nil + def set_dynamic_repo(conn) do + case env(:set_dynamic_repo) do + f when is_function(f) -> f.(conn) + _ -> nil + end + end + @doc """ Returns a list of contexts as atoms. @@ -211,6 +227,31 @@ defmodule Kaffy.Utils do get_in(full_resources(conn), [context, :resources, resource]) end + @doc """ + Returns the resource entry from the schema. + + Example: + + iex> get_resource_from_schema(conn, MyApp.Blog.Post) + [schema: MyApp.Blog.Post, admin: MyApp.Blog.PostAdmin] + """ + @spec get_resource_from_schema(Plug.Conn.t(), module()) :: list() + def get_resource_from_schema(conn, schema) do + full_resources(conn) + |> Enum.reduce_while(nil, fn {_, props}, acc -> + props[:resources] + |> Enum.reduce(fn {_name, resource_props}, _ -> + if resource_props[:schema] == schema do + resource_props + end + end) + |> case do + nil -> {:cont, acc} + resource -> {:halt, resource} + end + end) + end + @doc """ Returns all the schemas for the given context. @@ -340,7 +381,15 @@ defmodule Kaffy.Utils do end end) - %{stylesheets: stylesheets, javascripts: javascripts} + navigation_extras = + Enum.map(exts, fn ext -> + case function_exported?(ext, :navigation_extras, 1) do + true -> ext.navigation_extras(conn) + false -> [] + end + end) + + %{stylesheets: stylesheets, javascripts: javascripts, navigation_extras: navigation_extras} end defp env(key, default \\ nil) do diff --git a/lib/kaffy_web/controllers/resource_controller.ex b/lib/kaffy_web/controllers/resource_controller.ex index 2d84c5f3..9fe8122f 100644 --- a/lib/kaffy_web/controllers/resource_controller.ex +++ b/lib/kaffy_web/controllers/resource_controller.ex @@ -15,6 +15,7 @@ defmodule KaffyWeb.ResourceController do "pick" => _field } = params ) do + Kaffy.Utils.set_dynamic_repo(conn) my_resource = Kaffy.Utils.get_resource(conn, context, resource) case can_proceed?(my_resource, conn) do @@ -52,6 +53,7 @@ defmodule KaffyWeb.ResourceController do end def index(conn, %{"context" => context, "resource" => resource} = params) do + Kaffy.Utils.set_dynamic_repo(conn) my_resource = Kaffy.Utils.get_resource(conn, context, resource) case can_proceed?(my_resource, conn) do @@ -89,6 +91,7 @@ defmodule KaffyWeb.ResourceController do end def show(conn, %{"context" => context, "resource" => resource, "id" => id}) do + Kaffy.Utils.set_dynamic_repo(conn) my_resource = Kaffy.Utils.get_resource(conn, context, resource) schema = my_resource[:schema] resource_name = Kaffy.ResourceAdmin.singular_name(my_resource) @@ -121,6 +124,7 @@ defmodule KaffyWeb.ResourceController do end def update(conn, %{"context" => context, "resource" => resource, "id" => id} = params) do + Kaffy.Utils.set_dynamic_repo(conn) my_resource = Kaffy.Utils.get_resource(conn, context, resource) schema = my_resource[:schema] params = Kaffy.ResourceParams.decode_map_fields(resource, schema, params) @@ -200,6 +204,7 @@ defmodule KaffyWeb.ResourceController do end def new(conn, %{"context" => context, "resource" => resource}) do + Kaffy.Utils.set_dynamic_repo(conn) my_resource = Kaffy.Utils.get_resource(conn, context, resource) resource_name = Kaffy.ResourceAdmin.singular_name(my_resource) @@ -222,6 +227,7 @@ defmodule KaffyWeb.ResourceController do end def create(conn, %{"context" => context, "resource" => resource} = params) do + Kaffy.Utils.set_dynamic_repo(conn) my_resource = Kaffy.Utils.get_resource(conn, context, resource) params = Kaffy.ResourceParams.decode_map_fields(resource, my_resource[:schema], params) changes = Map.get(params, resource, %{}) @@ -281,6 +287,7 @@ defmodule KaffyWeb.ResourceController do end def delete(conn, %{"context" => context, "resource" => resource, "id" => id}) do + Kaffy.Utils.set_dynamic_repo(conn) my_resource = Kaffy.Utils.get_resource(conn, context, resource) case can_proceed?(my_resource, conn) do @@ -318,6 +325,7 @@ defmodule KaffyWeb.ResourceController do "id" => id, "action_key" => action_key }) do + Kaffy.Utils.set_dynamic_repo(conn) my_resource = Kaffy.Utils.get_resource(conn, context, resource) entry = Kaffy.ResourceQuery.fetch_resource(conn, my_resource, id) actions = Kaffy.ResourceAdmin.resource_actions(my_resource, conn) @@ -342,6 +350,7 @@ defmodule KaffyWeb.ResourceController do conn, %{"context" => context, "resource" => resource, "action_key" => action_key} = params ) do + Kaffy.Utils.set_dynamic_repo(conn) my_resource = Kaffy.Utils.get_resource(conn, context, resource) ids = Map.get(params, "ids", "") |> String.split(",") entries = Kaffy.ResourceQuery.fetch_list(my_resource, ids) diff --git a/lib/kaffy_web/templates/layout/app.html.eex b/lib/kaffy_web/templates/layout/app.html.eex index 1aa5308c..b479209f 100644 --- a/lib/kaffy_web/templates/layout/app.html.eex +++ b/lib/kaffy_web/templates/layout/app.html.eex @@ -26,7 +26,7 @@ logo <% end %> -