Current section
37 Versions
Jump to
Current section
37 Versions
Compare versions
32
files changed
+2234
additions
-15
deletions
| @@ -1,20 +1,37 @@ | |
| 1 1 | # Thesis |
| 2 2 | |
| 3 | - **TODO: Add description** |
| 3 | + Thesis is an Elixir/Phoenix hex package for quickly and easily adding content |
| 4 | + editing to any page. |
| 4 5 | |
| 5 | - ## Installation |
| 6 | + It's inspired by the [Rails gem](https://github.com/infinitered/thesis-rails) by |
| 7 | + the same name and author. |
| 6 8 | |
| 7 | - If [available in Hex](https://hex.pm/docs/publish), the package can be installed as: |
| 9 | +  |
| 8 10 | |
| 9 | - 1. Add thesis to your list of dependencies in `mix.exs`: |
| 11 | + ## Installation and Configuration |
| 10 12 | |
| 11 | - def deps do |
| 12 | - [{:thesis, "~> 0.0.1"}] |
| 13 | - end |
| 13 | + 1. Add thesis to your `mix.exs`: |
| 14 14 | |
| 15 | - 2. Ensure thesis is started before your application: |
| 15 | + ```elixir |
| 16 | + def deps do |
| 17 | + [{:thesis, "~> 0.0.1"}] |
| 18 | + end |
| 16 19 | |
| 17 | - def application do |
| 18 | - [applications: [:thesis]] |
| 19 | - end |
| 20 | + def application do |
| 21 | + [applications: [:thesis]] |
| 22 | + end |
| 23 | + ``` |
| 24 | + |
| 25 | + 2. Run `mix thesis.install` |
| 26 | + |
| 27 | + This will add Thesis to your `package.json`, `config.exs`, generate |
| 28 | + a migration, and generate an authorization module. |
| 29 | + |
| 30 | + ## Making Pages Editable |
| 31 | + |
| 32 | + TODO |
| 33 | + |
| 34 | + ## Authorization |
| 35 | + |
| 36 | + TODO |
| @@ -1,12 +1,32 @@ | |
| 1 1 | {<<"app">>,<<"thesis">>}. |
| 2 | - {<<"build_tools">>,[]}. |
| 2 | + {<<"build_tools">>,[<<"mix">>]}. |
| 3 3 | {<<"description">>, |
| 4 4 | <<"Thesis is a lightweight bolt-on content editing system\nfor Phoenix websites.">>}. |
| 5 5 | {<<"elixir">>,<<"~> 1.2">>}. |
| 6 | - {<<"files">>,[<<"README.md">>]}. |
| 6 | + {<<"files">>, |
| 7 | + [<<"lib/mix/tasks/thesis.install.ex">>,<<"lib/mix/utils.ex">>, |
| 8 | + <<"lib/thesis.ex">>,<<"lib/thesis/api_controller.ex">>, |
| 9 | + <<"lib/thesis/config.ex">>,<<"lib/thesis/controller.ex">>, |
| 10 | + <<"lib/thesis/models/page.ex">>,<<"lib/thesis/models/page_content.ex">>, |
| 11 | + <<"lib/thesis/router.ex">>,<<"lib/thesis/store.ex">>, |
| 12 | + <<"lib/thesis/view.ex">>,<<"priv/static/thesis-editor.js">>, |
| 13 | + <<"priv/static/thesis-old.js">>,<<"priv/static/thesis.css">>, |
| 14 | + <<"priv/static/thesis.js">>,<<"priv/templates/thesis.install/auth.exs">>, |
| 15 | + <<"priv/templates/thesis.install/migration.exs">>, |
| 16 | + <<"web/static/components/add_button.js">>, |
| 17 | + <<"web/static/components/cancel_button.js">>, |
| 18 | + <<"web/static/components/delete_button.js">>, |
| 19 | + <<"web/static/components/edit_button.js">>, |
| 20 | + <<"web/static/components/editor_toolbar.js">>, |
| 21 | + <<"web/static/components/page_content_editor.js">>, |
| 22 | + <<"web/static/components/save_button.js">>, |
| 23 | + <<"web/static/components/settings_button.js">>, |
| 24 | + <<"web/static/components/style_button.js">>, |
| 25 | + <<"web/static/thesis-editor.js">>,<<"web/static/utilities/net.js">>, |
| 26 | + <<"mix.exs">>,<<"README.md">>,<<"package.json">>]}. |
| 7 27 | {<<"licenses">>,[<<"MIT">>]}. |
| 8 28 | {<<"links">>, |
| 9 | - [{<<"Docs">>,<<"https://github.com/infinite_red/thesis/tree/master/docs">>}, |
| 29 | + [{<<"Docs">>,<<"https://hexdocs.pm/thesis/0.0.2/api-reference.html">>}, |
| 10 30 | {<<"GitHub">>,<<"https://github.com/infinite_red/thesis">>}]}. |
| 11 31 | {<<"maintainers">>, |
| 12 32 | [<<"Jamon Holmgren">>,<<"Ken Miller">>,<<"Daniel Berkompas">>]}. |
| @@ -32,4 +52,4 @@ | |
| 32 52 | [{<<"app">>,<<"plug">>}, |
| 33 53 | {<<"optional">>,false}, |
| 34 54 | {<<"requirement">>,<<"~> 1.0">>}]}]}. |
| 35 | - {<<"version">>,<<"0.0.1">>}. |
| 55 | + {<<"version">>,<<"0.0.2">>}. |
| @@ -0,0 +1,60 @@ | |
| 1 | + defmodule Mix.Tasks.Thesis.Install do |
| 2 | + use Mix.Task |
| 3 | + import Mix.Thesis.Utils |
| 4 | + |
| 5 | + @shortdoc "Generates Thesis code in your Phoenix app" |
| 6 | + |
| 7 | + @moduledoc """ |
| 8 | + TODO: Write docs. |
| 9 | + """ |
| 10 | + |
| 11 | + def run(_args) do |
| 12 | + thesis_templates |
| 13 | + thesis_npm |
| 14 | + thesis_config |
| 15 | + end |
| 16 | + |
| 17 | + def thesis_templates do |
| 18 | + template_files = [ {"priv/templates/thesis.install/auth.exs", "lib/auth.ex" } ] |
| 19 | + migration_exists = File.ls!("priv/repo/migrations") |> Enum.map(fn (f) -> String.contains?(f, "create_thesis_tables") end) |> Enum.any? |
| 20 | + unless migration_exists do |
| 21 | + template_files = [{"priv/templates/thesis.install/migration.exs", "priv/repo/migrations/#{timestamp}_create_thesis_tables.exs"} | template_files] |
| 22 | + end |
| 23 | + |
| 24 | + template_files |
| 25 | + |> Stream.map(&render_eex/1) |
| 26 | + |> Stream.map(©_to_target/1) |
| 27 | + |> Stream.run |
| 28 | + end |
| 29 | + |
| 30 | + def thesis_npm do |
| 31 | + status_msg("updating", "package.json") |
| 32 | + System.cmd("npm", ["install", "./deps/thesis", "--save"]) |
| 33 | + end |
| 34 | + |
| 35 | + def thesis_config do |
| 36 | + status_msg("updating", "config/config.exs") |
| 37 | + dest_path = Path.join [File.cwd! | ~w(config)] |
| 38 | + dest_file_path = Path.join dest_path, "config.exs" |
| 39 | + File.read!(dest_file_path) |
| 40 | + |> insert_thesis |
| 41 | + |> append_to_file(dest_file_path) |
| 42 | + end |
| 43 | + |
| 44 | + defp insert_thesis(source) do |
| 45 | + unless String.contains? source, "config :thesis" do |
| 46 | + source <> """ |
| 47 | + |
| 48 | + # Configure thesis content editor |
| 49 | + config :thesis, |
| 50 | + store: Thesis.Store, |
| 51 | + authorization: #{Mix.Phoenix.base}.ThesisAuth |
| 52 | + config :thesis, Thesis.Store, repo: #{Mix.Phoenix.base}.Repo |
| 53 | + """ |
| 54 | + else |
| 55 | + status_msg("skipping", "thesis config. It already exists.") |
| 56 | + :skip |
| 57 | + end |
| 58 | + end |
| 59 | + |
| 60 | + end |
| @@ -0,0 +1,60 @@ | |
| 1 | + defmodule Mix.Thesis.Utils do |
| 2 | + |
| 3 | + def get_package_path do |
| 4 | + __ENV__.file |
| 5 | + |> Path.dirname |
| 6 | + |> String.split("/lib/mix") |
| 7 | + |> hd |
| 8 | + end |
| 9 | + |
| 10 | + def get_module do |
| 11 | + Mix.Project.get |
| 12 | + |> Module.split |
| 13 | + |> Enum.reverse |
| 14 | + |> Enum.at(1) |
| 15 | + end |
| 16 | + |
| 17 | + @doc "Print a status message to the console" |
| 18 | + def status_msg(status, message), |
| 19 | + do: IO.puts "#{IO.ANSI.green}* #{status}#{IO.ANSI.reset} #{message}" |
| 20 | + |
| 21 | + @doc "Print an informational message without color" |
| 22 | + def debug(message), do: IO.puts "==> #{message}" |
| 23 | + @doc "Print an informational message in green" |
| 24 | + def info(message), do: IO.puts "==> #{IO.ANSI.green}#{message}#{IO.ANSI.reset}" |
| 25 | + @doc "Print a warning message in yellow" |
| 26 | + def warn(message), do: IO.puts "==> #{IO.ANSI.yellow}#{message}#{IO.ANSI.reset}" |
| 27 | + @doc "Print a notice in yellow" |
| 28 | + def notice(message), do: IO.puts "#{IO.ANSI.yellow}#{message}#{IO.ANSI.reset}" |
| 29 | + @doc "Print an error message in red" |
| 30 | + def error(message), do: IO.puts "==> #{IO.ANSI.red}#{message}#{IO.ANSI.reset}" |
| 31 | + |
| 32 | + def append_to_file(:skip, _dest_file_path), do: :ok |
| 33 | + def append_to_file(contents, dest_file_path) do |
| 34 | + File.write! dest_file_path, contents |
| 35 | + end |
| 36 | + |
| 37 | + def render_eex({source, target}) do |
| 38 | + source = Path.join(Application.app_dir(:thesis), source) |
| 39 | + rendered = EEx.eval_file(source, [base: Mix.Phoenix.base]) |
| 40 | + {rendered, target} |
| 41 | + end |
| 42 | + |
| 43 | + def copy_to_target({contents, target}) do |
| 44 | + if File.exists?(target) do |
| 45 | + status_msg("exists", "#{target}") |
| 46 | + else |
| 47 | + File.write!(target, contents) |
| 48 | + end |
| 49 | + end |
| 50 | + |
| 51 | + # Taken from mix phoenix.gen.model |
| 52 | + # https://github.com/phoenixframework/phoenix/blob/d3af3397c1f47381dd7ea69869772174a0c1811b/lib/mix/tasks/phoenix.gen.model.ex#L198 |
| 53 | + def timestamp do |
| 54 | + {{y, m, d}, {hh, mm, ss}} = :calendar.universal_time() |
| 55 | + "#{y}#{pad(m)}#{pad(d)}#{pad(hh)}#{pad(mm)}#{pad(ss)}" |
| 56 | + end |
| 57 | + |
| 58 | + defp pad(i) when i < 10, do: << ?0, ?0 + i >> |
| 59 | + defp pad(i), do: to_string(i) |
| 60 | + end |
| @@ -0,0 +1,2 @@ | |
| 1 | + defmodule Thesis do |
| 2 | + end |
Loading more files…