# `PhoenixKitKnowledgeBase.Places`
[🔗](https://github.com/BeamLabEU/phoenix_kit_knowledge_base/blob/v0.1.0/lib/phoenix_kit_knowledge_base/places.ex#L1)

Where a drop-in notes panel's notes live.

A panel is given a **path** — `["CRM", "Clients", "Acme"]`. The first segment
names the vault, the rest the folder chain inside it, and the panel shows that
folder and everything under it. Nothing exists until it is needed: the first
note or folder creates the vault and the chain (`ensure/2`).

Two kinds of place share a path:

  * `:shared` ("everyone here") — an **organisation vault** of that name, so
    the admin pages, its managers and its members work on it as on any other.
    Who may use it through a panel is the host's call: the `:authorize` hook.
  * `:private` ("only me") — the person's own **personal vault** of that
    name. Only they can reach it; the hook is not asked and cannot widen it.

A vault is found by `path_key` (the slugified first segment), which only a
vault created here carries — an organisation vault that merely happens to be
called "CRM" is not adopted. Names are not unique and slugs are unique across
everyone's vaults, so neither could say which vault a path means.

Notes are kept by path: a path built from something that changes (a client's
name) points at a new, empty folder after the change, and the old notes stay
where they were. Hosts should put something stable in the path.

## Access

    config :phoenix_kit_knowledge_base, :authorize, {MyApp.Notes, :can?}

`can?(action, user, path)`, with `action` `:view` or `:edit` and `path` the
panel's path, is asked about shared places only. Unconfigured, any signed-in
user may use a shared place, as with boards. A hook that raises, throws, exits
or answers anything but a boolean is a **no** — logged without its arguments.
It grants access through a panel, under its path; the admin pages keep their
own rules.

# `action`

```elixir
@type action() :: :view | :edit
```

# `t`

```elixir
@type t() :: %PhoenixKitKnowledgeBase.Places{
  folder: String.t(),
  key: String.t(),
  path: [String.t()],
  vault_name: String.t(),
  visibility: visibility()
}
```

A parsed panel path. `folder` is `""` for the vault root.

# `visibility`

```elixir
@type visibility() :: :shared | :private
```

# `authorize`

```elixir
@spec authorize(action(), PhoenixKit.Users.Auth.User.t() | nil, t()) :: boolean()
```

Whether `user` may `action` (`:view` / `:edit`) the place. See "Access".

# `ensure`

```elixir
@spec ensure(t(), PhoenixKit.Users.Auth.User.t() | nil) ::
  {:ok, PhoenixKitKnowledgeBase.Schemas.Vault.t()} | {:error, term()}
```

The place's vault and folder chain, created as needed. Asks `authorize/3` for
`:edit` first.

Two people writing the first note at once both reach the insert; the second
loses on the unique index, re-reads, and uses the first one's vault.

# `locate`

```elixir
@spec locate(t(), PhoenixKit.Users.Auth.User.t() | nil) ::
  PhoenixKitKnowledgeBase.Schemas.Vault.t() | nil
```

The vault a place lives in, if it has been created. No side effects — this is
what a panel shows before anyone has written anything.

# `parse`

```elixir
@spec parse(term(), term()) :: {:ok, t()} | {:error, term()}
```

Validates a panel's path and visibility.

Every segment must be usable as a file or directory name (`Storage.validate_filename/1`):
a segment is user-visible text *and* a directory on disk. An invalid one is a
developer error, reported, never written.

# `within?`

```elixir
@spec within?(t(), String.t() | nil) :: boolean()
```

Whether `relative_path` (a note's folder or a folder) sits inside the place's
folder — the fence every panel event checks, so a crafted event cannot reach
the rest of the vault.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
