all developments of milestone 1

This commit is contained in:
setop
2024-04-10 12:36:21 +00:00
parent a78dc261e5
commit 7030d56864
266 changed files with 5391 additions and 2609 deletions

View File

@@ -9,7 +9,8 @@ defmodule Mobilizon.Admin do
alias Mobilizon.Actors.Actor
alias Mobilizon.{Admin, Users}
alias Mobilizon.Admin.ActionLog
alias Mobilizon.Admin.Setting
alias Mobilizon.Admin.{Setting, SettingMedia}
alias Mobilizon.Medias.Media
alias Mobilizon.Storage.{Page, Repo}
alias Mobilizon.Users.User
@@ -78,9 +79,47 @@ defmodule Mobilizon.Admin do
defp stringify_struct(struct), do: struct
@spec get_all_admin_settings :: list(Setting.t())
@spec get_all_admin_settings :: map()
def get_all_admin_settings do
Repo.all(Setting)
medias =
SettingMedia
|> Repo.all()
|> Repo.preload(:media)
|> Enum.map(fn %SettingMedia{group: group, name: name, media: media} ->
{group, name, media}
end)
values =
Setting
|> Repo.all()
|> Enum.map(fn %Setting{group: group, name: name, value: value} ->
{group, name, get_setting_value(value)}
end)
all_settings = Enum.concat(values, medias)
Enum.reduce(
all_settings,
%{},
# For each {group,name,value}
fn {group, name, value}, acc ->
# We update the %{group: map} in the accumulator
{_, new_acc} =
Map.get_and_update(
acc,
group,
# We put the %{name: value} into the %{group: map}
fn group_map ->
{
group_map,
Map.put(group_map || %{}, name, value)
}
end
)
new_acc
end
)
end
@spec get_admin_setting_value(String.t(), String.t(), String.t() | nil) ::
@@ -119,21 +158,40 @@ defmodule Mobilizon.Admin do
end
end
@spec get_admin_setting_media(String.t(), String.t(), String.t() | nil) ::
{:ok, Media.t()} | {:error, :not_found} | nil
def get_admin_setting_media(group, name, fallback \\ nil)
when is_binary(group) and is_binary(name) do
case SettingMedia
|> where(group: ^group)
|> where(name: ^name)
|> preload(:media)
|> Repo.one() do
nil ->
fallback
%SettingMedia{media: media} ->
media
%SettingMedia{} ->
fallback
end
end
@spec save_settings(String.t(), map()) :: {:ok, any} | {:error, any}
def save_settings(group, args) do
{medias, values} = Map.split(args, [:instance_logo, :instance_favicon, :default_picture])
Multi.new()
|> do_save_setting(group, args)
|> do_save_media_setting(group, medias)
|> do_save_value_setting(group, values)
|> Repo.transaction()
end
def clear_settings(group) do
Setting |> where([s], s.group == ^group) |> Repo.delete_all()
end
@spec do_save_value_setting(Ecto.Multi.t(), String.t(), map()) :: Ecto.Multi.t()
defp do_save_value_setting(transaction, _group, args) when args == %{}, do: transaction
@spec do_save_setting(Ecto.Multi.t(), String.t(), map()) :: Ecto.Multi.t()
defp do_save_setting(transaction, _group, args) when args == %{}, do: transaction
defp do_save_setting(transaction, group, args) do
defp do_save_value_setting(transaction, group, args) do
key = hd(Map.keys(args))
{val, rest} = Map.pop(args, key)
@@ -150,7 +208,40 @@ defmodule Mobilizon.Admin do
conflict_target: [:group, :name]
)
do_save_setting(transaction, group, rest)
do_save_value_setting(transaction, group, rest)
end
@spec do_save_media_setting(Ecto.Multi.t(), String.t(), map()) :: Ecto.Multi.t()
defp do_save_media_setting(transaction, _group, args) when args == %{}, do: transaction
defp do_save_media_setting(transaction, group, args) do
key = hd(Map.keys(args))
{val, rest} = Map.pop(args, key)
transaction =
case val do
val ->
Multi.insert(
transaction,
key,
SettingMedia.changeset(%SettingMedia{}, %{
group: group,
name: Atom.to_string(key),
media: val
}),
on_conflict: :replace_all,
conflict_target: [:group, :name]
)
end
do_save_media_setting(transaction, group, rest)
end
def clear_settings(group) do
Multi.new()
|> Multi.delete_all(:settings, Setting |> where([s], s.group == ^group))
|> Multi.delete_all(:settings_medias, SettingMedia |> where([s], s.group == ^group))
|> Repo.transaction()
end
@spec convert_to_string(any()) :: String.t()

View File

