diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index efa5da33..ba83c62b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,9 +1,15 @@
image: "elixir:1.10"
+cache:
+ paths:
+ - _build
+ - deps
+
variables:
POSTGRES_PASSWORD: "postgres"
POSTGRES_USER: "postgres"
DATABASE_URL: "postgres"
+ MIX_ENV: "test"
services:
- name: postgres:12
diff --git a/apps/auth/lib/auth/users/user.ex b/apps/auth/lib/auth/user.ex
similarity index 90%
rename from apps/auth/lib/auth/users/user.ex
rename to apps/auth/lib/auth/user.ex
index 2d75e547..da9c4da6 100644
--- a/apps/auth/lib/auth/users/user.ex
+++ b/apps/auth/lib/auth/user.ex
@@ -1,4 +1,4 @@
-defmodule Auth.Users.User do
+defmodule Auth.User do
@moduledoc """
The baseline user schema module.
"""
@@ -13,6 +13,8 @@ defmodule Auth.Users.User do
schema "users" do
field :roles, {:array, :string}
+ field :display_name, :string
+ field :homepage_url, :string
pow_user_fields()
diff --git a/apps/auth/lib/mix/tasks/create_admin.ex b/apps/auth/lib/mix/tasks/create_admin.ex
index a831a387..b0d7e5fe 100644
--- a/apps/auth/lib/mix/tasks/create_admin.ex
+++ b/apps/auth/lib/mix/tasks/create_admin.ex
@@ -4,7 +4,7 @@ defmodule Mix.Tasks.Legendary.CreateAdmin do
"""
use Mix.Task
- alias Auth.Users.User
+ alias Auth.User
alias Auth.Repo
alias Ecto.Changeset
diff --git a/apps/auth/priv/repo/migrations/20200721214958_add_nicename_to_users.exs b/apps/auth/priv/repo/migrations/20200721214958_add_nicename_to_users.exs
new file mode 100644
index 00000000..819daa8e
--- /dev/null
+++ b/apps/auth/priv/repo/migrations/20200721214958_add_nicename_to_users.exs
@@ -0,0 +1,9 @@
+defmodule Auth.Repo.Migrations.AddNicenameToUsers do
+ use Ecto.Migration
+
+ def change do
+ alter table(:users) do
+ add :display_name, :string, default: nil
+ end
+ end
+end
diff --git a/apps/auth/priv/repo/migrations/20200721215212_add_url_to_users.exs b/apps/auth/priv/repo/migrations/20200721215212_add_url_to_users.exs
new file mode 100644
index 00000000..585ab333
--- /dev/null
+++ b/apps/auth/priv/repo/migrations/20200721215212_add_url_to_users.exs
@@ -0,0 +1,9 @@
+defmodule Auth.Repo.Migrations.AddUrlToUsers do
+ use Ecto.Migration
+
+ def change do
+ alter table(:users) do
+ add :homepage_url, :string, default: nil
+ end
+ end
+end
diff --git a/apps/content_web/.formatter.exs b/apps/content/.formatter.exs
similarity index 100%
rename from apps/content_web/.formatter.exs
rename to apps/content/.formatter.exs
diff --git a/apps/content/.gitignore b/apps/content/.gitignore
index 2ebe6e95..3d47f319 100644
--- a/apps/content/.gitignore
+++ b/apps/content/.gitignore
@@ -20,7 +20,7 @@ erl_crash.dump
*.ez
# Ignore package tarball (built via "mix hex.build").
-auth_web-*.tar
+content-*.tar
# If NPM crashes, it generates a log, let's ignore it too.
npm-debug.log
diff --git a/apps/content_web/README.md b/apps/content/README.md
similarity index 100%
rename from apps/content_web/README.md
rename to apps/content/README.md
diff --git a/apps/content_web/assets/.babelrc b/apps/content/assets/.babelrc
similarity index 100%
rename from apps/content_web/assets/.babelrc
rename to apps/content/assets/.babelrc
diff --git a/apps/content_web/assets/css/app.scss b/apps/content/assets/css/app.scss
similarity index 100%
rename from apps/content_web/assets/css/app.scss
rename to apps/content/assets/css/app.scss
diff --git a/apps/content_web/assets/js/app.js b/apps/content/assets/js/app.js
similarity index 100%
rename from apps/content_web/assets/js/app.js
rename to apps/content/assets/js/app.js
diff --git a/apps/content_web/assets/js/socket.js b/apps/content/assets/js/socket.js
similarity index 100%
rename from apps/content_web/assets/js/socket.js
rename to apps/content/assets/js/socket.js
diff --git a/apps/content_web/assets/package-lock.json b/apps/content/assets/package-lock.json
similarity index 100%
rename from apps/content_web/assets/package-lock.json
rename to apps/content/assets/package-lock.json
diff --git a/apps/content_web/assets/package.json b/apps/content/assets/package.json
similarity index 100%
rename from apps/content_web/assets/package.json
rename to apps/content/assets/package.json
diff --git a/apps/content_web/assets/static/favicon.ico b/apps/content/assets/static/favicon.ico
similarity index 100%
rename from apps/content_web/assets/static/favicon.ico
rename to apps/content/assets/static/favicon.ico
diff --git a/apps/content_web/assets/static/images/phoenix.png b/apps/content/assets/static/images/phoenix.png
similarity index 100%
rename from apps/content_web/assets/static/images/phoenix.png
rename to apps/content/assets/static/images/phoenix.png
diff --git a/apps/content_web/assets/static/robots.txt b/apps/content/assets/static/robots.txt
similarity index 100%
rename from apps/content_web/assets/static/robots.txt
rename to apps/content/assets/static/robots.txt
diff --git a/apps/content_web/assets/webpack.config.js b/apps/content/assets/webpack.config.js
similarity index 100%
rename from apps/content_web/assets/webpack.config.js
rename to apps/content/assets/webpack.config.js
diff --git a/apps/content/config/config.exs b/apps/content/config/config.exs
index 06eedc42..48625319 100644
--- a/apps/content/config/config.exs
+++ b/apps/content/config/config.exs
@@ -18,7 +18,7 @@ config :logger, :console,
config :content, Content.Scheduler,
jobs: [
- {"@hourly", {ContentWeb.Sitemaps, :generate, []}}
+ {"@hourly", {Content.Sitemaps, :generate, []}}
]
# Import environment specific config. This must remain at the bottom
diff --git a/apps/content/config/dev.exs b/apps/content/config/dev.exs
index 0655a1fc..604d7be0 100644
--- a/apps/content/config/dev.exs
+++ b/apps/content/config/dev.exs
@@ -4,7 +4,7 @@ use Mix.Config
config :content, Content.Repo,
username: "postgres",
password: "postgres",
- database: "content_dev",
+ database: "legendary_dev",
hostname: "localhost",
show_sensitive_data_on_connection_error: true,
pool_size: 10
@@ -15,7 +15,7 @@ config :content, Content.Repo,
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with webpack to recompile .js and .css sources.
-config :content, ContentWeb.Endpoint,
+config :content, Content.Endpoint,
http: [port: 4000],
debug_errors: true,
code_reloader: true,
@@ -55,7 +55,7 @@ config :content, ContentWeb.Endpoint,
# different ports.
# Watch static and templates for browser reloading.
-config :content, ContentWeb.Endpoint,
+config :content, Content.Endpoint,
live_reload: [
patterns: [
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
diff --git a/apps/content/config/test.exs b/apps/content/config/test.exs
index 39a41d15..af29307c 100644
--- a/apps/content/config/test.exs
+++ b/apps/content/config/test.exs
@@ -2,11 +2,3 @@ use Mix.Config
# Print only warnings and errors during test
config :logger, level: :warn
-
-config :content, Content.Repo,
- username: "postgres",
- password: "postgres",
- database: "content_test#{System.get_env("MIX_TEST_PARTITION")}",
- hostname: System.get_env("DATABASE_URL") || "localhost",
- show_sensitive_data_on_connection_error: true,
- pool: Ecto.Adapters.SQL.Sandbox
diff --git a/apps/content/lib/application.ex b/apps/content/lib/content/application.ex
similarity index 86%
rename from apps/content/lib/application.ex
rename to apps/content/lib/content/application.ex
index 0f71c2ef..2c03a6d2 100644
--- a/apps/content/lib/application.ex
+++ b/apps/content/lib/content/application.ex
@@ -1,6 +1,6 @@
defmodule Content.Application do
@moduledoc """
- The base module of the CMS application.
+ The base module of the Content application.
"""
use Application
@@ -17,6 +17,8 @@ defmodule Content.Application do
# Start your own worker by calling: Content.Worker.start_link(arg1, arg2, arg3)
# worker(Content.Worker, [arg1, arg2, arg3]),
worker(Content.Scheduler, []),
+ Content.Telemetry,
+ Content.Endpoint,
]
# See https://hexdocs.pm/elixir/Supervisor.html
@@ -28,7 +30,7 @@ defmodule Content.Application do
# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
def config_change(changed, _new, removed) do
- Endpoint.config_change(changed, removed)
+ Content.Endpoint.config_change(changed, removed)
:ok
end
end
diff --git a/apps/content/lib/attachment.ex b/apps/content/lib/content/attachment.ex
similarity index 100%
rename from apps/content/lib/attachment.ex
rename to apps/content/lib/content/attachment.ex
diff --git a/apps/content/lib/commands/update_menu.ex b/apps/content/lib/content/commands/update_menu.ex
similarity index 100%
rename from apps/content/lib/commands/update_menu.ex
rename to apps/content/lib/content/commands/update_menu.ex
diff --git a/apps/content/lib/comment.ex b/apps/content/lib/content/comment.ex
similarity index 100%
rename from apps/content/lib/comment.ex
rename to apps/content/lib/content/comment.ex
diff --git a/apps/content/lib/commentmeta.ex b/apps/content/lib/content/commentmeta.ex
similarity index 100%
rename from apps/content/lib/commentmeta.ex
rename to apps/content/lib/content/commentmeta.ex
diff --git a/apps/content/lib/comments.ex b/apps/content/lib/content/comments.ex
similarity index 100%
rename from apps/content/lib/comments.ex
rename to apps/content/lib/content/comments.ex
diff --git a/apps/content/lib/link.ex b/apps/content/lib/content/link.ex
similarity index 100%
rename from apps/content/lib/link.ex
rename to apps/content/lib/content/link.ex
diff --git a/apps/content/lib/menu.ex b/apps/content/lib/content/menu.ex
similarity index 100%
rename from apps/content/lib/menu.ex
rename to apps/content/lib/content/menu.ex
diff --git a/apps/content/lib/option.ex b/apps/content/lib/content/option.ex
similarity index 100%
rename from apps/content/lib/option.ex
rename to apps/content/lib/content/option.ex
diff --git a/apps/content/lib/options.ex b/apps/content/lib/content/options.ex
similarity index 100%
rename from apps/content/lib/options.ex
rename to apps/content/lib/content/options.ex
diff --git a/apps/content/lib/post.ex b/apps/content/lib/content/post.ex
similarity index 94%
rename from apps/content/lib/post.ex
rename to apps/content/lib/content/post.ex
index 3576a352..032f37cd 100644
--- a/apps/content/lib/post.ex
+++ b/apps/content/lib/content/post.ex
@@ -37,7 +37,7 @@ defmodule Content.Post do
has_many :categories, through: [:term_relationships, :category, :term]
has_many :tags, through: [:term_relationships, :tag, :term]
has_one :post_format, through: [:term_relationships, :post_format, :term]
- belongs_to :author, Content.User, foreign_key: :post_author, references: :ID
+ belongs_to :author, Auth.User, foreign_key: :post_author
end
def changeset(struct, params \\ %{}) do
@@ -123,14 +123,14 @@ defmodule Content.Post do
end
def maybe_put_guid(changeset) do
- import ContentWeb.Router.Helpers, only: [posts_url: 3]
+ import Content.Router.Helpers, only: [posts_url: 3]
slug = changeset |> get_field(:post_name)
case slug do
nil -> changeset
_ ->
changeset
- |> put_default(:guid, posts_url(ContentWeb.Endpoint, :show, slug))
+ |> put_default(:guid, posts_url(CoreWeb.Endpoint, :show, slug))
end
end
end
diff --git a/apps/content/lib/postmeta.ex b/apps/content/lib/content/postmeta.ex
similarity index 100%
rename from apps/content/lib/postmeta.ex
rename to apps/content/lib/content/postmeta.ex
diff --git a/apps/content/lib/posts.ex b/apps/content/lib/content/posts.ex
similarity index 97%
rename from apps/content/lib/posts.ex
rename to apps/content/lib/content/posts.ex
index f418515b..5099380f 100644
--- a/apps/content/lib/posts.ex
+++ b/apps/content/lib/content/posts.ex
@@ -159,6 +159,18 @@ defmodule Content.Posts do
"""
def get_posts!(slug) do
+ slug
+ |> get_post_scope()
+ |> Repo.one!()
+ end
+
+ def get_post(slug) do
+ slug
+ |> get_post_scope()
+ |> Repo.one()
+ end
+
+ defp get_post_scope(slug) do
id_filter = fn scope, id ->
case Integer.parse(id, 10) do
@@ -172,7 +184,6 @@ defmodule Content.Posts do
post_scope()
|> where([p], p.post_type != "nav_menu_item")
|> id_filter.(slug)
- |> Repo.one!()
end
@doc """
diff --git a/apps/content/lib/repo.ex b/apps/content/lib/content/repo.ex
similarity index 100%
rename from apps/content/lib/repo.ex
rename to apps/content/lib/content/repo.ex
diff --git a/apps/content/lib/scheduler.ex b/apps/content/lib/content/scheduler.ex
similarity index 100%
rename from apps/content/lib/scheduler.ex
rename to apps/content/lib/content/scheduler.ex
diff --git a/apps/content/lib/shortcodes.ex b/apps/content/lib/content/shortcodes.ex
similarity index 100%
rename from apps/content/lib/shortcodes.ex
rename to apps/content/lib/content/shortcodes.ex
diff --git a/apps/content/lib/slugs.ex b/apps/content/lib/content/slugs.ex
similarity index 100%
rename from apps/content/lib/slugs.ex
rename to apps/content/lib/content/slugs.ex
diff --git a/apps/content/lib/term.ex b/apps/content/lib/content/term.ex
similarity index 100%
rename from apps/content/lib/term.ex
rename to apps/content/lib/content/term.ex
diff --git a/apps/content/lib/term_relationship.ex b/apps/content/lib/content/term_relationship.ex
similarity index 100%
rename from apps/content/lib/term_relationship.ex
rename to apps/content/lib/content/term_relationship.ex
diff --git a/apps/content/lib/term_taxonomy.ex b/apps/content/lib/content/term_taxonomy.ex
similarity index 100%
rename from apps/content/lib/term_taxonomy.ex
rename to apps/content/lib/content/term_taxonomy.ex
diff --git a/apps/content/lib/termmeta.ex b/apps/content/lib/content/termmeta.ex
similarity index 100%
rename from apps/content/lib/termmeta.ex
rename to apps/content/lib/content/termmeta.ex
diff --git a/apps/content/lib/terms.ex b/apps/content/lib/content/terms.ex
similarity index 100%
rename from apps/content/lib/terms.ex
rename to apps/content/lib/content/terms.ex
diff --git a/apps/content_web/lib/content_web.ex b/apps/content/lib/content_web.ex
similarity index 73%
rename from apps/content_web/lib/content_web.ex
rename to apps/content/lib/content_web.ex
index ca60eb8e..a74b8885 100644
--- a/apps/content_web/lib/content_web.ex
+++ b/apps/content/lib/content_web.ex
@@ -1,12 +1,12 @@
-defmodule ContentWeb do
+defmodule Content do
@moduledoc """
The entrypoint for defining your web interface, such
as controllers, views, channels and so on.
This can be used in your application as:
- use ContentWeb, :controller
- use ContentWeb, :view
+ use Content, :controller
+ use Content, :view
The definitions below will be executed for every view,
controller, etc, so keep them short and clean, focused
@@ -19,11 +19,11 @@ defmodule ContentWeb do
def controller do
quote do
- use Phoenix.Controller, namespace: ContentWeb
+ use Phoenix.Controller, namespace: Content
import Plug.Conn
- import ContentWeb.Gettext
- alias ContentWeb.Router.Helpers, as: Routes
+ import Content.Gettext
+ alias Content.Router.Helpers, as: Routes
end
end
@@ -31,13 +31,21 @@ defmodule ContentWeb do
quote do
use Phoenix.View,
root: "lib/content_web/templates",
- namespace: ContentWeb,
+ namespace: Content,
pattern: "**/*"
+ use PhoenixHtmlSanitizer, :basic_html
+
# Import convenience functions from controllers
import Phoenix.Controller,
only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]
+ def process_content(text) do
+ text
+ |> Content.Shortcodes.expand_shortcodes()
+ |> Earmark.as_html!()
+ end
+
# Include shared imports and aliases for views
unquote(view_helpers())
end
@@ -55,7 +63,7 @@ defmodule ContentWeb do
def channel do
quote do
use Phoenix.Channel
- import ContentWeb.Gettext
+ import Content.Gettext
end
end
@@ -67,9 +75,9 @@ defmodule ContentWeb do
# Import basic rendering functionality (render, render_layout, etc)
import Phoenix.View
- import ContentWeb.ErrorHelpers
- import ContentWeb.Gettext
- alias ContentWeb.Router.Helpers, as: Routes
+ import Content.ErrorHelpers
+ import Content.Gettext
+ alias Content.Router.Helpers, as: Routes
end
end
diff --git a/apps/content_web/lib/content_web/channels/user_socket.ex b/apps/content/lib/content_web/channels/user_socket.ex
similarity index 85%
rename from apps/content_web/lib/content_web/channels/user_socket.ex
rename to apps/content/lib/content_web/channels/user_socket.ex
index 31d4aa3f..2aa52f77 100644
--- a/apps/content_web/lib/content_web/channels/user_socket.ex
+++ b/apps/content/lib/content_web/channels/user_socket.ex
@@ -1,8 +1,8 @@
-defmodule ContentWeb.UserSocket do
+defmodule Content.UserSocket do
use Phoenix.Socket
## Channels
- # channel "room:*", ContentWeb.RoomChannel
+ # channel "room:*", Content.RoomChannel
# Socket params are passed from the client and can
# be used to verify and authenticate a user. After
@@ -27,7 +27,7 @@ defmodule ContentWeb.UserSocket do
# Would allow you to broadcast a "disconnect" event and terminate
# all active sockets and channels for a given user:
#
- # ContentWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
+ # Content.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
#
# Returning `nil` makes this socket anonymous.
@impl true
diff --git a/apps/content/lib/content_web/controllers/admin/admin_home_controller.ex b/apps/content/lib/content_web/controllers/admin/admin_home_controller.ex
new file mode 100644
index 00000000..b6f337b0
--- /dev/null
+++ b/apps/content/lib/content_web/controllers/admin/admin_home_controller.ex
@@ -0,0 +1,7 @@
+defmodule Content.AdminHomeController do
+ use Content, :controller
+
+ def index(conn, _params) do
+ render conn, "index.html"
+ end
+end
diff --git a/apps/content/lib/content_web/controllers/admin/admin_posts_controller.ex b/apps/content/lib/content_web/controllers/admin/admin_posts_controller.ex
new file mode 100644
index 00000000..feb44b92
--- /dev/null
+++ b/apps/content/lib/content_web/controllers/admin/admin_posts_controller.ex
@@ -0,0 +1,27 @@
+defmodule Content.AdminPostsController do
+ use Content, :controller
+
+ alias Content.Posts
+
+ require Ecto.Query
+
+ def index(conn, %{"page" => page, "post_type" => post_type}) do
+ posts = Posts.list_admin_posts(page, post_type)
+ thumbs = posts |> Posts.thumbs_for_posts()
+ last_page = Posts.last_page
+ render(
+ conn,
+ "index.html",
+ [
+ posts: posts,
+ page: String.to_integer(page),
+ last_page: last_page,
+ thumbs: thumbs,
+ post_type: post_type,
+ ]
+ )
+ end
+ def index(conn, %{"page" => page} = params), do: index(conn, params |> Map.merge(%{"page" => page, "post_type" => "post"}))
+ def index(conn, %{"post_type" => post_type} = params), do: index(conn, params |> Map.merge(%{"page" => "1", "post_type" => post_type}))
+ def index(conn, params), do: index(conn, params |> Map.merge(%{"page" => "1", "post_type" => "post"}))
+end
diff --git a/apps/content/lib/content_web/controllers/comment_controller.ex b/apps/content/lib/content_web/controllers/comment_controller.ex
new file mode 100644
index 00000000..e6de477d
--- /dev/null
+++ b/apps/content/lib/content_web/controllers/comment_controller.ex
@@ -0,0 +1,71 @@
+defmodule Content.CommentController do
+ use Content, :controller
+
+ alias Content
+ alias Content.Comments
+ alias Content.Post
+ alias Content.Repo
+
+ import Ecto.Query
+
+ def create(conn, %{"comment" => comment_params}) do
+ comment_params = comment_params |> Map.merge(%{"comment_author_IP" => to_string(:inet_parse.ntoa(conn.remote_ip))})
+
+ case Comments.create_comment(comment_params) do
+ {:ok, comment} ->
+ post =
+ Post
+ |> where([p], p.'ID' == ^comment.comment_post_ID)
+ |> Repo.one()
+
+ conn
+ |> put_flash(:info, "Comment created successfully.")
+ |> redirect(to: Routes.posts_path(conn, :show, post))
+ {:error, _} ->
+ post =
+ Post
+ |> where([p], p.'ID' == ^comment_params["comment_post_ID"])
+ |> Repo.one()
+
+ conn
+ |> redirect(to: Routes.posts_path(conn, :show, post))
+ end
+ end
+
+ def update(conn, %{"id" => id, "comment" => comment_params}) do
+ comment = Comments.get_comment!(id)
+
+ case Comments.update_comment(comment, comment_params) do
+ {:ok, comment} ->
+ post =
+ Post
+ |> where([p], p.'ID' == ^comment.comment_post_ID)
+ |> Repo.one()
+
+ conn
+ |> put_flash(:info, "Comment updated successfully.")
+ |> redirect(to: Routes.posts_path(conn, :show, post))
+ {:error, _} ->
+ post =
+ Post
+ |> where([p], p.'ID' == ^comment_params["comment_post_ID"])
+ |> Repo.one()
+
+ conn
+ |> redirect(to: Routes.posts_path(conn, :show, post))
+ end
+ end
+
+ def delete(conn, %{"id" => id}) do
+ comment = Comments.get_comment!(id)
+ {:ok, comment} = Comments.delete_comment(comment)
+ post =
+ Post
+ |> where([p], p.'ID' == ^comment.comment_post_ID)
+ |> Repo.one()
+
+ conn
+ |> put_flash(:info, "Comment deleted successfully.")
+ |> redirect(to: Routes.posts_path(conn, :show, post))
+ end
+end
diff --git a/apps/content/lib/content_web/controllers/feeds_controller.ex b/apps/content/lib/content_web/controllers/feeds_controller.ex
new file mode 100644
index 00000000..b5f4a6bc
--- /dev/null
+++ b/apps/content/lib/content_web/controllers/feeds_controller.ex
@@ -0,0 +1,33 @@
+defmodule Content.FeedsController do
+ use Content, :controller
+
+ alias Content.{Posts}
+
+ plug :put_layout, false when action in [:preview]
+
+ def index(conn, params) do
+ category = params |> Map.get("category")
+ posts = Posts.list_posts(params)
+ thumbs = posts |> Posts.thumbs_for_posts()
+
+ feed_url =
+ case category do
+ nil ->
+ Routes.index_feed_path(conn, :index)
+ _ ->
+ Routes.category_feed_path(conn, :index, category)
+ end
+
+ conn
+ |> put_resp_content_type("text/xml")
+ |> render(
+ "index.rss",
+ [
+ posts: posts,
+ thumbs: thumbs,
+ category: category,
+ feed_url: feed_url,
+ ]
+ )
+ end
+end
diff --git a/apps/content/lib/content_web/controllers/menus_controller.ex b/apps/content/lib/content_web/controllers/menus_controller.ex
new file mode 100644
index 00000000..917e5e34
--- /dev/null
+++ b/apps/content/lib/content_web/controllers/menus_controller.ex
@@ -0,0 +1,39 @@
+defmodule Content.MenusController do
+ use Content, :controller
+
+ alias Content.Repo
+
+ def edit(conn, %{"id" => id}) do
+ menu = id |> Content.Menu.get_menu_from_id()
+ posts =
+ Content.Posts.post_scope
+ |> Repo.all()
+ |> Enum.map(fn post ->
+ post |> Map.take([:ID, :post_title, :post_name])
+ end)
+ categories =
+ Content.Terms.categories
+ |> Repo.all()
+ |> Enum.map(fn cat ->
+ cat |> Map.take([:name, :slug, :term_group, :term_id])
+ end)
+
+ conn
+ |> render(
+ "edit.html",
+ [
+ id: id,
+ menu: menu,
+ posts: posts,
+ categories: categories,
+ ]
+ )
+ end
+
+ def update(conn, %{"id" => id, "menu" => menu}) do
+ Content.UpdateMenu.run(id, menu |> Phoenix.json_library().decode!())
+
+ conn
+ |> redirect(to: Routes.menus_path(conn, :edit, id))
+ end
+end
diff --git a/apps/content_web/lib/content_web/controllers/page_controller.ex b/apps/content/lib/content_web/controllers/page_controller.ex
similarity index 67%
rename from apps/content_web/lib/content_web/controllers/page_controller.ex
rename to apps/content/lib/content_web/controllers/page_controller.ex
index cec971ab..b4019997 100644
--- a/apps/content_web/lib/content_web/controllers/page_controller.ex
+++ b/apps/content/lib/content_web/controllers/page_controller.ex
@@ -1,5 +1,5 @@
-defmodule ContentWeb.PageController do
- use ContentWeb, :controller
+defmodule Content.PageController do
+ use Content, :controller
def index(conn, _params) do
render(conn, "index.html")
diff --git a/apps/content/lib/content_web/controllers/post_password_controller.ex b/apps/content/lib/content_web/controllers/post_password_controller.ex
new file mode 100644
index 00000000..59554c2b
--- /dev/null
+++ b/apps/content/lib/content_web/controllers/post_password_controller.ex
@@ -0,0 +1,20 @@
+defmodule Content.PostPasswordController do
+ use Content, :controller
+
+ def create(conn, %{"post_password" => post_password}) do
+ conn = put_session(conn, "post_password", post_password)
+
+ redirect_path =
+ if Enum.count(get_req_header(conn, "referer")) > 0 do
+ conn
+ |> get_req_header("referer")
+ |> Enum.at(0)
+ |> URI.parse()
+ |> (&(&1.path)).()
+ else
+ "/"
+ end
+
+ redirect conn, to: redirect_path
+ end
+end
diff --git a/apps/content/lib/content_web/controllers/posts_controller.ex b/apps/content/lib/content_web/controllers/posts_controller.ex
new file mode 100644
index 00000000..1903b254
--- /dev/null
+++ b/apps/content/lib/content_web/controllers/posts_controller.ex
@@ -0,0 +1,176 @@
+defmodule Content.PostsController do
+ use Content, :controller
+
+ alias Auth.User
+ alias Content.{Options, Post, Posts, Repo}
+
+ plug :put_layout, false when action in [:preview]
+
+ def index(conn, params) do
+ show_on_front = Options.get_value("show_on_front") || "posts"
+
+ case show_on_front do
+ "posts" ->
+ conn |> index_posts(params)
+ "page" ->
+ conn |> index_page(params)
+ end
+ end
+
+ def index_posts(conn, params) do
+ page = params |> Map.get("page", "1")
+ params = params |> Map.merge(%{"page" => page})
+ category = params |> Map.get("category")
+ posts = Posts.list_posts(params)
+ thumbs = posts |> Posts.thumbs_for_posts()
+ last_page = Posts.last_page(params)
+
+ conn
+ |> render(
+ "index.html",
+ [
+ posts: posts,
+ page: String.to_integer(page),
+ last_page: last_page,
+ thumbs: thumbs,
+ category: category,
+ ]
+ )
+ end
+
+ def index_page(conn, _params) do
+ page_id = Options.get("page_on_front") |> (&(&1.option_value)).()
+
+ post = Posts.get_posts!(page_id)
+ page = String.to_integer("1")
+ thumbs = [post] |> Posts.thumbs_for_posts()
+ render(conn, "front.html", post: post, page: page, thumbs: thumbs)
+ end
+
+ def new(conn, params) do
+ changeset = Posts.change_posts(%Post{})
+ render(
+ conn,
+ "new.html",
+ changeset: changeset,
+ post_type: params["post_type"] || "post",
+ author_options: User |> Repo.all()
+ )
+ end
+
+ def create(conn, %{"post" => post_params}) do
+ case Posts.create_posts(post_params) do
+ {:ok, post} ->
+ conn
+ |> put_flash(:info, "Posts created successfully.")
+ |> redirect(to: Routes.posts_path(conn, :show, post))
+ {:error, %Ecto.Changeset{} = changeset} ->
+ render(
+ conn,
+ "new.html",
+ changeset: changeset,
+ post_type: post_params["post_type"] || "post",
+ author_options: User |> Repo.all()
+ )
+ end
+ end
+
+ def preview(conn, %{"post" => post_params}) do
+ post = Posts.preview_post(post_params)
+
+ conn
+ |> render("show.html", post: post, page: 1, thumbs: [])
+ end
+
+ def show(conn, %{"id" => id, "page" => page_string}) do
+ {page_id_for_posts, _} = Options.get_value_as_int("page_for_posts")
+
+ post = Posts.get_post(id)
+
+ if is_nil(post) do
+ try_static_post(conn, id)
+ else
+ if post.'ID' == page_id_for_posts do
+ conn |> index_posts(%{"id" => id, "page" => page_string})
+ else
+ conn |> show_one(post, page_string)
+ end
+ end
+ end
+ def show(conn, %{"id" => id}), do: show(conn, %{"id" => id, "page" => "1"})
+
+ defp try_static_post(conn, id) do
+ try do
+ render(conn, "static_pages/#{id}.html")
+ rescue
+ Phoenix.Template.UndefinedError ->
+ raise Phoenix.Router.NoRouteError.exception(conn: conn, router: Content.Router)
+ end
+ end
+
+ def show_one(conn, post, page_string) do
+ {front_page_id, _} = Options.get_value_as_int("page_on_front")
+
+ template =
+ if post.'ID' == front_page_id do
+ "front.html"
+ else
+ "show.html"
+ end
+
+ page = String.to_integer(page_string)
+ thumbs = [post] |> Posts.thumbs_for_posts()
+ case post.post_type do
+ "attachment" ->
+ {:ok, decoded} = post.post_content |> Base.decode64
+
+ conn
+ |> put_resp_content_type(post.post_mime_type, "binary")
+ |> send_resp(conn.status || 200, decoded)
+ _ ->
+ render(conn, template, post: post, page: page, thumbs: thumbs)
+ end
+ end
+
+ def edit(conn, %{"id" => id}) do
+ posts = Posts.get_post_with_drafts!(id)
+ changeset = Posts.change_posts(posts)
+ render(
+ conn,
+ "edit.html",
+ posts: posts,
+ changeset: changeset,
+ post_type: posts.post_type || "post",
+ author_options: User |> Repo.all()
+ )
+ end
+
+ def update(conn, %{"id" => id, "post" => posts_params}) do
+ posts = Posts.get_post_with_drafts!(id)
+
+ case Posts.update_posts(posts, posts_params) do
+ {:ok, posts} ->
+ conn
+ |> put_flash(:info, "Posts updated successfully.")
+ |> redirect(to: Routes.posts_path(conn, :edit, posts))
+ {:error, %Ecto.Changeset{} = changeset} ->
+ render(
+ conn,
+ "edit.html",
+ posts: posts,
+ changeset: changeset,
+ post_type: posts.post_type || "post",
+ author_options: User |> Repo.all()
+ )
+ end
+ end
+
+ def delete(conn, %{"id" => id}) do
+ posts = Posts.get_post_with_drafts!(id)
+ {:ok, _posts} = Posts.delete_posts(posts)
+
+ conn
+ |> put_flash(:info, "Posts deleted successfully.")
+ |> redirect(to: Routes.admin_posts_path(conn, :index))
+ end
+end
diff --git a/apps/content/lib/content_web/controllers/sitemap_controller.ex b/apps/content/lib/content_web/controllers/sitemap_controller.ex
new file mode 100644
index 00000000..7f8ad57c
--- /dev/null
+++ b/apps/content/lib/content_web/controllers/sitemap_controller.ex
@@ -0,0 +1,20 @@
+defmodule Content.SitemapController do
+ use Content, :controller
+
+ alias Content.{Posts, Repo, Terms}
+
+ import Ecto.Query
+
+ def index(conn, _params) do
+ posts =
+ Posts.post_scope
+ |> where([p], p.post_type not in ["nav_menu_item", "attachment"])
+ |> Repo.all()
+
+ categories =
+ Terms.categories
+ |> Repo.all()
+
+ render(conn, "index.html", conn: conn, posts: posts, categories: categories)
+ end
+end
diff --git a/apps/content_web/lib/content_web/endpoint.ex b/apps/content/lib/content_web/endpoint.ex
similarity index 83%
rename from apps/content_web/lib/content_web/endpoint.ex
rename to apps/content/lib/content_web/endpoint.ex
index bb508bff..25b1cb78 100644
--- a/apps/content_web/lib/content_web/endpoint.ex
+++ b/apps/content/lib/content_web/endpoint.ex
@@ -1,5 +1,5 @@
-defmodule ContentWeb.Endpoint do
- use Phoenix.Endpoint, otp_app: :content_web
+defmodule Content.Endpoint do
+ use Phoenix.Endpoint, otp_app: :content
# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
@@ -10,7 +10,7 @@ defmodule ContentWeb.Endpoint do
signing_salt: "wfYQp84C"
]
- socket "/socket", ContentWeb.UserSocket,
+ socket "/socket", Content.UserSocket,
websocket: true,
longpoll: false
@@ -22,7 +22,7 @@ defmodule ContentWeb.Endpoint do
# when deploying your static files in production.
plug Plug.Static,
at: "/",
- from: :content_web,
+ from: :content,
gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt)
@@ -32,7 +32,7 @@ defmodule ContentWeb.Endpoint do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
- plug Phoenix.Ecto.CheckRepoStatus, otp_app: :content_web
+ plug Phoenix.Ecto.CheckRepoStatus, otp_app: :content
end
plug Phoenix.LiveDashboard.RequestLogger,
@@ -50,6 +50,6 @@ defmodule ContentWeb.Endpoint do
plug Plug.MethodOverride
plug Plug.Head
plug Plug.Session, @session_options
- plug Pow.Plug.Session, otp_app: :content_web
- plug ContentWeb.Router
+ plug Pow.Plug.Session, otp_app: :content
+ plug Content.Router
end
diff --git a/apps/content_web/lib/content_web/gettext.ex b/apps/content/lib/content_web/gettext.ex
similarity index 85%
rename from apps/content_web/lib/content_web/gettext.ex
rename to apps/content/lib/content_web/gettext.ex
index 06a6ca8d..135b2da5 100644
--- a/apps/content_web/lib/content_web/gettext.ex
+++ b/apps/content/lib/content_web/gettext.ex
@@ -1,11 +1,11 @@
-defmodule ContentWeb.Gettext do
+defmodule Content.Gettext do
@moduledoc """
A module providing Internationalization with a gettext-based API.
By using [Gettext](https://hexdocs.pm/gettext),
your module gains a set of macros for translations, for example:
- import ContentWeb.Gettext
+ import Content.Gettext
# Simple translation
gettext("Here is the string to translate")
@@ -20,5 +20,5 @@ defmodule ContentWeb.Gettext do
See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
"""
- use Gettext, otp_app: :content_web
+ use Gettext, otp_app: :content
end
diff --git a/apps/content/lib/content_web/plugs/load_user.ex b/apps/content/lib/content_web/plugs/load_user.ex
new file mode 100644
index 00000000..4b50f2fd
--- /dev/null
+++ b/apps/content/lib/content_web/plugs/load_user.ex
@@ -0,0 +1,36 @@
+defmodule Content.LoadUser do
+ @moduledoc """
+ Loads user into connection if user has session
+ """
+
+ import Plug.Conn
+ alias Content.Users
+
+ def init(opts) do
+ opts
+ end
+
+ def call(conn, _) do
+ handle_auth(conn)
+ end
+
+ defp handle_auth(conn) do
+ user_id = get_session(conn, :user_id)
+
+ if user = (user_id && Users.get_user(user_id)) do
+ build_conn(conn, user)
+ else
+ build_conn(conn, nil)
+ end
+ end
+
+ defp build_conn(conn, nil) do
+ conn
+ |> assign(:current_user, nil)
+ end
+
+ defp build_conn(conn, user) do
+ conn
+ |> assign(:current_user, user)
+ end
+end
diff --git a/apps/content/lib/content_web/plugs/require_admin.ex b/apps/content/lib/content_web/plugs/require_admin.ex
new file mode 100644
index 00000000..9cd61d63
--- /dev/null
+++ b/apps/content/lib/content_web/plugs/require_admin.ex
@@ -0,0 +1,22 @@
+defmodule Content.RequireAdmin do
+ @moduledoc """
+ A plug that returns 403 unauthorized if the user is not an admin. Used
+ to block out logged-in-only routes.
+ """
+ import Plug.Conn
+ alias Auth.User
+
+ def init(opts) do
+ opts
+ end
+
+ def call(conn, _opts) do
+ if conn.assigns[:current_user] && User.is_admin?(conn.assigns[:current_user]) do
+ conn
+ else
+ conn
+ |> send_resp(403, "Unauthorized")
+ |> halt()
+ end
+ end
+end
diff --git a/apps/content/lib/content_web/plugs/require_auth.ex b/apps/content/lib/content_web/plugs/require_auth.ex
new file mode 100644
index 00000000..5583b097
--- /dev/null
+++ b/apps/content/lib/content_web/plugs/require_auth.ex
@@ -0,0 +1,22 @@
+defmodule Content.RequireAuth do
+ @moduledoc """
+ A plug that returns 403 unauthorized if the user is not authenticated. Used
+ to block out logged-in-only routes.
+ """
+ import Plug.Conn
+
+ def init(opts) do
+ opts
+ end
+
+ def call(conn, _) do
+ case conn.assigns[:current_user] do
+ nil ->
+ conn
+ |> send_resp(403, "Unauthorized")
+ |> halt()
+ _user ->
+ conn
+ end
+ end
+end
diff --git a/apps/content/lib/content_web/router.ex b/apps/content/lib/content_web/router.ex
new file mode 100644
index 00000000..398832e6
--- /dev/null
+++ b/apps/content/lib/content_web/router.ex
@@ -0,0 +1,72 @@
+defmodule Content.Router do
+ use Content, :router
+ alias Content.{RequireAdmin, RequireAuth}
+
+ pipeline :browser do
+ plug :accepts, ["html"]
+ plug :fetch_session
+ plug :fetch_flash
+ plug :protect_from_forgery
+ plug :put_secure_browser_headers
+ end
+
+ pipeline :api do
+ plug :accepts, ["json"]
+ end
+
+ pipeline :feed do
+ plug :accepts, ["rss"]
+ plug :fetch_session
+ plug :protect_from_forgery
+ plug :put_secure_browser_headers
+ end
+
+ pipeline :require_auth do
+ plug(RequireAuth)
+ end
+
+ pipeline :require_admin do
+ plug(RequireAdmin)
+ end
+
+ pipeline :admin_layout do
+ plug :put_layout, {Content.LayoutView, :admin}
+ end
+
+ scope "/", Content do
+ pipe_through([:browser, :require_auth, :require_admin, :admin_layout])
+
+ get "/wp-admin/", AdminHomeController, :index
+ get "/posts", AdminPostsController, :index, as: :admin_posts
+ post "/posts", PostsController, :create
+ get "/posts/new", PostsController, :new
+ put "/posts/preview", PostsController, :preview
+ post "/posts/preview", PostsController, :preview
+ get "/posts/:id/edit", PostsController, :edit
+ put "/posts/:id", PostsController, :update
+ delete "/posts/:id", PostsController, :delete
+ get "/menus/:id/edit", MenusController, :edit
+ put "/menus/:id", MenusController, :update
+ end
+
+ scope "/", Content do
+ pipe_through :feed # Use the default browser stack
+
+ get "/category/:category/feed.rss", FeedsController, :index, as: :category_feed
+ get "/feed.rss", FeedsController, :index, as: :index_feed
+ end
+
+ scope "/", Content do
+ pipe_through :browser # Use the default browser stack
+
+ resources "/comments", CommentController, as: :comment, only: [:create, :delete, :update]
+ get "/page/:page", PostsController, :index_posts, as: :blog_page
+ get "/category/:category", PostsController, :index_posts, as: :category
+ get "/category/:category/page/:page", PostsController, :index, as: :category_page
+ post "/wp-login.php", PostPasswordController, :create
+ get "/", PostsController, :index
+ resources "/sitemap", SitemapController, only: [:index]
+ get "/:id", PostsController, :show
+ get "/:id/:page", PostsController, :show, as: :paged_post
+ end
+end
diff --git a/apps/content_web/lib/content_web/telemetry.ex b/apps/content/lib/content_web/telemetry.ex
similarity index 75%
rename from apps/content_web/lib/content_web/telemetry.ex
rename to apps/content/lib/content_web/telemetry.ex
index 2d24639c..2b73f952 100644
--- a/apps/content_web/lib/content_web/telemetry.ex
+++ b/apps/content/lib/content_web/telemetry.ex
@@ -1,4 +1,4 @@
-defmodule ContentWeb.Telemetry do
+defmodule Content.Telemetry do
@moduledoc """
Collects metrics for the application and allows them to be transmitted using the Telemetry framework.
"""
@@ -34,11 +34,11 @@ defmodule ContentWeb.Telemetry do
),
# Database Metrics
- summary("content_web.repo.query.total_time", unit: {:native, :millisecond}),
- summary("content_web.repo.query.decode_time", unit: {:native, :millisecond}),
- summary("content_web.repo.query.query_time", unit: {:native, :millisecond}),
- summary("content_web.repo.query.queue_time", unit: {:native, :millisecond}),
- summary("content_web.repo.query.idle_time", unit: {:native, :millisecond}),
+ summary("content.repo.query.total_time", unit: {:native, :millisecond}),
+ summary("content.repo.query.decode_time", unit: {:native, :millisecond}),
+ summary("content.repo.query.query_time", unit: {:native, :millisecond}),
+ summary("content.repo.query.queue_time", unit: {:native, :millisecond}),
+ summary("content.repo.query.idle_time", unit: {:native, :millisecond}),
# VM Metrics
summary("vm.memory.total", unit: {:byte, :kilobyte}),
@@ -52,7 +52,7 @@ defmodule ContentWeb.Telemetry do
[
# A module, function and arguments to be invoked periodically.
# This function must call :telemetry.execute/3 and a metric must be added above.
- # {ContentWeb, :count_users, []}
+ # {Content, :count_users, []}
]
end
end
diff --git a/apps/content/lib/content_web/templates/admin_home/index.html.eex b/apps/content/lib/content_web/templates/admin_home/index.html.eex
new file mode 100644
index 00000000..464f78bb
--- /dev/null
+++ b/apps/content/lib/content_web/templates/admin_home/index.html.eex
@@ -0,0 +1 @@
+
Dashboard
diff --git a/apps/content/lib/content_web/templates/admin_posts/index.html.eex b/apps/content/lib/content_web/templates/admin_posts/index.html.eex
new file mode 100644
index 00000000..40cc06b9
--- /dev/null
+++ b/apps/content/lib/content_web/templates/admin_posts/index.html.eex
@@ -0,0 +1,93 @@
+
+
+
+ <%= humanize(@post_type) %>s
+
+
+
+ <%= link "New " <> humanize(@post_type), to: Routes.posts_path(@conn, :new, post_type: @post_type), class: "button" %>
+
+
+
+
+
+
+ Title
+ Author
+ Categories
+ Tags
+ Comments
+ Date
+
+
+
+ <%= for post <- @posts do %>
+
+
+ <%= link post.post_title, to: Routes.posts_path(@conn, :edit, post) %>
+
+
+ <%= if !is_nil(post.author) do %>
+ <%= post.author.display_name %>
+ <% end %>
+
+
+ <%= post.categories |> Enum.map(&(&1.name)) |> Enum.join(", ") %>
+
+
+ <%= post.tags |> Enum.map(&(&1.name)) |> Enum.join(", ") %>
+
+
+ <%= post.comments |> Enum.count() %>
+
+
+ <%= case post.post_status do %>
+ <% "publish" -> %>
+ <%= "Published" %>
+ <% "future" -> %>
+ <%= "Scheduled" %>
+ <% "draft" -> %>
+ <%= "Last Modified" %>
+ <% "pending" -> %>
+ <%= "Scheduled" %>
+ <% "private" -> %>
+ <%= "Published Privately" %>
+ <% "inherit" -> %>
+ <%= "Inherit" %>
+ <% end %>
+
+ <%= post.post_date |> Timex.format!("%F", :strftime) %>
+
+
+
+ <% end %>
+
+
+
+
+
+<%= if @page > 1 do %>
+ <%= link 1, to: Routes.admin_posts_path(@conn, :index, page: 1) %>
+<% end %>
+
+<%= if @page > 3 do %>
+ ...
+<% end %>
+
+<%= if @page > 2 do %>
+ <%= link @page - 1, to: Routes.admin_posts_path(@conn, :index, page: @page - 1) %>
+<% end %>
+
+<%= @page %>
+
+<%= if @page + 1 < @last_page do %>
+ <%= link @page + 1, to: Routes.admin_posts_path(@conn, :index, page: @page + 1) %>
+<% end %>
+
+<%= if @page + 2 < @last_page do %>
+ ...
+<% end %>
+
+<%= if @page < @last_page do %>
+ <%= link @last_page, to: Routes.admin_posts_path(@conn, :index, page: @last_page) %>
+<% end %>
diff --git a/apps/content/lib/content_web/templates/feeds/index.rss.eex b/apps/content/lib/content_web/templates/feeds/index.rss.eex
new file mode 100644
index 00000000..5c6f021b
--- /dev/null
+++ b/apps/content/lib/content_web/templates/feeds/index.rss.eex
@@ -0,0 +1,24 @@
+
+
+
+ <%= LayoutView.title(@view_module, @view_template, assigns) %>
+ <%= LayoutView.excerpt(@view_module, @view_template, assigns) %>
+ https://pre.hn
+
+
+<%= for post <- @posts do %>
+ -
+
<%= post.post_title |> HtmlSanitizeEx.strip_tags() %>
+
+ <%= post.post_content |> process_content |> html_escape |> safe_to_string %>
+
+ <%=
+ post.post_date
+ |> DateTime.from_naive!("Etc/UTC")
+ |> Timex.format!("{WDshort}, {D} {Mshort} {YYYY} {h24}:{m}:{s} {Z}")
+ %>
+ <%= post.guid %>
+
+<% end %>
+
+
diff --git a/apps/content_web/lib/content_web/templates/layout/_menu.html.eex b/apps/content/lib/content_web/templates/layout/_menu.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/layout/_menu.html.eex
rename to apps/content/lib/content_web/templates/layout/_menu.html.eex
diff --git a/apps/content/lib/content_web/templates/layout/admin.html.eex b/apps/content/lib/content_web/templates/layout/admin.html.eex
new file mode 100644
index 00000000..a1377533
--- /dev/null
+++ b/apps/content/lib/content_web/templates/layout/admin.html.eex
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+ <%= title(@view_module, @view_template, assigns) %>
+ ">
+ ">
+
+
+
+
+
+
+
+
+ <%= link "🏠 Home", to: Routes.admin_home_path(@conn, :index) %>
+
+
+ <%= link "📝 Posts", to: Routes.admin_posts_path(@conn, :index) %>
+
+
+ <%= link "📄 Pages", to: Routes.admin_posts_path(@conn, :index, post_type: "page") %>
+
+
+ <%= link "👋 Logout", to: AuthWeb.Router.Helpers.pow_session_path(@conn, :delete), method: :delete %>
+
+
+
+
+ <%= if get_flash(@conn, :info) do %>
+
+
+
+ <%= get_flash(@conn, :info) %>
+ Dismiss
+
+
+ <% end %>
+ <%= if get_flash(@conn, :error) do %>
+
+
+
+ <%= get_flash(@conn, :error) %>
+ Dismiss
+
+
+ <% end %>
+ <%= @inner_content %>
+
+
+
+
+
+
+
+
diff --git a/apps/content_web/lib/content_web/templates/layout/app.html.eex b/apps/content/lib/content_web/templates/layout/app.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/layout/app.html.eex
rename to apps/content/lib/content_web/templates/layout/app.html.eex
diff --git a/apps/content/lib/content_web/templates/menus/edit.html.eex b/apps/content/lib/content_web/templates/menus/edit.html.eex
new file mode 100644
index 00000000..97e656d0
--- /dev/null
+++ b/apps/content/lib/content_web/templates/menus/edit.html.eex
@@ -0,0 +1,5 @@
+
+
+
+<%= form_for @conn, Routes.menus_path(@conn, :update, @id), [method: :put], fn _f -> %>
+<% end %>
diff --git a/apps/content/lib/content_web/templates/posts/comments.html.eex b/apps/content/lib/content_web/templates/posts/comments.html.eex
new file mode 100644
index 00000000..cd2edf84
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/comments.html.eex
@@ -0,0 +1,35 @@
+<%= Enum.map(Content.Comments.children(@parent_id, @post.comments), fn comment -> %>
+
+<% end) %>
diff --git a/apps/content/lib/content_web/templates/posts/edit.html.eex b/apps/content/lib/content_web/templates/posts/edit.html.eex
new file mode 100644
index 00000000..742c25eb
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/edit.html.eex
@@ -0,0 +1 @@
+<%= render "form.html", Map.put(assigns, :action, Routes.posts_path(@conn, :update, @posts)) %>
diff --git a/apps/content/lib/content_web/templates/posts/form.html.eex b/apps/content/lib/content_web/templates/posts/form.html.eex
new file mode 100644
index 00000000..2d94f52e
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/form.html.eex
@@ -0,0 +1,82 @@
+<%= form_for @changeset, @action, fn f -> %>
+
+
+ <%= if @changeset.action do %>
+
+
Oops, something went wrong! Please check the errors below.
+
+
+ <%= for {error_key, error} <- @changeset.errors |> Keyword.drop([:post_title, :post_content]) do %>
+ <%= if error do %>
+ <%= error_key %>: <%= error_tag f, error_key %>
+ <% end %>
+ <% end %>
+
+ <% end %>
+
+ <%= label f, :post_title do %>
+ Title
+ <%= text_input f, :post_title %>
+ <% end %>
+
+ <%= label f, :post_content, "data-react-class": "Editor" do %>
+ Content
+ <%= textarea f, :post_content, "data-simplemde": true %>
+ <% end %>
+
+
+ <%= submit "Delete", class: "btn btn-warning", form: "deleteForm" %>
+
+
+ <%= submit "Save", class: "btn btn-primary" %>
+
+
+
+
+
+ <%= label f, :post_status, class: "column input-group" do %>
+
Status
+ <%= select f, :post_status, [{"Publish", :publish}, {"Draft", :draft}] %>
+ <% end %>
+ <%= label f, :post_author, class: "input-group" do %>
+
Author
+ <%= select f, :post_author, @author_options |> Enum.map(&({&1.display_name, &1."ID"})) %>
+ <% end %>
+ <%= label f, :post_excerpt, class: "input-group" do %>
+
Excerpt
+ <%= textarea f, :post_excerpt %>
+ <% end %>
+ <%= label f, :sticky, class: "input-group" do %>
+
Sticky?
+ <%= checkbox f, :sticky %>
+ <% end %>
+ <%= label f, :comment_status, class: "input-group" do %>
+
Comment Status
+ <%= select f, :comment_status, ["open", "closed"] %>
+ <% end %>
+ <%= label f, :ping_status, class: "input-group" do %>
+
Ping Status
+ <%= select f, :ping_status, ["open", "closed"] %>
+ <% end %>
+ <%= label f, :post_password, class: "input-group" do %>
+
Post Password
+ <%= text_input f, :post_password %>
+ <% end %>
+ <%= label f, :post_name, class: "input-group" do %>
+
Slug
+ <%= text_input f, :post_name %>
+ <% end %>
+ <%= label f, :post_order, class: "input-group" do %>
+
Post Order
+ <%= number_input f, :post_order %>
+ <% end %>
+
+
+
+ <%= hidden_input f, :post_type, value: @post_type %>
+<% end %>
+
+<%= if assigns[:posts] do %>
+ <%= form_for @changeset, Routes.posts_path(@conn, :delete, @posts), [method: :delete, id: "deleteForm"], fn _f -> %>
+ <% end %>
+<% end %>
diff --git a/apps/content/lib/content_web/templates/posts/front.html.eex b/apps/content/lib/content_web/templates/posts/front.html.eex
new file mode 100644
index 00000000..ca2011f5
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/front.html.eex
@@ -0,0 +1,15 @@
+
+
+
+ <%= link to: Routes.posts_path(@conn, :show, @post), class: "u-url" do %>
+ <%= raw @post.post_title %>
+ <% end %>
+
+ <%= post_topmatter(@conn, @post) %>
+
+
+ <%= render "thumb.html", post: @post, thumbs: @thumbs %>
+ <%= @post |> Content.Post.content_page(@page) |> process_content |> raw %>
+
+ <%= render "pagination.html", conn: @conn, post: @post, current_page: @page %>
+
diff --git a/apps/content/lib/content_web/templates/posts/index.html.eex b/apps/content/lib/content_web/templates/posts/index.html.eex
new file mode 100644
index 00000000..c4502eb4
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/index.html.eex
@@ -0,0 +1,62 @@
+<%= for post <- @posts do %>
+
+
+ <%= link to: Routes.posts_path(@conn, :show, post), class: "u-url" do %>
+ <%= raw post.post_title %>
+ <% end %>
+
+ <%= post_topmatter(@conn, post) %>
+
+ <%= if authenticated_for_post?(@conn, post) do %>
+ <%= render "thumb.html", post: post, thumbs: @thumbs %>
+
+ <%= raw post |> Content.Post.content_page(1) |> Content.Post.before_more |> process_content |> raw %>
+ <%= if post.post_content =~ "" do %>
+
+ <%= link "Keep Reading", to: Routes.posts_path(@conn, :show, post) %>
+
+ <% end %>
+ <%= render "pagination.html", conn: @conn, post: post %>
+
+ <% else %>
+ <%= render "password_form.html", post: post, conn: @conn %>
+ <% end %>
+
+
+ Categories:
+ <%= for term <- post.categories do %>
+ <%= link term.name, to: Routes.category_path(@conn, :index_posts, term.slug), class: "p-category" %>
+ <% end %>
+
+
+
+<% end %>
+
+
+ Page:
+ <%= if @page > 1 do %>
+ <%= link 1, to: paginated_posts_path(@conn, @category, 1) %>
+ <% end %>
+
+ <%= if @page > 3 do %>
+ ...
+ <% end %>
+
+ <%= if @page > 2 do %>
+ <%= link @page - 1, to: paginated_posts_path(@conn, @category, @page - 1) %>
+ <% end %>
+
+ <%= @page %>
+
+ <%= if @page + 1 < @last_page do %>
+ <%= link @page + 1, to: paginated_posts_path(@conn, @category, @page + 1) %>
+ <% end %>
+
+ <%= if @page + 2 < @last_page do %>
+ ...
+ <% end %>
+
+ <%= if @page < @last_page do %>
+ <%= link @last_page, to: paginated_posts_path(@conn, @category, @last_page) %>
+ <% end %>
+
diff --git a/apps/content/lib/content_web/templates/posts/new.html.eex b/apps/content/lib/content_web/templates/posts/new.html.eex
new file mode 100644
index 00000000..f7ce9a9b
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/new.html.eex
@@ -0,0 +1 @@
+<%= render "form.html", Map.put(assigns, :action, Routes.posts_path(@conn, :create)) %>
diff --git a/apps/content/lib/content_web/templates/posts/pagination.html.eex b/apps/content/lib/content_web/templates/posts/pagination.html.eex
new file mode 100644
index 00000000..c9cc5229
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/pagination.html.eex
@@ -0,0 +1,14 @@
+<%= if Content.Post.paginated_post?(@post) do %>
+
+ Page:
+ <%= Enum.map(
+ 1..Content.Post.content_page_count(@post),
+ fn page ->
+ if assigns[:current_page] == nil || assigns[:current_page] != page do
+ link page, to: Routes.paged_post_path(@conn, :show, @post, page)
+ else
+ content_tag :span, page, class: "paginator-page"
+ end
+ end) %>
+
+<% end %>
diff --git a/apps/content/lib/content_web/templates/posts/password_form.html.eex b/apps/content/lib/content_web/templates/posts/password_form.html.eex
new file mode 100644
index 00000000..f1b7f5e0
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/password_form.html.eex
@@ -0,0 +1,11 @@
+<%= form_for @conn, "/wp-login.php?action=postpass", [class: "post-password-form"], fn f -> %>
+ This content is password protected. To view it please enter your password below:
+
+ <%= label f, :post_password do %>
+ Password:
+ <% end %>
+
+
+
+
+<% end %>
diff --git a/apps/content/lib/content_web/templates/posts/reply_form.html.eex b/apps/content/lib/content_web/templates/posts/reply_form.html.eex
new file mode 100644
index 00000000..fbbd3292
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/reply_form.html.eex
@@ -0,0 +1,24 @@
+
+<%= form_for @comment_changeset, Routes.comment_path(@conn, :create), fn f -> %>
+ <%= hidden_input f, :comment_parent %>
+ <%= hidden_input f, :comment_post_ID %>
+ <%= label f, :comment_author do %>
+ Your Name
+ <%= text_input f, :comment_author %>
+ <% end %>
+ <%= label f, :comment_author_email do %>
+ Your Email
+ <%= email_input f, :comment_author_email %>
+ <% end %>
+ <%= label f, :comment_author_url do %>
+ Your Website
+ <%= text_input f, :comment_author_url %>
+ <% end %>
+ <%= label f, :comment_content do %>
+ Comment
+ <%= textarea f, :comment_content %>
+ <% end %>
+
+ <%= submit "\u27A6 Reply", class: "btn btn-primary" %>
+
+<% end %>
diff --git a/apps/content/lib/content_web/templates/posts/show.html.eex b/apps/content/lib/content_web/templates/posts/show.html.eex
new file mode 100644
index 00000000..e0cc9eef
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/show.html.eex
@@ -0,0 +1,39 @@
+
+
+
+ <%= link to: Routes.posts_path(@conn, :show, @post), class: "u-url" do %>
+ <%= raw @post.post_title %>
+ <% end %>
+
+ <%= post_topmatter(@conn, @post) %>
+
+
+ <%= render "thumb.html", post: @post, thumbs: @thumbs %>
+ <%= @post |> Content.Post.content_page(@page) |> process_content |> raw %>
+
+ Categories:
+ <%= for term <- @post.categories do %>
+ <%= link term.name, to: Routes.category_path(@conn, :index_posts, term.slug), class: "p-category" %>
+ <% end %>
+
+
+ <%= render "pagination.html", conn: @conn, post: @post, current_page: @page %>
+ <%= if @post.comment_status == "open" do %>
+ Comments
+
+ ➦ Reply to "<%= @post.post_title %>"
+
+
+ <% end %>
+
+
diff --git a/apps/content_web/lib/content_web/templates/page/index.html.eex b/apps/content/lib/content_web/templates/posts/static_pages/index.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/page/index.html.eex
rename to apps/content/lib/content_web/templates/posts/static_pages/index.html.eex
diff --git a/apps/content_web/lib/content_web/templates/page/theming.html.eex b/apps/content/lib/content_web/templates/posts/static_pages/theming.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/page/theming.html.eex
rename to apps/content/lib/content_web/templates/posts/static_pages/theming.html.eex
diff --git a/apps/content_web/lib/content_web/templates/page/theming/_buttons.html.eex b/apps/content/lib/content_web/templates/posts/static_pages/theming/_buttons.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/page/theming/_buttons.html.eex
rename to apps/content/lib/content_web/templates/posts/static_pages/theming/_buttons.html.eex
diff --git a/apps/content_web/lib/content_web/templates/page/theming/_card.html.eex b/apps/content/lib/content_web/templates/posts/static_pages/theming/_card.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/page/theming/_card.html.eex
rename to apps/content/lib/content_web/templates/posts/static_pages/theming/_card.html.eex
diff --git a/apps/content_web/lib/content_web/templates/page/theming/_input.html.eex b/apps/content/lib/content_web/templates/posts/static_pages/theming/_input.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/page/theming/_input.html.eex
rename to apps/content/lib/content_web/templates/posts/static_pages/theming/_input.html.eex
diff --git a/apps/content_web/lib/content_web/templates/page/theming/_menu.html.eex b/apps/content/lib/content_web/templates/posts/static_pages/theming/_menu.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/page/theming/_menu.html.eex
rename to apps/content/lib/content_web/templates/posts/static_pages/theming/_menu.html.eex
diff --git a/apps/content_web/lib/content_web/templates/page/theming/_site.html.eex b/apps/content/lib/content_web/templates/posts/static_pages/theming/_site.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/page/theming/_site.html.eex
rename to apps/content/lib/content_web/templates/posts/static_pages/theming/_site.html.eex
diff --git a/apps/content_web/lib/content_web/templates/page/theming/_tables.html.eex b/apps/content/lib/content_web/templates/posts/static_pages/theming/_tables.html.eex
similarity index 100%
rename from apps/content_web/lib/content_web/templates/page/theming/_tables.html.eex
rename to apps/content/lib/content_web/templates/posts/static_pages/theming/_tables.html.eex
diff --git a/apps/content/lib/content_web/templates/posts/thumb.html.eex b/apps/content/lib/content_web/templates/posts/thumb.html.eex
new file mode 100644
index 00000000..d2f3b06e
--- /dev/null
+++ b/apps/content/lib/content_web/templates/posts/thumb.html.eex
@@ -0,0 +1,13 @@
+<%= case @thumbs[@post.'ID'] do %>
+ <% thumb = %Content.Post{} -> %>
+ <%= if thumb |> Content.Attachment.vertical?() do %>
+
+ <%= img_tag thumb.guid %>
+
+ <% else %>
+
+ <%= img_tag thumb.guid %>
+
+ <% end %>
+ <% nil -> %>
+<% end %>
diff --git a/apps/content/lib/content_web/templates/sitemap/index.html.eex b/apps/content/lib/content_web/templates/sitemap/index.html.eex
new file mode 100644
index 00000000..14694fc7
--- /dev/null
+++ b/apps/content/lib/content_web/templates/sitemap/index.html.eex
@@ -0,0 +1,21 @@
+Site Index
+Posts and Pages
+
+ <%= for post <- @posts do %>
+
+ <%= link to: Routes.posts_path(@conn, :show, post) do %>
+ <%= raw post.post_title %>
+ <% end %>
+
+ <% end %>
+
+Categories
+
+ <%= for category <- @categories do %>
+
+ <%= link to: Routes.category_path(@conn, :index_posts, category.slug)do %>
+ <%= raw category.name %>
+ <% end %>
+
+ <% end %>
+
diff --git a/apps/content/lib/content_web/templates/wp_comment/edit.html.eex b/apps/content/lib/content_web/templates/wp_comment/edit.html.eex
new file mode 100644
index 00000000..18a8e31c
--- /dev/null
+++ b/apps/content/lib/content_web/templates/wp_comment/edit.html.eex
@@ -0,0 +1,5 @@
+Edit comment
+
+<%= render "form.html", Map.put(assigns, :action, Routes.comment_path(@conn, :update, @comment)) %>
+
+<%= link "Back", to: Routes.comment_path(@conn, :index) %>
diff --git a/apps/content/lib/content_web/templates/wp_comment/form.html.eex b/apps/content/lib/content_web/templates/wp_comment/form.html.eex
new file mode 100644
index 00000000..065ac163
--- /dev/null
+++ b/apps/content/lib/content_web/templates/wp_comment/form.html.eex
@@ -0,0 +1,11 @@
+<%= form_for @changeset, @action, fn f -> %>
+ <%= if @changeset.action do %>
+
+
Oops, something went wrong! Please check the errors below.
+
+ <% end %>
+
+
+ <%= submit "Submit", class: "btn btn-primary" %>
+
+<% end %>
diff --git a/apps/content/lib/content_web/templates/wp_comment/index.html.eex b/apps/content/lib/content_web/templates/wp_comment/index.html.eex
new file mode 100644
index 00000000..c3219ce5
--- /dev/null
+++ b/apps/content/lib/content_web/templates/wp_comment/index.html.eex
@@ -0,0 +1,24 @@
+Listing comments
+
+
+
+
+
+
+
+
+
+<%= for comment <- @comments do %>
+
+
+
+ <%= link "Show", to: Routes.comment_path(@conn, :show, comment), class: "btn btn-default btn-xs" %>
+ <%= link "Edit", to: Routes.comment_path(@conn, :edit, comment), class: "btn btn-default btn-xs" %>
+ <%= link "Delete", to: Routes.comment_path(@conn, :delete, comment), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %>
+
+
+<% end %>
+
+
+
+<%= link "New comment", to: Routes.comment_path(@conn, :new) %>
diff --git a/apps/content/lib/content_web/templates/wp_comment/new.html.eex b/apps/content/lib/content_web/templates/wp_comment/new.html.eex
new file mode 100644
index 00000000..594730d8
--- /dev/null
+++ b/apps/content/lib/content_web/templates/wp_comment/new.html.eex
@@ -0,0 +1,5 @@
+New comment
+
+<%= render "form.html", Map.put(assigns, :action, Routes.comment_path(@conn, :create)) %>
+
+<%= link "Back", to: Routes.comment_path(@conn, :index) %>
diff --git a/apps/content/lib/content_web/templates/wp_comment/show.html.eex b/apps/content/lib/content_web/templates/wp_comment/show.html.eex
new file mode 100644
index 00000000..93654c34
--- /dev/null
+++ b/apps/content/lib/content_web/templates/wp_comment/show.html.eex
@@ -0,0 +1,8 @@
+Show comment
+
+
+
+<%= link "Edit", to: Routes.comment_path(@conn, :edit, @comment) %>
+<%= link "Back", to: Routes.comment_path(@conn, :index) %>
diff --git a/apps/content/lib/content_web/views/admin_home_view.ex b/apps/content/lib/content_web/views/admin_home_view.ex
new file mode 100644
index 00000000..2af6c7b8
--- /dev/null
+++ b/apps/content/lib/content_web/views/admin_home_view.ex
@@ -0,0 +1,3 @@
+defmodule Content.AdminHomeView do
+ use Content, :view
+end
diff --git a/apps/content/lib/content_web/views/admin_posts_view.ex b/apps/content/lib/content_web/views/admin_posts_view.ex
new file mode 100644
index 00000000..81a5b838
--- /dev/null
+++ b/apps/content/lib/content_web/views/admin_posts_view.ex
@@ -0,0 +1,3 @@
+defmodule Content.AdminPostsView do
+ use Content, :view
+end
diff --git a/apps/content_web/lib/content_web/views/error_helpers.ex b/apps/content/lib/content_web/views/error_helpers.ex
similarity index 88%
rename from apps/content_web/lib/content_web/views/error_helpers.ex
rename to apps/content/lib/content_web/views/error_helpers.ex
index 490c9b58..2e83f363 100644
--- a/apps/content_web/lib/content_web/views/error_helpers.ex
+++ b/apps/content/lib/content_web/views/error_helpers.ex
@@ -1,4 +1,4 @@
-defmodule ContentWeb.ErrorHelpers do
+defmodule Content.ErrorHelpers do
@moduledoc """
Conveniences for translating and building error messages.
"""
@@ -39,9 +39,9 @@ defmodule ContentWeb.ErrorHelpers do
# should be written to the errors.po file. The :count option is
# set by Ecto and indicates we should also apply plural rules.
if count = opts[:count] do
- Gettext.dngettext(ContentWeb.Gettext, "errors", msg, msg, count, opts)
+ Gettext.dngettext(Content.Gettext, "errors", msg, msg, count, opts)
else
- Gettext.dgettext(ContentWeb.Gettext, "errors", msg, opts)
+ Gettext.dgettext(Content.Gettext, "errors", msg, opts)
end
end
end
diff --git a/apps/content_web/lib/content_web/views/error_view.ex b/apps/content/lib/content_web/views/error_view.ex
similarity index 88%
rename from apps/content_web/lib/content_web/views/error_view.ex
rename to apps/content/lib/content_web/views/error_view.ex
index 412e6454..29b08bff 100644
--- a/apps/content_web/lib/content_web/views/error_view.ex
+++ b/apps/content/lib/content_web/views/error_view.ex
@@ -1,5 +1,5 @@
-defmodule ContentWeb.ErrorView do
- use ContentWeb, :view
+defmodule Content.ErrorView do
+ use Content, :view
# If you want to customize a particular status code
# for a certain format, you may uncomment below.
diff --git a/apps/content/lib/content_web/views/feeds_view.ex b/apps/content/lib/content_web/views/feeds_view.ex
new file mode 100644
index 00000000..62f178e2
--- /dev/null
+++ b/apps/content/lib/content_web/views/feeds_view.ex
@@ -0,0 +1,70 @@
+defmodule Content.FeedsView do
+ use Content, :view
+ use Phoenix.HTML
+ alias Phoenix.HTML
+ alias Phoenix.HTML.Tag
+ alias Content.LayoutView
+
+ def gravatar_url_for_email(email) do
+ email
+ |> Kernel.||("noreply@example.com")
+ |> String.trim()
+ |> String.downcase()
+ |> (&(:crypto.hash(:md5, &1))).()
+ |> Base.encode16()
+ |> String.downcase()
+ |> (&("https://www.gravatar.com/avatar/#{&1}")).()
+ end
+
+ def auto_paragraph_tags(string) do
+ string
+ |> Kernel.||("")
+ |> String.split(["\n\n", "\r\n\r\n"], trim: true)
+ |> Enum.map(fn text ->
+ [Tag.content_tag(:p, text |> HTML.raw(), []), ?\n]
+ end)
+ |> HTML.html_escape()
+ end
+
+ def post_class(post) do
+ sticky =
+ if post.sticky do
+ "sticky"
+ end
+ "post post-#{post.'ID'} #{sticky}"
+ end
+
+ def post_topmatter(conn, post) do
+ author =
+ post.author ||
+ %Auth.User{
+ email: "example@example.org",
+ display_name: "Anonymous",
+ homepage_url: "#"
+ }
+ assigns = %{post: post, author: author, conn: conn}
+ ~E"""
+ <% _ = assigns # suppress unused assigns warning %>
+
+ """
+ end
+
+ def unauthenticated_post?(_conn, post) do
+ post.post_password == nil || String.length(post.post_password) == 0
+ end
+end
diff --git a/apps/content/lib/content_web/views/layout_view.ex b/apps/content/lib/content_web/views/layout_view.ex
new file mode 100644
index 00000000..06df8db4
--- /dev/null
+++ b/apps/content/lib/content_web/views/layout_view.ex
@@ -0,0 +1,92 @@
+defmodule Content.LayoutView do
+ use Content, :view
+
+ alias Content.{Option, Options}
+
+ def title(Content.PostsView, "index.html", assigns) do
+ "Page #{assigns.page} | #{title(nil, nil, nil)}"
+ end
+
+ def title(Content.FeedsView, "index.rss", %{category: category}) do
+ "#{category} | #{title(nil, nil, nil)}"
+ end
+
+ def title(Content.PostsView, "show.html", assigns) do
+ (assigns.post.post_title |> HtmlSanitizeEx.strip_tags()) <> " | " <> title(nil, nil, nil)
+ end
+
+ def title(_, _, _) do
+ case Options.get("blogname") do
+ opt = %Option{} ->
+ opt.option_value
+ _ ->
+ "Hello"
+ end
+ end
+
+ def excerpt(Content.PostsView, "show.html", assigns) do
+ assigns.post.post_excerpt
+ |> HtmlSanitizeEx.strip_tags()
+ end
+
+ def excerpt(Content.FeedsView, "index.rss", %{category: category}) do
+ "#{category} | #{excerpt(nil, nil, nil)}"
+ end
+
+ def excerpt(_, _, _) do
+ case Options.get("blogdescription") do
+ opt = %Option{} ->
+ opt.option_value
+ _ ->
+ "Yet another website"
+ end
+ end
+
+ def author(Content.PostsView, "show.html", assigns) do
+ case assigns do
+ %{author: %{display_name: name}} ->
+ name
+ _ ->
+ "Anonymous"
+ end
+ end
+
+ def author(_, _, _) do
+ "Anonymous"
+ end
+
+ def corresponding_feed_url(conn, _, _, %{category: nil}) do
+ Routes.index_feed_url(conn, :index)
+ end
+
+ def corresponding_feed_url(conn, Content.PostsView, "index.html", %{category: category}) do
+ Routes.category_feed_url(conn, :index, category)
+ end
+
+ def corresponding_feed_url(conn, _, _, _) do
+ Routes.index_feed_url(conn, :index)
+ end
+
+ def menu_markup(menu_items, conn), do: menu_markup(menu_items, conn, 0)
+ def menu_markup(nil, _, _), do: ""
+ def menu_markup([], _, _), do: ""
+ def menu_markup(menu_items, conn, level) do
+ ~E"""
+
+ <%= for item <- menu_items do %>
+
+
+ <%= case item[:type] do %>
+ <% "category" -> %>
+ <%= link item[:related_item].title, to: Routes.category_path(conn, :index_posts, item[:related_item].slug) %>
+ <% _ -> %>
+ <%= link item[:related_item].title, to: Routes.posts_path(conn, :show, item[:related_item].slug) %>
+ <% end %>
+ <%= menu_markup(item[:children], conn, level + 1) %>
+
+
+ <% end %>
+
+ """
+ end
+end
diff --git a/apps/content/lib/content_web/views/menus_view.ex b/apps/content/lib/content_web/views/menus_view.ex
new file mode 100644
index 00000000..7b315554
--- /dev/null
+++ b/apps/content/lib/content_web/views/menus_view.ex
@@ -0,0 +1,3 @@
+defmodule Content.MenusView do
+ use Content, :view
+end
diff --git a/apps/content/lib/content_web/views/page_view.ex b/apps/content/lib/content_web/views/page_view.ex
new file mode 100644
index 00000000..271c1cc2
--- /dev/null
+++ b/apps/content/lib/content_web/views/page_view.ex
@@ -0,0 +1,3 @@
+defmodule Content.PageView do
+ use Content, :view
+end
diff --git a/apps/content/lib/content_web/views/posts_view.ex b/apps/content/lib/content_web/views/posts_view.ex
new file mode 100644
index 00000000..094e3ce2
--- /dev/null
+++ b/apps/content/lib/content_web/views/posts_view.ex
@@ -0,0 +1,96 @@
+defmodule Content.PostsView do
+ use Content, :view
+ use Phoenix.HTML
+ import Plug.Conn
+ alias Content.Comment
+ alias Content.Post
+ alias Phoenix.HTML
+ alias Phoenix.HTML.Tag
+
+ def paginated_posts_path(conn, category, page) do
+ case category do
+ nil ->
+ Routes.blog_page_path(conn, :index_posts, page)
+ _ ->
+ Routes.category_page_path(conn, :index, category, page)
+ end
+ end
+
+ def authenticated_for_post?(conn, post) do
+ post.post_password == nil || String.length(post.post_password) == 0 || get_session(conn, :post_password) == post.post_password
+ end
+
+ def gravatar_url_for_email(email) do
+ email
+ |> Kernel.||("noreply@example.com")
+ |> String.trim()
+ |> String.downcase()
+ |> (&(:crypto.hash(:md5, &1))).()
+ |> Base.encode16()
+ |> String.downcase()
+ |> (&("https://www.gravatar.com/avatar/#{&1}")).()
+ end
+
+ def comment_changeset_for_post(%Post{} = post) do
+ %Comment{
+ comment_post_ID: post.'ID'
+ }
+ |> Comment.changeset()
+ end
+
+ def comment_changeset_for_parent(%Comment{} = comment) do
+ %Comment{
+ comment_parent: comment.comment_ID,
+ comment_post_ID: comment.comment_post_ID
+ }
+ |> Comment.changeset()
+ end
+
+ def auto_paragraph_tags(string) do
+ string
+ |> Kernel.||("")
+ |> String.split(["\n\n", "\r\n\r\n"], trim: true)
+ |> Enum.map(fn text ->
+ [Tag.content_tag(:p, text |> HTML.raw(), []), ?\n]
+ end)
+ |> HTML.html_escape()
+ end
+
+ def post_class(post) do
+ sticky =
+ if post.sticky do
+ "sticky"
+ end
+ "post post-#{post.'ID'} #{sticky}"
+ end
+
+ def post_topmatter(conn, post) do
+ author =
+ post.author ||
+ %Auth.User{
+ email: "example@example.org",
+ display_name: "Anonymous",
+ homepage_url: "#"
+ }
+ assigns = %{post: post, author: author, conn: conn}
+ ~E"""
+ <% _ = assigns # suppress unused assigns warning %>
+
+ """
+ end
+end
diff --git a/apps/content/lib/content_web/views/sitemap_view.ex b/apps/content/lib/content_web/views/sitemap_view.ex
new file mode 100644
index 00000000..10fadf9e
--- /dev/null
+++ b/apps/content/lib/content_web/views/sitemap_view.ex
@@ -0,0 +1,3 @@
+defmodule Content.SitemapView do
+ use Content, :view
+end
diff --git a/apps/content/lib/content_web/views/wp_comment_view.ex b/apps/content/lib/content_web/views/wp_comment_view.ex
new file mode 100644
index 00000000..0db9fcae
--- /dev/null
+++ b/apps/content/lib/content_web/views/wp_comment_view.ex
@@ -0,0 +1,3 @@
+defmodule Content.CommentView do
+ use Content, :view
+end
diff --git a/apps/content/mix.exs b/apps/content/mix.exs
index 64d1554c..f83f4dcd 100644
--- a/apps/content/mix.exs
+++ b/apps/content/mix.exs
@@ -1,18 +1,18 @@
-defmodule Content.Mixfile do
+defmodule Content.MixProject do
use Mix.Project
def project do
[
app: :content,
- version: "0.0.1",
+ version: "0.1.0",
build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",
- elixir: "~> 1.8",
- elixirc_paths: elixirc_paths(Mix.env),
- compilers: [:phoenix, :gettext] ++ Mix.compilers,
- start_permanent: Mix.env == :prod,
+ elixir: "~> 1.7",
+ elixirc_paths: elixirc_paths(Mix.env()),
+ compilers: [:phoenix, :gettext] ++ Mix.compilers(),
+ start_permanent: Mix.env() == :prod,
aliases: aliases(),
deps: deps(),
test_coverage: [tool: ExCoveralls],
@@ -32,39 +32,38 @@ defmodule Content.Mixfile do
# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
- defp elixirc_paths(_), do: ["lib"]
+ defp elixirc_paths(_), do: ["lib"]
# Specifies your project dependencies.
#
# Type `mix help deps` for examples and options.
defp deps do
[
- {:content_web, in_umbrella: true},
- {:phoenix, "~> 1.5.3"},
- {:phoenix_pubsub, "~> 2.0"},
- {:ecto_sql, "~> 3.4"},
- {:postgrex, "~> 0.15.0"},
- {:plug_cowboy, "~> 2.0"},
- {:phoenix_ecto, "~> 4.0"},
- {:phoenix_html, "~> 2.10"},
- {:phoenix_live_reload, "~> 1.2", only: :dev},
- {:gettext, "~> 0.11"},
- {:cowboy, "~> 2.7"},
- {:php_serializer, "~> 0.9.0"},
- {:quantum, "~> 2.3"},
- {:timex, "~> 3.1"},
- {:excoveralls, "~> 0.10", only: [:dev, :test]},
- {:phoenix_html_sanitizer, "~> 1.0.0"},
- {:bcrypt_elixir, "~> 1.0"},
- {:comeonin, "~> 4.0"},
+ {:auth, in_umbrella: true},
+ {:auth_web, in_umbrella: true},
+ {:core, in_umbrella: true},
{:earmark, "~> 1.4.2" },
- {:slugger, "~> 0.3"},
- {:ecto, "~> 3.4.3"},
+ {:excoveralls, "~> 0.10", only: [:dev, :test]},
{:floki, "~> 0.25.0"},
+ {:gettext, "~> 0.11"},
+ {:jason, "~> 1.0"},
{:mock, "~> 0.3.0", only: :test},
{:meck, "~> 0.8.13", only: :test},
- {:sitemap, "~> 1.1"},
{:neotomex, "~> 0.1.7"},
+ {:phoenix, "~> 1.5.3"},
+ {:phoenix_ecto, "~> 4.0"},
+ {:phoenix_html, "~> 2.11"},
+ {:phoenix_html_sanitizer, "~> 1.0.0"},
+ {:phoenix_live_reload, "~> 1.2", only: :dev},
+ {:phoenix_live_dashboard, "~> 0.2.0"},
+ {:php_serializer, "~> 0.9.0"},
+ {:plug_cowboy, "~> 2.0"},
+ {:quantum, "~> 2.3"},
+ {:sitemap, "~> 1.1"},
+ {:slugger, "~> 0.3"},
+ {:telemetry_metrics, "~> 0.4"},
+ {:telemetry_poller, "~> 0.4"},
+ {:timex, "~> 3.1"},
]
end
diff --git a/apps/content/priv/repo/migrations/20180825175607_create_wp_schema.exs b/apps/content/priv/repo/migrations/20180825175607_create_wp_schema.exs
index d5af16e0..5d74afc2 100644
--- a/apps/content/priv/repo/migrations/20180825175607_create_wp_schema.exs
+++ b/apps/content/priv/repo/migrations/20180825175607_create_wp_schema.exs
@@ -116,18 +116,5 @@ defmodule Content.Repo.Migrations.CreateWpSchema do
add :meta_key, :text
add :meta_value, :text
end
-
- create table("wp_users", primary_key: false) do
- add :ID, :integer, [:primary_key]
- add :user_login, :text
- add :user_pass, :text
- add :user_nicename, :text
- add :user_email, :text
- add :user_url, :text
- add :user_registered, :naive_datetime
- add :user_activation_key, :text
- add :user_status, :integer
- add :display_name, :text
- end
end
end
diff --git a/apps/content/test/content_web/controllers/admin_home_controller_test.exs b/apps/content/test/content_web/controllers/admin_home_controller_test.exs
new file mode 100644
index 00000000..c3652976
--- /dev/null
+++ b/apps/content/test/content_web/controllers/admin_home_controller_test.exs
@@ -0,0 +1,15 @@
+defmodule Content.AdminHomeControllerTest do
+ use Content.ConnCase
+
+ alias Content
+
+ describe "index" do
+ test "loads ok", %{conn: conn} do
+ conn =
+ as_admin do
+ get conn, Routes.admin_home_path(conn, :index)
+ end
+ assert html_response(conn, 200)
+ end
+ end
+end
diff --git a/apps/content/test/content_web/controllers/admin_posts_controller_test.exs b/apps/content/test/content_web/controllers/admin_posts_controller_test.exs
new file mode 100644
index 00000000..8977e176
--- /dev/null
+++ b/apps/content/test/content_web/controllers/admin_posts_controller_test.exs
@@ -0,0 +1,55 @@
+defmodule Content.AdminPostsControllerTest do
+ use Content.ConnCase
+
+ alias Content
+ alias Content.Posts
+
+ @create_attrs %{
+ ID: 123,
+ post_name: "my-post",
+ post_title: "Post in the admin list",
+ post_content: "",
+ post_status: "publish",
+ post_type: "post",
+ post_date: "2018-01-01T00:00:00Z"
+ }
+
+ def fixture(:post) do
+ {:ok, post} = Posts.create_posts(@create_attrs)
+ post
+ end
+
+ describe "index" do
+ test "lists all posts", %{conn: conn} do
+ fixture(:post)
+
+ conn =
+ as_admin do
+ get conn, Routes.admin_posts_path(conn, :index)
+ end
+ assert html_response(conn, 200) =~ "Post in the admin list"
+ end
+
+ test "lists all posts with page", %{conn: conn} do
+ fixture(:post)
+
+ conn =
+ as_admin do
+ get conn, Routes.admin_posts_path(conn, :index, page: "2")
+ end
+ assert html_response(conn, 200)
+ refute html_response(conn, 200) =~ "Post in the admin list"
+ end
+
+ test "lists all posts with post type", %{conn: conn} do
+ fixture(:post)
+
+ conn =
+ as_admin do
+ get conn, Routes.admin_posts_path(conn, :index, post_type: "page")
+ end
+ assert html_response(conn, 200)
+ refute html_response(conn, 200) =~ "Post in the admin list"
+ end
+ end
+end
diff --git a/apps/content/test/content_web/controllers/comment_controller_test.exs b/apps/content/test/content_web/controllers/comment_controller_test.exs
new file mode 100644
index 00000000..41be4aa6
--- /dev/null
+++ b/apps/content/test/content_web/controllers/comment_controller_test.exs
@@ -0,0 +1,66 @@
+defmodule Content.CommentControllerTest do
+ use Content.ConnCase
+
+ alias Content.Comments
+ alias Content.Posts
+
+ @post_attrs %{ID: 456, post_name: "blergh", post_status: "publish"}
+ @create_attrs %{comment_ID: 123, comment_content: "Hello world", comment_post_ID: 456}
+ @update_attrs %{comment_ID: 123, comment_content: "Goodbye", comment_post_ID: 456}
+ @invalid_attrs %{comment_ID: 123, comment_content: "", comment_post_ID: 456}
+
+ def fixture(:post) do
+ {:ok, post} = Posts.create_posts(@post_attrs)
+ post
+ end
+
+ def fixture(:comment) do
+ {:ok, comment} = Comments.create_comment(@create_attrs)
+ comment
+ end
+
+ describe "create comment" do
+ test "redirects to show when data is valid", %{conn: conn} do
+ post = fixture(:post)
+ conn = post conn, Routes.comment_path(conn, :create), comment: @create_attrs
+
+ assert %{id: id} = redirected_params(conn)
+ assert redirected_to(conn) == Routes.posts_path(conn, :show, post)
+ end
+
+ test "renders errors when data is invalid", %{conn: conn} do
+ post = fixture(:post)
+ conn = post conn, Routes.comment_path(conn, :create), comment: @invalid_attrs
+ assert redirected_to(conn) == Routes.posts_path(conn, :show, post)
+ end
+ end
+
+ describe "update comment" do
+ setup [:create_comment]
+
+ test "redirects when data is valid", %{conn: conn, comment: comment, post: post} do
+ conn = put conn, Routes.comment_path(conn, :update, comment), comment: @update_attrs
+ assert redirected_to(conn) == Routes.posts_path(conn, :show, post)
+ end
+
+ test "renders errors when data is invalid", %{conn: conn, comment: comment, post: post} do
+ conn = put conn, Routes.comment_path(conn, :update, comment), comment: @invalid_attrs
+ assert redirected_to(conn) == Routes.posts_path(conn, :show, post)
+ end
+ end
+
+ describe "delete comment" do
+ setup [:create_comment]
+
+ test "deletes chosen comment", %{conn: conn, comment: comment, post: post} do
+ conn = delete conn, Routes.comment_path(conn, :delete, comment)
+ assert redirected_to(conn) == Routes.posts_path(conn, :show, post)
+ end
+ end
+
+ defp create_comment(_) do
+ comment = fixture(:comment)
+ post = fixture(:post)
+ {:ok, comment: comment, post: post}
+ end
+end
diff --git a/apps/content_web/test/content/controllers/page_controller_test.exs b/apps/content/test/content_web/controllers/page_controller_test.exs
similarity index 61%
rename from apps/content_web/test/content/controllers/page_controller_test.exs
rename to apps/content/test/content_web/controllers/page_controller_test.exs
index e7d1f3e0..84071587 100644
--- a/apps/content_web/test/content/controllers/page_controller_test.exs
+++ b/apps/content/test/content_web/controllers/page_controller_test.exs
@@ -1,5 +1,5 @@
-defmodule ContentWeb.PageControllerTest do
- use ContentWeb.ConnCase
+defmodule Content.PageControllerTest do
+ use Content.ConnCase
test "GET /", %{conn: conn} do
conn = get(conn, "/index")
diff --git a/apps/content/test/content_web/controllers/post_password_controller_test.exs b/apps/content/test/content_web/controllers/post_password_controller_test.exs
new file mode 100644
index 00000000..b9904029
--- /dev/null
+++ b/apps/content/test/content_web/controllers/post_password_controller_test.exs
@@ -0,0 +1,22 @@
+defmodule Content.PostPasswordControllerTest do
+ use Content.ConnCase
+ use Plug.Test
+
+ alias Content
+ import Plug.Conn
+
+ describe "create" do
+ test "accepts post_password and puts in session (with referer)", %{conn: conn} do
+ conn = conn |> put_req_header("referer", "https://example.com/test")
+ conn = post conn, Routes.post_password_path(conn, :create), %{"post_password" => "testpass"}
+
+ assert conn |> get_session("post_password") == "testpass"
+ end
+
+ test "accepts post_password and puts in session (without referer)", %{conn: conn} do
+ conn = post conn, Routes.post_password_path(conn, :create), %{"post_password" => "testpass"}
+
+ assert conn |> get_session("post_password") == "testpass"
+ end
+ end
+end
diff --git a/apps/content/test/content_web/controllers/posts_controller_test.exs b/apps/content/test/content_web/controllers/posts_controller_test.exs
new file mode 100644
index 00000000..92d60aab
--- /dev/null
+++ b/apps/content/test/content_web/controllers/posts_controller_test.exs
@@ -0,0 +1,330 @@
+defmodule Content.PostsControllerTest do
+ use Content.ConnCase
+
+ alias Content
+ alias Content.{Comment, Posts, Repo, Term, TermRelationship, TermTaxonomy}
+
+ @create_attrs %{
+ ID: 123,
+ post_name: "my-post",
+ post_title: "My Post",
+ post_content: "Page One Page Two",
+ post_status: "publish",
+ post_type: "post",
+ post_date: "2018-01-01T00:00:00Z",
+ comment_status: "open",
+ }
+ @blog_post_attrs %{
+ ID: 456,
+ post_name: "my-blog-post",
+ post_title: "My Blog Post",
+ post_content: "Page One Page Two",
+ post_status: "publish",
+ post_type: "post",
+ post_date: "2018-01-01T00:00:00Z",
+ comment_status: "open",
+ }
+ @preview_attrs %{
+ post_name: "my-post",
+ post_title: "My Post",
+ post_content: "",
+ post_status: "publish",
+ post_type: "post",
+ post_date: "2018-01-01T00:00:00Z"
+ }
+ @thumb_attrs %{
+ ID: 124,
+ post_name: "my-thumb",
+ post_title: "My Thumb",
+ post_content: "",
+ post_status: "publish",
+ post_type: "attachment",
+ post_date: "2018-01-01T00:00:00Z",
+ guid: "http://placekitten.com/200/300"
+ }
+ @attachment_attrs %{
+ ID: 123,
+ post_name: "attachment.txt",
+ post_title: "",
+ post_content: "my text attachment" |> Base.encode64,
+ post_status: "publish",
+ post_type: "attachment",
+ post_mime_type: "text/plain",
+ post_date: "2018-01-01T00:00:00Z",
+ comment_status: "open",
+ }
+ @update_attrs %{post_name: "my-post"}
+ @invalid_attrs %{post_status: "", post_type: "post"}
+
+ @post_category %Term{
+ term_id: 42,
+ name: "Test Category",
+ slug: "test-category",
+ }
+
+ @post_category_taxonomy %TermTaxonomy{
+ term_taxonomy_id: 64,
+ term_id: 42,
+ taxonomy: "category",
+ description: "A test category",
+ parent: 0,
+ }
+
+ @post_category_relationship %TermRelationship{
+ term_taxonomy_id: 64,
+ object_id: 123,
+ }
+
+ def fixture(:posts) do
+ {:ok, post} = Posts.create_posts(@create_attrs)
+ {:ok, thumb} = Posts.create_posts(@thumb_attrs)
+ {:ok, _meta} = %Content.Postmeta{post_id: post.'ID', meta_key: "_thumbnail_id", meta_value: Integer.to_string(thumb.'ID')} |> Repo.insert()
+ {:ok, _option} = %Content.Option{option_name: "sticky_posts", option_value: "a:1:{i:0;i:123;}"} |> Repo.insert()
+
+ post
+ end
+
+ def fixture(:single_post) do
+ {:ok, post} = Posts.create_posts(@create_attrs)
+ {:ok, _comment} = %Comment{comment_post_ID: post.'ID', comment_parent: 0, comment_date: NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)} |> Repo.insert()
+ post
+ end
+
+ def fixture(:category) do
+ {:ok, category} = @post_category |> Repo.insert()
+ {:ok, _term_taxonomy} = @post_category_taxonomy |> Repo.insert()
+ {:ok, _term_relationship} = @post_category_relationship |> Repo.insert()
+ category
+ end
+
+ def fixture(:front_post) do
+ {:ok, post} = Posts.create_posts(@create_attrs)
+ {:ok, _option} = %Content.Option{option_name: "show_on_front", option_value: "page"} |> Repo.insert()
+ {:ok, _option} = %Content.Option{option_name: "page_on_front", option_value: post.'ID' |> Integer.to_string(10)} |> Repo.insert()
+
+ post
+ end
+
+ def fixture(:blog_page) do
+ {:ok, post} = Posts.create_posts(@create_attrs)
+ {:ok, _blog} = Posts.create_posts(@blog_post_attrs)
+ {:ok, _option} = %Content.Option{option_name: "page_for_posts", option_value: post.'ID' |> Integer.to_string(10)} |> Repo.insert()
+
+ post
+ end
+
+ def fixture(:attachment) do
+ {:ok, post} = Posts.create_posts(@attachment_attrs)
+
+ post
+ end
+
+ describe "index" do
+ test "lists all posts", %{conn: conn} do
+ fixture(:posts)
+
+ conn = get conn, Routes.posts_path(conn, :index)
+ assert html_response(conn, 200) =~ "My Post"
+ end
+
+ test "lists all posts by category", %{conn: conn} do
+ fixture(:posts)
+ fixture(:category)
+
+ conn = get conn, Routes.category_path(conn, :index_posts, @post_category.slug)
+ assert html_response(conn, 200) =~ "My Post"
+ end
+
+ test "lists all posts for page", %{conn: conn} do
+ fixture(:posts)
+
+ conn = get conn, Routes.blog_page_path(conn, :index_posts, "2")
+ assert html_response(conn, 200)
+ end
+
+ test "shows a page if it is set as the front page", %{conn: conn} do
+ fixture(:front_post)
+
+ conn = get conn, Routes.posts_path(conn, :index)
+ assert html_response(conn, 200) =~ "Page One"
+ end
+ end
+
+ describe "new posts" do
+ test "renders form", %{conn: conn} do
+ conn =
+ as_admin do
+ get conn, Routes.posts_path(conn, :new)
+ end
+ assert html_response(conn, 200) =~ "Posts"
+ end
+
+ test "renders form with post_type", %{conn: conn} do
+ conn =
+ as_admin do
+ get conn, Routes.posts_path(conn, :new), post_type: "page"
+ end
+ assert html_response(conn, 200) =~ "Posts"
+ end
+ end
+
+ describe "create wp_posts" do
+ test "redirects to show when data is valid", %{conn: conn} do
+ conn =
+ as_admin do
+ post conn, Routes.posts_path(conn, :create), post: @create_attrs
+ end
+
+ assert %{id: id} = redirected_params(conn)
+ assert redirected_to(conn) == Routes.posts_path(conn, :show, id)
+
+ conn = get conn, Routes.posts_path(conn, :show, id)
+ assert html_response(conn, 200)
+ end
+
+ test "renders errors when data is invalid", %{conn: conn} do
+ conn =
+ as_admin do
+ post conn, Routes.posts_path(conn, :create), post: @invalid_attrs
+ end
+ assert html_response(conn, 200) =~ "be blank"
+ end
+
+ test "renders errors when data is invalid & post_type is missing", %{conn: conn} do
+ conn =
+ as_admin do
+ post conn, Routes.posts_path(conn, :create), post: @invalid_attrs |> Map.drop([:post_type])
+ end
+ assert html_response(conn, 200) =~ "be blank"
+ end
+ end
+
+ describe "edit posts" do
+ setup [:create_posts]
+
+ test "renders form for editing chosen posts", %{conn: conn, posts: posts} do
+ conn =
+ as_admin do
+ get conn, Routes.posts_path(conn, :edit, posts)
+ end
+ assert html_response(conn, 200) =~ "My Post"
+ end
+ end
+
+ describe "update posts" do
+ setup [:create_posts]
+
+ test "redirects when data is valid", %{conn: conn, posts: posts} do
+ conn =
+ as_admin do
+ put conn, Routes.posts_path(conn, :update, posts), post: @update_attrs
+ end
+ assert redirected_to(conn) == Routes.posts_path(conn, :edit, posts)
+
+ conn = get conn, Routes.posts_path(conn, :show, posts)
+ assert html_response(conn, 200)
+ end
+
+ test "renders errors when data is invalid", %{conn: conn, posts: posts} do
+ conn =
+ as_admin do
+ put conn, Routes.posts_path(conn, :update, posts), post: @invalid_attrs
+ end
+ assert html_response(conn, 200) =~ "be blank"
+ end
+ end
+
+ describe "delete posts" do
+ setup [:create_posts]
+
+ test "deletes chosen posts", %{conn: conn, posts: posts} do
+ conn =
+ as_admin do
+ delete conn, Routes.posts_path(conn, :delete, posts)
+ end
+ assert redirected_to(conn) == Routes.admin_posts_path(conn, :index)
+ assert_error_sent 404, fn ->
+ get conn, Routes.posts_path(conn, :show, posts)
+ end
+ end
+ end
+
+ describe "show a post" do
+ setup [:create_a_post]
+
+ test "shows the post", %{conn: conn, posts: posts} do
+ conn = get conn, Routes.posts_path(conn, :show, posts)
+
+ assert html_response(conn, 200) =~ posts.post_title
+ end
+
+ test "shows the post by ID", %{conn: conn, posts: posts} do
+ conn = get conn, Routes.posts_path(conn, :show, posts.'ID')
+
+ assert html_response(conn, 200) =~ posts.post_title
+ end
+
+ test "shows the post with pagination", %{conn: conn, posts: posts} do
+ conn = get conn, Routes.paged_post_path(conn, :show, posts, "2")
+
+ assert html_response(conn, 200) =~ posts.post_title
+ end
+ end
+
+ describe "show the front post" do
+ test "shows the post if it is the front post", %{conn: conn} do
+ post = fixture(:front_post)
+ conn = get conn, Routes.posts_path(conn, :show, post)
+
+ assert html_response(conn, 200) =~ "Page One"
+ end
+ end
+
+ describe "show the blog_page" do
+ test "shows the post if it is the front post", %{conn: conn} do
+ page = fixture(:blog_page)
+ conn = get conn, Routes.posts_path(conn, :show, page)
+
+ assert html_response(conn, 200) =~ "My Blog Post"
+ end
+ end
+
+ describe "show an attachment post" do
+ test "shows the post", %{conn: conn} do
+ post = fixture(:attachment)
+ conn = get conn, Routes.posts_path(conn, :show, post)
+
+ assert text_response(conn, 200) =~ "my text attachment"
+ end
+ end
+
+ describe "preview a post" do
+ test "shows the post (post method)", %{conn: conn} do
+ conn =
+ as_admin do
+ post conn, Routes.posts_path(conn, :preview, %{"post" => @preview_attrs})
+ end
+
+ assert html_response(conn, 200) =~ @preview_attrs[:post_title]
+ end
+
+ test "shows the post (put method)", %{conn: conn} do
+ conn =
+ as_admin do
+ put conn, Routes.posts_path(conn, :preview, %{"post" => @preview_attrs})
+ end
+
+ assert html_response(conn, 200) =~ @preview_attrs[:post_title]
+ end
+ end
+
+ defp create_a_post(_) do
+ post = fixture(:single_post)
+ {:ok, posts: post}
+ end
+
+ defp create_posts(_) do
+ posts = fixture(:posts)
+ {:ok, posts: posts}
+ end
+end
diff --git a/apps/content/test/content_web/controllers/sitemap_controller_test.exs b/apps/content/test/content_web/controllers/sitemap_controller_test.exs
new file mode 100644
index 00000000..853e0bff
--- /dev/null
+++ b/apps/content/test/content_web/controllers/sitemap_controller_test.exs
@@ -0,0 +1,10 @@
+defmodule Content.SitemapControllerTest do
+ use Content.ConnCase
+
+ describe "index" do
+ test "is the site index", %{conn: conn} do
+ conn = get conn, Routes.sitemap_path(conn, :index)
+ assert html_response(conn, 200) =~ "Site Index"
+ end
+ end
+end
diff --git a/apps/content/test/content_web/views/error_view_test.exs b/apps/content/test/content_web/views/error_view_test.exs
new file mode 100644
index 00000000..ea70eb8a
--- /dev/null
+++ b/apps/content/test/content_web/views/error_view_test.exs
@@ -0,0 +1,14 @@
+defmodule Content.ErrorViewTest do
+ use Content.ConnCase, async: true
+
+ # Bring render/3 and render_to_string/3 for testing custom views
+ import Phoenix.View
+
+ test "renders 404.html" do
+ assert render_to_string(Content.ErrorView, "404.html", []) == "Not Found"
+ end
+
+ test "renders 500.html" do
+ assert render_to_string(Content.ErrorView, "500.html", []) == "Internal Server Error"
+ end
+end
diff --git a/apps/content_web/test/content/views/layout_view_test.exs b/apps/content/test/content_web/views/layout_view_test.exs
similarity index 71%
rename from apps/content_web/test/content/views/layout_view_test.exs
rename to apps/content/test/content_web/views/layout_view_test.exs
index 6298a32e..7d049194 100644
--- a/apps/content_web/test/content/views/layout_view_test.exs
+++ b/apps/content/test/content_web/views/layout_view_test.exs
@@ -1,5 +1,5 @@
-defmodule ContentWeb.LayoutViewTest do
- use ContentWeb.ConnCase, async: true
+defmodule Content.LayoutViewTest do
+ use Content.ConnCase, async: true
# When testing helpers, you may want to import Phoenix.HTML and
# use functions such as safe_to_string() to convert the helper
diff --git a/apps/content/test/content_web/views/page_view_test.exs b/apps/content/test/content_web/views/page_view_test.exs
new file mode 100644
index 00000000..0a4ae804
--- /dev/null
+++ b/apps/content/test/content_web/views/page_view_test.exs
@@ -0,0 +1,3 @@
+defmodule Content.PageViewTest do
+ use Content.ConnCase, async: true
+end
diff --git a/apps/content_web/test/support/channel_case.ex b/apps/content/test/support/channel_case.ex
similarity index 72%
rename from apps/content_web/test/support/channel_case.ex
rename to apps/content/test/support/channel_case.ex
index 83f22d75..8fc1fdb0 100644
--- a/apps/content_web/test/support/channel_case.ex
+++ b/apps/content/test/support/channel_case.ex
@@ -1,4 +1,4 @@
-defmodule ContentWeb.ChannelCase do
+defmodule Content.ChannelCase do
@moduledoc """
This module defines the test case to be used by
channel tests.
@@ -11,7 +11,7 @@ defmodule ContentWeb.ChannelCase do
we enable the SQL sandbox, so changes done to the database
are reverted at the end of every test. If you are using
PostgreSQL, you can even run database tests asynchronously
- by setting `use ContentWeb.ChannelCase, async: true`, although
+ by setting `use Content.ChannelCase, async: true`, although
this option is not recommended for other databases.
"""
@@ -21,18 +21,18 @@ defmodule ContentWeb.ChannelCase do
quote do
# Import conveniences for testing with channels
import Phoenix.ChannelTest
- import ContentWeb.ChannelCase
+ import Content.ChannelCase
# The default endpoint for testing
- @endpoint ContentWeb.Endpoint
+ @endpoint Content.Endpoint
end
end
setup tags do
- # :ok = Ecto.Adapters.SQL.Sandbox.checkout(ContentWeb.Repo)
+ :ok = Ecto.Adapters.SQL.Sandbox.checkout(Content.Repo)
unless tags[:async] do
- # Ecto.Adapters.SQL.Sandbox.mode(ContentWeb.Repo, {:shared, self()})
+ Ecto.Adapters.SQL.Sandbox.mode(Content.Repo, {:shared, self()})
end
:ok
diff --git a/apps/content_web/test/support/conn_case.ex b/apps/content/test/support/conn_case.ex
similarity index 57%
rename from apps/content_web/test/support/conn_case.ex
rename to apps/content/test/support/conn_case.ex
index a9100029..8755c9d3 100644
--- a/apps/content_web/test/support/conn_case.ex
+++ b/apps/content/test/support/conn_case.ex
@@ -1,4 +1,4 @@
-defmodule ContentWeb.ConnCase do
+defmodule Content.ConnCase do
@moduledoc """
This module defines the test case to be used by
tests that require setting up a connection.
@@ -11,7 +11,7 @@ defmodule ContentWeb.ConnCase do
we enable the SQL sandbox, so changes done to the database
are reverted at the end of every test. If you are using
PostgreSQL, you can even run database tests asynchronously
- by setting `use ContentWeb.ConnCase, async: true`, although
+ by setting `use Content.ConnCase, async: true`, although
this option is not recommended for other databases.
"""
@@ -22,20 +22,31 @@ defmodule ContentWeb.ConnCase do
# Import conveniences for testing with connections
import Plug.Conn
import Phoenix.ConnTest
- import ContentWeb.ConnCase
+ import Content.ConnCase
+ import Mock
- alias ContentWeb.Router.Helpers, as: Routes
+ alias Content.Router.Helpers, as: Routes
# The default endpoint for testing
- @endpoint ContentWeb.Endpoint
+ @endpoint Content.Endpoint
+
+ defmacro as_admin(do: expression) do
+ quote do
+ with_mock Content.RequireAuth, [call: fn(conn, _opts) -> conn end] do
+ with_mock Content.RequireAdmin, [call: fn(conn, _opts) -> conn end] do
+ unquote(expression)
+ end
+ end
+ end
+ end
end
end
setup tags do
- # :ok = Ecto.Adapters.SQL.Sandbox.checkout(ContentWeb.Repo)
+ :ok = Ecto.Adapters.SQL.Sandbox.checkout(Content.Repo)
unless tags[:async] do
- # Ecto.Adapters.SQL.Sandbox.mode(ContentWeb.Repo, {:shared, self()})
+ Ecto.Adapters.SQL.Sandbox.mode(Content.Repo, {:shared, self()})
end
{:ok, conn: Phoenix.ConnTest.build_conn()}
diff --git a/apps/content/test/test_helper.exs b/apps/content/test/test_helper.exs
index 95a6f8e0..9ab3ba67 100644
--- a/apps/content/test/test_helper.exs
+++ b/apps/content/test/test_helper.exs
@@ -1,3 +1,2 @@
ExUnit.start()
-
Ecto.Adapters.SQL.Sandbox.mode(Content.Repo, :manual)
diff --git a/apps/content_web/.gitignore b/apps/content_web/.gitignore
deleted file mode 100644
index 3d47f319..00000000
--- a/apps/content_web/.gitignore
+++ /dev/null
@@ -1,34 +0,0 @@
-# The directory Mix will write compiled artifacts to.
-/_build/
-
-# If you run "mix test --cover", coverage assets end up here.
-/cover/
-
-# The directory Mix downloads your dependencies sources to.
-/deps/
-
-# Where 3rd-party dependencies like ExDoc output generated docs.
-/doc/
-
-# Ignore .fetch files in case you like to edit your project deps locally.
-/.fetch
-
-# If the VM crashes, it generates a dump, let's ignore it too.
-erl_crash.dump
-
-# Also ignore archive artifacts (built via "mix archive.build").
-*.ez
-
-# Ignore package tarball (built via "mix hex.build").
-content-*.tar
-
-# If NPM crashes, it generates a log, let's ignore it too.
-npm-debug.log
-
-# The directory NPM downloads your dependencies sources to.
-/assets/node_modules/
-
-# Since we are building assets from assets/,
-# we ignore priv/static. You may want to comment
-# this depending on your deployment strategy.
-/priv/static/
diff --git a/apps/content_web/lib/content_web/application.ex b/apps/content_web/lib/content_web/application.ex
deleted file mode 100644
index ab522066..00000000
--- a/apps/content_web/lib/content_web/application.ex
+++ /dev/null
@@ -1,30 +0,0 @@
-defmodule ContentWeb.Application do
- # See https://hexdocs.pm/elixir/Application.html
- # for more information on OTP Applications
- @moduledoc false
-
- use Application
-
- def start(_type, _args) do
- children = [
- # Start the Telemetry supervisor
- ContentWeb.Telemetry,
- # Start the Endpoint (http/https)
- ContentWeb.Endpoint
- # Start a worker by calling: ContentWeb.Worker.start_link(arg)
- # {ContentWeb.Worker, arg}
- ]
-
- # See https://hexdocs.pm/elixir/Supervisor.html
- # for other strategies and supported options
- opts = [strategy: :one_for_one, name: ContentWeb.Supervisor]
- Supervisor.start_link(children, opts)
- end
-
- # Tell Phoenix to update the endpoint configuration
- # whenever the application is updated.
- def config_change(changed, _new, removed) do
- ContentWeb.Endpoint.config_change(changed, removed)
- :ok
- end
-end
diff --git a/apps/content_web/lib/content_web/router.ex b/apps/content_web/lib/content_web/router.ex
deleted file mode 100644
index 20b4d2a3..00000000
--- a/apps/content_web/lib/content_web/router.ex
+++ /dev/null
@@ -1,22 +0,0 @@
-defmodule ContentWeb.Router do
- use ContentWeb, :router
-
- pipeline :browser do
- plug :accepts, ["html"]
- plug :fetch_session
- plug :fetch_flash
- plug :protect_from_forgery
- plug :put_secure_browser_headers
- end
-
- pipeline :api do
- plug :accepts, ["json"]
- end
-
- scope "/", ContentWeb do
- pipe_through :browser
-
- get "/posts/:id", PostsController, :show
- get "/:id", PageController, :show
- end
-end
diff --git a/apps/content_web/lib/content_web/views/layout_view.ex b/apps/content_web/lib/content_web/views/layout_view.ex
deleted file mode 100644
index 705e4cd2..00000000
--- a/apps/content_web/lib/content_web/views/layout_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule ContentWeb.LayoutView do
- use ContentWeb, :view
-end
diff --git a/apps/content_web/lib/content_web/views/page_view.ex b/apps/content_web/lib/content_web/views/page_view.ex
deleted file mode 100644
index 29304db7..00000000
--- a/apps/content_web/lib/content_web/views/page_view.ex
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule ContentWeb.PageView do
- use ContentWeb, :view
-end
diff --git a/apps/content_web/mix.exs b/apps/content_web/mix.exs
deleted file mode 100644
index e159aeb4..00000000
--- a/apps/content_web/mix.exs
+++ /dev/null
@@ -1,67 +0,0 @@
-defmodule ContentWeb.MixProject do
- use Mix.Project
-
- def project do
- [
- app: :content_web,
- version: "0.1.0",
- build_path: "../../_build",
- config_path: "../../config/config.exs",
- deps_path: "../../deps",
- lockfile: "../../mix.lock",
- elixir: "~> 1.7",
- elixirc_paths: elixirc_paths(Mix.env()),
- compilers: [:phoenix, :gettext] ++ Mix.compilers(),
- start_permanent: Mix.env() == :prod,
- aliases: aliases(),
- deps: deps(),
- test_coverage: [tool: ExCoveralls],
- ]
- end
-
- # Configuration for the OTP application.
- #
- # Type `mix help compile.app` for more information.
- def application do
- [
- mod: {ContentWeb.Application, []},
- extra_applications: [:logger, :runtime_tools]
- ]
- end
-
- # Specifies which paths to compile per environment.
- defp elixirc_paths(:test), do: ["lib", "test/support"]
- defp elixirc_paths(_), do: ["lib"]
-
- # Specifies your project dependencies.
- #
- # Type `mix help deps` for examples and options.
- defp deps do
- [
- {:auth_web, in_umbrella: true},
- {:core, in_umbrella: true},
- {:excoveralls, "~> 0.10", only: [:dev, :test]},
- {:phoenix, "~> 1.5.3"},
- {:phoenix_ecto, "~> 4.0"},
- {:phoenix_html, "~> 2.11"},
- {:phoenix_live_reload, "~> 1.2", only: :dev},
- {:phoenix_live_dashboard, "~> 0.2.0"},
- {:telemetry_metrics, "~> 0.4"},
- {:telemetry_poller, "~> 0.4"},
- {:gettext, "~> 0.11"},
- {:jason, "~> 1.0"},
- {:plug_cowboy, "~> 2.0"}
- ]
- end
-
- # Aliases are shortcuts or tasks specific to the current project.
- #
- # See the documentation for `Mix` for more info on aliases.
- defp aliases do
- [
- setup: ["deps.get", "cmd npm install --prefix assets"],
- # test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
- "ecto.migrate": [],
- ]
- end
-end
diff --git a/apps/content_web/priv/gettext/en/LC_MESSAGES/errors.po b/apps/content_web/priv/gettext/en/LC_MESSAGES/errors.po
deleted file mode 100644
index a589998c..00000000
--- a/apps/content_web/priv/gettext/en/LC_MESSAGES/errors.po
+++ /dev/null
@@ -1,97 +0,0 @@
-## `msgid`s in this file come from POT (.pot) files.
-##
-## Do not add, change, or remove `msgid`s manually here as
-## they're tied to the ones in the corresponding POT file
-## (with the same domain).
-##
-## Use `mix gettext.extract --merge` or `mix gettext.merge`
-## to merge POT files into PO files.
-msgid ""
-msgstr ""
-"Language: en\n"
-
-## From Ecto.Changeset.cast/4
-msgid "can't be blank"
-msgstr ""
-
-## From Ecto.Changeset.unique_constraint/3
-msgid "has already been taken"
-msgstr ""
-
-## From Ecto.Changeset.put_change/3
-msgid "is invalid"
-msgstr ""
-
-## From Ecto.Changeset.validate_acceptance/3
-msgid "must be accepted"
-msgstr ""
-
-## From Ecto.Changeset.validate_format/3
-msgid "has invalid format"
-msgstr ""
-
-## From Ecto.Changeset.validate_subset/3
-msgid "has an invalid entry"
-msgstr ""
-
-## From Ecto.Changeset.validate_exclusion/3
-msgid "is reserved"
-msgstr ""
-
-## From Ecto.Changeset.validate_confirmation/3
-msgid "does not match confirmation"
-msgstr ""
-
-## From Ecto.Changeset.no_assoc_constraint/3
-msgid "is still associated with this entry"
-msgstr ""
-
-msgid "are still associated with this entry"
-msgstr ""
-
-## From Ecto.Changeset.validate_length/3
-msgid "should be %{count} character(s)"
-msgid_plural "should be %{count} character(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should have %{count} item(s)"
-msgid_plural "should have %{count} item(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should be at least %{count} character(s)"
-msgid_plural "should be at least %{count} character(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should have at least %{count} item(s)"
-msgid_plural "should have at least %{count} item(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should be at most %{count} character(s)"
-msgid_plural "should be at most %{count} character(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should have at most %{count} item(s)"
-msgid_plural "should have at most %{count} item(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-## From Ecto.Changeset.validate_number/3
-msgid "must be less than %{number}"
-msgstr ""
-
-msgid "must be greater than %{number}"
-msgstr ""
-
-msgid "must be less than or equal to %{number}"
-msgstr ""
-
-msgid "must be greater than or equal to %{number}"
-msgstr ""
-
-msgid "must be equal to %{number}"
-msgstr ""
diff --git a/apps/content_web/priv/gettext/errors.pot b/apps/content_web/priv/gettext/errors.pot
deleted file mode 100644
index 39a220be..00000000
--- a/apps/content_web/priv/gettext/errors.pot
+++ /dev/null
@@ -1,95 +0,0 @@
-## This is a PO Template file.
-##
-## `msgid`s here are often extracted from source code.
-## Add new translations manually only if they're dynamic
-## translations that can't be statically extracted.
-##
-## Run `mix gettext.extract` to bring this file up to
-## date. Leave `msgstr`s empty as changing them here has no
-## effect: edit them in PO (`.po`) files instead.
-
-## From Ecto.Changeset.cast/4
-msgid "can't be blank"
-msgstr ""
-
-## From Ecto.Changeset.unique_constraint/3
-msgid "has already been taken"
-msgstr ""
-
-## From Ecto.Changeset.put_change/3
-msgid "is invalid"
-msgstr ""
-
-## From Ecto.Changeset.validate_acceptance/3
-msgid "must be accepted"
-msgstr ""
-
-## From Ecto.Changeset.validate_format/3
-msgid "has invalid format"
-msgstr ""
-
-## From Ecto.Changeset.validate_subset/3
-msgid "has an invalid entry"
-msgstr ""
-
-## From Ecto.Changeset.validate_exclusion/3
-msgid "is reserved"
-msgstr ""
-
-## From Ecto.Changeset.validate_confirmation/3
-msgid "does not match confirmation"
-msgstr ""
-
-## From Ecto.Changeset.no_assoc_constraint/3
-msgid "is still associated with this entry"
-msgstr ""
-
-msgid "are still associated with this entry"
-msgstr ""
-
-## From Ecto.Changeset.validate_length/3
-msgid "should be %{count} character(s)"
-msgid_plural "should be %{count} character(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should have %{count} item(s)"
-msgid_plural "should have %{count} item(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should be at least %{count} character(s)"
-msgid_plural "should be at least %{count} character(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should have at least %{count} item(s)"
-msgid_plural "should have at least %{count} item(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should be at most %{count} character(s)"
-msgid_plural "should be at most %{count} character(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "should have at most %{count} item(s)"
-msgid_plural "should have at most %{count} item(s)"
-msgstr[0] ""
-msgstr[1] ""
-
-## From Ecto.Changeset.validate_number/3
-msgid "must be less than %{number}"
-msgstr ""
-
-msgid "must be greater than %{number}"
-msgstr ""
-
-msgid "must be less than or equal to %{number}"
-msgstr ""
-
-msgid "must be greater than or equal to %{number}"
-msgstr ""
-
-msgid "must be equal to %{number}"
-msgstr ""
diff --git a/apps/content_web/test/content/views/error_view_test.exs b/apps/content_web/test/content/views/error_view_test.exs
deleted file mode 100644
index 22aa1da0..00000000
--- a/apps/content_web/test/content/views/error_view_test.exs
+++ /dev/null
@@ -1,14 +0,0 @@
-defmodule ContentWeb.ErrorViewTest do
- use ContentWeb.ConnCase, async: true
-
- # Bring render/3 and render_to_string/3 for testing custom views
- import Phoenix.View
-
- test "renders 404.html" do
- assert render_to_string(ContentWeb.ErrorView, "404.html", []) == "Not Found"
- end
-
- test "renders 500.html" do
- assert render_to_string(ContentWeb.ErrorView, "500.html", []) == "Internal Server Error"
- end
-end
diff --git a/apps/content_web/test/content/views/page_view_test.exs b/apps/content_web/test/content/views/page_view_test.exs
deleted file mode 100644
index 2ad2f579..00000000
--- a/apps/content_web/test/content/views/page_view_test.exs
+++ /dev/null
@@ -1,3 +0,0 @@
-defmodule ContentWeb.PageViewTest do
- use ContentWeb.ConnCase, async: true
-end
diff --git a/apps/content_web/test/test_helper.exs b/apps/content_web/test/test_helper.exs
deleted file mode 100644
index ab36f0a3..00000000
--- a/apps/content_web/test/test_helper.exs
+++ /dev/null
@@ -1,2 +0,0 @@
-ExUnit.start()
-# Ecto.Adapters.SQL.Sandbox.mode(ContentWeb.Repo, :manual)
diff --git a/apps/core/lib/core_web/router.ex b/apps/core/lib/core_web/router.ex
index 81149ef9..eeeae6f9 100644
--- a/apps/core/lib/core_web/router.ex
+++ b/apps/core/lib/core_web/router.ex
@@ -27,7 +27,7 @@ defmodule CoreWeb.Router do
forward "/sent_emails", Bamboo.SentEmailViewerPlug
end
- scope "/", ContentWeb do
+ scope "/", Content do
pipe_through :browser
get "/", PageController, :index
diff --git a/config/config.exs b/config/config.exs
index f7bd051f..dfb48945 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -9,7 +9,7 @@ config :auth_web,
generators: [context_app: :auth]
config :auth_web, :pow,
- user: Auth.Users.User,
+ user: Auth.User,
repo: Auth.Repo,
extensions: [PowEmailConfirmation],
controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks,
@@ -25,14 +25,21 @@ config :auth_web, AuthWeb.Endpoint,
pubsub_server: AuthWeb.PubSub,
live_view: [signing_salt: "AwljJYaY"]
+config :content, Content.Endpoint,
+ url: [host: "localhost"],
+ secret_key_base: "kNJbLKCmuZYSK99S55+DmirA2TlmOxzs/xz3xnlXtOhQCoBMmYRabaRLTXkcsw5d",
+ render_errors: [view: Content.ErrorView, accepts: ~w(html json), layout: false],
+ pubsub_server: Content.PubSub,
+ live_view: [signing_salt: "Nb8V5NUr"]
+
config :core,
- router_forwards: [{ContentWeb.Router, "/pages"}, {AuthWeb.Router, "/auth"}],
+ router_forwards: [{Content.Router, "/pages"}, {AuthWeb.Router, "/auth"}],
email_from: "example@example.org"
-config :content_web,
+config :content,
generators: [context_app: false]
-config :content_web, ContentWeb.Endpoint, server: false
+config :content, Content.Endpoint, server: false
config :auth_web, AuthWeb.Endpoint, server: false
import_config "../apps/*/config/config.exs"
diff --git a/config/dev.exs b/config/dev.exs
index ba6d3c1a..bd4e3fb7 100644
--- a/config/dev.exs
+++ b/config/dev.exs
@@ -4,7 +4,7 @@ use Mix.Config
config :auth, Auth.Repo,
username: "postgres",
password: "postgres",
- database: "auth_dev",
+ database: "legendary_dev",
hostname: "localhost",
show_sensitive_data_on_connection_error: true,
pool_size: 10
@@ -71,7 +71,7 @@ config :auth_web, AuthWeb.Endpoint,
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with webpack to recompile .js and .css sources.
-config :content_web, ContentWeb.Endpoint,
+config :content, Content.Endpoint,
http: [port: 4000],
debug_errors: true,
code_reloader: true,
@@ -111,7 +111,7 @@ config :content_web, ContentWeb.Endpoint,
# different ports.
# Watch static and templates for browser reloading.
-config :content_web, ContentWeb.Endpoint,
+config :content, Content.Endpoint,
live_reload: [
patterns: [
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
diff --git a/config/prod.exs b/config/prod.exs
index 075fbb5e..6bd0b13d 100644
--- a/config/prod.exs
+++ b/config/prod.exs
@@ -56,7 +56,7 @@ config :auth_web, AuthWeb.Endpoint,
# manifest is generated by the `mix phx.digest` task,
# which you should run after static files are built and
# before starting your production server.
-config :content_web, ContentWeb.Endpoint,
+config :content, Content.Endpoint,
url: [host: "example.com", port: 80],
cache_static_manifest: "priv/static/cache_manifest.json"
@@ -65,7 +65,7 @@ config :content_web, ContentWeb.Endpoint,
# To get SSL working, you will need to add the `https` key
# to the previous section and set your `:url` port to 443:
#
-# config :content_web, ContentWeb.Endpoint,
+# config :content, Content.Endpoint,
# ...
# url: [host: "example.com", port: 443],
# https: [
@@ -89,7 +89,7 @@ config :content_web, ContentWeb.Endpoint,
# We also recommend setting `force_ssl` in your endpoint, ensuring
# no data is ever sent via http, always redirecting to https:
#
-# config :content_web, ContentWeb.Endpoint,
+# config :content, Content.Endpoint,
# force_ssl: [hsts: true]
#
# Check `Plug.SSL` for all available options in `force_ssl`.
diff --git a/config/test.exs b/config/test.exs
index 09062c88..88e3d8d0 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -8,10 +8,16 @@ use Mix.Config
config :auth, Auth.Repo,
username: "postgres",
password: "postgres",
- database: "auth_test#{System.get_env("MIX_TEST_PARTITION")}",
+ database: "legendary_test#{System.get_env("MIX_TEST_PARTITION")}",
hostname: System.get_env("DATABASE_URL") || "localhost",
pool: Ecto.Adapters.SQL.Sandbox
+config :content, Content.Repo,
+ username: "postgres",
+ password: "postgres",
+ database: "legendary_test#{System.get_env("MIX_TEST_PARTITION")}",
+ hostname: System.get_env("DATABASE_URL") || "localhost",
+ pool: Ecto.Adapters.SQL.Sandbox
# We don't run a server during test. If one is required,
# you can enable the server option below.
@@ -21,6 +27,6 @@ config :auth_web, AuthWeb.Endpoint,
# We don't run a server during test. If one is required,
# you can enable the server option below.
-config :content_web, ContentWeb.Endpoint,
+config :content, Content.Endpoint,
http: [port: 4002],
server: false
diff --git a/script/cibuild b/script/cibuild
index b9a3892a..d2b81bf3 100755
--- a/script/cibuild
+++ b/script/cibuild
@@ -5,6 +5,9 @@ set -e
mix local.hex --force
mix local.rebar --force
+
mix deps.get
+mix ecto.create
+mix ecto.migrate
mix test