mirror of
https://github.com/plausible/analytics.git
synced 2024-12-23 09:33:19 +03:00
Merge branch 'master' into stats-module
This commit is contained in:
commit
7e78157c4b
@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Menu (with auto-complete) to add new and edit existing filters directly plausible/analytics#1089
|
- Menu (with auto-complete) to add new and edit existing filters directly plausible/analytics#1089
|
||||||
- Added `CLICKHOUSE_FLUSH_INTERVAL_MS` and `CLICKHOUSE_MAX_BUFFER_SIZE` configuration parameters plausible/analytics#1073
|
- Added `CLICKHOUSE_FLUSH_INTERVAL_MS` and `CLICKHOUSE_MAX_BUFFER_SIZE` configuration parameters plausible/analytics#1073
|
||||||
- Ability to invite users to sites with different roles plausible/analytics#1122
|
- Ability to invite users to sites with different roles plausible/analytics#1122
|
||||||
|
- Option to configure a custom name for the script file
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix weekly report time range plausible/analytics#951
|
- Fix weekly report time range plausible/analytics#951
|
||||||
@ -23,7 +24,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Crash when changing theme on a loaded dashboard plausible/analytics#1123
|
- Crash when changing theme on a loaded dashboard plausible/analytics#1123
|
||||||
- UI fix for details button overlapping content on mobile plausible/analytics#1114
|
- UI fix for details button overlapping content on mobile plausible/analytics#1114
|
||||||
- UI fix for the main graph on mobile overlapping its tick items on both axis
|
- UI fix for the main graph on mobile overlapping its tick items on both axis
|
||||||
- UI fixes for text not showing properly in bars across multiple lines. This hides the totals on <768px and only shows the uniques and % to accommodate the goals text too.
|
- UI fixes for text not showing properly in bars across multiple lines. This hides the totals on <768px and only shows the uniques and % to accommodate the goals text too. Larger screens still truncate as usual.
|
||||||
- Turn off autocomplete for name and password inputs in the _New shared link_ form.
|
- Turn off autocomplete for name and password inputs in the _New shared link_ form.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
@ -59,14 +59,14 @@ export default class Conversions extends React.Component {
|
|||||||
|
|
||||||
renderGoalText(goalName) {
|
renderGoalText(goalName) {
|
||||||
if (this.props.query.period === 'realtime') {
|
if (this.props.query.period === 'realtime') {
|
||||||
return <span className="block px-2 py-1.5 relative z-9 break-words dark:text-gray-200">{goalName}</span>
|
return <span className="block px-2 py-1.5 relative z-9 md:truncate break-all dark:text-gray-200">{goalName}</span>
|
||||||
} else {
|
} else {
|
||||||
const query = new URLSearchParams(window.location.search)
|
const query = new URLSearchParams(window.location.search)
|
||||||
query.set('goal', goalName)
|
query.set('goal', goalName)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={{pathname: window.location.pathname, search: query.toString()}} className="block px-2 py-1.5 hover:underline relative z-9 break-words dark:text-gray-200">
|
<Link to={{pathname: window.location.pathname, search: query.toString()}} className="block px-2 py-1.5 hover:underline relative z-9 break-all lg:truncate dark:text-gray-200">
|
||||||
{ goalName }
|
{goalName}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -71,10 +71,10 @@ export default class PropertyBreakdown extends React.Component {
|
|||||||
|
|
||||||
renderPropContent(value, query) {
|
renderPropContent(value, query) {
|
||||||
return (
|
return (
|
||||||
<span className="flex px-2 py-1.5 group dark:text-gray-300 relative z-9 break-words">
|
<span className="flex px-2 py-1.5 group dark:text-gray-300 relative z-9 break-all">
|
||||||
<Link
|
<Link
|
||||||
to={{pathname: window.location.pathname, search: query.toString()}}
|
to={{pathname: window.location.pathname, search: query.toString()}}
|
||||||
className="hover:underline block"
|
className="md:truncate hover:underline block"
|
||||||
>
|
>
|
||||||
{ value.name }
|
{ value.name }
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -43,8 +43,8 @@ export default class Browsers extends React.Component {
|
|||||||
|
|
||||||
renderBrowserContent(browser, query) {
|
renderBrowserContent(browser, query) {
|
||||||
return (
|
return (
|
||||||
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-words">
|
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-all">
|
||||||
<Link className="block hover:underline" to={{search: query.toString()}}>
|
<Link className="md:truncate block hover:underline" to={{search: query.toString()}}>
|
||||||
{browser.name}
|
{browser.name}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
|
@ -86,7 +86,7 @@ class ScreenSizes extends React.Component {
|
|||||||
tooltip={EXPLANATION[size.name]}
|
tooltip={EXPLANATION[size.name]}
|
||||||
className="flex px-2 py-1.5 dark:text-gray-300"
|
className="flex px-2 py-1.5 dark:text-gray-300"
|
||||||
>
|
>
|
||||||
<Link className="block hover:underline" to={{search: query.toString()}}>
|
<Link className="md:truncate block hover:underline" to={{search: query.toString()}}>
|
||||||
{iconFor(size.name)} {size.name}
|
{iconFor(size.name)} {size.name}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
|
@ -55,8 +55,8 @@ export default class OperatingSystems extends React.Component {
|
|||||||
bg="bg-green-50 dark:gray-500 dark:bg-opacity-15"
|
bg="bg-green-50 dark:gray-500 dark:bg-opacity-15"
|
||||||
maxWidthDeduction="6rem"
|
maxWidthDeduction="6rem"
|
||||||
>
|
>
|
||||||
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-words">
|
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-all">
|
||||||
<Link className="block hover:underline" to={{search: query.toString()}}>
|
<Link className="md:truncate block hover:underline" to={{search: query.toString()}}>
|
||||||
{os.name}
|
{os.name}
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
|
@ -46,10 +46,10 @@ export default class EntryPages extends React.Component {
|
|||||||
bg="bg-orange-50 dark:bg-gray-500 dark:bg-opacity-15"
|
bg="bg-orange-50 dark:bg-gray-500 dark:bg-opacity-15"
|
||||||
maxWidthDeduction="4rem"
|
maxWidthDeduction="4rem"
|
||||||
>
|
>
|
||||||
<span className="flex px-2 py-1.5 group dark:text-gray-300 relative break-words z-9">
|
<span className="flex px-2 py-1.5 group dark:text-gray-300 relative break-all z-9">
|
||||||
<Link
|
<Link
|
||||||
to={{pathname: window.location.pathname, search: query.toString()}}
|
to={{pathname: window.location.pathname, search: query.toString()}}
|
||||||
className="block hover:underline"
|
className="md:truncate block hover:underline"
|
||||||
>
|
>
|
||||||
{page.name}
|
{page.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -46,10 +46,10 @@ export default class ExitPages extends React.Component {
|
|||||||
bg="bg-orange-50 dark:bg-gray-500 dark:bg-opacity-15"
|
bg="bg-orange-50 dark:bg-gray-500 dark:bg-opacity-15"
|
||||||
maxWidthDeduction="4rem"
|
maxWidthDeduction="4rem"
|
||||||
>
|
>
|
||||||
<span className="flex px-2 py-1.5 group dark:text-gray-300 z-9 relative break-words">
|
<span className="flex px-2 py-1.5 group dark:text-gray-300 z-9 relative break-all">
|
||||||
<Link
|
<Link
|
||||||
to={{pathname: window.location.pathname, search: query.toString()}}
|
to={{pathname: window.location.pathname, search: query.toString()}}
|
||||||
className="block hover:underline"
|
className="md:truncate block hover:underline"
|
||||||
>
|
>
|
||||||
{page.name}
|
{page.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -52,11 +52,11 @@ export default class Visits extends React.Component {
|
|||||||
maxWidthDeduction="4rem"
|
maxWidthDeduction="4rem"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="flex px-2 py-1.5 group dark:text-gray-300 relative z-9 break-words"
|
className="flex px-2 py-1.5 group dark:text-gray-300 relative z-9 break-all"
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
to={{pathname: window.location.pathname, search: query.toString()}}
|
to={{pathname: window.location.pathname, search: query.toString()}}
|
||||||
className="block hover:underline"
|
className="md:truncate block hover:underline"
|
||||||
>
|
>
|
||||||
{page.name}
|
{page.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
@ -78,9 +78,9 @@ export default class Referrers extends React.Component {
|
|||||||
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
|
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
|
||||||
maxWidthDeduction="4rem"
|
maxWidthDeduction="4rem"
|
||||||
>
|
>
|
||||||
<span className="flex px-2 py-1.5 z-9 relative break-words group">
|
<span className="flex px-2 py-1.5 z-9 relative break-all group">
|
||||||
<LinkOption
|
<LinkOption
|
||||||
className="block dark:text-gray-300"
|
className="block md:truncate dark:text-gray-300"
|
||||||
to={{search: query.toString()}}
|
to={{search: query.toString()}}
|
||||||
disabled={referrer.name === 'Direct / None'}
|
disabled={referrer.name === 'Direct / None'}
|
||||||
>
|
>
|
||||||
|
@ -42,8 +42,8 @@ export default class SearchTerms extends React.Component {
|
|||||||
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
|
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
|
||||||
maxWidthDeduction="4rem"
|
maxWidthDeduction="4rem"
|
||||||
>
|
>
|
||||||
<span className="flex px-2 py-1.5 dark:text-gray-300 z-9 relative break-words">
|
<span className="flex px-2 py-1.5 dark:text-gray-300 z-9 relative break-all">
|
||||||
<span className="block">
|
<span className="md:truncate block">
|
||||||
{ term.name }
|
{ term.name }
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -53,9 +53,9 @@ class AllSources extends React.Component {
|
|||||||
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
|
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
|
||||||
maxWidthDeduction="4rem"
|
maxWidthDeduction="4rem"
|
||||||
>
|
>
|
||||||
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-words">
|
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-all">
|
||||||
<Link
|
<Link
|
||||||
className="block hover:underline"
|
className="md:truncate block hover:underline"
|
||||||
to={{search: query.toString()}}
|
to={{search: query.toString()}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@ -171,9 +171,10 @@ class UTMSources extends React.Component {
|
|||||||
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
|
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
|
||||||
maxWidthDeduction="4rem"
|
maxWidthDeduction="4rem"
|
||||||
>
|
>
|
||||||
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-words">
|
|
||||||
|
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-all">
|
||||||
<Link
|
<Link
|
||||||
className="block hover:underline"
|
className="md:truncate block hover:underline"
|
||||||
to={{search: query.toString()}}
|
to={{search: query.toString()}}
|
||||||
>
|
>
|
||||||
{ referrer.name }
|
{ referrer.name }
|
||||||
|
@ -136,6 +136,10 @@ is_selfhost =
|
|||||||
|> get_var_from_path_or_env("SELFHOST", "true")
|
|> get_var_from_path_or_env("SELFHOST", "true")
|
||||||
|> String.to_existing_atom()
|
|> String.to_existing_atom()
|
||||||
|
|
||||||
|
custom_script_name =
|
||||||
|
config_dir
|
||||||
|
|> get_var_from_path_or_env("CUSTOM_SCRIPT_NAME", "script")
|
||||||
|
|
||||||
{site_limit, ""} =
|
{site_limit, ""} =
|
||||||
config_dir
|
config_dir
|
||||||
|> get_var_from_path_or_env("SITE_LIMIT", "50")
|
|> get_var_from_path_or_env("SITE_LIMIT", "50")
|
||||||
@ -171,7 +175,8 @@ config :plausible,
|
|||||||
admin_user_ids: admin_user_ids,
|
admin_user_ids: admin_user_ids,
|
||||||
site_limit: site_limit,
|
site_limit: site_limit,
|
||||||
site_limit_exempt: site_limit_exempt,
|
site_limit_exempt: site_limit_exempt,
|
||||||
is_selfhost: is_selfhost
|
is_selfhost: is_selfhost,
|
||||||
|
custom_script_name: custom_script_name
|
||||||
|
|
||||||
config :plausible, :selfhost,
|
config :plausible, :selfhost,
|
||||||
disable_authentication: disable_auth,
|
disable_authentication: disable_auth,
|
||||||
|
@ -2,7 +2,9 @@ defmodule PlausibleWeb.Tracker do
|
|||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
use Agent
|
use Agent
|
||||||
|
|
||||||
|
custom_script_name = Application.get_env(:plausible, :custom_script_name)
|
||||||
base_variants = ["hash", "outbound-links", "exclusions", "compat"]
|
base_variants = ["hash", "outbound-links", "exclusions", "compat"]
|
||||||
|
base_filenames = ["plausible", custom_script_name]
|
||||||
|
|
||||||
# Generates Power Set of all variants
|
# Generates Power Set of all variants
|
||||||
variants =
|
variants =
|
||||||
@ -24,8 +26,8 @@ defmodule PlausibleWeb.Tracker do
|
|||||||
String.split(x, ".")
|
String.split(x, ".")
|
||||||
|> Combination.permutate()
|
|> Combination.permutate()
|
||||||
|> Enum.map(fn p -> Enum.join(p, ".") end)
|
|> Enum.map(fn p -> Enum.join(p, ".") end)
|
||||||
|> Enum.filter(fn permutation -> permutation != x end)
|
|> Enum.map(fn v -> Enum.map(base_filenames, fn filename -> "#{filename}.#{v}.js" end) end)
|
||||||
|> Enum.map(fn v -> "plausible.#{v}.js" end)
|
|> List.flatten()
|
||||||
|
|
||||||
if Enum.count(variants) > 0 do
|
if Enum.count(variants) > 0 do
|
||||||
{"plausible.#{x}.js", variants}
|
{"plausible.#{x}.js", variants}
|
||||||
@ -33,7 +35,7 @@ defmodule PlausibleWeb.Tracker do
|
|||||||
end)
|
end)
|
||||||
|> Enum.reject(fn x -> x == nil end)
|
|> Enum.reject(fn x -> x == nil end)
|
||||||
|> Enum.into(%{})
|
|> Enum.into(%{})
|
||||||
|> Map.put("plausible.js", ["analytics.js"])
|
|> Map.put("plausible.js", ["analytics.js", "#{custom_script_name}.js"])
|
||||||
|
|
||||||
@templates files_available
|
@templates files_available
|
||||||
@aliases aliases_available
|
@aliases aliases_available
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
defmodule Plausible.Repo.Migrations.MakeInvitationEmailCaseInsensitive do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:invitations) do
|
||||||
|
modify :email, :citext, null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -44,6 +44,28 @@ defmodule PlausibleWeb.SiteControllerTest do
|
|||||||
assert html_response(conn, 200) =~ "<b>3</b> visitors in last 24h"
|
assert html_response(conn, 200) =~ "<b>3</b> visitors in last 24h"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "shows invitations for user by email address", %{conn: conn, user: user} do
|
||||||
|
site = insert(:site)
|
||||||
|
insert(:invitation, email: user.email, site_id: site.id, inviter: build(:user))
|
||||||
|
conn = get(conn, "/sites")
|
||||||
|
|
||||||
|
assert html_response(conn, 200) =~ site.domain
|
||||||
|
end
|
||||||
|
|
||||||
|
test "invitations are case insensitive", %{conn: conn, user: user} do
|
||||||
|
site = insert(:site)
|
||||||
|
|
||||||
|
insert(:invitation,
|
||||||
|
email: String.upcase(user.email),
|
||||||
|
site_id: site.id,
|
||||||
|
inviter: build(:user)
|
||||||
|
)
|
||||||
|
|
||||||
|
conn = get(conn, "/sites")
|
||||||
|
|
||||||
|
assert html_response(conn, 200) =~ site.domain
|
||||||
|
end
|
||||||
|
|
||||||
test "paginates sites", %{conn: conn, user: user} do
|
test "paginates sites", %{conn: conn, user: user} do
|
||||||
insert(:site, members: [user], domain: "test-site1.com")
|
insert(:site, members: [user], domain: "test-site1.com")
|
||||||
insert(:site, members: [user], domain: "test-site2.com")
|
insert(:site, members: [user], domain: "test-site2.com")
|
||||||
|
Loading…
Reference in New Issue
Block a user