@@ -4,6 +4,7 @@ defmodule Mobilizon.Admin.Setting do
"""
use Ecto.Schema
import Ecto.Changeset
alias Ecto.Changeset
@required_attrs [:group, :name]
@optional_attrs [:value]
@@ -32,3 +33,93 @@ defmodule Mobilizon.Admin.Setting do
|> unique_constraint(:group, name: :admin_settings_group_name_index)
end
end
defmodule Mobilizon.Admin.SettingMedia do
@moduledoc """
A Key-Value settings table for media settings
"""
use Ecto.Schema
import Ecto.Changeset
alias Ecto.Changeset
alias Mobilizon.Federation.ActivityPub.Relay
alias Mobilizon.Medias
alias Mobilizon.Medias.Media
alias Mobilizon.Storage.Repo
@required_attrs [:group, :name]
@type t :: %{
group: String.t(),
name: String.t(),
media: Media.t()
}
schema "admin_settings_medias" do
field(:group, :string)
field(:name, :string)
belongs_to(:media, Media, on_replace: :delete)
timestamps()
end
@doc false
@spec changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
def changeset(setting_media, attrs) do
setting_media
|> Repo.preload(:media)
|> cast(attrs, @required_attrs)
|> put_media(attrs)
|> validate_required(@required_attrs)
|> unique_constraint(:group, name: :admin_settings_medias_group_name_index)
end
# # In case the provided media is an existing one
@spec put_media(Changeset.t(), map) :: Changeset.t()
defp put_media(%Changeset{} = changeset, %{media: %{media_id: id}}) do
%Media{} = media = Medias.get_media!(id)
put_assoc(changeset, :media, media)
end
# In case it's a new media
defp put_media(%Changeset{} = changeset, %{media: %{media: media}}) do
{:ok, media} = upload_media(media)
put_assoc(changeset, :media, media)
end
# In case there is no media
defp put_media(%Changeset{} = changeset, _media) do
put_assoc(changeset, :media, nil)
end
import Mobilizon.Web.Gettext
@spec upload_media(map) :: {:ok, Media.t()} | {:error, any}
defp upload_media(%{file: %Plug.Upload{} = file} = args) do
with {:ok,
%{
name: _name,
url: url,
content_type: content_type,
size: size
} = uploaded} <-
Mobilizon.Web.Upload.store(file),
args <-
args
|> Map.put(:url, url)
|> Map.put(:size, size)
|> Map.put(:content_type, content_type),
{:ok, media = %Media{}} <-
Medias.create_media(%{
file: args,
actor_id: Map.get(args, :actor_id, Relay.get_actor().id),
metadata: Map.take(uploaded, [:width, :height, :blurhash])
}) do
{:ok, media}
else
{:error, :mime_type_not_allowed} ->
{:error, dgettext("errors", "File doesn't have an allowed MIME type.")}
error ->
{:error, error}
end
end
end

View File

@@ -4,7 +4,8 @@ defmodule Mobilizon.Config do
"""
alias Mobilizon.Actors
alias Mobilizon.Admin.Setting
alias Mobilizon.Admin
alias Mobilizon.Medias.Media
alias Mobilizon.Service.GitStatus
require Logger
import Mobilizon.Service.Export.Participants.Common, only: [enabled_formats: 0]
@@ -29,56 +30,18 @@ defmodule Mobilizon.Config do
@spec instance_config :: mobilizon_config
def instance_config, do: Application.get_env(:mobilizon, :instance)
@spec db_instance_config :: list(Setting.t())
def db_instance_config, do: Mobilizon.Admin.get_all_admin_settings()
@spec config_cache :: map()
def config_cache do
case Cachex.fetch(:config, :all_db_config, fn _key ->
value =
Enum.reduce(
Mobilizon.Admin.get_all_admin_settings(),
%{},
&arrange_values/2
)
{:commit, value}
end) do
case Cachex.fetch(
:config,
:all_db_config,
fn _key -> {:commit, Admin.get_all_admin_settings()} end
) do
{status, value} when status in [:ok, :commit] -> value
_err -> %{}
end
end
@spec arrange_values(Setting.t(), map()) :: map()
defp arrange_values(setting, acc) do
{_, new_data} =
Map.get_and_update(acc, setting.group, fn current_value ->
new_value = current_value || %{}
{current_value, Map.put(new_value, setting.name, process_value(setting.value))}
end)
new_data
end
@spec process_value(String.t() | nil) :: any()
defp process_value(nil), do: nil
defp process_value(""), do: nil
defp process_value(value) do
case Jason.decode(value) do
{:ok, val} ->
val
{:error, _} ->
case value do
"true" -> true
"false" -> false
value -> value
end
end
end
@spec config_cached_value(String.t(), String.t(), String.t()) :: any()
def config_cached_value(group, name, fallback \\ nil) do
config_cache()
@@ -115,10 +78,23 @@ defmodule Mobilizon.Config do
@spec instance_slogan :: String.t() | nil
def instance_slogan, do: config_cached_value("instance", "instance_slogan")
@spec instance_logo :: Media.t() | nil
def instance_logo, do: config_cached_value("instance", "instance_logo")
@spec instance_favicon :: Media.t() | nil
def instance_favicon, do: config_cached_value("instance", "instance_favicon")
@spec default_picture :: Media.t() | nil
def default_picture, do: config_cached_value("instance", "default_picture")
@spec primary_color :: Media.t() | nil
def primary_color, do: config_cached_value("instance", "primary_color")
@spec secondary_color :: Media.t() | nil
def secondary_color, do: config_cached_value("instance", "secondary_color")
@spec contact :: String.t() | nil
def contact do
config_cached_value("instance", "contact")
end
def contact, do: config_cached_value("instance", "contact")
@spec instance_terms(String.t()) :: String.t()
def instance_terms(locale \\ "en") do
@@ -202,6 +178,9 @@ defmodule Mobilizon.Config do
@spec instance_demo_mode? :: boolean
def instance_demo_mode?, do: to_boolean(instance_config()[:demo])
@spec instance_long_events? :: boolean
def instance_long_events?, do: instance_config()[:duration_of_long_event] > 0
@spec instance_repository :: String.t()
def instance_repository, do: instance_config()[:repository]
@@ -470,6 +449,9 @@ defmodule Mobilizon.Config do
instance_slogan: instance_slogan(),
registrations_open: instance_registrations_open?(),
contact: contact(),
primary_color: primary_color(),
secondary_color: secondary_color(),
instance_logo: instance_logo(),
instance_terms: instance_terms(),
instance_terms_type: instance_terms_type(),
instance_terms_url: instance_terms_url(),

