aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2018-11-02 14:04:52 +0100
committerAlex Auvolat <alex@adnab.me>2018-11-02 14:04:52 +0100
commit94b1f6b0ae387b7fcb0714a4c6e213212097a100 (patch)
tree33fa5f6f626b075c389b4bb977d9fbb5e7e05c37
parent3baa53f1da7f581619b066832b8303efbe9a46ba (diff)
downloadshard-94b1f6b0ae387b7fcb0714a4c6e213212097a100.tar.gz
shard-94b1f6b0ae387b7fcb0714a4c6e213212097a100.zip
Directory stuff
-rw-r--r--shard/lib/app/chat.ex5
-rw-r--r--shard/lib/app/directory.ex164
-rw-r--r--shard/lib/app/identity.ex14
-rw-r--r--shard/lib/manager.ex8
-rw-r--r--shardweb/assets/css/app.css8
-rw-r--r--shardweb/lib/controllers/chat_controller.ex8
-rw-r--r--shardweb/lib/controllers/directory_controller.ex63
-rw-r--r--shardweb/lib/controllers/identity_controller.ex4
-rw-r--r--shardweb/lib/router.ex3
-rw-r--r--shardweb/lib/shard_uri.ex42
-rw-r--r--shardweb/lib/templates/chat/chat.html.eex4
-rw-r--r--shardweb/lib/templates/directory/view.html.eex75
-rw-r--r--shardweb/lib/templates/identity/view.html.eex3
-rw-r--r--shardweb/lib/templates/layout/app.html.eex8
-rw-r--r--shardweb/lib/templates/page/shard_list.html.eex8
-rw-r--r--shardweb/lib/views/directory_view.ex2
16 files changed, 334 insertions, 85 deletions
diff --git a/shard/lib/app/chat.ex b/shard/lib/app/chat.ex
index 2a3872b..405210b 100644
--- a/shard/lib/app/chat.ex
+++ b/shard/lib/app/chat.ex
@@ -39,6 +39,7 @@ defmodule SApp.Chat do
defimpl Shard.Manifest do
def module(_m), do: SApp.Chat
+ def is_valid?(_m), do: true
end
end
@@ -60,6 +61,10 @@ defmodule SApp.Chat do
defimpl Shard.Manifest do
def module(_m), do: SApp.Chat
+ def is_valid?(m) do
+ Enum.all?(m.pk_list, &(byte_size(&1)==32))
+ and m.pk_list == m.pk_list |> Enum.sort |> Enum.uniq
+ end
end
end
diff --git a/shard/lib/app/directory.ex b/shard/lib/app/directory.ex
index f4b5a0b..96e3a2b 100644
--- a/shard/lib/app/directory.ex
+++ b/shard/lib/app/directory.ex
@@ -1,8 +1,9 @@
defmodule SApp.Directory do
@moduledoc"""
Shard application for a directory of other shards.
+ Items can be stored (they become dependencies) or just links for possible reference.
- TODO: use MST for file list instead of plain list
+ TODO: use MST for item list instead of plain list
"""
use GenServer
@@ -19,11 +20,16 @@ defmodule SApp.Directory do
defimpl Shard.Manifest do
def module(_m), do: SApp.Directory
+ def is_valid?(m) do
+ is_boolean(m.public)
+ and is_binary(m.name)
+ and byte_size(m.owner) == 32
+ end
end
end
defmodule State do
- defstruct [:owner, :public, :name, :manifest, :id, :netgroup, :files, :revfiles]
+ defstruct [:owner, :public, :name, :manifest, :id, :netgroup, :items, :revitems]
end
def start_link(manifest) do
@@ -35,7 +41,7 @@ defmodule SApp.Directory do
id = SData.term_hash manifest
Shard.Manager.dispatch_to(id, nil, self())
- files = case Shard.Manager.load_state(id) do
+ items = case Shard.Manager.load_state(id) do
nil ->
SData.SignRev.new %{}
st -> st
@@ -46,37 +52,37 @@ defmodule SApp.Directory do
end
SNet.Group.init_lookup(netgroup, self())
- revfiles = for {n, m} <- SData.SignRev.get(files), into: %{}, do: {m, n}
+ revitems = for {n, m} <- SData.SignRev.get(items), into: %{}, do: {m, n}
{:ok, %State{
owner: owner, public: public, name: name,
manifest: manifest, id: id, netgroup: netgroup,
- files: files, revfiles: revfiles}}
+ items: items, revitems: revitems}}
end
def handle_call(:manifest, _from, state) do
{:reply, state.manifest, state}
end
- def handle_call(:get_files, _from, state) do
- {:reply, SData.SignRev.get(state.files), state}
+ def handle_call(:get_items, _from, state) do
+ {:reply, SData.SignRev.get(state.items), state}
end
- def handle_call({:add_file, name, manifest}, _from, state) do
+ def handle_call({:add_item, name, manifest, stored}, _from, state) do
if Shard.Keys.have_sk?(state.owner) do
- dict = SData.SignRev.get(state.files)
- if dict[name] != nil and dict[name] != manifest do
+ dict = SData.SignRev.get(state.items)
+ if dict[name] != nil and elem(dict[name], 0) != manifest do
{:reply, :exists_already, state}
else
- dict = Map.put(dict, name, manifest)
- GenServer.cast(Shard.Manager, {:dep_list, state.id, Map.values(dict)})
- {:ok, st2} = SData.SignRev.set(state.files, dict, state.owner)
+ dict = Map.put(dict, name, {manifest, stored})
+ {:ok, st2} = SData.SignRev.set(state.items, dict, state.owner)
Shard.Manager.save_state(state.id, st2)
state = %{state |
- files: st2,
- revfiles: Map.put(state.revfiles, manifest, name)
+ items: st2,
+ revitems: Map.put(state.revitems, manifest, name)
}
bcast_state(state)
+ send_deps(state)
{:reply, :ok, state}
end
else
@@ -84,20 +90,41 @@ defmodule SApp.Directory do
end
end
- def handle_call({:rm_file, name}, _from, state) do
+ def handle_call({:rm_item, item}, _from, state) do
if Shard.Keys.have_sk?(state.owner) do
- dict = SData.SignRev.get(state.files)
- if dict[name] == nil do
- {:reply, :not_found, state}
- else
- state = put_in(state.revfiles, Map.delete(state.revfiles, dict[name]))
- dict = Map.delete(dict, name)
- GenServer.cast(Shard.Manager, {:dep_list, state.id, Map.values(dict)})
- {:ok, st2} = SData.SignRev.set(state.files, dict, state.owner)
- Shard.Manager.save_state(state.id, st2)
- state = put_in(state.files, st2)
- bcast_state(state)
- {:reply, :ok, state}
+ dict = SData.SignRev.get(state.items)
+ case find(state, dict, item) do
+ {name, manifest} ->
+ state = put_in(state.revitems, Map.delete(state.revitems, manifest))
+ dict = Map.delete(dict, name)
+ {:ok, st2} = SData.SignRev.set(state.items, dict, state.owner)
+ Shard.Manager.save_state(state.id, st2)
+ state = put_in(state.items, st2)
+ bcast_state(state)
+ send_deps(state)
+ {:reply, :ok, state}
+ nil ->
+ {:reply, :not_found, state}
+ end
+ else
+ {:reply, :impossible, state}
+ end
+ end
+
+ def handle_call({:set_stored, item, stored}, _from, state) do
+ if Shard.Keys.have_sk?(state.owner) do
+ dict = SData.SignRev.get(state.items)
+ case find(state, dict, item) do
+ {name, manifest} ->
+ dict = Map.put(dict, name, {manifest, stored})
+ {:ok, st2} = SData.SignRev.set(state.items, dict, state.owner)
+ Shard.Manager.save_state(state.id, st2)
+ state = put_in(state.items, st2)
+ bcast_state(state)
+ send_deps(state)
+ {:reply, :ok, state}
+ nil ->
+ {:reply, :not_found, state}
end
else
{:reply, :impossible, state}
@@ -105,23 +132,22 @@ defmodule SApp.Directory do
end
def handle_call({:read, name}, _from, state) do
- dict = SData.SignRev.get(state.files)
+ dict = SData.SignRev.get(state.items)
{:reply, dict[name], state}
end
def handle_call({:find, manifest}, _from, state) do
- {:reply, state.revfiles[manifest], state}
+ {:reply, state.revitems[manifest], state}
end
def handle_cast(:send_deps, state) do
- dict = SData.SignRev.get(state.files)
- GenServer.cast(Shard.Manager, {:dep_list, state.id, Map.values(dict)})
+ send_deps(state)
{:noreply, state}
end
def handle_cast({:interested, peer_pid, auth}, state) do
if SNet.Group.in_group?(state.netgroup, peer_pid, auth) do
- SNet.Manager.send_pid(peer_pid, {state.id, nil, {:update, SData.SignRev.signed(state.files), true}})
+ SNet.Manager.send_pid(peer_pid, {state.id, nil, {:update, SData.SignRev.signed(state.items), true}})
end
{:noreply, state}
end
@@ -132,17 +158,17 @@ defmodule SApp.Directory do
else
state = case msg do
{:update, signed, ask_reply} when signed != nil ->
- state = case SData.SignRev.merge(state.files, signed, state.pk) do
- {true, newfiles} ->
- Shard.Manager.save_state(state.id, newfiles)
- state = put_in(state.files, newfiles)
+ state = case SData.SignRev.merge(state.items, signed, state.pk) do
+ {true, newitems} ->
+ Shard.Manager.save_state(state.id, newitems)
+ state = put_in(state.items, newitems)
bcast_state(state, [conn_pid])
state
{false, _} ->
state
end
if ask_reply do
- SNet.Manager.send_pid(conn_pid, {state.id, nil, {:update, SData.SignRev.signed(state.files), false}})
+ SNet.Manager.send_pid(conn_pid, {state.id, nil, {:update, SData.SignRev.signed(state.items), false}})
end
state
_ -> state
@@ -151,53 +177,83 @@ defmodule SApp.Directory do
end
end
+ defp find(state, dict, item) do
+ cond do
+ dict[item] != nil ->
+ {manifest, _} = dict[item]
+ {item, manifest}
+ state.revitems[item] != nil ->
+ name = state.revitems[item]
+ {name, item}
+ true ->
+ nil
+ end
+ end
+
defp bcast_state(state, exclude \\ []) do
- msg = {state.id, nil, {:update, SData.SignRev.signed(state.files), false}}
+ msg = {state.id, nil, {:update, SData.SignRev.signed(state.items), false}}
SNet.Group.broadcast(state.netgroup, msg, exclude_pid: exclude)
end
+ defp send_deps(state) do
+ dict = SData.SignRev.get(state.items)
+ IO.puts("items: #{inspect dict}")
+ deps = for {_, {m, stored}} <- dict, stored, do: m
+ IO.puts("stored: #{inspect deps}")
+ GenServer.cast(Shard.Manager, {:dep_list, state.id, deps})
+ end
+
# ================
# PUBLIC INTERFACE
# ================
@doc"""
- Return list of files stored in this directory.
+ Return list of items stored in this directory.
- Returns a dictionnary of %{name => manifest}.
+ Returns a dictionnary of %{name => {manifest, stored?}}.
"""
- def get_files(pid) do
- GenServer.call(pid, :get_files)
+ def get_items(pid) do
+ GenServer.call(pid, :get_items)
end
@doc"""
- Return the manifest of file with a given name in directory, or nil if not found.
+ Return the manifest of item with a given name in directory, or nil if not found.
- Equivalent to get_files(pid)[name] but better.
+ Equivalent to get_items(pid)[name] but better.
"""
def read(pid, name) do
GenServer.call(pid, {:read, name})
end
@doc"""
- Find a file in the directory by its manifest. Returns name if found or nil if not found.
+ Find an item in the directory by its manifest. Returns name if found or nil if not found.
"""
def find(pid, manifest) do
GenServer.call(pid, {:find, manifest})
end
@doc"""
- Add a file to this directory. A file is a name for a shard manifest.
- A file added to a directory becomes a dependency of the directory, i.e.
- if the directory is pinned then all files inside are pinned as well.
+ Add an item to this directory. An item is a name for a shard manifest.
+ An item added to a directory becomes a dependency of the directory, i.e.
+ if the directory is pinned then all items inside are pinned as well.
+ """
+ def add_item(pid, name, manifest, stored \\ true) do
+ GenServer.call(pid, {:add_item, name, manifest, stored})
+ end
+
+ @doc"""
+ Remove a named item from this directory.
+ Argument can be either a manifest or the name of an item.
"""
- def add_file(pid, name, manifest) do
- GenServer.call(pid, {:add_file, name, manifest})
+ def rm_item(pid, item) do
+ GenServer.call(pid, {:rm_item, item})
end
@doc"""
- Remove a named file from this directory.
+ Set an item as stored or not stored.
+ Argument can be either a manifest or the name of an item.
"""
- def rm_file(pid, name) do
- GenServer.call(pid, {:rm_file, name})
+ def set_stored(pid, item, stored) do
+ GenServer.call(pid, {:set_stored, item, stored})
end
end
diff --git a/shard/lib/app/identity.ex b/shard/lib/app/identity.ex
index 69bed93..59a4b90 100644
--- a/shard/lib/app/identity.ex
+++ b/shard/lib/app/identity.ex
@@ -27,6 +27,9 @@ defmodule SApp.Identity do
defimpl Shard.Manifest do
def module(_m), do: SApp.Identity
+ def is_valid?(m) do
+ byte_size(m.pk) == 32
+ end
end
end
@@ -79,9 +82,16 @@ defmodule SApp.Identity do
end
def handle_cast(:send_deps, state) do
- # TODO: collections
+ deps = if Shard.Keys.have_sk?(state.pk) do
+ [
+ %SApp.Directory.Manifest{owner: state.pk, public: true, name: "collection"},
+ %SApp.Directory.Manifest{owner: state.pk, public: false, name: "collection"}
+ ]
+ else
+ []
+ end
- GenServer.cast(Shard.Manager, {:dep_list, state.id, []})
+ GenServer.cast(Shard.Manager, {:dep_list, state.id, deps})
{:noreply, state}
end
diff --git a/shard/lib/manager.ex b/shard/lib/manager.ex
index 990bcea..c178e2f 100644
--- a/shard/lib/manager.ex
+++ b/shard/lib/manager.ex
@@ -17,6 +17,11 @@ defprotocol Shard.Manifest do
Get the module in question.
"""
def module(manifest)
+
+ @doc"""
+ Check if manifest is valid
+ """
+ def is_valid?(manifest)
end
defmodule Shard.Manager do
@@ -189,6 +194,8 @@ defmodule Shard.Manager do
end
defp find_or_start(state, shard_id, manifest) do
+ true = Shard.Manifest.is_valid?(manifest)
+
case :dets.lookup(@shard_db, shard_id) do
[] ->
:dets.insert(@shard_db, {shard_id, manifest, cached(), nil})
@@ -200,6 +207,7 @@ defmodule Shard.Manager do
case :ets.lookup(:shard_procs, {shard_id, nil}) do
[] ->
{:ok, pid} = apply(Shard.Manifest.module(manifest), :start_link, [manifest])
+ GenServer.cast(pid, :send_deps)
:ets.insert(:shard_procs, {{shard_id, nil}, pid})
state = Map.put(state, pid, {shard_id, nil})
{pid, state}
diff --git a/shardweb/assets/css/app.css b/shardweb/assets/css/app.css
index 93cba41..140f32e 100644
--- a/shardweb/assets/css/app.css
+++ b/shardweb/assets/css/app.css
@@ -13,3 +13,11 @@
.conn_establishing {
color: #999;
}
+
+.shard_uri {
+ display: inline-block;
+ max-width: 400px;
+ overflow: hidden;
+ text-overflow:ellipsis;
+ font: monospace;
+}
diff --git a/shardweb/lib/controllers/chat_controller.ex b/shardweb/lib/controllers/chat_controller.ex
index 080ee61..1ca66d4 100644
--- a/shardweb/lib/controllers/chat_controller.ex
+++ b/shardweb/lib/controllers/chat_controller.ex
@@ -4,11 +4,13 @@ defmodule ShardWeb.ChatController do
def chat(conn, %{"chan" => chan}) do
conn = put_gon(conn, chat_channel: "chat:" <> chan)
- shard = %SApp.Chat.Manifest{channel: chan} |> SData.term_hash
+ manifest = %SApp.Chat.Manifest{channel: chan}
+ shard = manifest |> SData.term_hash
render conn, "chat.html",
public: true,
shard: shard,
+ manifest: manifest,
chan: chan
end
@@ -37,10 +39,12 @@ defmodule ShardWeb.ChatController do
|> Enum.map(&SApp.Identity.get_nick/1)
|> Enum.join(", ")
- shard = [conn.assigns.pk | pk_list] |> SApp.Chat.PrivChat.Manifest.new |> SData.term_hash
+ manifest = [conn.assigns.pk | pk_list] |> SApp.Chat.PrivChat.Manifest.new
+ shard = manifest |> SData.term_hash
render conn, "chat.html",
public: false,
+ manifest: manifest,
shard: shard,
nicks: name
else
diff --git a/shardweb/lib/controllers/directory_controller.ex b/shardweb/lib/controllers/directory_controller.ex
index 1e3a0b0..10fdec2 100644
--- a/shardweb/lib/controllers/directory_controller.ex
+++ b/shardweb/lib/controllers/directory_controller.ex
@@ -1,29 +1,64 @@
defmodule ShardWeb.DirectoryController do
use ShardWeb, :controller
- def view_pub(conn, %{"owner" => owner, "name" => name}) do
- owner = Base.decode16! owner
- shard = %SApp.Directory.Manifest{public: true, owner: owner, name: name}
- pid = Shard.Manager.find_or_start shard
+ def view_pub(conn, args) do
+ view(conn, true, args)
+ end
- render conn, "view.html",
- public: true,
- shard: shard,
- pid: pid,
- owner: owner,
- name: name
+ def view_priv(conn, args) do
+ view(conn, false, args)
end
- def view_priv(conn, %{"owner" => owner, "name" => name}) do
+ def view(conn, public, %{"owner" => owner, "name" => name}=args) do
owner = Base.decode16! owner
- shard = %SApp.Directory.Manifest{public: false, owner: owner, name: name}
- pid = Shard.Manager.find_or_start shard
+ manifest = %SApp.Directory.Manifest{public: public, owner: owner, name: name}
+ shard = SData.term_hash manifest
+ pid = Shard.Manager.find_or_start manifest
render conn, "view.html",
- public: false,
+ public: public,
shard: shard,
+ manifest: manifest,
pid: pid,
owner: owner,
name: name
end
+
+ def dir_add(conn, %{"dir_name" => dir_name, "dir_public" => dir_public, "add_name" => add_name, "add_uri" => add_uri, "add_stored" => add_stored}) do
+ dir_public = (dir_public == "true")
+
+ manifest = %SApp.Directory.Manifest{public: dir_public, owner: conn.assigns.pk, name: dir_name}
+ shard = SData.term_hash manifest
+ pid = Shard.Manager.find_or_start manifest
+
+ item_manifest = ShardURI.to_manifest(add_uri)
+ SApp.Directory.add_item(pid, add_name, item_manifest, (add_stored == "yes"))
+
+ redirect conn, to: directory_path(conn, (if dir_public do :view_pub else :view_priv end), conn.assigns.pk|>Base.encode16, dir_name)
+ end
+
+ def dir_rm(conn, %{"dir_name" => dir_name, "dir_public" => dir_public, "item_name" => rm_name}) do
+ dir_public = (dir_public == "true")
+
+ manifest = %SApp.Directory.Manifest{public: dir_public, owner: conn.assigns.pk, name: dir_name}
+ shard = SData.term_hash manifest
+ pid = Shard.Manager.find_or_start manifest
+
+ SApp.Directory.rm_item(pid, rm_name)
+
+ redirect conn, to: directory_path(conn, (if dir_public do :view_pub else :view_priv end), conn.assigns.pk|>Base.encode16, dir_name)
+ end
+
+ def dir_set_stored(conn, %{"dir_name" => dir_name, "dir_public" => dir_public, "item_name" => item_name, "item_stored" => item_stored}) do
+ dir_public = (dir_public == "true")
+ item_stored = (item_stored == "true")
+
+ manifest = %SApp.Directory.Manifest{public: dir_public, owner: conn.assigns.pk, name: dir_name}
+ shard = SData.term_hash manifest
+ pid = Shard.Manager.find_or_start manifest
+
+ SApp.Directory.set_stored(pid, item_name, item_stored)
+
+ redirect conn, to: directory_path(conn, (if dir_public do :view_pub else :view_priv end), conn.assigns.pk|>Base.encode16, dir_name)
+ end
end
diff --git a/shardweb/lib/controllers/identity_controller.ex b/shardweb/lib/controllers/identity_controller.ex
index 962a888..7452453 100644
--- a/shardweb/lib/controllers/identity_controller.ex
+++ b/shardweb/lib/controllers/identity_controller.ex
@@ -11,7 +11,8 @@ defmodule ShardWeb.IdentityController do
def view(conn, %{"pk" => pk}) do
{:ok, pk} = Base.decode16(pk)
- shard = %SApp.Identity.Manifest{pk: pk} |> SData.term_hash
+ manifest = %SApp.Identity.Manifest{pk: pk}
+ shard = manifest |> SData.term_hash
pid = Shard.Manager.find_proc shard
if pid == nil do
@@ -21,6 +22,7 @@ defmodule ShardWeb.IdentityController do
view_pk: pk,
view_nick: SApp.Identity.get_nick(pk),
shard: shard,
+ manifest: manifest,
pid: pid
end
end
diff --git a/shardweb/lib/router.ex b/shardweb/lib/router.ex
index 61f8209..3f78afd 100644
--- a/shardweb/lib/router.ex
+++ b/shardweb/lib/router.ex
@@ -31,6 +31,9 @@ defmodule ShardWeb.Router do
get "/pub/:owner/:name", DirectoryController, :view_pub
get "/priv/:owner/:name", DirectoryController, :view_priv
+ post "/dir/add", DirectoryController, :dir_add
+ post "/dir/rm", DirectoryController, :dir_rm
+ post "/dir/set_stored", DirectoryController, :dir_set_stored
get "/chat/:chan", ChatController, :chat
get "/pm/:people_list", ChatController, :privchat
diff --git a/shardweb/lib/shard_uri.ex b/shardweb/lib/shard_uri.ex
new file mode 100644
index 0000000..71c0904
--- /dev/null
+++ b/shardweb/lib/shard_uri.ex
@@ -0,0 +1,42 @@
+defmodule ShardURI do
+ @moduledoc"""
+ Convert Shard manifests to and from strings.
+ """
+
+ def from_manifest(m) do
+ case m do
+ %SApp.Chat.Manifest{channel: chan} -> "shard:chat:#{chan}"
+ %SApp.Chat.PrivChat.Manifest{pk_list: pk_list} ->
+ "shard:privchat:#{pk_list|>Enum.map(&Base.encode16/1)|>Enum.join(",")}"
+ %SApp.Identity.Manifest{pk: pk} ->
+ "shard:identity:#{pk|>Base.encode16}"
+ %SApp.Directory.Manifest{owner: owner, public: true, name: name} ->
+ "shard:dir:pub:#{owner|>Base.encode16}:#{name}"
+ %SApp.Directory.Manifest{owner: owner, public: false, name: name} ->
+ "shard:dir:priv:#{owner|>Base.encode16}:#{name}"
+ end
+ end
+
+ def to_manifest(p) do
+ case p do
+ "shard:chat:" <> chan ->
+ %SApp.Chat.Manifest{channel: chan}
+ "shard:privchat:" <> pklist ->
+ pklist
+ |> String.split(",")
+ |> Enum.map(&parse_pk/1)
+ |> SApp.Chat.PrivChat.Manifest.new()
+ "shard:identity:" <> pk ->
+ %SApp.Identity.Manifest{pk: parse_pk pk}
+ "shard:dir:pub:" <> <<pk::bytes-size(64)>> <> ":" <> name ->
+ %SApp.Directory.Manifest{owner: parse_pk(pk), public: true, name: name}
+ "shard:dir:priv:" <> <<pk::bytes-size(64)>> <> ":" <> name ->
+ %SApp.Directory.Manifest{owner: parse_pk(pk), public: false, name: name}
+ end
+ end
+
+ def parse_pk(pkstr) do
+ 64 = byte_size pkstr
+ Base.decode16! pkstr
+ end
+end
diff --git a/shardweb/lib/templates/chat/chat.html.eex b/shardweb/lib/templates/chat/chat.html.eex
index 499e14d..947e4c3 100644
--- a/shardweb/lib/templates/chat/chat.html.eex
+++ b/shardweb/lib/templates/chat/chat.html.eex
@@ -4,11 +4,11 @@
<h1 class="page-header">
<%= if @public do %>
#<%= @chan %>
- <small>public chat room</small>
<% else %>
<%= @nicks %>
- <small>private chat</small>
<% end %>
+ <small class="shard_uri"><%= @manifest |> ShardURI.from_manifest %></small>
+
</h1>
<ol class="breadcrumb">
diff --git a/shardweb/lib/templates/directory/view.html.eex b/shardweb/lib/templates/directory/view.html.eex
index 43ded97..b4cbfaf 100644
--- a/shardweb/lib/templates/directory/view.html.eex
+++ b/shardweb/lib/templates/directory/view.html.eex
@@ -3,7 +3,7 @@
<div class="col-lg-12">
<h1 class="page-header">
<%= @name %>
- <small>directory contents</small>
+ <small class="shard_uri"><%= @manifest |> ShardURI.from_manifest %></small>
</h1>
<ol class="breadcrumb">
@@ -18,6 +18,11 @@
</li>
<li class="active">
<i class="fa fa-folder"></i> <%= @name %>
+ <%= if @public do %>
+ (public)
+ <% else %>
+ (private)
+ <% end %>
</li>
</ol>
</div>
@@ -30,18 +35,80 @@
<tr>
<th>Name</th>
<th>Shard</th>
- <th>Id</th>
+ <th style="width: 150px"></th>
+ <th>URI</th>
</tr>
- <%= for {name, manifest} <- dir_contents(@conn, @shard) do %>
+ <%= for {name, {manifest, stored}} <- dir_contents(@conn, @manifest) do %>
<tr>
<td>
+ <%= if stored do %>
+ <i class="fa fa-save"></i>
+ <% else %>
+ <i class="fa fa-link"></i>
+ <% end %>
<strong><%= name %></strong>
</td>
<td>
<%= render ShardWeb.PageView, "shard_entry.html", conn: @conn, manifest: manifest %>
</td>
- <td><small><%= (SData.term_hash manifest) |> Base.encode16 %></small></td>
+ <td>
+ <%= form_for @conn, directory_path(@conn, :dir_rm), [class: "form-inline", style: "display: inline"], fn f -> %>
+ <%= hidden_input f, :dir_name, value: @name %>
+ <%= hidden_input f, :dir_public, value: @public %>
+ <%= hidden_input f, :item_name, value: name %>
+ <%= submit "Remove", [class: "btn btn-xs btn-danger"] %>
+ <% end %>
+ <%= form_for @conn, directory_path(@conn, :dir_set_stored), [class: "form-inline", style: "display: inline"], fn f -> %>
+ <%= hidden_input f, :dir_name, value: @name %>
+ <%= hidden_input f, :dir_public, value: @public %>
+ <%= hidden_input f, :item_name, value: name %>
+ <%= hidden_input f, :item_stored, value: (if stored do "false" else "true" end) %>
+ <%= if stored do %>
+ <%= submit "Don't store", [class: "btn btn-xs btn-warning"] %>
+ <% else %>
+ <%= submit "Store", [class: "btn btn-xs btn-success"] %>
+ <% end %>
+ <% end %>
+ </td>
+ <td><small class="shard_uri"><%= manifest |> ShardURI.from_manifest %></small></td>
</tr>
<% end %>
</table>
+<%= if @owner == @pk do %>
+ <fieldset><legend>Add existing item</legend>
+ <%= form_for @conn, directory_path(@conn, :dir_add), [class: "form-horizontal"], fn f -> %>
+ <%= hidden_input f, :dir_name, value: @name %>
+ <%= hidden_input f, :dir_public, value: @public %>
+ <div class="form-group">
+ <%= label :add_name, "Name:", class: ["col-sm-2 control-label"] %>
+ <div class="col-sm-10">
+ <%= text_input f, :add_name, [class: "form-control", value: ""] %>
+ </div>
+ </div>
+ <div class="form-group">
+ <%= label :add_uri, "URI:", class: ["col-sm-2 control-label"] %>
+ <div class="col-sm-10">
+ <%= text_input f, :add_uri, [class: "form-control", placeholder: "example: shard:identity:xxx"] %>
+ </div>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label">Stored?</label>
+ <div class="col-sm-10">
+ <div class="radio">
+ <label><%= radio_button f, :add_stored, "yes" %>Stored <i class="fa fa-save"></i></label>
+ </div>
+ <div class="radio">
+ <label><%= radio_button f, :add_stored, "no" %>Linked <i class="fa fa-link"></i></label>
+ </div>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <div class="col-sm-offset-2 col-sm-10">
+ <%= submit "Add", [class: "btn btn-default"] %>
+ </div>
+ </div>
+ <% end %>
+ </fieldset>
+<% end %>
diff --git a/shardweb/lib/templates/identity/view.html.eex b/shardweb/lib/templates/identity/view.html.eex
index 3fb5a3f..c5dfaf2 100644
--- a/shardweb/lib/templates/identity/view.html.eex
+++ b/shardweb/lib/templates/identity/view.html.eex
@@ -2,7 +2,8 @@
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">
- <%= @view_nick %> <small><%= @view_pk |> Base.encode16 %></small>
+ <%= @view_nick %>
+ <small class="shard_uri"><%= @manifest |> ShardURI.from_manifest %></small>
</h1>
<ol class="breadcrumb">
<li>
diff --git a/shardweb/lib/templates/layout/app.html.eex b/shardweb/lib/templates/layout/app.html.eex
index 1310131..bd5e3b2 100644
--- a/shardweb/lib/templates/layout/app.html.eex
+++ b/shardweb/lib/templates/layout/app.html.eex
@@ -127,6 +127,14 @@
<!-- Sidebar Menu Items - These collapse to the responsive navigation menu on small screens -->
<div class="collapse navbar-collapse navbar-ex1-collapse">
<ul class="nav navbar-nav side-nav">
+
+ <li class="<%= if @view_module == ShardWeb.Directoryview and @view_template == "view.html" do "active" else "" end %>">
+ <a href="<%= directory_path(@conn, :view_pub, Base.encode16(@pk), "collection") %>"><i class="fa fa-fw fa-globe"></i> My public stuff</a>
+ </li>
+ <li class="<%= if @view_module == ShardWeb.Directoryview and @view_template == "view.html" do "active" else "" end %>">
+ <a href="<%= directory_path(@conn, :view_priv, Base.encode16(@pk), "collection") %>"><i class="fa fa-fw fa-folder"></i> My private stuff</a>
+ </li>
+
<li class="<%= if @view_module == ShardWeb.ChatView and @view_template == "chat.html" and @public do "active" else "" end %>">
<a href="javascript:;" data-toggle="collapse" data-target="#demo"><i class="fa fa-fw fa-hashtag"></i> Chat rooms <i class="fa fa-fw fa-caret-down"></i></a>
<ul id="demo" class="<%= if not (@view_module == ShardWeb.ChatView and @view_template == "chat.html" and @public) do "collapse" else "" end %>">
diff --git a/shardweb/lib/templates/page/shard_list.html.eex b/shardweb/lib/templates/page/shard_list.html.eex
index dd846d8..3fffef7 100644
--- a/shardweb/lib/templates/page/shard_list.html.eex
+++ b/shardweb/lib/templates/page/shard_list.html.eex
@@ -23,21 +23,21 @@
<tr>
<th></th>
<th>Shard</th>
- <th>Id</th>
+ <th>URI</th>
</tr>
- <%= for {id, manifest, why_have_it} <- shard_list() do %>
+ <%= for {_id, manifest, why_have_it} <- shard_list() do %>
<tr>
<td>
<%= case why_have_it do %>
<% {:cached, _} -> %><span class="label label-warning">cache</span>
<% {:pinned, _, _} -> %><span class="label label-success">pinned</span>
- <% {:req, a, _} -> %><span class="label label-default">req (<%= MapSet.size(a) %>)</span>
+ <% {:req, a, _} -> %><span class="label label-success">req (<%= MapSet.size(a) %>)</span>
<% end %>
</td>
<td>
<%= render "shard_entry.html", conn: @conn, manifest: manifest, pk: @pk %>
</td>
- <td><small><%= id |> Base.encode16 %></small></td>
+ <td><small class="shard_uri"><%= manifest |> ShardURI.from_manifest %></small></td>
</tr>
<% end %>
</table>
diff --git a/shardweb/lib/views/directory_view.ex b/shardweb/lib/views/directory_view.ex
index acbd950..ea31482 100644
--- a/shardweb/lib/views/directory_view.ex
+++ b/shardweb/lib/views/directory_view.ex
@@ -4,6 +4,6 @@ defmodule ShardWeb.DirectoryView do
def dir_contents(_conn, manifest) do
IO.puts(inspect manifest)
pid = Shard.Manager.find_or_start manifest
- SApp.Directory.get_files pid
+ SApp.Directory.get_items pid
end
end