Current section

Files

Jump to
ex_admin lib mix tasks admin.install.ex
Raw

lib/mix/tasks/admin.install.ex

defmodule Mix.Tasks.Admin.Install do
@moduledoc """
Install ExAdmin supporting files.
mix admin.install
# Defaults:
* assets - copy js, css, and image files
* config - add configuration to config/config.exs
* dashboard - create a default dashboard
* route - display instructions to add the admin routes
## Options:
* --no-assets - Skip the assets
* --no-config - Skip the config
* --no-dashboard - Skip the dashboard
* --no-route - Skip the route instructions
"""
# @shortdoc "Install ExAdmin"
use Mix.Task
import Mix.ExAdmin.Utils
defmodule Config do
defstruct route: true, assets: true, dashboard: true,
package_path: nil, config: true
end
def run(args) do
do_run(args)
end
def do_run(args) do
parse_args(args)
|> do_install()
end
def do_install(config) do
config
|> do_assets
|> do_config
|> do_dashboard
|> do_route
|> do_paging
end
def do_assets(%Config{assets: true} = config) do
base_path = Path.join(~w(priv static))
status_msg("creating", "css files")
~w(active_admin.css active_admin.css.css)
|> Enum.each(&(copy_file base_path, "css", &1))
status_msg("creating", "js files")
~w(jquery-ujs.js.js jquery.js jquery-ui.min.js active_admin.js) ++
~w(best_in_place.js best_in_place.purr.js)
|> Enum.each(&(copy_vendor_file base_path, "js", &1))
do_active_admin_js(base_path)
status_msg("creating", "image files")
do_active_admin_images(base_path)
~w(glyphicons-halflings-white.png glyphicons-halflings.png)
|> Enum.each(&(copy_file base_path, "images", &1))
config
end
def do_assets(config) do
config
end
def do_route(%Config{route: true} = config) do
IO.puts ""
IO.puts "Add the admin routes to your web/router.ex:"
IO.puts ""
IO.puts " use ExAdmin.Router\n"
IO.puts " # your app's routes\n"
IO.puts " scope \"/admin\", ExAdmin do"
IO.puts " pipe_through :browser"
IO.puts " admin_routes"
IO.puts " end"
config
end
def do_route(config) do
config
end
def do_config(%Config{config: true} = config) do
status_msg("updating", "config/config.exs")
dest_path = Path.join [File.cwd! | ~w(config)]
dest_file_path = Path.join dest_path, "config.exs"
source = File.read!(dest_file_path)
""
|> config_xain(config, source)
|> config_write(config, dest_file_path, source)
end
def do_config(config) do
config
end
defp config_xain(append, _config, source) do
unless String.contains? source, ":xain, :quote" do
append <> """
config :xain, :quote, "'"
config :xain, :after_callback, {Phoenix.HTML, :raw}
"""
else
status_msg("skipping", "xain config. It already exists.")
append
end
end
defp config_write("", config, _dest_file_path, _source), do: config
defp config_write(append, config, dest_file_path, source) do
File.write! dest_file_path, source <> "\n" <> append
config
end
def do_dashboard(%Config{dashboard: true} = config) do
dest_path = Path.join [File.cwd! | ~w(web admin)]
dest_file_path = Path.join dest_path, "dashboard.ex"
source = Path.join([config.package_path | ~w(priv templates admin.install dashboard.exs)] )
|> EEx.eval_file(base: get_module)
status_msg "creating", Path.join(~w(web admin dashboard.ex))
File.mkdir dest_path
File.write! dest_file_path, source
dashboard_instructions
config
end
def do_dashboard(config) do
config
end
def dashboard_instructions do
base = get_module
IO.puts ""
IO.puts "Remember to update your config file:"
IO.puts ""
IO.puts """
config :ex_admin,
repo: #{base}.Repo,
module: #{base},
modules: [
#{base}.ExAdmin.Dashboard,
]
"""
end
def do_paging(config) do
base = get_module
IO.puts ""
IO.puts "Add Scrivener paging to your Repo:"
IO.puts ""
IO.puts " defmodule #{base}.Repo do"
IO.puts " use Ecto.Repo, otp_app: :#{String.downcase base}"
IO.puts " use Scrivener, page_size: 10"
IO.puts " end"
config
end
defp copy_file(base_path, path, file_name) do
File.cp Path.join([get_package_path, base_path, path, file_name]),
Path.join([File.cwd!, base_path, path, file_name])
base_path
end
defp copy_vendor_file(base_path, path, file_name) do
File.cp Path.join([get_package_path, "web", "static", "vendor", file_name]),
Path.join([File.cwd!, base_path, path, file_name])
base_path
end
defp do_active_admin_images(base_path) do
aa_rel_path = Path.join "images", "active_admin"
aa_images_path = Path.join(
[File.cwd!, base_path, aa_rel_path])
if File.exists? aa_images_path do
status_msg("skipping",
aa_rel_path <> " files. Directory already exists.")
else
:ok = File.mkdir(aa_images_path)
~w(admin_notes_icon.png orderable.png)
|> Enum.each(&(copy_file base_path, aa_rel_path, &1))
aa_dp_rel_path = Path.join aa_rel_path, "datepicker"
aa_dp_images_path = Path.join aa_images_path, "datepicker"
:ok = File.mkdir(aa_dp_images_path)
~w(datepicker-header-bg.png datepicker-input-icon.png) ++
~w(datepicker-next-link-icon.png datepicker-nipple.png) ++
~w(datepicker-prev-link-icon.png)
|> Enum.each(&(copy_file base_path, aa_dp_rel_path, &1))
end
end
#
# Build the active_admin_lib.js file.
# Note: if you change one of the coffee script files, you must manually
# compile the js files with the following command
# `coffee --output web/static/vendor/active_admin/ --compile web/static/js/active_admin/`
#
defp do_active_admin_js(base_path) do
src_path = Path.join([get_package_path | ~w(web static vendor active_admin)])
lib_files = ~w(batch_actions.js.js checkbox-toggler.js.js dropdown-menu.js.js) ++
~w(flash.js.js has_many.js.js modal_dialog.js.js per_page.js.js) ++
~w(popover.js.js table-checkbox-toggler.js.js)
source =
""
|> read_active_admin_js(src_path, ~w(application.js.js base.js.js))
|> read_active_admin_js(Path.join([src_path, "lib"]), lib_files)
File.write! Path.join([base_path, "js", "active_admin_lib.js"]), source
end
defp read_active_admin_js(source, src_path, files) do
files
|> Enum.reduce(source, fn(fname, acc) ->
acc <> get_file_banner(fname) <> File.read!(Path.join([src_path, fname])) <> "\n\n"
end)
end
defp get_file_banner(file_name) do
"// File: " <> file_name <> "\n"
end
defp parse_args(args) do
{opts, _values, _} = OptionParser.parse args, switches:
[assets: :boolean, config: :boolean, route: :boolean, dashboard: :boolean]
Enum.reduce opts, %Config{package_path: get_package_path}, fn(item, config) ->
case item do
{key, value} ->
if key in Map.keys(config) do
struct(config, [{key, value}])
else
IO.puts "Incorrect option: #{key}"
config
end
_default -> config
end
end
end
end