Validate external_links on both server and client sides.
Display errors from SaveAdminSettings to the user.
This commit is contained in:
@@ -5,6 +5,7 @@ defmodule Mobilizon.Admin do
|
|||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import EctoEnum
|
import EctoEnum
|
||||||
|
import Mobilizon.Web.Gettext, only: [dgettext: 2]
|
||||||
|
|
||||||
alias Mobilizon.Actors.Actor
|
alias Mobilizon.Actors.Actor
|
||||||
alias Mobilizon.{Admin, Users}
|
alias Mobilizon.{Admin, Users}
|
||||||
@@ -182,10 +183,19 @@ defmodule Mobilizon.Admin do
|
|||||||
def save_settings(group, args) do
|
def save_settings(group, args) do
|
||||||
{medias, values} = Map.split(args, [:instance_logo, :instance_favicon, :default_picture])
|
{medias, values} = Map.split(args, [:instance_logo, :instance_favicon, :default_picture])
|
||||||
|
|
||||||
Multi.new()
|
multi =
|
||||||
|> do_save_media_setting(group, medias)
|
Multi.new()
|
||||||
|> do_save_value_setting(group, values)
|
|> do_save_media_setting(group, medias)
|
||||||
|> Repo.transaction()
|
|> do_save_value_setting(group, values)
|
||||||
|
|> Repo.transaction()
|
||||||
|
|
||||||
|
case multi do
|
||||||
|
{:ok, result} ->
|
||||||
|
{:ok, result}
|
||||||
|
|
||||||
|
{:error, _err, %Ecto.Changeset{} = err, _} ->
|
||||||
|
{:error, err}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec do_save_value_setting(Ecto.Multi.t(), String.t(), map()) :: Ecto.Multi.t()
|
@spec do_save_value_setting(Ecto.Multi.t(), String.t(), map()) :: Ecto.Multi.t()
|
||||||
@@ -195,15 +205,20 @@ defmodule Mobilizon.Admin do
|
|||||||
key = hd(Map.keys(args))
|
key = hd(Map.keys(args))
|
||||||
{val, rest} = Map.pop(args, key)
|
{val, rest} = Map.pop(args, key)
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
%Setting{}
|
||||||
|
|> Setting.changeset(%{
|
||||||
|
group: group,
|
||||||
|
name: Atom.to_string(key),
|
||||||
|
value: convert_to_string(val)
|
||||||
|
})
|
||||||
|
|> maybe_validate_external_links(key, val)
|
||||||
|
|
||||||
transaction =
|
transaction =
|
||||||
Multi.insert(
|
Multi.insert(
|
||||||
transaction,
|
transaction,
|
||||||
key,
|
key,
|
||||||
Setting.changeset(%Setting{}, %{
|
changeset,
|
||||||
group: group,
|
|
||||||
name: Atom.to_string(key),
|
|
||||||
value: convert_to_string(val)
|
|
||||||
}),
|
|
||||||
on_conflict: :replace_all,
|
on_conflict: :replace_all,
|
||||||
conflict_target: [:group, :name]
|
conflict_target: [:group, :name]
|
||||||
)
|
)
|
||||||
@@ -251,4 +266,38 @@ defmodule Mobilizon.Admin do
|
|||||||
val -> to_string(val)
|
val -> to_string(val)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_validate_external_links(changeset, :external_links, external_links) do
|
||||||
|
external_links
|
||||||
|
|> List.wrap()
|
||||||
|
|> Enum.reduce(changeset, fn link, cs ->
|
||||||
|
case validate_external_link(link) do
|
||||||
|
{:ok, _} -> cs
|
||||||
|
{:error, msg} -> Ecto.Changeset.add_error(cs, :external_links, msg)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_validate_external_links(changeset, _key, _val), do: changeset
|
||||||
|
|
||||||
|
defp validate_external_link(%{:url => url, :label => label} = link) do
|
||||||
|
uri = URI.parse(url)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
is_nil(label) or String.trim(label) == "" ->
|
||||||
|
{:error, dgettext("errors", "External link label cannot be blank")}
|
||||||
|
|
||||||
|
String.length(label) < 2 ->
|
||||||
|
{:error, dgettext("errors", "External link label must be at least 2 characters")}
|
||||||
|
|
||||||
|
String.length(label) > 256 ->
|
||||||
|
{:error, dgettext("errors", "External link label must be at most 256 characters")}
|
||||||
|
|
||||||
|
not (uri.scheme in ["http", "https"] and is_binary(uri.host) and uri.host != "") ->
|
||||||
|
{:error, dgettext("errors", "External link URL must be a valid http/https URL")}
|
||||||
|
|
||||||
|
true ->
|
||||||
|
{:ok, link}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1292,7 +1292,7 @@
|
|||||||
"This profile is from another instance, the informations shown here may be incomplete.": "Ce profil provient d'une autre instance, les informations montrées ici peuvent être incomplètes.",
|
"This profile is from another instance, the informations shown here may be incomplete.": "Ce profil provient d'une autre instance, les informations montrées ici peuvent être incomplètes.",
|
||||||
"This profile is located on this instance, so you need to {access_the_corresponding_account} to suspend it.": "Ce profil se situe sur cette instance, vous devez donc {access_the_corresponding_account} afin de le suspendre.",
|
"This profile is located on this instance, so you need to {access_the_corresponding_account} to suspend it.": "Ce profil se situe sur cette instance, vous devez donc {access_the_corresponding_account} afin de le suspendre.",
|
||||||
"This profile was not found": "Ce profil n'a pas été trouvé",
|
"This profile was not found": "Ce profil n'a pas été trouvé",
|
||||||
"This section lets you add links to external websites to the menu.": "Cette section vous permet d'ajouter des liens vers des sites internets externes au menu.",
|
"This section lets you add links to external websites to the menu.": "Cette section vous permet d'ajouter des liens vers des sites internet externes au menu.",
|
||||||
"This setting will be used to display the website and send you emails in the correct language.": "Ce paramètre sera utilisé pour l'affichage du site et pour vous envoyer des courriels dans la bonne langue.",
|
"This setting will be used to display the website and send you emails in the correct language.": "Ce paramètre sera utilisé pour l'affichage du site et pour vous envoyer des courriels dans la bonne langue.",
|
||||||
"This URL doesn't seem to be valid": "Cette URL ne semble pas être valide",
|
"This URL doesn't seem to be valid": "Cette URL ne semble pas être valide",
|
||||||
"This URL is not supported": "Cette URL n'est pas supportée",
|
"This URL is not supported": "Cette URL n'est pas supportée",
|
||||||
|
|||||||
@@ -480,11 +480,17 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<o-field :label="t('URL')" class="!mt-0"
|
<o-field :label="t('URL')" class="!mt-0"
|
||||||
><o-input expanded v-model="link.url" type="text"
|
><o-input expanded v-model="link.url" type="url" required
|
||||||
/></o-field>
|
/></o-field>
|
||||||
|
|
||||||
<o-field :label="t('Label')"
|
<o-field :label="t('Label')"
|
||||||
><o-input expanded v-model="link.label" type="text"
|
><o-input
|
||||||
|
expanded
|
||||||
|
v-model="link.label"
|
||||||
|
type="text"
|
||||||
|
minlength="2"
|
||||||
|
maxlength="256"
|
||||||
|
required
|
||||||
/></o-field>
|
/></o-field>
|
||||||
|
|
||||||
<o-field
|
<o-field
|
||||||
@@ -609,7 +615,7 @@ const addLink = () => {
|
|||||||
settingsToWrite.value.externalLinks.push({
|
settingsToWrite.value.externalLinks.push({
|
||||||
url: "",
|
url: "",
|
||||||
label: "",
|
label: "",
|
||||||
enabled: false,
|
enabled: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -700,7 +706,9 @@ saveAdminSettingsDone(() => {
|
|||||||
|
|
||||||
saveAdminSettingsError((e) => {
|
saveAdminSettingsError((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
notifier?.error(t("Failed to save admin settings") as string);
|
notifier?.error(
|
||||||
|
(t("Failed to save admin settings") as string) + ": " + e.message
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateSettings = async (): Promise<void> => {
|
const updateSettings = async (): Promise<void> => {
|
||||||
|
|||||||
Reference in New Issue
Block a user