diff options
author | href <href@random.sh> | 2020-07-07 21:39:10 +0200 |
---|---|---|
committer | href <href@random.sh> | 2020-07-07 21:39:51 +0200 |
commit | d6ee134a5957e299c3ad59011df320b3c41e6e61 (patch) | |
tree | 29567e6635466f8a3415a935b3cc8a777019f5bc /lib/lsg_web | |
parent | bleh (diff) |
pouet
Diffstat (limited to 'lib/lsg_web')
21 files changed, 520 insertions, 114 deletions
diff --git a/lib/lsg_web/channels/user_socket.ex b/lib/lsg_web/channels/user_socket.ex index 6fedf18..691a26c 100644 --- a/lib/lsg_web/channels/user_socket.ex +++ b/lib/lsg_web/channels/user_socket.ex @@ -5,7 +5,7 @@ defmodule LSGWeb.UserSocket do # channel "room:*", LSGWeb.RoomChannel ## Transports - transport :websocket, Phoenix.Transports.WebSocket + #transport :websocket, Phoenix.Transports.WebSocket # transport :longpoll, Phoenix.Transports.LongPoll # Socket params are passed from the client and can diff --git a/lib/lsg_web/context_plug.ex b/lib/lsg_web/context_plug.ex new file mode 100644 index 0000000..7896ace --- /dev/null +++ b/lib/lsg_web/context_plug.ex @@ -0,0 +1,81 @@ +defmodule LSGWeb.ContextPlug do + import Plug.Conn + import Phoenix.Controller + + def init(opts \\ []) do + opts || [] + end + + def call(conn, opts) do + account = with \ + {:account, account_id} when is_binary(account_id) <- {:account, get_session(conn, :account)}, + {:account, account} when not is_nil(account) <- {:account, IRC.Account.get(account_id)} + do + account + else + _ -> nil + end + + network = Map.get(conn.params, "network") + network = if network == "-", do: nil, else: network + + conns = IRC.Connection.get_network(network) + chan = if c = Map.get(conn.params, "chan") do + LSGWeb.reformat_chan(c) + end + chan_conn = IRC.Connection.get_network(network, chan) + + memberships = if account do + IRC.Membership.of_account(account) + end + + auth_required = cond do + Keyword.get(opts, :restrict) == :public -> false + account == nil -> true + network == nil -> false + Keyword.get(opts, :restrict) == :logged_in -> false + network && chan -> + !Enum.member?(memberships, {network, chan}) + network -> + !Enum.any?(memberships, fn({n, _}) -> n == network end) + end + + bot = cond do + network && chan && chan_conn -> chan_conn.nick + network && conns -> conns.nick + true -> nil + end + + + cond do + account && auth_required -> + conn + |> put_status(404) + |> text("Page not found") + |> halt() + auth_required -> + conn + |> put_status(403) + |> render(LSGWeb.AlcoologView, "auth.html", bot: bot, no_header: true, network: network) + |> halt() + (network && !conns) -> + conn + |> put_status(404) + |> text("Page not found") + |> halt() + (chan && !chan_conn) -> + conn + |> put_status(404) + |> text("Page not found") + |> halt() + true -> + conn = conn + |> assign(:network, network) + |> assign(:chan, chan) + |> assign(:bot, bot) + |> assign(:account, account) + |> assign(:memberships, memberships) + end + end + +end diff --git a/lib/lsg_web/controllers/alcoolog_controller.ex b/lib/lsg_web/controllers/alcoolog_controller.ex index 9d5d9d9..b88faa3 100644 --- a/lib/lsg_web/controllers/alcoolog_controller.ex +++ b/lib/lsg_web/controllers/alcoolog_controller.ex @@ -2,9 +2,12 @@ defmodule LSGWeb.AlcoologController do use LSGWeb, :controller require Logger - def index(conn, %{"channel" => channel}) do - case LSG.Token.lookup(channel) do - {:ok, obj} -> index(conn, obj) + plug LSGWeb.ContextPlug when action not in [:token] + plug LSGWeb.ContextPlug, [restrict: :public] when action in [:token] + + def token(conn, %{"token" => token}) do + case LSG.Token.lookup(token) do + {:ok, {:alcoolog, :index, network, channel}} -> index(conn, nil, network, channel) err -> Logger.debug("AlcoologControler: token #{inspect err} invalid") conn @@ -13,8 +16,31 @@ defmodule LSGWeb.AlcoologController do end end - def index(conn, {:alcoolog, :index, channel}) do - aday = 7*((24 * 60)*60) + def index(conn = %{assigns: %{account: account}}, %{"network" => network, "chan" => channel}) do + index(conn, account, network, LSGWeb.reformat_chan(channel)) + end + + def index(conn = %{assigns: %{account: account}}, _) do + index(conn, account, nil, nil) + end + + #def index(conn, params) do + # network = Map.get(params, "network") + # chan = if c = Map.get(params, "chan") do + # LSGWeb.reformat_chan(c) + # end + # irc_conn = if network do + # IRC.Connection.get_network(network, chan) + # end + # bot = if(irc_conn, do: irc_conn.nick)# + # + # conn + # |> put_status(403) + # |> render("auth.html", network: network, channel: chan, irc_conn: conn, bot: bot) + #end + + def index(conn, account, network, channel) do + aday = 18*((24 * 60)*60) now = DateTime.utc_now() before = now |> DateTime.add(-aday, :second) @@ -27,22 +53,28 @@ defmodule LSGWeb.AlcoologController do ], [:"$_"]} ] - nicks_in_channel = IRC.UserTrack.channel(channel) - |> Enum.map(fn({_, nick, _, _, _, _, _}) -> String.downcase(nick) end) # tuple ets: {{nick, date}, volumes, current, nom, commentaire} + members = IRC.Membership.expanded_members_or_friends(account, network, channel) + members_ids = Enum.map(members, fn({account, _, nick}) -> account.id end) + member_names = Enum.reduce(members, %{}, fn({account, _, nick}, acc) -> Map.put(acc, account.id, nick) end) drinks = :ets.select(LSG.IRC.AlcoologPlugin.ETS, match) - #|> Enum.filter(fn({{nick, _}, _, _, _, _}) -> Enum.member?(nicks_in_channel, nick) end) - |> Enum.sort_by(fn({{_, ts}, _, _, _, _}) -> ts end, &>/2) + |> Enum.filter(fn({{account, _}, _, _, _, _}) -> Enum.member?(members_ids, account) end) + |> Enum.map(fn({{account, _}, _, _, _, _} = object) -> {object, Map.get(member_names, account)} end) + |> Enum.sort_by(fn({{{_, ts}, _, _, _, _}, _}) -> ts end, &>/2) - stats = LSG.IRC.AlcoologPlugin.get_channel_statistics(channel) + stats = LSG.IRC.AlcoologPlugin.get_channel_statistics(account, network, channel) - top = Enum.reduce(drinks, %{}, fn({{nick, _}, vol, _, _, _}, acc) -> + top = Enum.reduce(drinks, %{}, fn({{{account_id, _}, vol, _, _, _}, _}, acc) -> + nick = Map.get(member_names, account_id) all = Map.get(acc, nick, 0) Map.put(acc, nick, all + vol) end) |> Enum.sort_by(fn({_nick, count}) -> count end, &>/2) # {date, single_peak} - render(conn, "index.html", channel: channel, drinks: drinks, top: top, stats: stats) + # + conn + |> assign(:title, "alcoolog") + |> render("index.html", network: network, channel: channel, drinks: drinks, top: top, stats: stats) end end diff --git a/lib/lsg_web/controllers/irc_auth_sse_controller.ex b/lib/lsg_web/controllers/irc_auth_sse_controller.ex new file mode 100644 index 0000000..c39a866 --- /dev/null +++ b/lib/lsg_web/controllers/irc_auth_sse_controller.ex @@ -0,0 +1,66 @@ +defmodule LSGWeb.IrcAuthSseController do + use LSGWeb, :controller + require Logger + + @ping_interval 20_000 + @expire_delay :timer.minutes(3) + + def sse(conn, params) do + perks = if uri = Map.get(params, "redirect_to") do + {:redirect, uri} + else + nil + end + token = String.downcase(EntropyString.random_string(65)) + conn + |> assign(:token, token) + |> assign(:perks, perks) + |> put_resp_header("X-Accel-Buffering", "no") + |> put_resp_header("content-type", "text/event-stream") + |> send_chunked(200) + |> subscribe() + |> send_sse_message("token", token) + |> sse_loop + end + + def subscribe(conn) do + :timer.send_interval(@ping_interval, {:event, :ping}) + :timer.send_after(@expire_delay, {:event, :expire}) + {:ok, _} = Registry.register(IRC.PubSub, "message:private", []) + conn + end + + def sse_loop(conn) do + {type, event, exit} = receive do + {:event, :ping} -> {"ping", "ping", false} + {:event, :expire} -> {"expire", "expire", true} + {:irc, :text, %{account: account, text: token} = m} -> + if String.downcase(String.trim(token)) == conn.assigns.token do + path = LSG.AuthToken.new_path(account.id, conn.assigns.perks) + m.replyfun.("ok!") + {"authenticated", path, true} + else + {nil, nil, false} + end + _ -> {nil, nil, false} + end + + conn = if type do + send_sse_message(conn, type, event) + else + conn + end + + if exit do + conn + else + sse_loop(conn) + end + end + + defp send_sse_message(conn, type, data) do + {:ok, conn} = chunk(conn, "event: #{type}\ndata: #{data}\n\n") + conn + end + +end diff --git a/lib/lsg_web/controllers/irc_controller.ex b/lib/lsg_web/controllers/irc_controller.ex index 022807d..e0bf24d 100644 --- a/lib/lsg_web/controllers/irc_controller.ex +++ b/lib/lsg_web/controllers/irc_controller.ex @@ -1,13 +1,21 @@ defmodule LSGWeb.IrcController do use LSGWeb, :controller - def index(conn, _) do - commands = for mod <- (Application.get_env(:lsg, :irc)[:plugins] ++ Application.get_env(:lsg, :irc)[:handlers]) do + plug LSGWeb.ContextPlug + + def index(conn, params) do + network = Map.get(params, "network") + channel = if c = Map.get(params, "channel"), do: LSGWeb.reformat_chan(c) + commands = for mod <- ([IRC.Account.AccountPlugin] ++ Application.get_env(:lsg, :irc)[:plugins] ++ Application.get_env(:lsg, :irc)[:handlers]) do identifier = Module.split(mod) |> List.last |> String.replace("Plugin", "") |> Macro.underscore {identifier, mod.irc_doc()} end |> Enum.reject(fn({_, i}) -> i == nil end) - render conn, "index.html", commands: commands + members = cond do + network -> IRC.Membership.expanded_members_or_friends(conn.assigns.account, network, channel) + true -> IRC.Membership.of_account(conn.assigns.account) + end + render conn, "index.html", network: network, commands: commands, channel: channel, members: members end def txt(conn, %{"name" => name}) do @@ -33,13 +41,22 @@ defmodule LSGWeb.IrcController do doc = LSG.IRC.TxtPlugin.irc_doc() data = data() lines = Enum.reduce(data, 0, fn({_, lines}, acc) -> acc + Enum.count(lines) end) - render conn, "txts.html", data: data, doc: doc, files: Enum.count(data), lines: lines + conn + |> assign(:title, "txt") + |> render("txts.html", data: data, doc: doc, files: Enum.count(data), lines: lines) end defp do_txt(conn, txt) do data = data() + base_url = cond do + conn.assigns[:chan] -> "/#{conn.assigns.network}/#{LSGWeb.format_chan(conn.assigns.chan)}" + true -> "/-" + end if Map.has_key?(data, txt) do - render(conn, "txt.html", name: txt, data: data[txt], doc: nil) + conn + |> assign(:breadcrumbs, [{"txt", "#{base_url}/txt"}]) + |> assign(:title, "#{txt}.txt") + |> render("txt.html", name: txt, data: data[txt], doc: nil) else conn |> put_status(404) diff --git a/lib/lsg_web/controllers/network_controller.ex b/lib/lsg_web/controllers/network_controller.ex new file mode 100644 index 0000000..537c2f6 --- /dev/null +++ b/lib/lsg_web/controllers/network_controller.ex @@ -0,0 +1,11 @@ +defmodule LSGWeb.NetworkController do + use LSGWeb, :controller + plug LSGWeb.ContextPlug + + def index(conn, %{"network" => network}) do + conn + |> assign(:title, network) + |> render("index.html") + end + +end diff --git a/lib/lsg_web/controllers/page_controller.ex b/lib/lsg_web/controllers/page_controller.ex index b356b9c..a87cf1d 100644 --- a/lib/lsg_web/controllers/page_controller.ex +++ b/lib/lsg_web/controllers/page_controller.ex @@ -1,12 +1,35 @@ defmodule LSGWeb.PageController do use LSGWeb, :controller - def index(conn, _params) do - render conn, "index.html" + plug LSGWeb.ContextPlug when action not in [:token] + plug LSGWeb.ContextPlug, [restrict: :public] when action in [:token] + + def token(conn, %{"token" => token}) do + with \ + {:ok, account, perks} <- LSG.AuthToken.lookup(token) + do + IO.puts("Authenticated account #{inspect account}") + conn = put_session(conn, :account, account) + case perks do + nil -> redirect(conn, to: "/") + {:redirect, path} -> redirect(conn, to: path) + {:external_redirect, url} -> redirect(conn, external: url) + end + else + z -> + IO.inspect(z) + text(conn, "Error: invalid or expired token") + end end - def api(conn, _params) do - render conn, "api.html" + def index(conn = %{assigns: %{account: account}}, _) do + memberships = IRC.Membership.of_account(account) + users = IRC.UserTrack.find_by_account(account) + metas = IRC.Account.get_all_meta(account) + predicates = IRC.Account.get_predicates(account) + conn + |> assign(:title, account.name) + |> render("user.html", users: users, memberships: memberships, metas: metas, predicates: predicates) end def irc(conn, _) do @@ -16,16 +39,15 @@ defmodule LSGWeb.PageController do render conn, "irc.html", bot_helps: bot_helps end - def icecast(conn, _params) do - conn - |> json(LSG.IcecastAgent.get) - end - - def widget(conn, options) do - icecast = LSG.IcecastAgent.get - conn - |> put_layout(false) - |> render("widget.html", icecast: icecast) + def authenticate(conn, _) do + with \ + {:account, account_id} when is_binary(account_id) <- {:account, get_session(conn, :account)}, + {:account, account} when not is_nil(account) <- {:account, IRC.Account.get(account_id)} + do + assign(conn, :account, account) + else + _ -> conn + end end end diff --git a/lib/lsg_web/controllers/sms_controller.ex b/lib/lsg_web/controllers/sms_controller.ex new file mode 100644 index 0000000..00c6352 --- /dev/null +++ b/lib/lsg_web/controllers/sms_controller.ex @@ -0,0 +1,10 @@ +defmodule LSGWeb.SmsController do + use LSGWeb, :controller + require Logger + + def ovh_callback(conn, %{"senderid" => from, "message" => message}) do + spawn(fn() -> LSG.IRC.SmsPlugin.incoming(from, String.trim(message)) end) + text(conn, "") + end + +end diff --git a/lib/lsg_web/controllers/untappd_controller.ex b/lib/lsg_web/controllers/untappd_controller.ex new file mode 100644 index 0000000..1c3ceb1 --- /dev/null +++ b/lib/lsg_web/controllers/untappd_controller.ex @@ -0,0 +1,18 @@ +defmodule LSGWeb.UntappdController do + use LSGWeb, :controller + + def callback(conn, %{"code" => code}) do + with \ + {:account, account_id} when is_binary(account_id) <- {:account, get_session(conn, :account)}, + {:account, account} when not is_nil(account) <- {:account, IRC.Account.get(account_id)}, + {:ok, auth_token} <- Untappd.auth_callback(code) + do + IRC.Account.put_meta(account, "untappd-token", auth_token) + text(conn, "OK!") + else + {:account, _} -> text(conn, "Error: account not found") + :error -> text(conn, "Error: untappd authentication failed") + end + end + +end diff --git a/lib/lsg_web/endpoint.ex b/lib/lsg_web/endpoint.ex index 2d0a6be..e89dc12 100644 --- a/lib/lsg_web/endpoint.ex +++ b/lib/lsg_web/endpoint.ex @@ -1,7 +1,7 @@ defmodule LSGWeb.Endpoint do use Phoenix.Endpoint, otp_app: :lsg - socket "/socket", LSGWeb.UserSocket + socket "/socket", LSGWeb.UserSocket, websocket: true # Serve at "/" the static files from "priv/static" directory. # diff --git a/lib/lsg_web/router.ex b/lib/lsg_web/router.ex index a834083..de7fafb 100644 --- a/lib/lsg_web/router.ex +++ b/lib/lsg_web/router.ex @@ -3,8 +3,8 @@ defmodule LSGWeb.Router do pipeline :browser do plug :accepts, ["html", "txt"] - #plug :fetch_session - #plug :fetch_flash + plug :fetch_session + plug :fetch_flash #plug :protect_from_forgery #plug :put_secure_browser_headers end @@ -13,22 +13,33 @@ defmodule LSGWeb.Router do plug :accepts, ["json", "sse"] end + scope "/api", LSGWeb do + pipe_through :api + get "/irc-auth.sse", IrcAuthSseController, :sse + post "/sms/callback/Ovh", SmsController, :ovh_callback, as: :sms + end + scope "/", LSGWeb do pipe_through :browser get "/", PageController, :index get "/embed/widget", PageController, :widget get "/api", PageController, :api - get "/irc", IrcController, :index - get "/irc/txt", IrcController, :txt - get "/irc/txt/:name", IrcController, :txt - get "/irc/alcoolog/:channel", AlcoologController, :index - get "/irc/alcoolog/~/:nick", AlcoologController, :nick + get "/login/irc/:token", PageController, :token, as: :login + get "/api/untappd/callback", UntappdController, :callback, as: :untappd_callback + get "/-", IrcController, :index + get "/-/txt", IrcController, :txt + get "/-/txt/:name", IrcController, :txt + get "/-/alcoolog", AlcoologController, :index + get "/-/alcoolog/~/:account_name", AlcoologController, :index + get "/:network", NetworkController, :index + get "/:network/:chan", IrcController, :index + get "/:network/:chan/txt", IrcController, :txt + get "/:network/:chan/txt/:name", IrcController, :txt + get "/:network/:channel/preums", IrcController, :preums + get "/:network/:chan/alcoolog", AlcoologController, :index + get "/:network/:chan/alcoolog/t/:token", AlcoologController, :token + get "/:network/alcoolog/~/:nick", AlcoologController, :nick end - scope "/api", LSGWeb do - pipe_through :api - get "/icecast.json", PageController, :icecast - get "/icecast.sse", IcecastSseController, :sse - end end diff --git a/lib/lsg_web/templates/alcoolog/auth.html.eex b/lib/lsg_web/templates/alcoolog/auth.html.eex new file mode 100644 index 0000000..af6db53 --- /dev/null +++ b/lib/lsg_web/templates/alcoolog/auth.html.eex @@ -0,0 +1,39 @@ +<h1>authentication</h1> + +<div id="authenticator"> + <p> + <%= if @bot, do: "Send this to #{@bot} on #{@network}:", else: "Find your bot nickname and send:" %><br /><br /> + <strong>/msg <%= @bot || "the-bot-nickname" %> web</strong> + <br /><br /> + ... then come back to this address. + </p> +</div> + +<script type="text/javascript"> + var authSse = new EventSource("/api/irc-auth.sse?redirect_to=" + window.location.pathname); + authSse.addEventListener("token", function(event) { + html = "<%= if @bot, do: "Send this to #{@bot} on #{@network}:", else: "Find your bot nickname and send:" %><br /><br /><strong>/msg <%= @bot || "the-bot-nickname" %> "+event.data+"</strong>"; + elem = document.getElementById("authenticator"); + elem.innerHTML = html; + }); + authSse.addEventListener("authenticated", function(event) { + elem = document.getElementById("authenticator"); + elem.innerHTML = "<strong>success, redirecting...</strong>"; + window.keepInner = true; + window.location.pathname = event.data; + }); + authSse.addEventListener("expire", function(event) { + elem = document.getElementById("authenticator"); + elem.innerHTML = "<strong>authentication expired</strong>"; + window.keepInner = true; + }); + authSse.onerror = function() { + authSse.close(); + if (!window.keepInner) { + elem = document.getElementById("authenticator"); + elem.innerHTML = "<strong>server closed connection :(</strong>"; + } + }; + +</script> + diff --git a/lib/lsg_web/templates/alcoolog/index.html.eex b/lib/lsg_web/templates/alcoolog/index.html.eex index 507be71..e656e64 100644 --- a/lib/lsg_web/templates/alcoolog/index.html.eex +++ b/lib/lsg_web/templates/alcoolog/index.html.eex @@ -1,16 +1,12 @@ <style type="text/css"> -h1 small { - font-size: 14px; -} ol li { margin-bottom: 5px } </style> -<h1> - <small><a href="/irc"><%= Keyword.get(Application.get_env(:lsg, :irc), :name, "ircbot") %></a> › </small><br/> - alcoolog <%= @channel %> -</h1> +<%= if @stats == [] do %> + </strong><i>:o personne ne boit</i></strong> +<% end %> <ul> <%= for {nick, status} <- @stats do %> @@ -28,28 +24,35 @@ ol li { <% end %> </ul> -<p> - top consommateur par volume, les 7 derniers jours: <%= Enum.intersperse(for({nick, count} <- @top, do: "#{nick}: #{Float.round(count,4)}"), ", ") %> -</p> +<%= if @stats == %{} do %> + <strong><i>... et personne n'a bu :o :o :o</i></strong> +<% else %> + <p> + top consommateur par volume, les 7 derniers jours: <%= Enum.intersperse(for({nick, count} <- @top, do: "#{nick}: #{Float.round(count,4)}"), ", ") %> + </p> -<table class="table"> - <thead> - <tr> - <th scope="col">date</th> - <th scope="col">nick</th> - <th scope="col">points</th> - <th scope="col">nom</th> - </tr> - </thead> - <tbody> - <%= for {{nick, date}, points, _, nom, comment} <- @drinks do %> - <% date = DateTime.from_unix!(date, :millisecond) %> - <th scope="row"><%= LSGWeb.LayoutView.format_time(date, false) %></th> - <td><%= nick %></td> - <td><%= Float.round(points+0.0, 5) %></td> - <td><%= nom||"" %> <%= comment||"" %></td> + <table class="table"> + <thead> + <tr> + <th scope="col">date</th> + <th scope="col">nick</th> + <th scope="col">points</th> + <th scope="col">nom</th> </tr> - <% end %> - </tbody> -</table> + </thead> + <tbody> + <%= for {{{account, date}, points, _, nom, comment}, nick} <- @drinks do %> + <% date = DateTime.from_unix!(date, :millisecond) %> + <th scope="row"><%= LSGWeb.LayoutView.format_time(date, false) %></th> + <td><%= nick %></td> + <td><%= Float.round(points+0.0, 5) %></td> + <td><%= nom||"" %> <%= comment||"" %></td> + </tr> + <% end %> + </tbody> + </table> +<% end %> +<%= if @conn.assigns.account && (@network || @channel) do %> + <%= link("alcoolog global", to: alcoolog_path(@conn, :index)) %> +<% end %> diff --git a/lib/lsg_web/templates/irc/index.html.eex b/lib/lsg_web/templates/irc/index.html.eex index f127101..9e4f724 100644 --- a/lib/lsg_web/templates/irc/index.html.eex +++ b/lib/lsg_web/templates/irc/index.html.eex @@ -1,23 +1,4 @@ <style type="text/css"> -h1 small { - font-size: 14px; -} -</style> - -<h1><%= Keyword.get(Application.get_env(:lsg, :irc), :name, "ircbot") %></h1> - -<!--<p> -Serveur IRC: <a href="irc://irc.quakenet.org/lsg">irc.quakenet.org #lsg</a>. - Matrix: <a href="https://matrix.random.sh/riot/#rooms/#lsg:random.sh">#lsg:random.sh</a>. -<br /> -<a href="https://115ans.net/irc/">Webchat</a>. -<br /> -<a href="/irc/stats/">Statistiques</a>. -</del> -</p>--> - - -<style type="text/css"> .help-entry h1 { font-size: 18px; } @@ -29,15 +10,22 @@ Serveur IRC: <a href="irc://irc.quakenet.org/lsg">irc.quakenet.org #lsg</a>. <p> <% list = for {identifier, _} <- @commands do %> <% name = String.replace(identifier, "_", " ") %> - <%= link(name, to: "##{identifier}") #raw("<a href='##{identifier}'>#{name}</a>") %> + <%= link(name, to: "##{identifier}") %> <% end %> <small><%= Enum.intersperse(list, " - ") %></small> </p> +<%= if @members != [] do %> +<% users = for {_acc, user, nick} <- @members, do: if(user, do: nick, else: content_tag(:i, nick)) %> +<%= for u <- Enum.intersperse(users, ", ") do %> + <%= u %> +<% end %> +<% end %> + <div class="irchelps"> <%= for {identifier, help} <- @commands do %> <%= if help do %> - <div class="help-entry" id="<%= identifier %>"><%= help |> Earmark.as_html! |> raw() %></div> + <div class="help-entry" id="<%= identifier %>"><%= LSGWeb.LayoutView.liquid_markdown(@conn, help) %></div> <% end %> <% end %> </div> diff --git a/lib/lsg_web/templates/irc/txt.html.eex b/lib/lsg_web/templates/irc/txt.html.eex index 5b31320..e9df681 100644 --- a/lib/lsg_web/templates/irc/txt.html.eex +++ b/lib/lsg_web/templates/irc/txt.html.eex @@ -1,16 +1,9 @@ <style type="text/css"> -h1 small { - font-size: 14px; -} ol li { margin-bottom: 5px } </style> -<h1> - <small><a href="/irc"><%= Keyword.get(Application.get_env(:lsg, :irc), :name, "ircbot") %></a> › <a href="/irc/txt">txt</a> ›</small><br/> - <%= @name %>.txt</h1> - <ol> <%= for {txt, id} <- Enum.with_index(@data) do %> <li id="<%= @name %>-<%= id %>"><%= txt %></li> diff --git a/lib/lsg_web/templates/irc/txts.html.eex b/lib/lsg_web/templates/irc/txts.html.eex index 28d0220..5971ffa 100644 --- a/lib/lsg_web/templates/irc/txts.html.eex +++ b/lib/lsg_web/templates/irc/txts.html.eex @@ -5,13 +5,7 @@ .help-entry h2 { font-size: 16px; } -h1 small { - font-size: 14px; -} </style> -<h1> - <small><a href="/irc"><%= Keyword.get(Application.get_env(:lsg, :irc), :name, "ircbot") %></a> › </small><br/> - txt</h1> <p><strong><%= @lines %></strong> lignes dans <strong><%= @files %></strong> fichiers. <a href="#help">Aide</a>. @@ -19,9 +13,13 @@ h1 small { <ul> <%= for {txt, data} <- @data do %> - <li><a href="/irc/txt/<%= txt %>"><%= txt %></a> <i>(<%= Enum.count(data) %>)</i></li> + <% base_url = cond do + @conn.assigns[:chan] -> "/#{@conn.assigns.network}/#{LSGWeb.format_chan(@conn.assigns.chan)}" + true -> "/-" + end %> + <li><a href="<%= base_url %>/txt/<%= txt %>"><%= txt %></a> <i>(<%= Enum.count(data) %>)</i></li> <% end %> </ul> -<div class="help-entry" id="help"><%= @doc |> Earmark.as_html! |> raw() %></div> +<div class="help-entry" id="help"><%= LSGWeb.LayoutView.liquid_markdown(@conn, @doc) %></div> diff --git a/lib/lsg_web/templates/layout/app.html.eex b/lib/lsg_web/templates/layout/app.html.eex index 4c2ad44..1871e8b 100644 --- a/lib/lsg_web/templates/layout/app.html.eex +++ b/lib/lsg_web/templates/layout/app.html.eex @@ -1,16 +1,32 @@ <!DOCTYPE html> <html lang="en"> <head> + <%= page_title(@conn) %> <meta charset="utf-8"> <meta name="robots" content="noindex, nofollow, nosnippet"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="robots" content="noindex, noarchive, nofollow, nosnippet" /> + <title><%= Map.get(assigns, :title, "") %></title> <link rel="stylesheet" href="<%= static_path(@conn, "/assets/css/app.css") %>"> </head> <body> <div class="container"> <main role="main"> + <%= if !@conn.assigns[:no_header] do %> + <h1> + <small style="font-size: 14px;"> + <a href="/"><%= Keyword.get(Application.get_env(:lsg, :irc), :name, "ircbot") %></a> + <%= if @conn.assigns[:account], do: @conn.assigns.account.name %> + › + <%= if n = @conn.assigns[:network] do %><a href="/<%= n %>"><%= n %></a> › <% end %> + <%= if c = @conn.assigns[:chan] do %><a href="/<%= @conn.assigns.network %>/<%= LSGWeb.format_chan(c) %>"><%= c %></a> › <% end %> + <%= for({name, href} <- @conn.assigns[:breadcrumbs]||[], do: [link(name, to: href), raw(" › ")]) %> + </small><br /> + <%= @conn.assigns[:title] || @conn.assigns[:chan] || @conn.assigns[:network] %> + </h1> + <% end %> <%= render @view_module, @view_template, assigns %> </main> </div> diff --git a/lib/lsg_web/templates/network/index.html.eex b/lib/lsg_web/templates/network/index.html.eex new file mode 100644 index 0000000..fc024dd --- /dev/null +++ b/lib/lsg_web/templates/network/index.html.eex @@ -0,0 +1 @@ +pouet diff --git a/lib/lsg_web/templates/page/user.html.eex b/lib/lsg_web/templates/page/user.html.eex new file mode 100644 index 0000000..8a043a0 --- /dev/null +++ b/lib/lsg_web/templates/page/user.html.eex @@ -0,0 +1,38 @@ +<ul> + <li><%= link("Help", to: "/-") %></li> +</ul> + +<h2>channels</h2> +<ul> + <%= for {net, channel} <- @memberships do %> + <li> + <% url = LSGWeb.Router.Helpers.irc_path(LSGWeb.Endpoint, :index, net, LSGWeb.format_chan(channel)) %> + <%= link([net, ": ", content_tag(:strong, channel)], to: url) %> + </li> + <% end %> +</ul> + +<h2>connections</h2> +<ul> + <%= for user <- @users do %> + <li> + <strong><%= user.network %></strong>: <strong><%= user.nick %></strong>!<%= user.username %>@<%= user.host %> <i><%= user.realname %></i><br /> + <%= Enum.join(Enum.intersperse(Enum.map(user.privileges, fn({c, _}) -> c end), ", ")) %> + </li> + <% end %> +</ul> + +<h2>account</h2> +<ul> + <li>account-id: <%= @conn.assigns.account.id %></li> + <%= for {k, v} <- @metas do %> + <li><%= k %>: <%= to_string(v) %></li> + <% end %> +</ul> + +<strong>irc auths:</strong> +<ul> + <%= for {net, {predicate, v}} <- @predicates do %> + <li><%= net %>: <%= to_string(predicate) %>, <%= v %></li> + <% end %> +</ul> diff --git a/lib/lsg_web/views/layout_view.ex b/lib/lsg_web/views/layout_view.ex index 956d703..b28d3c5 100644 --- a/lib/lsg_web/views/layout_view.ex +++ b/lib/lsg_web/views/layout_view.ex @@ -1,20 +1,78 @@ defmodule LSGWeb.LayoutView do use LSGWeb, :view - + + def liquid_markdown(conn, text) do + context_path = cond do + conn.assigns[:chan] -> "/#{conn.assigns[:network]}/#{LSGWeb.format_chan(conn.assigns[:chan])}" + conn.assigns[:network] -> "/#{conn.assigns[:network]}/-" + true -> "/-" + end + + {:ok, ast} = Liquex.parse(text) + context = Liquex.Context.new(%{ + "context_path" => context_path + }) + {content, _} = Liquex.render(ast, context) + content + |> to_string() + |> Earmark.as_html!() + |> raw() + end + + def page_title(conn) do + target = cond do + conn.assigns[:chan] -> + "#{conn.assigns.chan} @ #{conn.assigns.network}" + conn.assigns[:network] -> conn.assigns.network + true -> Keyword.get(Application.get_env(:lsg, :irc), :name, "ircbot") + end + + breadcrumb_title = Enum.map(Map.get(conn.assigns, :breadcrumbs)||[], fn({title, _href}) -> title end) + + title = [conn.assigns[:title], breadcrumb_title, target] + |> List.flatten() + |> Enum.uniq() + |> Enum.filter(fn(x) -> x end) + |> Enum.intersperse(" / ") + |> Enum.join() + + content_tag(:title, title) + end + def format_time(date, with_relative \\ true) do alias Timex.Format.DateTime.Formatters alias Timex.Timezone date = if is_integer(date) do date |> DateTime.from_unix!(:millisecond) - |> Timezone.convert("Europe/Paris") + |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase) else date + |> DateTime.shift_zone!("Europe/Paris", Tzdata.TimeZoneDatabase) + end + + now = DateTime.now!("Europe/Paris", Tzdata.TimeZoneDatabase) + + now_week = Timex.iso_week(now) + date_week = Timex.iso_week(date) + + {y, w} = now_week + now_last_week = {y, w-1} + now_last_roll = 7-Timex.days_to_beginning_of_week(now) + + format = cond do + date.year != now.year -> "{D}/{M}/{YYYY} {h24}:{m}" + date.day == now.day -> "{h24}:{m}" + (now_week == date_week) || (date_week == now_last_week && (Date.day_of_week(date) >= now_last_roll)) -> "{WDfull} {h24}:{m}" + (now.month == date.month) -> "{WDfull} {D} {h24}:{m}" + true -> "{WDfull} {D} {M} {h24}:{m}" end + {:ok, relative} = Formatters.Relative.relative_to(date, Timex.now("Europe/Paris"), "{relative}", "fr") - {:ok, detail} = Formatters.Default.lformat(date, "{D}/{M}/{YYYY} {h24}:{m}", "fr") + {:ok, full} = Formatters.Default.lformat(date, "{WDfull} {D} {YYYY} {h24}:{m}", "fr") #"{h24}:{m} {WDfull} {D}", "fr") + {:ok, detail} = Formatters.Default.lformat(date, format, "fr") #"{h24}:{m} {WDfull} {D}", "fr") - content_tag(:time, if(with_relative, do: relative, else: detail), [title: detail]) + content_tag(:time, if(with_relative, do: relative, else: detail), [title: full]) end end diff --git a/lib/lsg_web/views/network_view.ex b/lib/lsg_web/views/network_view.ex new file mode 100644 index 0000000..c369ce6 --- /dev/null +++ b/lib/lsg_web/views/network_view.ex @@ -0,0 +1,4 @@ +defmodule LSGWeb.NetworkView do + use LSGWeb, :view + +end |