View File

@@ -25,6 +25,7 @@ defmodule Mobilizon.Events.EventOptions do
show_participation_price: boolean,
offers: [EventOffer.t()],
participation_condition: [EventParticipationCondition.t()],
hide_number_of_participants: boolean,
show_start_time: boolean,
show_end_time: boolean,
timezone: String.t() | nil,
@@ -41,6 +42,7 @@ defmodule Mobilizon.Events.EventOptions do
:program,
:comment_moderation,
:show_participation_price,
:hide_number_of_participants,
:show_start_time,
:show_end_time,
:timezone,
@@ -59,6 +61,7 @@ defmodule Mobilizon.Events.EventOptions do
field(:program, :string)
field(:comment_moderation, CommentModeration)
field(:show_participation_price, :boolean)
field(:hide_number_of_participants, :boolean, default: false)
field(:show_start_time, :boolean, default: true)
field(:show_end_time, :boolean, default: true)
field(:timezone, :string)

View File

@@ -16,6 +16,7 @@ defmodule Mobilizon.Events do
alias Mobilizon.Actors.{Actor, Follower}
alias Mobilizon.Addresses.Address
alias Mobilizon.Config
alias Mobilizon.Events.{
Event,
@@ -571,6 +572,7 @@ defmodule Mobilizon.Events do
|> events_for_search_query()
|> events_for_begins_on(Map.get(args, :begins_on, DateTime.utc_now()))
|> events_for_ends_on(Map.get(args, :ends_on))
|> events_for_longevents(args)
|> events_for_category(args)
|> events_for_categories(args)
|> events_for_languages(args)
@@ -1377,6 +1379,38 @@ defmodule Mobilizon.Events do
end
end
@spec events_for_longevents(Ecto.Queryable.t(), map()) :: Ecto.Query.t()
defp events_for_longevents(query, args) do
duration = Config.get([:instance, :duration_of_long_event], 0)
if duration <= 0 do
query
else
longevents = Map.get(args, :longevents)
case longevents do
nil ->
query
true ->
where(
query,
[q],
not is_nil(q.ends_on) and
q.ends_on > fragment("? + '1 days'::interval * ?", q.begins_on, ^duration)
)
false ->
where(
query,
[q],
is_nil(q.ends_on) or
q.ends_on <= fragment("? + '1 days'::interval * ?", q.begins_on, ^duration)
)
end
end
end
@spec events_for_category(Ecto.Queryable.t(), map()) :: Ecto.Query.t()
defp events_for_category(query, %{category: category}) when is_valid_string(category) do
where(query, [q], q.category == ^category)

View File

@@ -185,7 +185,8 @@ defmodule Mobilizon.Medias do
[from: "events_medias", param: "media_id"],
[from: "posts", param: "picture_id"],
[from: "posts_medias", param: "media_id"],
[from: "comments_medias", param: "media_id"]
[from: "comments_medias", param: "media_id"],
[from: "admin_settings_medias", param: "media_id"]
]
|> Enum.map_join(" UNION ", fn [from: from, param: param] ->
"SELECT 1 FROM #{from} WHERE #{from}.#{param} = m0.id"