defmodule BDS.Scripting.Capabilities.Projects do @moduledoc false import BDS.Scripting.Capabilities.Crud import BDS.Scripting.Capabilities.Util alias BDS.Metadata alias BDS.Projects, as: ProjectsCtx alias BDS.Projects.Project alias BDS.Repo alias BDS.Tags def create_project(attrs), do: attrs |> normalize_map() |> ProjectsCtx.create_project() |> unwrap_result() def delete_project(project_id), do: boolean_result(ProjectsCtx.delete_project(string_or_nil(project_id))) def delete_project_with_data(project_id) do case string_or_nil(project_id) && ProjectsCtx.get_project(string_or_nil(project_id)) do %Project{} = project -> data_dir = ProjectsCtx.project_data_dir(project) case ProjectsCtx.delete_project(project.id) do {:ok, _deleted_project} -> _ = File.rm_rf(data_dir) true {:error, _reason} -> false end _other -> false end end def load_project(project_id) do case string_or_nil(project_id) do nil -> nil id -> ProjectsCtx.get_project(id) |> sanitize_nilable() end end def list_projects do ProjectsCtx.list_projects() |> Enum.map(&sanitize/1) end def set_active_project(project_id) do project_id |> string_or_nil() |> then(fn nil -> {:error, :not_found} id -> ProjectsCtx.set_active_project(id) end) |> unwrap_result() end def update_project(project_id, attrs) do case string_or_nil(project_id) && ProjectsCtx.get_project(string_or_nil(project_id)) do %Project{} = project -> attrs = normalize_map(attrs) updates = %{ name: Map.get(attrs, "name", project.name), description: Map.get(attrs, "description", project.description), data_path: Map.get(attrs, "data_path", project.data_path), updated_at: System.system_time(:millisecond), is_active: Map.get(attrs, "is_active", project.is_active) } project |> Project.changeset(updates) |> Repo.update() |> unwrap_result() _other -> nil end end def load_metadata(project_id) do {:ok, metadata} = Metadata.get_project_metadata(project_id) sanitize(metadata) end def update_project_metadata(project_id, attrs) do Metadata.update_project_metadata(project_id, normalize_map(attrs)) |> unwrap_result() end def add_category(project_id, name) do Metadata.add_category(project_id, string_or_nil(name) || "") |> unwrap_result() end def remove_category(project_id, name) do Metadata.remove_category(project_id, string_or_nil(name) || "") |> unwrap_result() end def metadata_categories(project_id) do load_metadata(project_id) |> Map.get("categories", []) end def metadata_tags(project_id) do project_id |> list_tags() |> Enum.map(&Map.get(&1, "name")) end def add_meta_tag(project_id, name) do normalized_name = string_or_nil(name) |> to_string() |> String.trim() cond do normalized_name == "" -> metadata_tags(project_id) load_tag_by_name(project_id, normalized_name) -> metadata_tags(project_id) true -> create_tag(project_id, %{"name" => normalized_name}) metadata_tags(project_id) end end def remove_meta_tag(project_id, name) do case load_tag_by_name(project_id, name) do %{"id" => tag_id} -> _ = delete_tag(project_id, tag_id) metadata_tags(project_id) _other -> metadata_tags(project_id) end end def publishing_preferences(project_id) do load_metadata(project_id) |> Map.get("publishing_preferences") end def set_publishing_preferences(project_id, prefs) do project_id |> Metadata.set_publishing_preferences(normalize_map(prefs)) |> unwrap_result() |> case do nil -> nil metadata -> Map.get(metadata, "publishing_preferences") end end def clear_publishing_preferences(project_id) do set_publishing_preferences(project_id, %{}) end def sync_meta_on_startup(project_id) do _ = Tags.sync_tags_from_posts(project_id) %{ tags: metadata_tags(project_id), categories: metadata_categories(project_id), project_metadata: load_metadata(project_id) } end def data_paths(project_id) do database_path = Repo.config()[:database] project_dir = project_path(project_id) %{ database: database_path, project: project_dir, posts: Path.join(project_dir, "posts"), media: Path.join(project_dir, "media") } end def read_project_metadata(folder_path) do case project_for_folder(folder_path) do nil -> read_project_metadata_file(folder_path) project -> load_metadata(project.id) end end def project_for_folder(folder_path) do normalized = string_or_nil(folder_path) ProjectsCtx.list_projects() |> Enum.find(fn project -> ProjectsCtx.project_data_dir(project) == normalized end) end def read_project_metadata_file(folder_path) do path = Path.join([string_or_nil(folder_path) || "", "meta", "project.json"]) case File.read(path) do {:ok, contents} -> case Jason.decode(contents) do {:ok, decoded} when is_map(decoded) -> sanitize(decoded) _other -> nil end {:error, _reason} -> nil end end end