Packages
phoenix_html
2.11.0
4.3.0
4.2.1
4.2.0
4.1.1
4.1.0
4.0.0
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.0
3.1.0
3.0.4
3.0.3
retired
3.0.2
retired
3.0.1
retired
3.0.0
retired
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.0
2.11.2
2.11.1
2.11.0
2.10.5
2.10.4
2.10.3
2.10.2
2.10.1
2.10.0
2.9.3
2.9.2
2.9.1
2.9.0
2.8.0
2.7.0
2.7.0-dev
2.6.2
2.6.1
2.6.0
2.5.1
2.5.0
2.4.0
2.4.0-dev
2.3.1
2.3.0
2.2.0
2.1.2
2.1.1
2.1.0
2.0.1
2.0.0
2.0.0-dev
1.4.0
1.3.0
1.2.1
1.2.0
1.1.0
1.0.1
1.0.0
Phoenix view functions for working with HTML templates
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/phoenix_html/link.ex
defmodule Phoenix.HTML.Link do
@moduledoc """
Conveniences for working with links and URLs in HTML.
"""
import Phoenix.HTML.Tag
@doc """
Generates a link to the given URL.
## Examples
link("hello", to: "/world")
#=> <a href="/world">hello</a>
link("<hello>", to: "/world")
#=> <a href="/world"><hello></a>
link("<hello>", to: "/world", class: "btn")
#=> <a class="btn" href="/world"><hello></a>
link("delete", to: "/the_world", data: [confirm: "Really?"])
#=> <a data-confirm="Really?" href="/the_world">delete</a>
# If you supply a method other than `:get`:
link("delete", to: "/everything", method: :delete)
#=> <a href="#" data-csrf="csrf_token" data-method="delete" data-to="/everything">delete</a>
# You can use a `do ... end` block too:
link to: "/hello" do
"world"
end
## Options
* `:to` - the page to link to. This option is required
* `:method` - the method to use with the link. In case the
method is not `:get`, the link is generated inside the form
which sets the proper information. In order to submit the
form, JavaScript must be enabled
All other options are forwarded to the underlying `<a>` tag.
## Data attributes
Data attributes are added as a keyword list passed to the
`data` key. The following data attributes are supported:
* `data-confirm` - shows a confirmation prompt before
generating and submitting the form when `:method`
is not `:get`.
## JavaScript dependency
In order to support links where `:method` is not `:get`
or use the above data attributes, `Phoenix.HTML` relies
on JavaScript. You can load `priv/static/phoenix_html.js`
into your build tool.
## CSRF Protection
By default, CSRF tokens are generated through `Plug.CSRFProtection`.
"""
@valid_uri_schemes [
"http:",
"https:",
"ftp:",
"ftps:",
"mailto:",
"news:",
"irc:",
"gopher:",
"nntp:",
"feed:",
"telnet:",
"mms:",
"rtsp:",
"svn:",
"tel:",
"fax:",
"xmpp:"
]
def link(text, opts)
def link(opts, do: contents) when is_list(opts) do
link(contents, opts)
end
def link(_text, opts) when not is_list(opts) do
raise ArgumentError, "link/2 requires a keyword list as second argument"
end
def link(text, opts) do
{to, opts} = pop_required_option!(opts, :to, "expected non-nil value for :to in link/2")
to = valid_destination!(to, "link/2")
{method, opts} = Keyword.pop(opts, :method, :get)
if method == :get do
opts = skip_csrf(opts)
content_tag(:a, text, [href: to] ++ opts)
else
{csrf_data, opts} = csrf_data(to, opts)
opts = Keyword.put_new(opts, :rel, "nofollow")
content_tag(:a, text, [href: "#", data: [method: method, to: to] ++ csrf_data] ++ opts)
end
end
@doc false
# No docs since this function is only called when a `do` block is passed as
# `do:` instead of `do...end` (and that case is documented in `link/2`).
def link(opts) when is_list(opts) do
error = "link/2 requires a text as first argument or contents in the :do block"
{contents, opts} = pop_required_option!(opts, :do, error)
link(contents, opts)
end
@doc """
Generates a button that uses a regular HTML form to submit to the given URL.
Useful to ensure that links that change data are not triggered by
search engines and other spidering software.
## Examples
button("hello", to: "/world")
#=> <button class="button" data-csrf="csrf_token" data-method="post" data-to="/world">hello</button>
button("hello", to: "/world", method: "get", class: "btn")
#=> <button class="btn" data-method="get" data-to="/world">hello</button>
## Options
* `:to` - the page to link to. This option is required
* `:method` - the method to use with the button. Defaults to :post.
All other options are forwarded to the underlying button input.
"""
def button(opts, do: contents) do
button(contents, opts)
end
def button(text, opts) do
{to, opts} = pop_required_option!(opts, :to, "option :to is required in button/2")
{method, opts} = Keyword.pop(opts, :method, :post)
to = valid_destination!(to, "button/2")
if method == :get do
opts = skip_csrf(opts)
content_tag(:button, text, [data: [method: method, to: to]] ++ opts)
else
{csrf_data, opts} = csrf_data(to, opts)
content_tag(:button, text, [data: [method: method, to: to] ++ csrf_data] ++ opts)
end
end
defp skip_csrf(opts) do
Keyword.delete(opts, :csrf_token)
end
defp csrf_data(to, opts) do
case Keyword.pop(opts, :csrf_token, true) do
{csrf, opts} when is_binary(csrf) ->
{[csrf: csrf], opts}
{true, opts} ->
{[csrf: Plug.CSRFProtection.get_csrf_token_for(to)], opts}
{false, opts} ->
{[], opts}
end
end
defp pop_required_option!(opts, key, error_message) do
{value, opts} = Keyword.pop(opts, key)
unless value do
raise ArgumentError, error_message
end
{value, opts}
end
defp valid_destination!({:safe, to}, context) do
{:safe, valid_string_destination!(IO.iodata_to_binary(to), context)}
end
defp valid_destination!({other, to}, _context) when is_atom(other) do
[Atom.to_string(other), ?:, to]
end
defp valid_destination!(to, context) do
valid_string_destination!(IO.iodata_to_binary(to), context)
end
for scheme <- @valid_uri_schemes do
defp valid_string_destination!(unquote(scheme) <> _ = string, _context), do: string
end
defp valid_string_destination!(to, context) do
if String.contains?(to, ":") do
raise ArgumentError, """
unsupported scheme given to #{context}. In case you want to link to an
unknown or unsafe scheme, such as javascript, use a tuple: {:javascript, rest}
"""
else
to
end
end
end