From 4b4ecec69381129756f5506443a7f6a2e28fe126 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 26 Jan 2018 11:02:11 +0100 Subject: [PATCH] Introduce avatar and banner and fetch Gravatar to fill avatar during registration Signed-off-by: Thomas Citharel typo Signed-off-by: Thomas Citharel Rename avatar to avatar_url, same with header. Add a comment to explain why the tweak with HTTPoison and TLS1.2 Signed-off-by: Thomas Citharel Rename avatar to avatar_url Signed-off-by: Thomas Citharel rename old avatar properties in front-end to avatar_url Signed-off-by: Thomas Citharel fix change gravatar from ?d= to ?default= Signed-off-by: Thomas Citharel reorganize aliases and imports Signed-off-by: Thomas Citharel set avatar url only when gravatar exists, add a test for that case Signed-off-by: Thomas Citharel --- js/src/components/Account/Account.vue | 4 ++-- js/src/components/Event/Event.vue | 8 +++---- js/src/components/Group/Group.vue | 8 +++---- lib/eventos/accounts/account.ex | 6 ++++-- lib/eventos/accounts/accounts.ex | 21 ++++++++++++++++--- lib/eventos/accounts/user.ex | 7 ++++--- lib/eventos_web/views/account_view.ex | 4 ++++ mix.exs | 7 +++++-- mix.lock | 8 +++++-- ...94416_add_avatar_and_banner_to_account.exs | 10 +++++++++ .../controllers/user_controller_test.exs | 11 +++++++++- 11 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 priv/repo/migrations/20180126094416_add_avatar_and_banner_to_account.exs diff --git a/js/src/components/Account/Account.vue b/js/src/components/Account/Account.vue index 4f30a6eb..f320450e 100644 --- a/js/src/components/Account/Account.vue +++ b/js/src/components/Account/Account.vue @@ -20,13 +20,13 @@
-
diff --git a/js/src/components/Event/Event.vue b/js/src/components/Event/Event.vue index 86c79069..3fccb339 100644 --- a/js/src/components/Event/Event.vue +++ b/js/src/components/Event/Event.vue @@ -42,13 +42,13 @@ - @@ -57,13 +57,13 @@ - diff --git a/js/src/components/Group/Group.vue b/js/src/components/Group/Group.vue index d531976d..9fc26840 100644 --- a/js/src/components/Group/Group.vue +++ b/js/src/components/Group/Group.vue @@ -20,13 +20,13 @@
- @@ -77,13 +77,13 @@ stars - diff --git a/lib/eventos/accounts/account.ex b/lib/eventos/accounts/account.ex index 18dcdfd1..70a85a5a 100644 --- a/lib/eventos/accounts/account.ex +++ b/lib/eventos/accounts/account.ex @@ -18,6 +18,8 @@ defmodule Eventos.Accounts.Account do field :uri, :string field :url, :string field :username, :string + field :avatar_url, :string + field :banner_url, :string has_many :organized_events, Event, [foreign_key: :organizer_account_id] many_to_many :groups, Group, join_through: Member has_many :group_request, Request @@ -29,14 +31,14 @@ defmodule Eventos.Accounts.Account do @doc false def changeset(%Account{} = account, attrs) do account - |> cast(attrs, [:username, :domain, :display_name, :description, :private_key, :public_key, :suspended, :uri, :url]) + |> cast(attrs, [:username, :domain, :display_name, :description, :private_key, :public_key, :suspended, :uri, :url, :avatar_url, :banner_url]) |> validate_required([:username, :public_key, :suspended, :uri, :url]) |> unique_constraint(:username, name: :accounts_username_domain_index) end def registration_changeset(%Account{} = account, attrs) do account - |> cast(attrs, [:username, :domain, :display_name, :description, :private_key, :public_key, :suspended, :uri, :url]) + |> cast(attrs, [:username, :domain, :display_name, :description, :private_key, :public_key, :suspended, :uri, :url, :avatar_url, :banner_url]) |> validate_required([:username, :public_key, :suspended, :uri, :url]) |> unique_constraint(:username) end diff --git a/lib/eventos/accounts/accounts.ex b/lib/eventos/accounts/accounts.ex index 07685e42..2a5e3909 100644 --- a/lib/eventos/accounts/accounts.ex +++ b/lib/eventos/accounts/accounts.ex @@ -4,8 +4,9 @@ defmodule Eventos.Accounts do """ import Ecto.Query, warn: false - alias Eventos.Repo + import Exgravatar + alias Eventos.Repo alias Eventos.Accounts.Account @doc """ @@ -173,6 +174,19 @@ defmodule Eventos.Accounts do end end + @doc """ + Fetch gravatar url for email and set it as avatar if it exists + """ + defp gravatar(email) do + url = gravatar_url(email, default: "404") + case HTTPoison.get(url, [], [ssl: [{:versions, [:'tlsv1.2']}]]) do # See https://github.com/edgurgel/httpoison#note-about-broken-ssl-in-erlang-19 + {:ok, %HTTPoison.Response{status_code: 200}} -> + url + _ -> # User doesn't have a gravatar email, or other issues + nil + end + end + @doc """ Register user """ @@ -180,13 +194,15 @@ defmodule Eventos.Accounts do {:ok, {privkey, pubkey}} = RsaEx.generate_keypair("4096") + avatar = gravatar(email) account = Eventos.Accounts.Account.registration_changeset(%Eventos.Accounts.Account{}, %{ username: username, domain: nil, private_key: privkey, public_key: pubkey, uri: "h", - url: "h" + url: "h", + avatar_url: avatar, }) user = Eventos.Accounts.User.registration_changeset(%Eventos.Accounts.User{}, %{ @@ -207,7 +223,6 @@ defmodule Eventos.Accounts do end end - @doc """ Creates a user. diff --git a/lib/eventos/accounts/user.ex b/lib/eventos/accounts/user.ex index aa294d6b..1c39d77f 100644 --- a/lib/eventos/accounts/user.ex +++ b/lib/eventos/accounts/user.ex @@ -6,7 +6,6 @@ defmodule Eventos.Accounts.User do import Ecto.Changeset alias Eventos.Accounts.{Account, User} - schema "users" do field :email, :string field :password_hash, :string @@ -31,9 +30,12 @@ defmodule Eventos.Accounts.User do |> changeset(params) |> cast(params, ~w(password)a, []) |> validate_length(:password, min: 6, max: 100) - |> hash_password + |> hash_password() end + @doc """ + Hash password when it's changed + """ defp hash_password(changeset) do case changeset do %Ecto.Changeset{valid?: true, @@ -45,5 +47,4 @@ defmodule Eventos.Accounts.User do changeset end end - end diff --git a/lib/eventos_web/views/account_view.ex b/lib/eventos_web/views/account_view.ex index e6c49cae..30d44bcb 100644 --- a/lib/eventos_web/views/account_view.ex +++ b/lib/eventos_web/views/account_view.ex @@ -27,6 +27,8 @@ defmodule EventosWeb.AccountView do suspended: account.suspended, uri: account.uri, url: account.url, + avatar_url: account.avatar_url, + banner_url: account.banner_url, } end @@ -40,6 +42,8 @@ defmodule EventosWeb.AccountView do suspended: account.suspended, uri: account.uri, url: account.url, + avatar_url: account.avatar_url, + banner_url: account.banner_url, organized_events: render_many(account.organized_events, EventView, "event_simple.json") } end diff --git a/mix.exs b/mix.exs index 23ffb509..ebaebac3 100644 --- a/mix.exs +++ b/mix.exs @@ -44,7 +44,6 @@ defmodule Eventos.Mixfile do {:phoenix_ecto, "~> 3.2"}, {:postgrex, ">= 0.0.0"}, {:phoenix_html, "~> 2.10"}, - {:phoenix_live_reload, "~> 1.0", only: :dev}, {:gettext, "~> 0.11"}, {:cowboy, "~> 1.0"}, {:guardian, "~> 1.0"}, @@ -58,10 +57,14 @@ defmodule Eventos.Mixfile do {:geo_postgis, "~> 1.0"}, {:timex, "~> 3.0"}, {:timex_ecto, "~> 3.0"}, + {:icalendar, "~> 0.6"}, + {:exgravatar, "~> 2.0.1"}, + {:httpoison, "~> 1.0"}, + # Dev and test dependencies + {:phoenix_live_reload, "~> 1.0", only: :dev}, {:ex_machina, "~> 2.1", only: :test}, {:credo, "~> 0.8", only: [:dev, :test], runtime: false}, {:excoveralls, "~> 0.8", only: :test}, - {:icalendar, "~> 0.6"}, {:ex_doc, "~> 0.16", only: :dev, runtime: false}, {:mix_test_watch, "~> 0.5", only: :dev, runtime: false}, {:ex_unit_notifier, "~> 0.1", only: :test} diff --git a/mix.lock b/mix.lock index 7447b805..2e7cc9ae 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,5 @@ -%{"argon2_elixir": {:hex, :argon2_elixir, "1.2.14", "0fc4bfbc1b7e459954987d3d2f3836befd72d63f3a355e3978f5005dd6e80816", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, +%{ + "argon2_elixir": {:hex, :argon2_elixir, "1.2.14", "0fc4bfbc1b7e459954987d3d2f3836befd72d63f3a355e3978f5005dd6e80816", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [], [], "hexpm"}, "certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [], [], "hexpm"}, @@ -21,6 +22,7 @@ "ex_machina": {:hex, :ex_machina, "2.1.0", "4874dc9c78e7cf2d429f24dc3c4005674d4e4da6a08be961ffccc08fb528e28b", [], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, "ex_unit_notifier": {:hex, :ex_unit_notifier, "0.1.4", "36a2dcab829f506e01bf17816590680dd1474407926d43e64c1263e627c364b8", [], [], "hexpm"}, "excoveralls": {:hex, :excoveralls, "0.8.0", "99d2691d3edf8612f128be3f9869c4d44b91c67cec92186ce49470ae7a7404cf", [], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "exgravatar": {:hex, :exgravatar, "2.0.1", "66d595c7d63dd6bbac442c5542a724375ae29144059c6fe093e61553850aace4", [], [], "hexpm"}, "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, "file_system": {:hex, :file_system, "0.2.2", "7f1e9de4746f4eb8a4ca8f2fbab582d84a4e40fa394cce7bfcb068b988625b06", [:mix], [], "hexpm"}, "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [], [], "hexpm"}, @@ -30,6 +32,7 @@ "guardian": {:hex, :guardian, "1.0.1", "db0fbaf571c3b874785818b7272eaf5f1ed97a2f9b1f8bc5dc8b0fb8f8f7bb06", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}, {:uuid, ">= 1.1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm"}, "guardian_db": {:hex, :guardian_db, "1.1.0", "45ab94206cce38f7443dc27de6dc52966ccbdeff65ca1b1f11a6d8f3daceb556", [], [{:ecto, "~> 2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm"}, "hackney": {:hex, :hackney, "1.10.1", "c38d0ca52ea80254936a32c45bb7eb414e7a96a521b4ce76d00a69753b157f21", [], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "httpoison": {:hex, :httpoison, "1.0.0", "1f02f827148d945d40b24f0b0a89afe40bfe037171a6cf70f2486976d86921cd", [], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "icalendar": {:hex, :icalendar, "0.6.0", "0e30054b234752fa1ec3e2b928101f8c98f70067766590360d7790b41faab315", [], [{:timex, "~> 3.0", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm"}, "idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, "jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, @@ -57,4 +60,5 @@ "timex_ecto": {:hex, :timex_ecto, "3.2.1", "461140751026e1ca03298fab628f78ab189e78784175f5e301eefa034ee530aa", [], [{:ecto, "~> 2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:timex, "~> 3.1", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm"}, "tzdata": {:hex, :tzdata, "0.5.14", "56f05ea3dd87db946966ab3c7168c0b35025c7ee0e9b4fc130a04631f5611eb1", [], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [], [], "hexpm"}, - "uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], [], "hexpm"}} + "uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], [], "hexpm"}, +} diff --git a/priv/repo/migrations/20180126094416_add_avatar_and_banner_to_account.exs b/priv/repo/migrations/20180126094416_add_avatar_and_banner_to_account.exs new file mode 100644 index 00000000..b0ddf1d1 --- /dev/null +++ b/priv/repo/migrations/20180126094416_add_avatar_and_banner_to_account.exs @@ -0,0 +1,10 @@ +defmodule Eventos.Repo.Migrations.AddAvatarAndBannerToAccount do + use Ecto.Migration + + def change do + alter table(:accounts) do + add :avatar_url, :string, null: true + add :banner_url, :string, null: true + end + end +end diff --git a/test/eventos_web/controllers/user_controller_test.exs b/test/eventos_web/controllers/user_controller_test.exs index 2112df86..ceca47fe 100644 --- a/test/eventos_web/controllers/user_controller_test.exs +++ b/test/eventos_web/controllers/user_controller_test.exs @@ -32,14 +32,23 @@ defmodule EventosWeb.UserControllerTest do describe "create user" do test "renders user when data is valid", %{conn: conn} do conn = post conn, user_path(conn, :create), @create_attrs - assert %{"user" => %{"id" => id}} = json_response(conn, 201) + assert %{"user" => %{"id" => id, "account" => %{"avatar_url" => avatar_url}}} = json_response(conn, 201) assert id > 0 + assert avatar_url == nil end test "renders errors when data is invalid", %{conn: conn} do conn = post conn, user_path(conn, :create), @invalid_attrs assert json_response(conn, 400)["msg"] != %{} end + + test "renders user with avatar when email is valid", %{conn: conn} do + attrs = %{email: "contact@framasoft.org", password: "some password_hash", username: "framasoft"} + conn = post conn, user_path(conn, :create), attrs + assert %{"user" => %{"id" => id, "account" => %{"avatar_url" => avatar_url}}} = json_response(conn, 201) + assert id > 0 + assert avatar_url == "https://secure.gravatar.com/avatar/68b2910a6bb84a482d920e1057533100?default=404" + end end # describe "update user" do