5 Commits

Author SHA1 Message Date
7813738f91 add json data export
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-20 19:15:08 -05:00
c1337ebc10 fix formatting in note/context/step contents 2022-12-20 18:36:56 -05:00
59283a0217 fix overflow on note/contexts/step contents
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 23:16:12 -05:00
634891ee73 Added translation using Weblate (German)
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-20 03:35:57 +00:00
571f6fffdb improve tagging logic
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-19 22:34:00 -05:00
35 changed files with 401 additions and 160 deletions

View File

@ -1,7 +1,15 @@
# v0.1.6
- fix formatting in note/context/step contents
- add json export for data
# v0.1.5
- fix overflow on note/contexts/step contents
# v0.1.4 # v0.1.4
- fix docker-compose - fix docker-compose
- fix newlines in notes/context/step contents - fix newlines in note/context/step contents
- fix user invite page - fix user invite page
- improve tagging logic
# v0.1.3 # v0.1.3
- backlink to other notes in notes - backlink to other notes in notes

10
de.tbx Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0"?>
<!DOCTYPE martif PUBLIC "ISO 12200:1999A//DTD MARTIF core (DXFcdV04)//EN" "TBXcdv04.dtd">
<martif type="TBX">
<martifHeader>
<fileDesc>
<sourceDesc><p>Translate Toolkit</p></sourceDesc>
</fileDesc>
</martifHeader>
<text><body></body></text>
</martif>

View File

