8 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
926805ba9b fix user invite page 2022-12-19 21:14:14 -05:00
220122dec6 add newlines to content components 2022-12-19 21:09:57 -05:00
de399b4819 fix broken docker-compose 2022-12-19 21:09:50 -05:00
37 changed files with 431 additions and 183 deletions

View File

@ -1,3 +1,16 @@
# 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
- fix docker-compose
- fix newlines in note/context/step contents
- 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
- search tags on click - search tags on click

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

@ -2,8 +2,7 @@ version: '3'
services: services:
memex: memex:
build: image: shibaobun/memex
context: .
container_name: memex container_name: memex
restart: always restart: always
environment: environment:

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

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

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

View File

@ -22,15 +22,15 @@ defmodule MemexWeb.Components.UserCard do
<%= if @user.confirmed_at |> is_nil() do %> <%= if @user.confirmed_at |> is_nil() do %>
<%= gettext("email unconfirmed") %> <%= gettext("email unconfirmed") %>
<% else %> <% else %>
<%= gettext( <p>
"user was confirmed at %{relative_datetime}", <%= gettext("user confirmed on") %>
relative_datetime: @user.confirmed_at |> display_datetime() <%= @user.confirmed_at |> display_datetime() %>
) %> </p>
<% end %> <% end %>
</p> </p>
<p> <p>
<%= gettext("User registered on") %> <%= gettext("user registered on") %>
<%= @user.inserted_at |> display_datetime() %> <%= @user.inserted_at |> display_datetime() %>
</p> </p>
</h3> </h3>

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.3", 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

@ -66,11 +66,6 @@ msgstr ""
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr ""
#: lib/memex_web/components/invite_card.ex:19 #: lib/memex_web/components/invite_card.ex:19
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Uses Left:" msgid "Uses Left:"
@ -306,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 ""
@ -350,11 +345,6 @@ msgstr ""
msgid "unlimited" msgid "unlimited"
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:25
#, elixir-autogen, elixir-format
msgid "user was confirmed at %{relative_datetime}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:120 #: lib/memex_web/live/invite_live/index.html.heex:120
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "users" msgid "users"
@ -665,3 +655,13 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document." msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:26
#, elixir-autogen, elixir-format
msgid "user confirmed on"
msgstr ""
#: lib/memex_web/components/user_card.ex:33
#, elixir-autogen, elixir-format, fuzzy
msgid "user registered on"
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

@ -55,11 +55,6 @@ msgstr ""
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "User registered on"
msgstr ""
#: lib/memex_web/components/invite_card.ex:19 #: lib/memex_web/components/invite_card.ex:19
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Uses Left:" msgid "Uses Left:"
@ -295,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 ""
@ -339,11 +334,6 @@ msgstr ""
msgid "unlimited" msgid "unlimited"
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:25
#, elixir-autogen, elixir-format
msgid "user was confirmed at %{relative_datetime}"
msgstr ""
#: lib/memex_web/live/invite_live/index.html.heex:120 #: lib/memex_web/live/invite_live/index.html.heex:120
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "users" msgid "users"
@ -654,3 +644,13 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document." msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
msgstr "" msgstr ""
#: lib/memex_web/components/user_card.ex:26
#, elixir-autogen, elixir-format
msgid "user confirmed on"
msgstr ""
#: lib/memex_web/components/user_card.ex:33
#, elixir-autogen, elixir-format
msgid "user registered on"
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

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

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

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