Compare commits
2 Commits
59283a0217
...
7813738f91
Author | SHA1 | Date | |
---|---|---|---|
7813738f91 | |||
c1337ebc10 |
@ -1,3 +1,7 @@
|
||||
# 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
|
||||
|
||||
|
@ -9,6 +9,16 @@ defmodule Memex.Accounts.User do
|
||||
alias Ecto.{Changeset, UUID}
|
||||
alias Memex.Invites.Invite
|
||||
|
||||
@derive {Jason.Encoder,
|
||||
only: [
|
||||
:id,
|
||||
:email,
|
||||
:confirmed_at,
|
||||
:role,
|
||||
:locale,
|
||||
:inserted_at,
|
||||
:updated_at
|
||||
]}
|
||||
@derive {Inspect, except: [:password]}
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
|
@ -9,6 +9,15 @@ defmodule Memex.Contexts.Context do
|
||||
alias Ecto.{Changeset, UUID}
|
||||
alias Memex.{Accounts.User, Repo}
|
||||
|
||||
@derive {Jason.Encoder,
|
||||
only: [
|
||||
:slug,
|
||||
:content,
|
||||
:tags,
|
||||
:visibility,
|
||||
:inserted_at,
|
||||
:updated_at
|
||||
]}
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
schema "contexts" do
|
||||
|
@ -8,6 +8,15 @@ defmodule Memex.Notes.Note do
|
||||
alias Ecto.{Changeset, UUID}
|
||||
alias Memex.{Accounts.User, Repo}
|
||||
|
||||
@derive {Jason.Encoder,
|
||||
only: [
|
||||
:slug,
|
||||
:content,
|
||||
:tags,
|
||||
:visibility,
|
||||
:inserted_at,
|
||||
:updated_at
|
||||
]}
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
schema "notes" do
|
||||
|
@ -8,6 +8,16 @@ defmodule Memex.Pipelines.Pipeline do
|
||||
alias Ecto.{Changeset, UUID}
|
||||
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}
|
||||
@foreign_key_type :binary_id
|
||||
schema "pipelines" do
|
||||
|
@ -7,6 +7,14 @@ defmodule Memex.Pipelines.Steps.Step do
|
||||
alias Ecto.{Changeset, UUID}
|
||||
alias Memex.{Accounts.User, Pipelines.Pipeline}
|
||||
|
||||
@derive {Jason.Encoder,
|
||||
only: [
|
||||
:title,
|
||||
:content,
|
||||
:position,
|
||||
:inserted_at,
|
||||
:updated_at
|
||||
]}
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
schema "steps" do
|
||||
|
@ -12,7 +12,7 @@ defmodule MemexWeb.Components.ContextContent do
|
||||
~H"""
|
||||
<div
|
||||
id={"show-context-content-#{@context.id}"}
|
||||
class="input input-primary h-128 min-h-128 inline-block overflow-x-hidden overflow-y-auto"
|
||||
class="input input-primary h-128 min-h-128 inline-block whitespace-pre-wrap overflow-x-hidden overflow-y-auto"
|
||||
phx-hook="MaintainAttrs"
|
||||
phx-update="ignore"
|
||||
readonly
|
||||
@ -39,7 +39,6 @@ defmodule MemexWeb.Components.ContextContent do
|
||||
"</p>#{link}<p class=\"inline\">"
|
||||
end
|
||||
)
|
||||
|> String.replace("\n", "<br>")
|
||||
|> HTML.raw()
|
||||
end
|
||||
end
|
||||
|
@ -12,7 +12,7 @@ defmodule MemexWeb.Components.NoteContent do
|
||||
~H"""
|
||||
<div
|
||||
id={"show-note-content-#{@note.id}"}
|
||||
class="input input-primary h-128 min-h-128 inline-block overflow-x-hidden overflow-y-auto"
|
||||
class="input input-primary h-128 min-h-128 inline-block whitespace-pre-wrap overflow-x-hidden overflow-y-auto"
|
||||
phx-hook="MaintainAttrs"
|
||||
phx-update="ignore"
|
||||
readonly
|
||||
@ -39,7 +39,6 @@ defmodule MemexWeb.Components.NoteContent do
|
||||
"</p>#{link}<p class=\"inline\">"
|
||||
end
|
||||
)
|
||||
|> String.replace("\n", "<br>")
|
||||
|> HTML.raw()
|
||||
end
|
||||
end
|
||||
|
@ -12,7 +12,7 @@ defmodule MemexWeb.Components.StepContent do
|
||||
~H"""
|
||||
<div
|
||||
id={"show-step-content-#{@step.id}"}
|
||||
class="input input-primary h-32 min-h-32 inline-block overflow-x-hidden overflow-y-auto"
|
||||
class="input input-primary h-32 min-h-32 inline-block whitespace-pre-wrap overflow-x-hidden overflow-y-auto"
|
||||
phx-hook="MaintainAttrs"
|
||||
phx-update="ignore"
|
||||
readonly
|
||||
@ -39,7 +39,6 @@ defmodule MemexWeb.Components.StepContent do
|
||||
"</p>#{link}<p class=\"inline\">"
|
||||
end
|
||||
)
|
||||
|> String.replace("\n", "<br>")
|
||||
|> HTML.raw()
|
||||
end
|
||||
end
|
||||
|
17
lib/memex_web/controllers/export_controller.ex
Normal file
17
lib/memex_web/controllers/export_controller.ex
Normal 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
|
@ -76,6 +76,7 @@ defmodule MemexWeb.Router do
|
||||
put "/users/settings", UserSettingsController, :update
|
||||
delete "/users/settings/:id", UserSettingsController, :delete
|
||||
get "/users/settings/confirm_email/:token", UserSettingsController, :confirm_email
|
||||
get "/export/:mode", ExportController, :export
|
||||
end
|
||||
|
||||
scope "/", MemexWeb do
|
||||
|
@ -136,12 +136,22 @@
|
||||
|
||||
<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
|
||||
href={Routes.user_settings_path(@conn, :delete, @current_user)}
|
||||
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?")}
|
||||
>
|
||||
<%= dgettext("actions", "delete user") %>
|
||||
</.link>
|
||||
</div>
|
||||
</div>
|
||||
|
2
mix.exs
2
mix.exs
@ -4,7 +4,7 @@ defmodule Memex.MixProject do
|
||||
def project do
|
||||
[
|
||||
app: :memex,
|
||||
version: "0.1.5",
|
||||
version: "0.1.6",
|
||||
elixir: "~> 1.14",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: Mix.compilers(),
|
||||
|
@ -66,7 +66,7 @@ msgstr ""
|
||||
msgid "delete"
|
||||
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
|
||||
msgid "delete user"
|
||||
msgstr ""
|
||||
@ -155,3 +155,8 @@ msgstr ""
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "send instructions to reset password"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex_web/templates/user_settings/edit.html.heex:145
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "export data as json"
|
||||
msgstr ""
|
||||
|
@ -67,7 +67,7 @@ msgstr ""
|
||||
msgid "delete"
|
||||
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
|
||||
msgid "delete user"
|
||||
msgstr ""
|
||||
@ -156,3 +156,8 @@ msgstr ""
|
||||
#, elixir-autogen, elixir-format, fuzzy
|
||||
msgid "send instructions to reset password"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex_web/templates/user_settings/edit.html.heex:145
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "export data as json"
|
||||
msgstr ""
|
||||
|
@ -76,22 +76,22 @@ msgstr ""
|
||||
msgid "You must confirm your account and log in to access this page."
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/accounts/user.ex:129
|
||||
#: lib/memex/accounts/user.ex:139
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "did not change"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/accounts/user.ex:150
|
||||
#: lib/memex/accounts/user.ex:160
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "does not match password"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/accounts/user.ex:187
|
||||
#: lib/memex/accounts/user.ex:197
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "is not valid"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/accounts/user.ex:85
|
||||
#: lib/memex/accounts/user.ex:95
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "must have the @ sign and no spaces"
|
||||
msgstr ""
|
||||
@ -102,12 +102,12 @@ msgstr ""
|
||||
msgid "oops, something went wrong! Please check the errors below"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/contexts/context.ex:49
|
||||
#: lib/memex/contexts/context.ex:62
|
||||
#: lib/memex/notes/note.ex:48
|
||||
#: lib/memex/notes/note.ex:61
|
||||
#: lib/memex/pipelines/pipeline.ex:50
|
||||
#: lib/memex/pipelines/pipeline.ex:63
|
||||
#: lib/memex/contexts/context.ex:58
|
||||
#: lib/memex/contexts/context.ex:71
|
||||
#: lib/memex/notes/note.ex:57
|
||||
#: lib/memex/notes/note.ex:70
|
||||
#: lib/memex/pipelines/pipeline.ex:60
|
||||
#: lib/memex/pipelines/pipeline.ex:73
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "invalid format: only numbers, letters and hyphen are accepted"
|
||||
msgstr ""
|
||||
@ -132,9 +132,9 @@ msgstr ""
|
||||
msgid "unauthorized"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/contexts/context.ex:75
|
||||
#: lib/memex/notes/note.ex:74
|
||||
#: lib/memex/pipelines/pipeline.ex:76
|
||||
#: 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 ""
|
||||
|
@ -122,7 +122,7 @@ msgstr ""
|
||||
msgid "are you sure you want to delete the invite for %{invite_name}?"
|
||||
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
|
||||
msgid "are you sure you want to delete your account?"
|
||||
msgstr ""
|
||||
|
@ -75,22 +75,22 @@ msgstr ""
|
||||
msgid "You must confirm your account and log in to access this page."
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/accounts/user.ex:129
|
||||
#: lib/memex/accounts/user.ex:139
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "did not change"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/accounts/user.ex:150
|
||||
#: lib/memex/accounts/user.ex:160
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "does not match password"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/accounts/user.ex:187
|
||||
#: lib/memex/accounts/user.ex:197
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "is not valid"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/accounts/user.ex:85
|
||||
#: lib/memex/accounts/user.ex:95
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "must have the @ sign and no spaces"
|
||||
msgstr ""
|
||||
@ -101,12 +101,12 @@ msgstr ""
|
||||
msgid "oops, something went wrong! Please check the errors below"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/contexts/context.ex:49
|
||||
#: lib/memex/contexts/context.ex:62
|
||||
#: lib/memex/notes/note.ex:48
|
||||
#: lib/memex/notes/note.ex:61
|
||||
#: lib/memex/pipelines/pipeline.ex:50
|
||||
#: lib/memex/pipelines/pipeline.ex:63
|
||||
#: lib/memex/contexts/context.ex:58
|
||||
#: lib/memex/contexts/context.ex:71
|
||||
#: lib/memex/notes/note.ex:57
|
||||
#: lib/memex/notes/note.ex:70
|
||||
#: lib/memex/pipelines/pipeline.ex:60
|
||||
#: lib/memex/pipelines/pipeline.ex:73
|
||||
#, elixir-autogen, elixir-format
|
||||
msgid "invalid format: only numbers, letters and hyphen are accepted"
|
||||
msgstr ""
|
||||
@ -131,9 +131,9 @@ msgstr ""
|
||||
msgid "unauthorized"
|
||||
msgstr ""
|
||||
|
||||
#: lib/memex/contexts/context.ex:75
|
||||
#: lib/memex/notes/note.ex:74
|
||||
#: lib/memex/pipelines/pipeline.ex:76
|
||||
#: 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 ""
|
||||
|
@ -121,7 +121,7 @@ msgstr ""
|
||||
msgid "are you sure you want to delete the invite for %{invite_name}?"
|
||||
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
|
||||
msgid "are you sure you want to delete your account?"
|
||||
msgstr ""
|
||||
|
101
test/memex_web/controllers/export_controller_test.exs
Normal file
101
test/memex_web/controllers/export_controller_test.exs
Normal 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
|
Loading…
Reference in New Issue
Block a user