You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

303 lines
5.7 KiB

defmodule Mirage.Notes do
@moduledoc """
The Notes context.
"""
import Ecto.Query, warn: false
alias Mirage.Repo
alias Mirage.Notes.{Note, NoteNote, NoteTopic, NoteLink}
@note_preloads [:links, :topics, :backlinks, :list]
defmacro contains(content, search_term) do
quote do
fragment(
"? %> ?",
unquote(content),
unquote(search_term)
)
end
end
def search_notes(query) do
query = String.downcase(query)
q =
from n in Note,
limit: 5,
select: n,
preload: ^@note_preloads,
where: contains(n.content, ^query),
or_where: contains(n.title, ^query),
order_by: [:views]
Repo.all(q)
end
@doc """
Returns the list of notes.
## Examples
iex> list_notes()
[%Note{}, ...]
"""
def list_notes do
q =
from n in Note,
limit: 100,
select: n,
order_by: [desc: :inserted_at],
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:at) do
q =
from n in Note,
where: like(n.title, "@%"),
limit: 3,
select: n,
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:last_edited) do
q =
from n in Note,
limit: 3,
select: n,
order_by: [desc: n.updated_at],
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:most_viewed) do
q =
from n in Note,
limit: 3,
select: n,
order_by: [desc: n.views],
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:unlinked) do
q =
from n in Note,
full_join: nn in NoteNote,
on: nn.target_id == n.id or nn.source_id == n.id,
where: not (like(n.title, "@%") or like(n.title, "____-__-__")),
where: is_nil(nn.target_id) or is_nil(nn.source_id),
select: n,
limit: 3,
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:untagged) do
q =
from n in Note,
full_join: t in NoteTopic,
on: t.note_id == n.id,
where: not (like(n.title, "@%") or like(n.title, "____-__-__")),
where: is_nil(t.id),
select: n,
limit: 3,
preload: ^@note_preloads
Repo.all(q)
end
def list_notes(:today) do
date_title = today_title()
q =
from n in Note,
where: n.title == ^date_title,
select: n
note =
case Repo.one(q) do
%Note{} = note ->
[note]
nil ->
template = Mirage.Settings.get_setting_by_name!("daily_template")
{:ok, note} =
create_note(%{
title: date_title,
content: template.value
})
[note]
end
note |> preload_note()
end
def today_title() do
Timex.now() |> Timex.format!("{YYYY}-{0M}-{0D}")
end
@doc """
Gets a single note.
Raises `Ecto.NoResultsError` if the Note does not exist.
## Examples
iex> get_note!(123)
%Note{}
iex> get_note!(456)
** (Ecto.NoResultsError)
"""
def get_note!(id), do: Repo.get!(Note, id)
def get_note(id), do: Repo.get(Note, id)
def get_note_by_title(title), do: Repo.get_by(Note, title: title)
def preload_note(note), do: Repo.preload(note, @note_preloads)
def view_note!(note) do
now = DateTime.utc_now()
from(n in Note,
where: ^note.id == n.id,
update: [
inc: [views: 1],
set: [viewed_at: ^now]
]
)
|> Repo.update_all([])
note
end
@doc """
Creates a note.
## Examples
iex> create_note(%{field: value})
{:ok, %Note{}}
iex> create_note(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_note(attrs \\ %{}) do
%Note{}
|> Note.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a note.
## Examples
iex> update_note(note, %{field: new_value})
{:ok, %Note{}}
iex> update_note(note, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_note(%Note{} = note, attrs) do
note
|> Note.changeset(attrs)
|> Repo.update()
end
def update_note!(%Note{} = note, attrs) do
note
|> Note.changeset(attrs)
|> Repo.update!()
end
@doc """
Deletes a note.
## Examples
iex> delete_note(note)
{:ok, %Note{}}
iex> delete_note(note)
{:error, %Ecto.Changeset{}}
"""
def delete_note(%Note{} = note) do
Repo.delete(note)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking note changes.
## Examples
iex> change_note(note)
%Ecto.Changeset{data: %Note{}}
"""
def change_note(%Note{} = note, attrs \\ %{}) do
Note.changeset(note, attrs)
end
@doc """
Links a note to all references notes in the content-field
## Examples
A content field with the following content:
```
This is a link to [[42|some other note]]
```
will link this note to the note with id 42.
"""
def link_note(%Note{} = note) do
multi = Ecto.Multi.new()
query = from(n in NoteNote, where: n.source_id == ^note.id)
refs =
note
|> Mirage.Markdown.get_references()
|> Enum.map(&String.to_integer/1)
# Delete all old references for this note
multi =
multi
|> Ecto.Multi.delete_all(:delete_links, query)
# Create a link to all referenced notes
multi =
refs
|> Enum.map(fn ref -> %NoteNote{source_id: note.id, target_id: ref} end)
|> Enum.reduce(multi, fn link_struct, multi ->
Ecto.Multi.insert(multi, {:link, link_struct.target_id}, link_struct)
end)
Repo.transaction(multi)
end
def create_note_link(attrs) do
%NoteLink{}
|> NoteLink.changeset(attrs)
|> Repo.insert()
end
end