315 lines
8 KiB
Elixir
315 lines
8 KiB
Elixir
defmodule Kaffy.ResourceSchema do
|
|
@moduledoc false
|
|
|
|
def primary_key(schema) do
|
|
schema.__schema__(:primary_key)
|
|
end
|
|
|
|
def excluded_fields(schema) do
|
|
{pk, _, _} = schema.__schema__(:autogenerate_id)
|
|
autogenerated = schema.__schema__(:autogenerate)
|
|
|
|
case length(autogenerated) do
|
|
1 ->
|
|
[{auto_fields, _}] = autogenerated
|
|
[pk] ++ auto_fields
|
|
|
|
_ ->
|
|
[pk]
|
|
end
|
|
end
|
|
|
|
def index_fields(schema) do
|
|
Keyword.drop(fields(schema), fields_to_be_removed(schema))
|
|
end
|
|
|
|
def form_fields(schema) do
|
|
to_be_removed = fields_to_be_removed(schema) ++ [:id, :inserted_at, :updated_at]
|
|
Keyword.drop(fields(schema), to_be_removed)
|
|
end
|
|
|
|
def cast_fields(schema) do
|
|
to_be_removed =
|
|
fields_to_be_removed(schema) ++
|
|
get_has_many_associations(schema) ++
|
|
get_has_one_assocations(schema) ++
|
|
get_many_to_many_associations(schema) ++ [:id, :inserted_at, :updated_at]
|
|
|
|
Keyword.drop(fields(schema), to_be_removed)
|
|
end
|
|
|
|
def fields(schema) do
|
|
schema
|
|
|> get_all_fields()
|
|
|> reorder_fields(schema)
|
|
end
|
|
|
|
defp get_all_fields(schema) do
|
|
schema.__changeset__()
|
|
|> Enum.map(fn {k, _} -> {k, default_field_options(schema, k)} end)
|
|
end
|
|
|
|
def default_field_options(schema, field) do
|
|
type = field_type(schema, field)
|
|
label = Kaffy.ResourceForm.form_label_string(field)
|
|
merge_field_options(%{label: label, type: type})
|
|
end
|
|
|
|
def merge_field_options(options) do
|
|
default = %{
|
|
create: :editable,
|
|
update: :editable,
|
|
label: nil,
|
|
type: nil,
|
|
choices: nil
|
|
}
|
|
|
|
Map.merge(default, options || %{})
|
|
end
|
|
|
|
defp fields_to_be_removed(schema) do
|
|
# if schema defines belongs_to assocations, remove assoc fields and keep their actual *_id fields.
|
|
schema.__changeset__()
|
|
|> Enum.reduce([], fn {field, type}, all ->
|
|
case type do
|
|
{:assoc, %Ecto.Association.BelongsTo{}} ->
|
|
[field | all]
|
|
|
|
{:assoc, %Ecto.Association.Has{cardinality: :many}} ->
|
|
[field | all]
|
|
|
|
{:assoc, %Ecto.Association.Has{cardinality: :one}} ->
|
|
[field | all]
|
|
|
|
_ ->
|
|
all
|
|
end
|
|
end)
|
|
end
|
|
|
|
defp reorder_fields(fields_list, schema) do
|
|
[_id, first_field | _fields] = schema.__schema__(:fields)
|
|
|
|
# this is a "nice" feature to re-order the default fields to put the specified fields at the top/bottom of the form
|
|
fields_list
|
|
|> reorder_field(first_field, :first)
|
|
|> reorder_field(:email, :first)
|
|
|> reorder_field(:name, :first)
|
|
|> reorder_field(:title, :first)
|
|
|> reorder_field(:id, :first)
|
|
|> reorder_field(:inserted_at, :last)
|
|
|> reorder_field(:updated_at, :last)
|
|
|
|
# |> reorder_field(Kaffy.ResourceSchema.embeds(schema), :last)
|
|
end
|
|
|
|
defp reorder_field(fields_list, [], _), do: fields_list
|
|
|
|
defp reorder_field(fields_list, [field | rest], position) do
|
|
fields_list = reorder_field(fields_list, field, position)
|
|
reorder_field(fields_list, rest, position)
|
|
end
|
|
|
|
defp reorder_field(fields_list, field_name, position) do
|
|
if field_name in Keyword.keys(fields_list) do
|
|
{field_options, fields_list} = Keyword.pop(fields_list, field_name)
|
|
|
|
case position do
|
|
:first -> [{field_name, field_options}] ++ fields_list
|
|
:last -> fields_list ++ [{field_name, field_options}]
|
|
end
|
|
else
|
|
fields_list
|
|
end
|
|
end
|
|
|
|
def has_field_filters?(resource) do
|
|
admin_fields = Kaffy.ResourceAdmin.index(resource)
|
|
|
|
fields_with_filters =
|
|
Enum.map(admin_fields, fn f -> kaffy_field_filters(resource[:schema], f) end)
|
|
|
|
Enum.any?(fields_with_filters, fn
|
|
{_, filters} -> filters
|
|
_ -> false
|
|
end)
|
|
end
|
|
|
|
def kaffy_field_filters(_schema, {field, options}) do
|
|
{field, Map.get(options || %{}, :filters, false)}
|
|
end
|
|
|
|
def kaffy_field_filters(_, _), do: false
|
|
|
|
def kaffy_field_name(schema, {field, options}) do
|
|
default_name = kaffy_field_name(schema, field)
|
|
name = Map.get(options || %{}, :name)
|
|
|
|
cond do
|
|
is_binary(name) -> name
|
|
is_function(name) -> name.(schema)
|
|
true -> default_name
|
|
end
|
|
end
|
|
|
|
def kaffy_field_name(_schema, field) when is_atom(field) do
|
|
Kaffy.ResourceAdmin.humanize_term(field)
|
|
end
|
|
|
|
def kaffy_field_value(conn, schema, {field, options}) do
|
|
default_value = kaffy_field_value(schema, field)
|
|
ft = Kaffy.ResourceSchema.field_type(schema.__struct__, field)
|
|
value = Map.get(options || %{}, :value)
|
|
|
|
cond do
|
|
is_function(value) ->
|
|
value.(schema)
|
|
|
|
is_map(value) && Map.has_key?(value, :__struct__) ->
|
|
if value.__struct__ in [NaiveDateTime, DateTime, Date, Time] do
|
|
value
|
|
else
|
|
Map.from_struct(value)
|
|
|> Map.drop([:__meta__])
|
|
|> Kaffy.Utils.json().encode!(escape: :html_safe, pretty: true)
|
|
end
|
|
|
|
Kaffy.Utils.is_module(ft) && Keyword.has_key?(ft.__info__(:functions), :render_index) ->
|
|
ft.render_index(conn, schema, field, options)
|
|
|
|
is_map(value) ->
|
|
Kaffy.Utils.json().encode!(value, escape: :html_safe, pretty: true)
|
|
|
|
is_binary(value) ->
|
|
value
|
|
|
|
true ->
|
|
default_value
|
|
end
|
|
end
|
|
|
|
def kaffy_field_value(schema, field) when is_atom(field) do
|
|
value = Map.get(schema, field, "")
|
|
|
|
cond do
|
|
is_map(value) && Map.has_key?(value, :__struct__) && value.__struct__ == Decimal ->
|
|
value
|
|
|
|
is_map(value) && Map.has_key?(value, :__struct__) ->
|
|
if value.__struct__ in [NaiveDateTime, DateTime, Date, Time] do
|
|
value
|
|
else
|
|
Map.from_struct(value)
|
|
|> Map.drop([:__meta__])
|
|
|> Kaffy.Utils.json().encode!(escape: :html_safe, pretty: true)
|
|
end
|
|
|
|
is_map(value) ->
|
|
Kaffy.Utils.json().encode!(value, escape: :html_safe, pretty: true)
|
|
|
|
is_binary(value) ->
|
|
String.slice(value, 0, 140)
|
|
|
|
true ->
|
|
value
|
|
end
|
|
end
|
|
|
|
def display_string_fields([], all), do: Enum.reverse(all) |> Enum.join(",")
|
|
|
|
def display_string_fields([{field, _} | rest], all) do
|
|
display_string_fields(rest, [field | all])
|
|
end
|
|
|
|
def display_string_fields([field | rest], all) do
|
|
display_string_fields(rest, [field | all])
|
|
end
|
|
|
|
def associations(schema) do
|
|
schema.__schema__(:associations)
|
|
end
|
|
|
|
def get_has_many_associations(schema) do
|
|
associations(schema)
|
|
|> Enum.filter(fn a ->
|
|
case association(schema, a) do
|
|
%Ecto.Association.Has{cardinality: :many} -> true
|
|
_ -> false
|
|
end
|
|
end)
|
|
end
|
|
|
|
def get_has_one_assocations(schema) do
|
|
associations(schema)
|
|
|> Enum.filter(fn a ->
|
|
case association(schema, a) do
|
|
%Ecto.Association.Has{cardinality: :one} -> true
|
|
_ -> false
|
|
end
|
|
end)
|
|
end
|
|
|
|
def get_many_to_many_associations(schema) do
|
|
associations(schema)
|
|
|> Enum.filter(fn a ->
|
|
case association(schema, a) do
|
|
%Ecto.Association.ManyToMany{cardinality: :many} -> true
|
|
_ -> false
|
|
end
|
|
end)
|
|
end
|
|
|
|
def association(schema, name) do
|
|
schema.__schema__(:association, name)
|
|
end
|
|
|
|
def association_schema(schema, assoc) do
|
|
association(schema, assoc).queryable
|
|
end
|
|
|
|
def embeds(schema) do
|
|
schema.__schema__(:embeds)
|
|
end
|
|
|
|
def embed(schema, name) do
|
|
schema.__schema__(:embed, name)
|
|
end
|
|
|
|
def embed_struct(schema, name) do
|
|
embed(schema, name).related
|
|
end
|
|
|
|
def search_fields(resource) do
|
|
schema = resource[:schema]
|
|
persisted_fields = schema.__schema__(:fields)
|
|
|
|
Enum.filter(fields(schema), fn f ->
|
|
field_name = elem(f, 0)
|
|
|
|
field_type(schema, f).type in [:string, :textarea, :richtext] &&
|
|
field_name in persisted_fields
|
|
end)
|
|
|> Enum.map(fn {f, _} -> f end)
|
|
end
|
|
|
|
def filter_fields(_), do: nil
|
|
|
|
def field_type(_schema, {_, type}), do: type
|
|
def field_type(schema, field), do: schema.__changeset__() |> Map.get(field, :string)
|
|
# def field_type(schema, field), do: schema.__schema__(:type, field)
|
|
|
|
def get_map_fields(schema) do
|
|
get_all_fields(schema)
|
|
|> Enum.filter(fn
|
|
{_f, options} ->
|
|
options.type == :map
|
|
|
|
f when is_atom(f) ->
|
|
f == :map
|
|
end)
|
|
end
|
|
|
|
def widgets(_resource) do
|
|
[]
|
|
end
|
|
end
|