Introduce avatar and banner and fetch Gravatar to fill avatar during registration

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

typo

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

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 <tcit@tcit.fr>

Rename avatar to avatar_url

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

rename old avatar properties in front-end to avatar_url

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

fix change gravatar from ?d= to ?default=

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

reorganize aliases and imports

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

set avatar url only when gravatar exists, add a test for that case

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2018-01-26 11:02:11 +01:00
parent 35492570de
commit 4b4ecec693
11 changed files with 71 additions and 23 deletions

View file

@ -20,13 +20,13 @@
<v-spacer></v-spacer>
<div class="text-xs-center">
<v-avatar size="125px">
<img v-if="!account.avatarRemoteUrl"
<img v-if="!account.avatar_url"
class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/"
>
<img v-else
class="img-circle elevation-7 mb-1"
:src="account.avatarRemoteUrl"
:src="account.avatar_url"
>
</v-avatar>
</div>

View file

@ -42,13 +42,13 @@
<v-flex xs2>
<router-link :to="{name: 'Account', params: {'id': event.organizer.id}}">
<v-avatar size="75px">
<img v-if="!event.organizer.avatarRemoteUrl"
<img v-if="!event.organizer.avatar_url"
class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/"
>
<img v-else
class="img-circle elevation-7 mb-1"
:src="event.organizer.avatarRemoteUrl"
:src="event.organizer.avatar_url"
>
</v-avatar>
</router-link>
@ -57,13 +57,13 @@
<v-flex xs2 v-for="account in event.participants" :key="account.id">
<router-link :to="{name: 'Account', params: {'id': account.id}}">
<v-avatar size="75px">
<img v-if="!account.avatarRemoteUrl"
<img v-if="!account.avatar_url"
class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/"
>
<img v-else
class="img-circle elevation-7 mb-1"
:src="account.avatarRemoteUrl"
:src="account.avatar_url"
>
</v-avatar>
</router-link>

View file

@ -20,13 +20,13 @@
<v-spacer></v-spacer>
<div class="text-xs-center">
<v-avatar size="125px">
<img v-if="!group.avatarRemoteUrl"
<img v-if="!group.avatar_url"
class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/"
>
<img v-else
class="img-circle elevation-7 mb-1"
:src="group.avatarRemoteUrl"
:src="group.avatar_url"
>
</v-avatar>
<v-card-title class="pl-5 pt-5">
@ -77,13 +77,13 @@
<v-badge overlap>
<span slot="badge" v-if="member.role == 3"><v-icon>stars</v-icon></span>
<v-avatar size="75px">
<img v-if="!member.account.avatarRemoteUrl"
<img v-if="!member.account.avatar_url"
class="img-circle elevation-7 mb-1"
src="http://lorempixel.com/125/125/"
>
<img v-else
class="img-circle elevation-7 mb-1"
:src="member.account.avatarRemoteUrl"
:src="member.account.avatar_url"
>
</v-avatar>
</v-badge>

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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}

View file

@ -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"},
}

View file

@ -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

View file

@ -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