How to realize extra operations with Trackable View Source
List (all) connections of the current user
In your "user" context:
alias YourApp.Repo
def user_connections(user) do
user
|> Haytni.TrackablePlugin.QueryHelpers.connections_from_user()
|> Repo.all() # paginate if you want/like
end
In your controller:
def index(conn, _params) do
if current_user = conn.assigns[:current_user] do
conn
# ...
|> assign(:connections, user_connections(current_user))
|> render(:index)
else
{:error, :forbidden}
end
end
Part of the template:
<ul>
<%= for connection <- @connections do %>
<li><%= c.ip %> (<%= c.inserted_at %>)</li>
<% end %>
</ul>
## Search who used an IP address
First, to have easier to handle the form for search and maintain/enhance it, we will introduce a schema less module called YourApp.Admin.IpSearch
:
# lib/your_app/admin/ipsearch.ex
defmodule YourApp.Admin.IpSearch do
import Ecto.Changeset
@types %{
ip: :string,
first: :utc_datetime,
last: :utc_datetime,
}
@attributes Map.keys(@types)
defstruct @attributes
@required ~W[ip]a
def changeset(%__MODULE__{} = struct, params \\ %{}) do
{struct, @types}
|> Ecto.Changeset.cast(params, @attributes)
|> validate_required(@required)
|> validate_ipaddress(:ip)
end
end
Create an admin context or reuse one where we will implement the functions change_ipsearch/[01]
, create_ipsearch/1
, find_by_ip/1
:
# lib/your_app/admin/admin.ex
defmodule YourApp.Admin do
import Ecto.Query, warn: false
alias YourApp.Repo
alias YourApp.Admin.IpSearch
def change_ipsearch do
change_ipsearch(%IpSearch{})
end
def change_ipsearch(%IpSearch{} = search) do
IpSearch.changeset(search, %{})
end
def create_ipsearch(attrs \\ %{}) do
%IpSearch{}
|> IpSearch.changeset(attrs)
|> Ecto.Changeset.apply_action(:insert)
end
def find_by_ip(search = %IpSearch{}) do
import Haytni.TrackablePlugin.QueryHelpers
%User{}
|> connections_from_all()
|> and_where_ip_equals(search.ip)
|> and_where_date_between(search.first, search.last)
|> preload([:user])
|> order_by([desc: :inserted_at])
|> Repo.all()
end
end
Then here is the controller:
# lib/your_app_web/controllers/admin/ip_search_controller.ex
defmodule YourAppWeb.Admin.IpSearchController do
use YourAppWeb, :controller
alias YourApp.Repo
alias YourApp.User
alias YourApp.Admin
defp render_new(conn, %Ecto.Changeset{} = changeset) do
conn
|> assign(:changeset, changeset)
|> render(:new)
end
def new(conn, _params) do
render_new(conn, Admin.change_ipsearch())
end
def create(conn, params) do
search_params
|> Admin.create_ipsearch()
|> case do
{:ok, search} ->
conn
|> assign(:search, search)
|> assign(:results, Admin.find_by_ip(search))
|> render(:create)
{:error, changeset} ->
render_new(conn, changeset)
end
end
end
The new
template:
# lib/your_app_web/templates/admin/ip_search/new.html.heex
<%= form_for @changeset, Routes.admin_ip_search_path(@conn, :create), [as: :search], fn f -> %>
<div class="form-group">
<%= label f, :ip %>
<%= text_input f, :ip %>
<%= error_tag f, :ip %>
</div>
<div class="form-group">
<%= label f, :first %>
<%= datetime_select f, :first %>
<%= error_tag f, :first %>
</div>
<div class="form-group">
<%= label f, :last %>
<%= datetime_select f, :last %>
<%= error_tag f, :last %>
</div>
<br/>
<%= submit "Search" %>
<% end %>
The create
template:
# lib/your_app_web/templates/admin/ip_search/create.html.heex
<h2>Results for <b><%= @search.ip %></b></h2>
<%= if Enum.empty?(@results) do %>
<p>This address has not been used</p>
<% else %>
<p><%= Enum.count(@results) %> résult(s) found.</p>
<table>
<thead>
<tr>
<th>Address</th>
<th>When</th>
<th>User</th>
</tr>
</thead>
<tbody>
<%= for r <- @results do %>
<tr>
<td><%= r.ip %></td>
<td><%= r.inserted_at %></td>
<td><%= link r.user.name, to: Routes.admin_user_path(@conn, :show, r.user) %></td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
The route, according to the previous codes, would be:
# lib/your_app_web/router.ex
defmodule YourAppWeb.Router do
use YourAppWeb, :router
require YourAppWeb.Haytni
# ...
scope "/back", as: :admin do
# ...
resources "/ipsearch", YourAppWeb.Admin.IpSearchController, only: ~W[new create]a
end
end
The empty view just as remainder to not forget it:
# lib/your_app_web/views/admin/ip_search_view.ex
defmodule YourAppWeb.Admin.IpSearchView do
use YourAppWeb, :view
end
NOTE: the plug to restrict access to the YourAppWeb.Admin.IpSearchController controller is not shown