Packages

phoenix_kit

1.7.49
1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit_web controllers context_controller.ex
Raw

lib/phoenix_kit_web/controllers/context_controller.ex

defmodule PhoenixKitWeb.ContextController do
@moduledoc """
Controller for handling dashboard context switching.
Provides POST endpoints that set the selected context ID(s) in the session
and redirect back to the referring page.
## Routes
- `POST /context/:id` - Legacy single selector (backward compatible)
- `POST /context/:key/:id` - Multi-selector with keyed context
## Multi-Selector Behavior
When using multiple selectors, changing a parent selector will automatically
reset dependent selectors based on their `on_parent_change` configuration:
- `:reset` (default) - Clears the dependent selector's stored ID
- `:keep` - Preserves the dependent selector's stored ID
- `{:redirect, path}` - Redirects to specified path instead of referer
"""
use PhoenixKitWeb, :controller
alias PhoenixKit.Dashboard.ContextSelector
@doc """
Sets the current context for a specific selector key.
This is the multi-selector route that handles `POST /context/:key/:id`.
## Parameters
- `key` - The selector key (e.g., "organization", "project")
- `id` - The context ID to set as current
## Response
Redirects to the referer URL, or `/dashboard` if no referer is present.
Dependent selectors may be reset based on their `on_parent_change` setting.
"""
def set(conn, %{"key" => key, "id" => id}) do
key_atom = String.to_existing_atom(key)
configs = ContextSelector.get_all_configs()
# Find the config for this key
config = Enum.find(configs, fn c -> c.key == key_atom end)
if config && config.enabled do
redirect_path = get_redirect_path(conn)
# Get current stored IDs
current_ids = get_session(conn, ContextSelector.multi_session_key()) || %{}
# Update with new ID
updated_ids = Map.put(current_ids, key, id)
# Reset dependent selectors
updated_ids = reset_dependent_selectors(updated_ids, key_atom, configs)
conn
|> put_session(ContextSelector.multi_session_key(), updated_ids)
|> redirect(to: redirect_path)
else
# Try legacy handling if key not found in multi-selector
set_legacy(conn, %{"id" => id})
end
rescue
ArgumentError ->
# Key doesn't exist as atom - try legacy route
set_legacy(conn, %{"id" => id})
end
# Legacy single selector route for backward compatibility.
# Handles `POST /context/:id` when multi-selector is not configured.
def set(conn, %{"id" => id}) do
set_legacy(conn, %{"id" => id})
end
def set(conn, _params) do
conn
|> put_flash(:error, "Invalid context")
|> redirect(to: get_redirect_path(conn))
end
@doc """
Sets context using the legacy single-selector session key.
"""
def set_legacy(conn, %{"id" => id}) do
config = ContextSelector.get_config()
if config.enabled do
session_key = config.session_key
redirect_path = get_redirect_path(conn)
conn
|> put_session(session_key, id)
|> redirect(to: redirect_path)
else
conn
|> put_flash(:error, "Context switching is not enabled")
|> redirect(to: default_redirect())
end
end
# Private functions
defp get_redirect_path(conn) do
referer = get_req_header(conn, "referer") |> List.first()
if is_nil(referer) or referer == "" do
default_redirect()
else
parse_referer(referer, conn)
end
end
defp parse_referer(referer, conn) do
case URI.parse(referer) do
%URI{host: host, path: path} when is_binary(path) ->
# Only allow same-host redirects for security
if same_host?(host, conn) do
path
else
default_redirect()
end
_ ->
default_redirect()
end
end
defp same_host?(nil, _conn), do: true
defp same_host?("", _conn), do: true
defp same_host?(referer_host, conn) do
request_host = conn.host
referer_host == request_host
end
defp default_redirect do
url_prefix = PhoenixKit.Config.get_url_prefix()
"#{url_prefix}/dashboard"
end
defp reset_dependent_selectors(ids, changed_key, configs) do
# Find all selectors that depend on the changed key
dependent_keys = ContextSelector.get_dependent_keys(configs, changed_key)
# Reset each dependent selector based on its on_parent_change setting
Enum.reduce(dependent_keys, ids, fn dep_key, acc_ids ->
dep_config = Enum.find(configs, fn c -> c.key == dep_key end)
case dep_config && dep_config.on_parent_change do
:reset ->
# Remove the stored ID so it defaults to first
Map.delete(acc_ids, to_string(dep_key))
:keep ->
# Keep the current ID
acc_ids
{:redirect, _path} ->
# For redirect, still reset the ID (redirect is handled elsewhere)
Map.delete(acc_ids, to_string(dep_key))
_ ->
# Default to reset
Map.delete(acc_ids, to_string(dep_key))
end
end)
end
end