352 lines
11 KiB
Elixir
352 lines
11 KiB
Elixir
defmodule PlausibleWeb.Live.Installation do
|
|
@moduledoc """
|
|
User assistance module around Plausible installation instructions/onboarding
|
|
"""
|
|
|
|
use Plausible
|
|
use PlausibleWeb, :live_view
|
|
|
|
require Logger
|
|
|
|
alias PlausibleWeb.Flows
|
|
alias Phoenix.LiveView.AsyncResult
|
|
alias PlausibleWeb.Live.Installation.Icons
|
|
alias PlausibleWeb.Live.Installation.Instructions
|
|
|
|
on_ee do
|
|
alias Plausible.InstallationSupport.{Detection, Result}
|
|
end
|
|
|
|
def mount(
|
|
%{"domain" => domain} = params,
|
|
_session,
|
|
socket
|
|
) do
|
|
site =
|
|
Plausible.Sites.get_for_user!(socket.assigns.current_user, domain,
|
|
roles: [
|
|
:owner,
|
|
:admin,
|
|
:editor,
|
|
:super_admin,
|
|
:viewer
|
|
]
|
|
)
|
|
|
|
flow = params["flow"] || Flows.provisioning()
|
|
|
|
socket =
|
|
on_ee do
|
|
if connected?(socket) do
|
|
assign_async(
|
|
socket,
|
|
[
|
|
:recommended_installation_type,
|
|
:installation_type,
|
|
:tracker_script_configuration_form,
|
|
:v1_detected
|
|
],
|
|
fn -> initialize_installation_data(flow, site, params) end
|
|
)
|
|
else
|
|
assign_loading_states(socket)
|
|
end
|
|
else
|
|
# On Community Edition, there's no v1 detection, nor pre-installation
|
|
# site scan - we just default the pre-selected tab to "manual".
|
|
|
|
# Although it's functionally unnecessary, we stick to using `%AsyncResult{}`
|
|
# for these assigns to minimize branching out the CE code and maintain only
|
|
# a single `render` function.
|
|
|
|
{:ok, installation_data} = initialize_installation_data(flow, site, params)
|
|
|
|
assign(socket,
|
|
recommended_installation_type: %AsyncResult{
|
|
result: installation_data.recommended_installation_type,
|
|
ok?: true
|
|
},
|
|
installation_type: %AsyncResult{
|
|
result: installation_data.installation_type,
|
|
ok?: true
|
|
},
|
|
tracker_script_configuration_form: %AsyncResult{
|
|
result: installation_data.tracker_script_configuration_form,
|
|
ok?: true
|
|
},
|
|
v1_detected: %AsyncResult{
|
|
result: installation_data.v1_detected,
|
|
ok?: true
|
|
}
|
|
)
|
|
end
|
|
|
|
{:ok,
|
|
assign(socket,
|
|
site: site,
|
|
flow: flow
|
|
)}
|
|
end
|
|
|
|
def handle_params(params, _url, socket) do
|
|
socket =
|
|
if connected?(socket) && socket.assigns.recommended_installation_type.result &&
|
|
params["type"] in PlausibleWeb.Tracker.supported_installation_types() do
|
|
assign(socket,
|
|
installation_type: %AsyncResult{result: params["type"]}
|
|
)
|
|
else
|
|
socket
|
|
end
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
def render(assigns) do
|
|
~H"""
|
|
<div>
|
|
<PlausibleWeb.Components.FlowProgress.render flow={@flow} current_step="Install Plausible" />
|
|
|
|
<.focus_box>
|
|
<.async_result :let={recommended_installation_type} assign={@recommended_installation_type}>
|
|
<:loading>
|
|
<div class="text-center text-gray-500">
|
|
{if(@flow == Flows.review(),
|
|
do: "Scanning your site to detect how Plausible is integrated...",
|
|
else: "Determining the simplest integration path for your website..."
|
|
)}
|
|
</div>
|
|
<div class="flex items-center justify-center py-8">
|
|
<.spinner class="w-6 h-6" />
|
|
</div>
|
|
</:loading>
|
|
|
|
<div class="grid grid-cols-2 sm:flex sm:flex-row gap-1 rounded-md">
|
|
<.tab
|
|
patch={"?type=manual&flow=#{@flow}"}
|
|
selected={@installation_type.result == "manual"}
|
|
>
|
|
<Icons.script_icon /> Script
|
|
</.tab>
|
|
<.tab
|
|
patch={"?type=wordpress&flow=#{@flow}"}
|
|
selected={@installation_type.result == "wordpress"}
|
|
>
|
|
<Icons.wordpress_icon /> WordPress
|
|
</.tab>
|
|
<%= on_ee do %>
|
|
<.tab patch={"?type=gtm&flow=#{@flow}"} selected={@installation_type.result == "gtm"}>
|
|
<Icons.tag_manager_icon /> Tag Manager
|
|
</.tab>
|
|
<% end %>
|
|
<.tab patch={"?type=npm&flow=#{@flow}"} selected={@installation_type.result == "npm"}>
|
|
<Icons.npm_icon /> NPM
|
|
</.tab>
|
|
</div>
|
|
|
|
<%= on_ee do %>
|
|
<.outdated_script_notice
|
|
:if={@v1_detected.result == true}
|
|
recommended_installation_type={@recommended_installation_type}
|
|
installation_type={@installation_type}
|
|
/>
|
|
<% end %>
|
|
|
|
<.form for={@tracker_script_configuration_form.result} phx-submit="submit" class="mt-4">
|
|
<.input
|
|
type="hidden"
|
|
field={@tracker_script_configuration_form.result[:installation_type]}
|
|
value={@installation_type.result}
|
|
/>
|
|
<Instructions.manual_instructions
|
|
:if={@installation_type.result == "manual"}
|
|
tracker_script_configuration_form={@tracker_script_configuration_form.result}
|
|
/>
|
|
|
|
<Instructions.wordpress_instructions
|
|
:if={@installation_type.result == "wordpress"}
|
|
flow={@flow}
|
|
recommended_installation_type={recommended_installation_type}
|
|
/>
|
|
<%= on_ee do %>
|
|
<Instructions.gtm_instructions
|
|
:if={@installation_type.result == "gtm"}
|
|
recommended_installation_type={recommended_installation_type}
|
|
tracker_script_configuration_form={@tracker_script_configuration_form.result}
|
|
/>
|
|
<% end %>
|
|
<Instructions.npm_instructions :if={@installation_type.result == "npm"} />
|
|
|
|
<.button type="submit" class="w-full mt-8">
|
|
{verify_cta(@installation_type.result)}
|
|
</.button>
|
|
</.form>
|
|
</.async_result>
|
|
<:footer :if={ce?() and @installation_type.result == "manual"}>
|
|
<.focus_list>
|
|
<:item>
|
|
Still using the legacy snippet with the data-domain attribute? See
|
|
<.styled_link href="https://plausible.io/docs/script-update-guide">
|
|
migration guide
|
|
</.styled_link>
|
|
</:item>
|
|
</.focus_list>
|
|
</:footer>
|
|
</.focus_box>
|
|
</div>
|
|
"""
|
|
end
|
|
|
|
defp verify_cta("manual"), do: "Verify Script installation"
|
|
defp verify_cta("wordpress"), do: "Verify WordPress installation"
|
|
defp verify_cta("gtm"), do: "Verify Tag Manager installation"
|
|
defp verify_cta("npm"), do: "Verify NPM installation"
|
|
|
|
on_ee do
|
|
defp detect_recommended_installation_type(flow, site) do
|
|
with {:ok, detection_result} <-
|
|
Detection.Checks.run_with_rate_limit(nil, site.domain,
|
|
detect_v1?: flow == Flows.review(),
|
|
report_to: nil,
|
|
slowdown: 0,
|
|
async?: false
|
|
),
|
|
%Result{ok?: true, data: data} <-
|
|
Detection.Checks.interpret_diagnostics(detection_result) do
|
|
{data.suggested_technology, data.v1_detected}
|
|
else
|
|
_ -> {PlausibleWeb.Tracker.fallback_installation_type(), false}
|
|
end
|
|
end
|
|
else
|
|
defp detect_recommended_installation_type(_flow, _site) do
|
|
{PlausibleWeb.Tracker.fallback_installation_type(), false}
|
|
end
|
|
end
|
|
|
|
on_ee do
|
|
defp outdated_script_notice(assigns) do
|
|
~H"""
|
|
<div :if={
|
|
@recommended_installation_type.result == "manual" and
|
|
@installation_type.result == "manual"
|
|
}>
|
|
<.notice class="mt-4" theme={:yellow}>
|
|
Your website is running an outdated version of the tracking script. Please
|
|
<.styled_link new_tab href="https://plausible.io/docs/script-update-guide">
|
|
update
|
|
</.styled_link>
|
|
your tracking script before continuing
|
|
</.notice>
|
|
</div>
|
|
|
|
<div :if={
|
|
@recommended_installation_type.result == "gtm" and
|
|
@installation_type.result == "gtm"
|
|
}>
|
|
<.notice class="mt-4" theme={:yellow}>
|
|
Your website might be using an outdated version of our Google Tag Manager template.
|
|
If so,
|
|
<.styled_link new_tab href="https://plausible.io/docs/script-update-guide#gtm">
|
|
update
|
|
</.styled_link>
|
|
your Google Tag Manager template before continuing
|
|
</.notice>
|
|
</div>
|
|
"""
|
|
end
|
|
|
|
defp assign_loading_states(socket) do
|
|
assign(socket,
|
|
recommended_installation_type: AsyncResult.loading(),
|
|
v1_detected: AsyncResult.loading(),
|
|
installation_type: AsyncResult.loading(),
|
|
tracker_script_configuration_form: AsyncResult.loading()
|
|
)
|
|
end
|
|
end
|
|
|
|
attr :selected, :boolean, default: false
|
|
attr :patch, :string, required: true
|
|
slot :inner_block, required: true
|
|
|
|
defp tab(assigns) do
|
|
base_classes =
|
|
"rounded-md px-3.5 py-2.5 text-sm font-medium flex items-center flex-1 justify-center whitespace-nowrap hover:bg-gray-100 dark:hover:bg-gray-750 transition-colors duration-150"
|
|
|
|
selected_class =
|
|
if assigns[:selected] do
|
|
"bg-gray-150 dark:bg-gray-700"
|
|
else
|
|
"bg-transparent cursor-pointer"
|
|
end
|
|
|
|
assigns = assign(assigns, class: "#{selected_class} #{base_classes}")
|
|
|
|
~H"""
|
|
<.link patch={@patch} class={@class}>
|
|
{render_slot(@inner_block)}
|
|
</.link>
|
|
"""
|
|
end
|
|
|
|
def handle_event("submit", %{"tracker_script_configuration" => params}, socket) do
|
|
config =
|
|
PlausibleWeb.Tracker.update_script_configuration!(
|
|
socket.assigns.site,
|
|
params,
|
|
:installation
|
|
)
|
|
|
|
{:noreply,
|
|
push_navigate(socket,
|
|
to:
|
|
Routes.site_path(socket, :verification, socket.assigns.site.domain,
|
|
flow: socket.assigns.flow,
|
|
installation_type: config.installation_type
|
|
)
|
|
)}
|
|
end
|
|
|
|
defp initialize_installation_data(flow, site, params) do
|
|
{recommended_installation_type, v1_detected} =
|
|
detect_recommended_installation_type(flow, site)
|
|
|
|
tracker_script_configuration =
|
|
PlausibleWeb.Tracker.get_or_create_tracker_script_configuration!(site, %{
|
|
outbound_links: true,
|
|
form_submissions: true,
|
|
file_downloads: true,
|
|
track_404_pages: true,
|
|
installation_type: recommended_installation_type
|
|
})
|
|
|
|
selected_installation_type =
|
|
cond do
|
|
params["type"] in PlausibleWeb.Tracker.supported_installation_types() ->
|
|
params["type"]
|
|
|
|
flow == Flows.review() and
|
|
not is_nil(tracker_script_configuration.installation_type) ->
|
|
Atom.to_string(tracker_script_configuration.installation_type)
|
|
|
|
true ->
|
|
recommended_installation_type
|
|
end
|
|
|
|
{:ok,
|
|
%{
|
|
recommended_installation_type: recommended_installation_type,
|
|
v1_detected: v1_detected,
|
|
installation_type: selected_installation_type,
|
|
tracker_script_configuration_form:
|
|
to_form(
|
|
Plausible.Site.TrackerScriptConfiguration.installation_changeset(
|
|
tracker_script_configuration,
|
|
%{}
|
|
)
|
|
)
|
|
}}
|
|
end
|
|
end
|