mirror of
https://github.com/plausible/analytics.git
synced 2024-12-23 09:33:19 +03:00
Add name to shared links (#910)
* Add name to shared links * Add changelog entry
This commit is contained in:
parent
968d280304
commit
58cff47b6f
@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Site switching keybinds (1-9 for respective sites) plausible/analytics#735
|
||||
- Glob (wildcard) based pageview goals plausible/analytics#750
|
||||
- Support for embedding shared links in an iframe plausible/analytics#812
|
||||
- Add name/label to shared links plausible/analytics#910
|
||||
|
||||
### Fixed
|
||||
- Capitalized date/time selection keybinds not working plausible/analytics#709
|
||||
|
@ -4,6 +4,7 @@ defmodule Plausible.Site.SharedLink do
|
||||
|
||||
schema "shared_links" do
|
||||
belongs_to :site, Plausible.Site
|
||||
field :name, :string
|
||||
field :slug, :string
|
||||
field :password_hash, :string
|
||||
field :password, :string, virtual: true
|
||||
@ -13,8 +14,8 @@ defmodule Plausible.Site.SharedLink do
|
||||
|
||||
def changeset(link, attrs \\ %{}) do
|
||||
link
|
||||
|> cast(attrs, [:slug, :password])
|
||||
|> validate_required([:slug])
|
||||
|> cast(attrs, [:slug, :password, :name])
|
||||
|> validate_required([:slug, :name])
|
||||
|> unique_constraint(:slug)
|
||||
|> hash_password()
|
||||
end
|
||||
|
@ -541,6 +541,40 @@ defmodule PlausibleWeb.SiteController do
|
||||
end
|
||||
end
|
||||
|
||||
def edit_shared_link(conn, %{"website" => website, "slug" => slug}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
shared_link = Repo.get_by(Plausible.Site.SharedLink, slug: slug)
|
||||
changeset = Plausible.Site.SharedLink.changeset(shared_link, %{})
|
||||
|
||||
conn
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
|> render("edit_shared_link.html",
|
||||
site: site,
|
||||
changeset: changeset,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
end
|
||||
|
||||
def update_shared_link(conn, %{"website" => website, "slug" => slug, "shared_link" => params}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
shared_link = Repo.get_by(Plausible.Site.SharedLink, slug: slug)
|
||||
changeset = Plausible.Site.SharedLink.changeset(shared_link, params)
|
||||
|
||||
case Repo.update(changeset) do
|
||||
{:ok, _created} ->
|
||||
redirect(conn, to: "/#{URI.encode_www_form(site.domain)}/settings/visibility")
|
||||
|
||||
{:error, changeset} ->
|
||||
conn
|
||||
|> assign(:skip_plausible_tracking, true)
|
||||
|> render("edit_shared_link.html",
|
||||
site: site,
|
||||
changeset: changeset,
|
||||
layout: {PlausibleWeb.LayoutView, "focus.html"}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def delete_shared_link(conn, %{"website" => website, "slug" => slug}) do
|
||||
site = Sites.get_for_user!(conn.assigns[:current_user].id, website)
|
||||
|
||||
|
@ -179,6 +179,8 @@ defmodule PlausibleWeb.Router do
|
||||
|
||||
get "/sites/:website/shared-links/new", SiteController, :new_shared_link
|
||||
post "/sites/:website/shared-links", SiteController, :create_shared_link
|
||||
get "/sites/:website/shared-links/:slug/edit", SiteController, :edit_shared_link
|
||||
put "/sites/:website/shared-links/:slug", SiteController, :update_shared_link
|
||||
delete "/sites/:website/shared-links/:slug", SiteController, :delete_shared_link
|
||||
|
||||
get "/sites/:website/custom-domains/new", SiteController, :new_custom_domain
|
||||
|
12
lib/plausible_web/templates/site/edit_shared_link.html.eex
Normal file
12
lib/plausible_web/templates/site/edit_shared_link.html.eex
Normal file
@ -0,0 +1,12 @@
|
||||
<%= form_for @changeset, "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/#{@changeset.data.slug}", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<h2 class="text-xl font-black dark:text-gray-100">Edit shared link</h2>
|
||||
<div class="my-4">
|
||||
<%= label f, :name, "Name", class: "block text-sm font-medium text-gray-700 dark:text-gray-100" %>
|
||||
<div class="mt-1">
|
||||
<%= text_input f, :name, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md", required: "required" %>
|
||||
<%= error_tag f, :name %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= submit "Edit shared link", class: "button mt-4 w-full" %>
|
||||
<% end %>
|
@ -1,13 +1,23 @@
|
||||
<%= form_for @changeset, "/sites/#{URI.encode_www_form(@site.domain)}/shared-links", [class: "max-w-md w-full mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 pt-6 pb-8 mb-4 mt-8"], fn f -> %>
|
||||
<h2 class="text-xl font-black dark:text-gray-100">New shared link</h2>
|
||||
<div class="my-4 dark:text-gray-100">
|
||||
Add a password or leave it blank so anyone with the link can see the stats.
|
||||
Once the link is created, we cannot reveal the password. Please make sure you save it in a secure place.
|
||||
</div>
|
||||
<div class="my-6">
|
||||
<%= label f, :password, "Password (optional)", class: "block text-sm font-bold dark:text-gray-100" %>
|
||||
<%= password_input f, :password, class: "transition mt-3 bg-gray-100 dark:bg-gray-900 outline-none appearance-none border border-transparent rounded w-full p-2 text-gray-700 dark:text-gray-300 leading-normal focus:outline-none focus:bg-white dark:focus:bg-gray-800 focus:border-gray-300 dark:focus:border-gray-500" %>
|
||||
<%= error_tag f, :password %>
|
||||
<div class="my-4">
|
||||
<%= label f, :name, "Name", class: "block text-sm font-medium text-gray-700 dark:text-gray-100" %>
|
||||
<div class="mt-1">
|
||||
<%= text_input f, :name, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md", required: "required" %>
|
||||
<%= error_tag f, :name %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-4">
|
||||
<%= label f, :password, "Password (optional)", class: "block text-sm font-medium text-gray-700 dark:text-gray-100" %>
|
||||
<div class="mt-1">
|
||||
<%= password_input f, :password, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md" %>
|
||||
<%= error_tag f, :password %>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
Password protection is optional. Please make sure you save it in a secure place. Once the link is created, we cannot reveal the password.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= submit "Create shared link", class: "button mt-4 w-full" %>
|
||||
|
@ -33,16 +33,30 @@
|
||||
<% end %>
|
||||
</header>
|
||||
|
||||
<div class="mt-6">
|
||||
<div class="mt-6 flex flex-col divide-y divide-gray-200">
|
||||
<%= for link <- @shared_links do %>
|
||||
<div class="relative flex w-full max-w-xl mt-2 text-sm">
|
||||
<input type="text" id="<%= link.slug %>" readonly="readonly" value="<%= shared_link_dest(@site, link) %>" class="w-full p-2 text-gray-700 bg-gray-100 border border-transparent rounded rounded-r-none outline-none appearance-none transition dark:bg-gray-900 dark:text-gray-300 focus:outline-none focus:border-gray-300 dark:focus:border-gray-500" />
|
||||
<button onclick="var input = document.getElementById('<%= link.slug %>'); input.focus(); input.select(); document.execCommand('copy');" href="javascript:void(0)" class="px-4 py-2 text-indigo-800 bg-gray-200 border-r border-gray-300 rounded-none dark:bg-gray-850 dark:text-indigo-500 dark:border-gray-500 hover:bg-gray-300 dark:hover:bg-gray-825">
|
||||
<svg class="feather-sm" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
|
||||
</button>
|
||||
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/#{link.slug}", method: :delete, class: "py-2 px-4 bg-gray-200 dark:bg-gray-850 text-red-600 dark:text-red-500 rounded-l-none hover:bg-gray-300 dark:hover:bg-gray-825", data: [confirm: "Are you sure you want to delete this shared link? The stats will not be accessible with this link anymore."]) do %>
|
||||
<svg class="feather feather-sm" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
|
||||
<% end %>
|
||||
<div class="py-4">
|
||||
<label for="<%= link.slug %>" class="flex content-center text-sm font-medium text-gray-700">
|
||||
<%= link.name %>
|
||||
<%= if link.password_hash do %>
|
||||
<svg class="ml-1 w-4 h-4 mt-px" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"></path></svg>
|
||||
<% else %>
|
||||
<svg class="ml-1 w-4 h-4 mt-px" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10 2a5 5 0 00-5 5v2a2 2 0 00-2 2v5a2 2 0 002 2h10a2 2 0 002-2v-5a2 2 0 00-2-2H7V7a3 3 0 015.905-.75 1 1 0 001.937-.5A5.002 5.002 0 0010 2z"></path></svg>
|
||||
<% end %>
|
||||
</label>
|
||||
<div class="relative flex w-full mt-2 text-sm">
|
||||
<input type="text" id="<%= link.slug %>" readonly="readonly" value="<%= shared_link_dest(@site, link) %>" class="w-full p-2 text-gray-700 bg-gray-100 border-none rounded rounded-r-none outline-none appearance-none transition dark:bg-gray-900 dark:text-gray-300 focus:outline-none focus:border-gray-300 dark:focus:border-gray-500" />
|
||||
<button onclick="var input = document.getElementById('<%= link.slug %>'); input.focus(); input.select(); document.execCommand('copy');" href="javascript:void(0)" class="px-4 py-2 inline-flex items-center text-indigo-800 bg-gray-200 border-r border-gray-300 rounded-none dark:bg-gray-850 dark:text-indigo-500 dark:border-gray-500 hover:bg-gray-300 dark:hover:bg-gray-825">
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z"></path><path d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z"></path></svg>
|
||||
<span class="ml-1">Copy</span>
|
||||
</button>
|
||||
<%= link(to: "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/#{link.slug}/edit", class: "px-4 py-2 inline-flex items-center text-indigo-800 bg-gray-200 border-r border-gray-300 rounded-none dark:bg-gray-850 dark:text-indigo-500 dark:border-gray-500 hover:bg-gray-300 dark:hover:bg-gray-825") do %>
|
||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"></path><path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd"></path></svg>
|
||||
<% end %>
|
||||
<%= button(to: "/sites/#{URI.encode_www_form(@site.domain)}/shared-links/#{link.slug}", method: :delete, class: "py-2 px-4 inline-flex items-center bg-gray-200 dark:bg-gray-850 text-red-600 dark:text-red-500 rounded-l-none hover:bg-gray-300 dark:hover:bg-gray-825", data: [confirm: "Are you sure you want to delete this shared link? The stats will not be accessible with this link anymore."]) do %>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
defmodule Plausible.Repo.Migrations.AddNameToSharedLinks do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:shared_links) do
|
||||
add :name, :string
|
||||
end
|
||||
|
||||
execute "UPDATE shared_links SET name=slug"
|
||||
|
||||
alter table(:shared_links) do
|
||||
modify :name, :string, null: false
|
||||
end
|
||||
end
|
||||
end
|
@ -493,23 +493,54 @@ defmodule PlausibleWeb.SiteControllerTest do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "creates shared link without password", %{conn: conn, site: site} do
|
||||
post(conn, "/sites/#{site.domain}/shared-links", %{"shared_link" => %{}})
|
||||
post(conn, "/sites/#{site.domain}/shared-links", %{
|
||||
"shared_link" => %{"name" => "Link name"}
|
||||
})
|
||||
|
||||
link = Repo.one(Plausible.Site.SharedLink)
|
||||
|
||||
refute is_nil(link.slug)
|
||||
assert is_nil(link.password_hash)
|
||||
assert link.name == "Link name"
|
||||
end
|
||||
|
||||
test "creates shared link with password", %{conn: conn, site: site} do
|
||||
post(conn, "/sites/#{site.domain}/shared-links", %{
|
||||
"shared_link" => %{"password" => "password"}
|
||||
"shared_link" => %{"password" => "password", "name" => "New name"}
|
||||
})
|
||||
|
||||
link = Repo.one(Plausible.Site.SharedLink)
|
||||
|
||||
refute is_nil(link.slug)
|
||||
refute is_nil(link.password_hash)
|
||||
assert link.name == "New name"
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /sites/:website/shared-links/edit" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "shows form to edit shared link", %{conn: conn, site: site} do
|
||||
link = insert(:shared_link, site: site)
|
||||
conn = get(conn, "/sites/#{site.domain}/shared-links/#{link.slug}/edit")
|
||||
|
||||
assert html_response(conn, 200) =~ "Edit shared link"
|
||||
end
|
||||
end
|
||||
|
||||
describe "PUT /sites/:website/shared-links/:slug" do
|
||||
setup [:create_user, :log_in, :create_site]
|
||||
|
||||
test "can update link name", %{conn: conn, site: site} do
|
||||
link = insert(:shared_link, site: site)
|
||||
|
||||
put(conn, "/sites/#{site.domain}/shared-links/#{link.slug}", %{
|
||||
"shared_link" => %{"name" => "Updated link name"}
|
||||
})
|
||||
|
||||
link = Repo.one(Plausible.Site.SharedLink)
|
||||
|
||||
assert link.name == "Updated link name"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -147,6 +147,7 @@ defmodule Plausible.Factory do
|
||||
|
||||
def shared_link_factory do
|
||||
%Plausible.Site.SharedLink{
|
||||
name: "Link name",
|
||||
slug: Nanoid.generate()
|
||||
}
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user