@ -9,6 +9,16 @@ defmodule Memex.Accounts.User do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.Invites.Invite alias Memex.Invites.Invite
@derive {Jason.Encoder,
only: [
:id,
:email,
:confirmed_at,
:role,
:locale,
:inserted_at,
:updated_at
]}
@derive {Inspect, except: [:password]} @derive {Inspect, except: [:password]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id

View File

@ -4,7 +4,6 @@ defmodule Memex.Contexts do
""" """
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Ecto.Changeset
alias Memex.{Accounts.User, Contexts.Context, Repo} alias Memex.{Accounts.User, Contexts.Context, Repo}
@doc """ @doc """
@ -229,18 +228,4 @@ defmodule Memex.Contexts do
def change_context(%Context{} = context, attrs \\ %{}, user) do def change_context(%Context{} = context, attrs \\ %{}, user) do
context |> Context.update_changeset(attrs, user) context |> Context.update_changeset(attrs, user)
end end
@doc """
Gets a canonical string representation of the `:tags` field for a Note
"""
@spec get_tags_string(Context.t() | Context.changeset() | [String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
def get_tags_string(%Context{tags: tags}), do: tags |> get_tags_string()
def get_tags_string(%Changeset{} = changeset) do
changeset
|> Changeset.get_field(:tags)
|> get_tags_string()
end
end end

View File

@ -9,6 +9,15 @@ defmodule Memex.Contexts.Context do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Repo} alias Memex.{Accounts.User, Repo}
@derive {Jason.Encoder,
only: [
:slug,
:content,
:tags,
:visibility,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "contexts" do schema "contexts" do
@ -27,7 +36,7 @@ defmodule Memex.Contexts.Context do
slug: slug(), slug: slug(),
content: String.t(), content: String.t(),
tags: [String.t()] | nil, tags: [String.t()] | nil,
tags_string: String.t(), tags_string: String.t() | nil,
visibility: :public | :private | :unlisted, visibility: :public | :private | :unlisted,
user: User.t() | Ecto.Association.NotLoaded.t(), user: User.t() | Ecto.Association.NotLoaded.t(),
user_id: User.id(), user_id: User.id(),
@ -66,16 +75,38 @@ defmodule Memex.Contexts.Context do
|> unsafe_validate_unique(:slug, Repo) |> unsafe_validate_unique(:slug, Repo)
end end
defp cast_tags_string(changeset, %{"tags_string" => tags_string}) defp cast_tags_string(changeset, attrs) do
when tags_string |> is_binary() do changeset
tags = |> put_change(:tags_string, changeset |> get_field(:tags) |> get_tags_string())
|> cast(attrs, [:tags_string])
|> validate_format(:tags_string, ~r/^[\p{L}\p{N}\-\,]+$/,
message:
dgettext(
"errors",
"invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
)
)
|> cast_tags()
end
defp cast_tags(%{valid?: false} = changeset), do: changeset
defp cast_tags(%{valid?: true} = changeset) do
tags = changeset |> get_field(:tags_string) |> process_tags()
changeset |> put_change(:tags, tags)
end
defp process_tags(tags_string) when tags_string |> is_binary() do
tags_string tags_string
|> String.split(",", trim: true) |> String.split(",", trim: true)
|> Enum.map(fn str -> str |> String.trim() end) |> Enum.map(fn str -> str |> String.trim() end)
|> Enum.reject(fn str -> str |> is_nil() end)
|> Enum.sort() |> Enum.sort()
changeset |> change(tags: tags)
end end
defp cast_tags_string(changeset, _attrs), do: changeset defp process_tags(_other_tags_string), do: []
@spec get_tags_string([String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
end end

View File

@ -4,7 +4,6 @@ defmodule Memex.Notes do
""" """
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Ecto.Changeset
alias Memex.{Accounts.User, Notes.Note, Repo} alias Memex.{Accounts.User, Notes.Note, Repo}
@doc """ @doc """
@ -229,18 +228,4 @@ defmodule Memex.Notes do
def change_note(%Note{} = note, attrs \\ %{}, user) do def change_note(%Note{} = note, attrs \\ %{}, user) do
note |> Note.update_changeset(attrs, user) note |> Note.update_changeset(attrs, user)
end end
@doc """
Gets a canonical string representation of the `:tags` field for a Note
"""
@spec get_tags_string(Note.t() | Note.changeset() | [String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
def get_tags_string(%Note{tags: tags}), do: tags |> get_tags_string()
def get_tags_string(%Changeset{} = changeset) do
changeset
|> Changeset.get_field(:tags)
|> get_tags_string()
end
end end

View File

@ -8,6 +8,15 @@ defmodule Memex.Notes.Note do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Repo} alias Memex.{Accounts.User, Repo}
@derive {Jason.Encoder,
only: [
:slug,
:content,
:tags,
:visibility,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "notes" do schema "notes" do
@ -26,7 +35,7 @@ defmodule Memex.Notes.Note do
slug: slug(), slug: slug(),
content: String.t(), content: String.t(),
tags: [String.t()] | nil, tags: [String.t()] | nil,
tags_string: String.t(), tags_string: String.t() | nil,
visibility: :public | :private | :unlisted, visibility: :public | :private | :unlisted,
user: User.t() | Ecto.Association.NotLoaded.t(), user: User.t() | Ecto.Association.NotLoaded.t(),
user_id: User.id(), user_id: User.id(),
@ -65,16 +74,38 @@ defmodule Memex.Notes.Note do
|> unsafe_validate_unique(:slug, Repo) |> unsafe_validate_unique(:slug, Repo)
end end
defp cast_tags_string(changeset, %{"tags_string" => tags_string}) defp cast_tags_string(changeset, attrs) do
when tags_string |> is_binary() do changeset
tags = |> put_change(:tags_string, changeset |> get_field(:tags) |> get_tags_string())
|> cast(attrs, [:tags_string])
|> validate_format(:tags_string, ~r/^[\p{L}\p{N}\-\,]+$/,
message:
dgettext(
"errors",
"invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
)
)
|> cast_tags()
end
defp cast_tags(%{valid?: false} = changeset), do: changeset
defp cast_tags(%{valid?: true} = changeset) do
tags = changeset |> get_field(:tags_string) |> process_tags()
changeset |> put_change(:tags, tags)
end
defp process_tags(tags_string) when tags_string |> is_binary() do
tags_string tags_string
|> String.split(",", trim: true) |> String.split(",", trim: true)
|> Enum.map(fn str -> str |> String.trim() end) |> Enum.map(fn str -> str |> String.trim() end)
|> Enum.reject(fn str -> str |> is_nil() end)
|> Enum.sort() |> Enum.sort()
changeset |> change(tags: tags)
end end
defp cast_tags_string(changeset, _attrs), do: changeset defp process_tags(_other_tags_string), do: []
@spec get_tags_string([String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
end end

View File

@ -4,7 +4,6 @@ defmodule Memex.Pipelines do
""" """
import Ecto.Query, warn: false import Ecto.Query, warn: false
alias Ecto.Changeset
alias Memex.{Accounts.User, Pipelines.Pipeline, Repo} alias Memex.{Accounts.User, Pipelines.Pipeline, Repo}
@doc """ @doc """
@ -231,18 +230,4 @@ defmodule Memex.Pipelines do
def change_pipeline(%Pipeline{} = pipeline, attrs \\ %{}, user) do def change_pipeline(%Pipeline{} = pipeline, attrs \\ %{}, user) do
pipeline |> Pipeline.update_changeset(attrs, user) pipeline |> Pipeline.update_changeset(attrs, user)
end end
@doc """
Gets a canonical string representation of the `:tags` field for a Pipeline
"""
@spec get_tags_string(Pipeline.t() | Pipeline.changeset() | [String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
def get_tags_string(%Pipeline{tags: tags}), do: tags |> get_tags_string()
def get_tags_string(%Changeset{} = changeset) do
changeset
|> Changeset.get_field(:tags)
|> get_tags_string()
end
end end

View File

@ -8,6 +8,16 @@ defmodule Memex.Pipelines.Pipeline do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Pipelines.Steps.Step, Repo} alias Memex.{Accounts.User, Pipelines.Steps.Step, Repo}
@derive {Jason.Encoder,
only: [
:slug,
:description,
:tags,
:visibility,
:inserted_at,
:steps,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "pipelines" do schema "pipelines" do
@ -28,7 +38,7 @@ defmodule Memex.Pipelines.Pipeline do
slug: slug(), slug: slug(),
description: String.t(), description: String.t(),
tags: [String.t()] | nil, tags: [String.t()] | nil,
tags_string: String.t(), tags_string: String.t() | nil,
visibility: :public | :private | :unlisted, visibility: :public | :private | :unlisted,
user: User.t() | Ecto.Association.NotLoaded.t(), user: User.t() | Ecto.Association.NotLoaded.t(),
user_id: User.id(), user_id: User.id(),
@ -67,16 +77,38 @@ defmodule Memex.Pipelines.Pipeline do
|> unsafe_validate_unique(:slug, Repo) |> unsafe_validate_unique(:slug, Repo)
end end
defp cast_tags_string(changeset, %{"tags_string" => tags_string}) defp cast_tags_string(changeset, attrs) do
when tags_string |> is_binary() do changeset
tags = |> put_change(:tags_string, changeset |> get_field(:tags) |> get_tags_string())
|> cast(attrs, [:tags_string])
|> validate_format(:tags_string, ~r/^[\p{L}\p{N}\-\,]+$/,
message:
dgettext(
"errors",
"invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
)
)
|> cast_tags()
end
defp cast_tags(%{valid?: false} = changeset), do: changeset
defp cast_tags(%{valid?: true} = changeset) do
tags = changeset |> get_field(:tags_string) |> process_tags()
changeset |> put_change(:tags, tags)
end
defp process_tags(tags_string) when tags_string |> is_binary() do
tags_string tags_string
|> String.split(",", trim: true) |> String.split(",", trim: true)
|> Enum.map(fn str -> str |> String.trim() end) |> Enum.map(fn str -> str |> String.trim() end)
|> Enum.reject(fn str -> str |> is_nil() end)
|> Enum.sort() |> Enum.sort()
changeset |> change(tags: tags)
end end
defp cast_tags_string(changeset, _attrs), do: changeset defp process_tags(_other_tags_string), do: []
@spec get_tags_string([String.t()] | nil) :: String.t()
def get_tags_string(nil), do: ""
def get_tags_string(tags) when tags |> is_list(), do: tags |> Enum.join(",")
end end

View File

@ -7,6 +7,14 @@ defmodule Memex.Pipelines.Steps.Step do
alias Ecto.{Changeset, UUID} alias Ecto.{Changeset, UUID}
alias Memex.{Accounts.User, Pipelines.Pipeline} alias Memex.{Accounts.User, Pipelines.Pipeline}
@derive {Jason.Encoder,
only: [
:title,
:content,
:position,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: true} @primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id @foreign_key_type :binary_id
schema "steps" do schema "steps" do

View File

@ -12,7 +12,7 @@ defmodule MemexWeb.Components.ContextContent do
~H""" ~H"""
<div <div
id={"show-context-content-#{@context.id}"} id={"show-context-content-#{@context.id}"}
class="input input-primary h-128 min-h-128 inline-block" class="input input-primary h-128 min-h-128 inline-block whitespace-pre-wrap overflow-x-hidden overflow-y-auto"
phx-hook="MaintainAttrs" phx-hook="MaintainAttrs"
phx-update="ignore" phx-update="ignore"
readonly readonly
@ -39,7 +39,6 @@ defmodule MemexWeb.Components.ContextContent do
"</p>#{link}<p class=\"inline\">" "</p>#{link}<p class=\"inline\">"
end end
) )
|> String.replace("\n", "<br>")
|> HTML.raw() |> HTML.raw()
end end
end end

View File

@ -12,7 +12,7 @@ defmodule MemexWeb.Components.NoteContent do
~H""" ~H"""
<div <div
id={"show-note-content-#{@note.id}"} id={"show-note-content-#{@note.id}"}
class="input input-primary h-128 min-h-128 inline-block" class="input input-primary h-128 min-h-128 inline-block whitespace-pre-wrap overflow-x-hidden overflow-y-auto"
phx-hook="MaintainAttrs" phx-hook="MaintainAttrs"
phx-update="ignore" phx-update="ignore"
readonly readonly
@ -39,7 +39,6 @@ defmodule MemexWeb.Components.NoteContent do
"</p>#{link}<p class=\"inline\">" "</p>#{link}<p class=\"inline\">"
end end
) )
|> String.replace("\n", "<br>")
|> HTML.raw() |> HTML.raw()
end end
end end

View File

@ -12,7 +12,7 @@ defmodule MemexWeb.Components.StepContent do
~H""" ~H"""
<div <div
id={"show-step-content-#{@step.id}"} id={"show-step-content-#{@step.id}"}
class="input input-primary h-32 min-h-32 inline-block" class="input input-primary h-32 min-h-32 inline-block whitespace-pre-wrap overflow-x-hidden overflow-y-auto"
phx-hook="MaintainAttrs" phx-hook="MaintainAttrs"
phx-update="ignore" phx-update="ignore"
readonly readonly
@ -39,7 +39,6 @@ defmodule MemexWeb.Components.StepContent do
"</p>#{link}<p class=\"inline\">" "</p>#{link}<p class=\"inline\">"
end end
) )
|> String.replace("\n", "<br>")
|> HTML.raw() |> HTML.raw()
end end
end end

View File

@ -0,0 +1,17 @@
defmodule MemexWeb.ExportController do
use MemexWeb, :controller
alias Memex.{Contexts, Notes, Pipelines, Pipelines.Steps}
def export(%{assigns: %{current_user: current_user}} = conn, %{"mode" => "json"}) do
pipelines =
Pipelines.list_pipelines(current_user)
|> Enum.map(fn pipeline -> Steps.preload_steps(pipeline, current_user) end)
json(conn, %{
user: current_user,
notes: Notes.list_notes(current_user),
contexts: Contexts.list_contexts(current_user),
pipelines: pipelines
})
end
end

View File

@ -27,9 +27,7 @@
<%= text_input(f, :tags_string, <%= text_input(f, :tags_string,
id: "tags-input", id: "tags-input",
class: "input input-primary", class: "input input-primary",
placeholder: gettext("tag1,tag2"), placeholder: gettext("tag1,tag2")
phx_update: "ignore",
value: Contexts.get_tags_string(@changeset)
) %> ) %>
<%= error_tag(f, :tags_string) %> <%= error_tag(f, :tags_string) %>

View File

@ -27,9 +27,7 @@
<%= text_input(f, :tags_string, <%= text_input(f, :tags_string,
id: "tags-input", id: "tags-input",
class: "input input-primary", class: "input input-primary",
placeholder: gettext("tag1,tag2"), placeholder: gettext("tag1,tag2")
phx_update: "ignore",
value: Notes.get_tags_string(@changeset)
) %> ) %>
<%= error_tag(f, :tags_string) %> <%= error_tag(f, :tags_string) %>

View File

@ -27,9 +27,7 @@
<%= text_input(f, :tags_string, <%= text_input(f, :tags_string,
id: "tags-input", id: "tags-input",
class: "input input-primary", class: "input input-primary",
placeholder: gettext("tag1,tag2"), placeholder: gettext("tag1,tag2")
phx_update: "ignore",
value: Pipelines.get_tags_string(@changeset)
) %> ) %>
<%= error_tag(f, :tags_string) %> <%= error_tag(f, :tags_string) %>

View File

@ -76,6 +76,7 @@ defmodule MemexWeb.Router do
put "/users/settings", UserSettingsController, :update put "/users/settings", UserSettingsController, :update
delete "/users/settings/:id", UserSettingsController, :delete delete "/users/settings/:id", UserSettingsController, :delete
get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
get "/export/:mode", ExportController, :export
end end
scope "/", MemexWeb do scope "/", MemexWeb do

View File

@ -136,12 +136,22 @@
<hr class="hr" /> <hr class="hr" />
<div class="flex justify-center items-center">
<.link
href={Routes.export_path(@conn, :export, :json)}
class="mx-4 my-2 btn btn-primary"
target="_blank"
>
<%= dgettext("actions", "export data as json") %>
</.link>
<.link <.link
href={Routes.user_settings_path(@conn, :delete, @current_user)} href={Routes.user_settings_path(@conn, :delete, @current_user)}
method={:delete} method={:delete}
class="btn btn-alert" class="mx-4 my-2 btn btn-alert"
data-confirm={dgettext("prompts", "are you sure you want to delete your account?")} data-confirm={dgettext("prompts", "are you sure you want to delete your account?")}
> >
<%= dgettext("actions", "delete user") %> <%= dgettext("actions", "delete user") %>
</.link> </.link>
</div>
</div> </div>

View File

@ -4,7 +4,7 @@ defmodule Memex.MixProject do
def project do def project do
[ [
app: :memex, app: :memex,
version: "0.1.4", version: "0.1.6",
elixir: "~> 1.14", elixir: "~> 1.14",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: Mix.compilers(), compilers: Mix.compilers(),

View File

@ -66,7 +66,7 @@ msgstr ""
msgid "delete" msgid "delete"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145 #: lib/memex_web/templates/user_settings/edit.html.heex:154
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "delete user" msgid "delete user"
msgstr "" msgstr ""
@ -124,9 +124,9 @@ msgstr ""
msgid "register" msgid "register"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:42 #: lib/memex_web/live/context_live/form_component.html.heex:40
#: lib/memex_web/live/note_live/form_component.html.heex:42 #: lib/memex_web/live/note_live/form_component.html.heex:40
#: lib/memex_web/live/pipeline_live/form_component.html.heex:42 #: lib/memex_web/live/pipeline_live/form_component.html.heex:40
#: lib/memex_web/live/step_live/form_component.html.heex:28 #: lib/memex_web/live/step_live/form_component.html.heex:28
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "save" msgid "save"
@ -155,3 +155,8 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "send instructions to reset password" msgid "send instructions to reset password"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145
#, elixir-autogen, elixir-format
msgid "export data as json"
msgstr ""

View File

@ -67,7 +67,7 @@ msgstr ""
msgid "delete" msgid "delete"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145 #: lib/memex_web/templates/user_settings/edit.html.heex:154
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "delete user" msgid "delete user"
msgstr "" msgstr ""
@ -125,9 +125,9 @@ msgstr ""
msgid "register" msgid "register"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:42 #: lib/memex_web/live/context_live/form_component.html.heex:40
#: lib/memex_web/live/note_live/form_component.html.heex:42 #: lib/memex_web/live/note_live/form_component.html.heex:40
#: lib/memex_web/live/pipeline_live/form_component.html.heex:42 #: lib/memex_web/live/pipeline_live/form_component.html.heex:40
#: lib/memex_web/live/step_live/form_component.html.heex:28 #: lib/memex_web/live/step_live/form_component.html.heex:28
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "save" msgid "save"
@ -156,3 +156,8 @@ msgstr ""
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "send instructions to reset password" msgid "send instructions to reset password"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:145
#, elixir-autogen, elixir-format
msgid "export data as json"
msgstr ""

View File

@ -301,17 +301,17 @@ msgstr ""
msgid "report bugs or request features" msgid "report bugs or request features"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:43 #: lib/memex_web/live/context_live/form_component.html.heex:41
#: lib/memex_web/live/note_live/form_component.html.heex:43 #: lib/memex_web/live/note_live/form_component.html.heex:41
#: lib/memex_web/live/pipeline_live/form_component.html.heex:43 #: lib/memex_web/live/pipeline_live/form_component.html.heex:41
#: lib/memex_web/live/step_live/form_component.html.heex:29 #: lib/memex_web/live/step_live/form_component.html.heex:29
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "saving..." msgid "saving..."
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:39 #: lib/memex_web/live/context_live/form_component.html.heex:37
#: lib/memex_web/live/note_live/form_component.html.heex:39 #: lib/memex_web/live/note_live/form_component.html.heex:37
#: lib/memex_web/live/pipeline_live/form_component.html.heex:39 #: lib/memex_web/live/pipeline_live/form_component.html.heex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "select privacy" msgid "select privacy"
msgstr "" msgstr ""

View File

@ -76,22 +76,22 @@ msgstr ""
msgid "You must confirm your account and log in to access this page." msgid "You must confirm your account and log in to access this page."
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:129 #: lib/memex/accounts/user.ex:139
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:150 #: lib/memex/accounts/user.ex:160
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:187 #: lib/memex/accounts/user.ex:197
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:85 #: lib/memex/accounts/user.ex:95
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "" msgstr ""
@ -102,12 +102,12 @@ msgstr ""
msgid "oops, something went wrong! Please check the errors below" msgid "oops, something went wrong! Please check the errors below"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:49 #: lib/memex/contexts/context.ex:58
#: lib/memex/contexts/context.ex:62 #: lib/memex/contexts/context.ex:71
#: lib/memex/notes/note.ex:48 #: lib/memex/notes/note.ex:57
#: lib/memex/notes/note.ex:61 #: lib/memex/notes/note.ex:70
#: lib/memex/pipelines/pipeline.ex:50 #: lib/memex/pipelines/pipeline.ex:60
#: lib/memex/pipelines/pipeline.ex:63 #: lib/memex/pipelines/pipeline.ex:73
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted" msgid "invalid format: only numbers, letters and hyphen are accepted"
msgstr "" msgstr ""
@ -131,3 +131,10 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "unauthorized" msgid "unauthorized"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:84
#: lib/memex/notes/note.ex:83
#: lib/memex/pipelines/pipeline.ex:86
#, elixir-autogen, elixir-format, fuzzy
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
msgstr ""

View File

@ -122,7 +122,7 @@ msgstr ""
msgid "are you sure you want to delete the invite for %{invite_name}?" msgid "are you sure you want to delete the invite for %{invite_name}?"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:143 #: lib/memex_web/templates/user_settings/edit.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "are you sure you want to delete your account?" msgid "are you sure you want to delete your account?"
msgstr "" msgstr ""

View File

@ -290,17 +290,17 @@ msgstr ""
msgid "report bugs or request features" msgid "report bugs or request features"
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:43 #: lib/memex_web/live/context_live/form_component.html.heex:41
#: lib/memex_web/live/note_live/form_component.html.heex:43 #: lib/memex_web/live/note_live/form_component.html.heex:41
#: lib/memex_web/live/pipeline_live/form_component.html.heex:43 #: lib/memex_web/live/pipeline_live/form_component.html.heex:41
#: lib/memex_web/live/step_live/form_component.html.heex:29 #: lib/memex_web/live/step_live/form_component.html.heex:29
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "saving..." msgid "saving..."
msgstr "" msgstr ""
#: lib/memex_web/live/context_live/form_component.html.heex:39 #: lib/memex_web/live/context_live/form_component.html.heex:37
#: lib/memex_web/live/note_live/form_component.html.heex:39 #: lib/memex_web/live/note_live/form_component.html.heex:37
#: lib/memex_web/live/pipeline_live/form_component.html.heex:39 #: lib/memex_web/live/pipeline_live/form_component.html.heex:37
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "select privacy" msgid "select privacy"
msgstr "" msgstr ""

View File

@ -75,22 +75,22 @@ msgstr ""
msgid "You must confirm your account and log in to access this page." msgid "You must confirm your account and log in to access this page."
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:129 #: lib/memex/accounts/user.ex:139
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "did not change" msgid "did not change"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:150 #: lib/memex/accounts/user.ex:160
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "does not match password" msgid "does not match password"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:187 #: lib/memex/accounts/user.ex:197
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "is not valid" msgid "is not valid"
msgstr "" msgstr ""
#: lib/memex/accounts/user.ex:85 #: lib/memex/accounts/user.ex:95
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "must have the @ sign and no spaces" msgid "must have the @ sign and no spaces"
msgstr "" msgstr ""
@ -101,12 +101,12 @@ msgstr ""
msgid "oops, something went wrong! Please check the errors below" msgid "oops, something went wrong! Please check the errors below"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:49 #: lib/memex/contexts/context.ex:58
#: lib/memex/contexts/context.ex:62 #: lib/memex/contexts/context.ex:71
#: lib/memex/notes/note.ex:48 #: lib/memex/notes/note.ex:57
#: lib/memex/notes/note.ex:61 #: lib/memex/notes/note.ex:70
#: lib/memex/pipelines/pipeline.ex:50 #: lib/memex/pipelines/pipeline.ex:60
#: lib/memex/pipelines/pipeline.ex:63 #: lib/memex/pipelines/pipeline.ex:73
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted" msgid "invalid format: only numbers, letters and hyphen are accepted"
msgstr "" msgstr ""
@ -130,3 +130,10 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "unauthorized" msgid "unauthorized"
msgstr "" msgstr ""
#: lib/memex/contexts/context.ex:84
#: lib/memex/notes/note.ex:83
#: lib/memex/pipelines/pipeline.ex:86
#, elixir-autogen, elixir-format
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
msgstr ""

View File

@ -121,7 +121,7 @@ msgstr ""
msgid "are you sure you want to delete the invite for %{invite_name}?" msgid "are you sure you want to delete the invite for %{invite_name}?"
msgstr "" msgstr ""
#: lib/memex_web/templates/user_settings/edit.html.heex:143 #: lib/memex_web/templates/user_settings/edit.html.heex:152
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "are you sure you want to delete your account?" msgid "are you sure you want to delete your account?"
msgstr "" msgstr ""

View File

@ -0,0 +1,101 @@
defmodule MemexWeb.ExportControllerTest do
@moduledoc """
Tests the export function
"""
use MemexWeb.ConnCase
import Memex.{ContextsFixtures, NotesFixtures, PipelinesFixtures, StepsFixtures}
@moduletag :export_controller_test
setup %{conn: conn} do
current_user = user_fixture() |> confirm_user()
[
current_user: current_user,
conn: conn |> log_in_user(current_user)
]
end
defp add_data(%{current_user: current_user}) do
note = note_fixture(current_user)
context = context_fixture(current_user)
pipeline = pipeline_fixture(current_user)
step = step_fixture(0, pipeline, current_user)
%{
note: note,
context: context,
pipeline: pipeline,
step: step
}
end
describe "Exports data" do
setup [:add_data]
test "in JSON", %{
conn: conn,
current_user: current_user,
note: note,
context: context,
pipeline: pipeline,
step: step
} do
conn = get(conn, Routes.export_path(conn, :export, :json))
ideal_note = %{
"slug" => note.slug,
"content" => note.content,
"tags" => note.tags,
"visibility" => note.visibility |> to_string(),
"inserted_at" => note.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => note.updated_at |> NaiveDateTime.to_iso8601()
}
ideal_context = %{
"slug" => context.slug,
"content" => context.content,
"tags" => context.tags,
"visibility" => context.visibility |> to_string(),
"inserted_at" => context.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => context.updated_at |> NaiveDateTime.to_iso8601()
}
ideal_pipeline = %{
"slug" => pipeline.slug,
"description" => pipeline.description,
"tags" => pipeline.tags,
"visibility" => pipeline.visibility |> to_string(),
"inserted_at" => pipeline.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => pipeline.updated_at |> NaiveDateTime.to_iso8601(),
"steps" => [
%{
"title" => step.title,
"content" => step.content,
"position" => step.position,
"inserted_at" => step.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => step.updated_at |> NaiveDateTime.to_iso8601()
}
]
}
ideal_user = %{
"confirmed_at" =>
current_user.confirmed_at |> Jason.encode!() |> String.replace(~r/\"/, ""),
"email" => current_user.email,
"id" => current_user.id,
"locale" => current_user.locale,
"role" => to_string(current_user.role),
"inserted_at" => current_user.inserted_at |> NaiveDateTime.to_iso8601(),
"updated_at" => current_user.updated_at |> NaiveDateTime.to_iso8601()
}
json_resp = conn |> json_response(200)
assert %{"notes" => [^ideal_note]} = json_resp
assert %{"contexts" => [^ideal_context]} = json_resp
assert %{"pipelines" => [^ideal_pipeline]} = json_resp
assert %{"user" => ^ideal_user} = json_resp
end
end
end

View File

@ -18,7 +18,7 @@ defmodule MemexWeb.ContextLiveTest do
} }
@invalid_attrs %{ @invalid_attrs %{
"content" => nil, "content" => nil,
"tags_string" => "", "tags_string" => " ",
"slug" => nil, "slug" => nil,
"visibility" => nil "visibility" => nil
} }
@ -114,9 +114,13 @@ defmodule MemexWeb.ContextLiveTest do
assert_patch(show_live, Routes.context_show_path(conn, :edit, context.slug)) assert_patch(show_live, Routes.context_show_path(conn, :edit, context.slug))
assert show_live html =
show_live
|> form("#context-form", context: @invalid_attrs) |> form("#context-form", context: @invalid_attrs)
|> render_change() =~ "can&#39;t be blank" |> render_change()
assert html =~ "can&#39;t be blank"
assert html =~ "tags must be comma-delimited"
{:ok, _, html} = {:ok, _, html} =
show_live show_live

View File

@ -19,7 +19,7 @@ defmodule MemexWeb.NoteLiveTest do
} }
@invalid_attrs %{ @invalid_attrs %{
"content" => nil, "content" => nil,
"tags_string" => "", "tags_string" => " ",
"slug" => nil, "slug" => nil,
"visibility" => nil "visibility" => nil
} }
@ -54,9 +54,13 @@ defmodule MemexWeb.NoteLiveTest do
assert_patch(index_live, Routes.note_index_path(conn, :new)) assert_patch(index_live, Routes.note_index_path(conn, :new))
assert index_live html =
index_live
|> form("#note-form", note: @invalid_attrs) |> form("#note-form", note: @invalid_attrs)
|> render_change() =~ "can&#39;t be blank" |> render_change()
assert html =~ "can&#39;t be blank"
assert html =~ "tags must be comma-delimited"
{:ok, _, html} = {:ok, _, html} =
index_live index_live

View File

@ -17,7 +17,7 @@ defmodule MemexWeb.PipelineLiveTest do
} }
@invalid_attrs %{ @invalid_attrs %{
"description" => nil, "description" => nil,
"tags_string" => "", "tags_string" => " ",
"slug" => nil, "slug" => nil,
"visibility" => nil "visibility" => nil
} }
@ -128,9 +128,13 @@ defmodule MemexWeb.PipelineLiveTest do
assert_patch(show_live, Routes.pipeline_show_path(conn, :edit, pipeline.slug)) assert_patch(show_live, Routes.pipeline_show_path(conn, :edit, pipeline.slug))
assert show_live html =
show_live
|> form("#pipeline-form", pipeline: @invalid_attrs) |> form("#pipeline-form", pipeline: @invalid_attrs)
|> render_change() =~ "can&#39;t be blank" |> render_change()
assert html =~ "can&#39;t be blank"
assert html =~ "tags must be comma-delimited"
{:ok, _, html} = {:ok, _, html} =
show_live show_live

View File

@ -22,6 +22,6 @@ defmodule Memex.ContextsFixtures do
}) })
|> Contexts.create_context(user) |> Contexts.create_context(user)
context %{context | tags_string: nil}
end end
end end

View File

@ -22,6 +22,6 @@ defmodule Memex.NotesFixtures do
}) })
|> Notes.create_note(user) |> Notes.create_note(user)
note %{note | tags_string: nil}
end end
end end

View File

@ -22,6 +22,6 @@ defmodule Memex.PipelinesFixtures do
}) })
|> Pipelines.create_pipeline(user) |> Pipelines.create_pipeline(user)
pipeline %{pipeline | tags_string: nil}
end end
end end