aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorkaniini <nenolod@gmail.com>2019-04-22 02:07:19 +0000
committerkaniini <nenolod@gmail.com>2019-04-22 02:07:19 +0000
commit7fcbda702e76b6390076c28832f5aea80086d15a (patch)
tree265bf6acf11ad7987850c8733ea1b36248cc8c24 /lib
parent6c0e07b8bdc85f757e2d1e6993e64f2e1540deb6 (diff)
parent3cefdc62e0dda75985221853fb123572d6fc068b (diff)
downloadpleroma-7fcbda702e76b6390076c28832f5aea80086d15a.tar.gz
Merge branch 'rework-emoji-management' into 'develop'
Remove finmoji and add a way to download emojis in packs Closes #817 and #821 See merge request pleroma/pleroma!1073
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/pleroma/emoji.ex293
-rw-r--r--lib/pleroma/emoji.ex165
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex2
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex2
4 files changed, 378 insertions, 84 deletions
diff --git a/lib/mix/tasks/pleroma/emoji.ex b/lib/mix/tasks/pleroma/emoji.ex
new file mode 100644
index 000000000..2754dd876
--- /dev/null
+++ b/lib/mix/tasks/pleroma/emoji.ex
@@ -0,0 +1,293 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Mix.Tasks.Pleroma.Emoji do
+ use Mix.Task
+
+ @shortdoc "Manages emoji packs"
+ @moduledoc """
+ Manages emoji packs
+
+ ## ls-packs
+
+ mix pleroma.emoji ls-packs [OPTION...]
+
+ Lists the emoji packs and metadata specified in the manifest.
+
+ ### Options
+
+ - `-m, --manifest PATH/URL` - path to a custom manifest, it can
+ either be an URL starting with `http`, in that case the
+ manifest will be fetched from that address, or a local path
+
+ ## get-packs
+
+ mix pleroma.emoji get-packs [OPTION...] PACKS
+
+ Fetches, verifies and installs the specified PACKS from the
+ manifest into the `STATIC-DIR/emoji/PACK-NAME
+
+ ### Options
+
+ - `-m, --manifest PATH/URL` - same as ls-packs
+
+ ## gen-pack
+
+ mix pleroma.emoji gen-pack PACK-URL
+
+ Creates a new manifest entry and a file list from the specified
+ remote pack file. Currently, only .zip archives are recognized
+ as remote pack files and packs are therefore assumed to be zip
+ archives. This command is intended to run interactively and will
+ first ask you some basic questions about the pack, then download
+ the remote file and generate an SHA256 checksum for it, then
+ generate an emoji file list for you.
+
+ The manifest entry will either be written to a newly created
+ `index.json` file or appended to the existing one, *replacing*
+ the old pack with the same name if it was in the file previously.
+
+ The file list will be written to the file specified previously,
+ *replacing* that file. You _should_ check that the file list doesn't
+ contain anything you don't need in the pack, that is, anything that is
+ not an emoji (the whole pack is downloaded, but only emoji files
+ are extracted).
+ """
+
+ @default_manifest Pleroma.Config.get!([:emoji, :default_manifest])
+
+ def run(["ls-packs" | args]) do
+ Application.ensure_all_started(:hackney)
+
+ {options, [], []} = parse_global_opts(args)
+
+ manifest =
+ fetch_manifest(if options[:manifest], do: options[:manifest], else: @default_manifest)
+
+ Enum.each(manifest, fn {name, info} ->
+ to_print = [
+ {"Name", name},
+ {"Homepage", info["homepage"]},
+ {"Description", info["description"]},
+ {"License", info["license"]},
+ {"Source", info["src"]}
+ ]
+
+ for {param, value} <- to_print do
+ IO.puts(IO.ANSI.format([:bright, param, :normal, ": ", value]))
+ end
+
+ # A newline
+ IO.puts("")
+ end)
+ end
+
+ def run(["get-packs" | args]) do
+ Application.ensure_all_started(:hackney)
+
+ {options, pack_names, []} = parse_global_opts(args)
+
+ manifest_url = if options[:manifest], do: options[:manifest], else: @default_manifest
+
+ manifest = fetch_manifest(manifest_url)
+
+ for pack_name <- pack_names do
+ if Map.has_key?(manifest, pack_name) do
+ pack = manifest[pack_name]
+ src_url = pack["src"]
+
+ IO.puts(
+ IO.ANSI.format([
+ "Downloading ",
+ :bright,
+ pack_name,
+ :normal,
+ " from ",
+ :underline,
+ src_url
+ ])
+ )
+
+ binary_archive = Tesla.get!(src_url).body
+ archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
+
+ sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
+
+ if archive_sha == String.upcase(pack["src_sha256"]) do
+ IO.puts(IO.ANSI.format(sha_status_text ++ [:green, "OK"]))
+ else
+ IO.puts(IO.ANSI.format(sha_status_text ++ [:red, "BAD"]))
+
+ raise "Bad SHA256 for #{pack_name}"
+ end
+
+ # The url specified in files should be in the same directory
+ files_url = Path.join(Path.dirname(manifest_url), pack["files"])
+
+ IO.puts(
+ IO.ANSI.format([
+ "Fetching the file list for ",
+ :bright,
+ pack_name,
+ :normal,
+ " from ",
+ :underline,
+ files_url
+ ])
+ )
+
+ files = Tesla.get!(files_url).body |> Poison.decode!()
+
+ IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
+
+ pack_path =
+ Path.join([
+ Pleroma.Config.get!([:instance, :static_dir]),
+ "emoji",
+ pack_name
+ ])
+
+ files_to_unzip =
+ Enum.map(
+ files,
+ fn {_, f} -> to_charlist(f) end
+ )
+
+ {:ok, _} =
+ :zip.unzip(binary_archive,
+ cwd: pack_path,
+ file_list: files_to_unzip
+ )
+
+ IO.puts(IO.ANSI.format(["Writing emoji.txt for ", :bright, pack_name]))
+
+ emoji_txt_str =
+ Enum.map(
+ files,
+ fn {shortcode, path} ->
+ emojo_path = Path.join("/emoji/#{pack_name}", path)
+ "#{shortcode}, #{emojo_path}"
+ end
+ )
+ |> Enum.join("\n")
+
+ File.write!(Path.join(pack_path, "emoji.txt"), emoji_txt_str)
+ else
+ IO.puts(IO.ANSI.format([:bright, :red, "No pack named \"#{pack_name}\" found"]))
+ end
+ end
+ end
+
+ def run(["gen-pack", src]) do
+ Application.ensure_all_started(:hackney)
+
+ proposed_name = Path.basename(src) |> Path.rootname()
+ name = String.trim(IO.gets("Pack name [#{proposed_name}]: "))
+ # If there's no name, use the default one
+ name = if String.length(name) > 0, do: name, else: proposed_name
+
+ license = String.trim(IO.gets("License: "))
+ homepage = String.trim(IO.gets("Homepage: "))
+ description = String.trim(IO.gets("Description: "))
+
+ proposed_files_name = "#{name}.json"
+ files_name = String.trim(IO.gets("Save file list to [#{proposed_files_name}]: "))
+ files_name = if String.length(files_name) > 0, do: files_name, else: proposed_files_name
+
+ default_exts = [".png", ".gif"]
+ default_exts_str = Enum.join(default_exts, " ")
+
+ exts =
+ String.trim(
+ IO.gets("Emoji file extensions (separated with spaces) [#{default_exts_str}]: ")
+ )
+
+ exts =
+ if String.length(exts) > 0 do
+ String.split(exts, " ")
+ |> Enum.filter(fn e -> e |> String.trim() |> String.length() > 0 end)
+ else
+ default_exts
+ end
+
+ IO.puts("Downloading the pack and generating SHA256")
+
+ binary_archive = Tesla.get!(src).body
+ archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
+
+ IO.puts("SHA256 is #{archive_sha}")
+
+ pack_json = %{
+ name => %{
+ license: license,
+ homepage: homepage,
+ description: description,
+ src: src,
+ src_sha256: archive_sha,
+ files: files_name
+ }
+ }
+
+ tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
+
+ {:ok, _} =
+ :zip.unzip(
+ binary_archive,
+ cwd: tmp_pack_dir
+ )
+
+ emoji_map = Pleroma.Emoji.make_shortcode_to_file_map(tmp_pack_dir, exts)
+
+ File.write!(files_name, Poison.encode!(emoji_map, pretty: true))
+
+ IO.puts("""
+
+ #{files_name} has been created and contains the list of all found emojis in the pack.
+ Please review the files in the remove those not needed.
+ """)
+
+ if File.exists?("index.json") do
+ existing_data = File.read!("index.json") |> Poison.decode!()
+
+ File.write!(
+ "index.json",
+ Poison.encode!(
+ Map.merge(
+ existing_data,
+ pack_json
+ ),
+ pretty: true
+ )
+ )
+
+ IO.puts("index.json file has been update with the #{name} pack")
+ else
+ File.write!("index.json", Poison.encode!(pack_json, pretty: true))
+
+ IO.puts("index.json has been created with the #{name} pack")
+ end
+ end
+
+ defp fetch_manifest(from) do
+ Poison.decode!(
+ if String.starts_with?(from, "http") do
+ Tesla.get!(from).body
+ else
+ File.read!(from)
+ end
+ )
+ end
+
+ defp parse_global_opts(args) do
+ OptionParser.parse(
+ args,
+ strict: [
+ manifest: :string
+ ],
+ aliases: [
+ m: :manifest
+ ]
+ )
+ end
+end
diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex
index 87c7f2cec..6390cce4c 100644
--- a/lib/pleroma/emoji.ex
+++ b/lib/pleroma/emoji.ex
@@ -6,7 +6,7 @@ defmodule Pleroma.Emoji do
@moduledoc """
The emojis are loaded from:
- * the built-in Finmojis (if enabled in configuration),
+ * emoji packs in INSTANCE-DIR/emoji
* the files: `config/emoji.txt` and `config/custom_emoji.txt`
* glob paths, nested folder is used as tag name for grouping e.g. priv/static/emoji/custom/nested_folder
@@ -14,6 +14,8 @@ defmodule Pleroma.Emoji do
"""
use GenServer
+ require Logger
+
@type pattern :: Regex.t() | module() | String.t()
@type patterns :: pattern() | [pattern()]
@type group_patterns :: keyword(patterns())
@@ -79,95 +81,94 @@ defmodule Pleroma.Emoji do
end
defp load do
- finmoji_enabled = Keyword.get(Application.get_env(:pleroma, :instance), :finmoji_enabled)
+ emoji_dir_path =
+ Path.join(
+ Pleroma.Config.get!([:instance, :static_dir]),
+ "emoji"
+ )
+
+ case File.ls(emoji_dir_path) do
+ {:error, :enoent} ->
+ # The custom emoji directory doesn't exist,
+ # don't do anything
+ nil
+
+ {:error, e} ->
+ # There was some other error
+ Logger.error("Could not access the custom emoji directory #{emoji_dir_path}: #{e}")
+
+ {:ok, packs} ->
+ # Print the packs we've found
+ Logger.info("Found emoji packs: #{Enum.join(packs, ", ")}")
+
+ emojis =
+ Enum.flat_map(
+ packs,
+ fn pack -> load_pack(Path.join(emoji_dir_path, pack)) end
+ )
+
+ true = :ets.insert(@ets, emojis)
+ end
+
+ # Compat thing for old custom emoji handling & default emoji,
+ # it should run even if there are no emoji packs
shortcode_globs = Application.get_env(:pleroma, :emoji)[:shortcode_globs] || []
emojis =
- (load_finmoji(finmoji_enabled) ++
- load_from_file("config/emoji.txt") ++
+ (load_from_file("config/emoji.txt") ++
load_from_file("config/custom_emoji.txt") ++
load_from_globs(shortcode_globs))
|> Enum.reject(fn value -> value == nil end)
true = :ets.insert(@ets, emojis)
+
:ok
end
- @finmoji [
- "a_trusted_friend",
- "alandislands",
- "association",
- "auroraborealis",
- "baby_in_a_box",
- "bear",
- "black_gold",
- "christmasparty",
- "crosscountryskiing",
- "cupofcoffee",
- "education",
- "fashionista_finns",
- "finnishlove",
- "flag",
- "forest",
- "four_seasons_of_bbq",
- "girlpower",
- "handshake",
- "happiness",
- "headbanger",
- "icebreaker",
- "iceman",
- "joulutorttu",
- "kaamos",
- "kalsarikannit_f",
- "kalsarikannit_m",
- "karjalanpiirakka",
- "kicksled",
- "kokko",
- "lavatanssit",
- "losthopes_f",
- "losthopes_m",
- "mattinykanen",
- "meanwhileinfinland",
- "moominmamma",
- "nordicfamily",
- "out_of_office",
- "peacemaker",
- "perkele",
- "pesapallo",
- "polarbear",
- "pusa_hispida_saimensis",
- "reindeer",
- "sami",
- "sauna_f",
- "sauna_m",
- "sauna_whisk",
- "sisu",
- "stuck",
- "suomimainittu",
- "superfood",
- "swan",
- "the_cap",
- "the_conductor",
- "the_king",
- "the_voice",
- "theoriginalsanta",
- "tomoffinland",
- "torillatavataan",
- "unbreakable",
- "waiting",
- "white_nights",
- "woollysocks"
- ]
-
- defp load_finmoji(true) do
- Enum.map(@finmoji, fn finmoji ->
- file_name = "/finmoji/128px/#{finmoji}-128.png"
- group = match_extra(@groups, file_name)
- {finmoji, file_name, to_string(group)}
- end)
+ defp load_pack(pack_dir) do
+ pack_name = Path.basename(pack_dir)
+
+ emoji_txt = Path.join(pack_dir, "emoji.txt")
+
+ if File.exists?(emoji_txt) do
+ load_from_file(emoji_txt)
+ else
+ Logger.info(
+ "No emoji.txt found for pack \"#{pack_name}\", assuming all .png files are emoji"
+ )
+
+ make_shortcode_to_file_map(pack_dir, [".png"])
+ |> Enum.map(fn {shortcode, rel_file} ->
+ filename = Path.join("/emoji/#{pack_name}", rel_file)
+
+ {shortcode, filename, [to_string(match_extra(@groups, filename))]}
+ end)
+ end
+ end
+
+ def make_shortcode_to_file_map(pack_dir, exts) do
+ find_all_emoji(pack_dir, exts)
+ |> Enum.map(&Path.relative_to(&1, pack_dir))
+ |> Enum.map(fn f -> {f |> Path.basename() |> Path.rootname(), f} end)
+ |> Enum.into(%{})
end
- defp load_finmoji(_), do: []
+ def find_all_emoji(dir, exts) do
+ Enum.reduce(
+ File.ls!(dir),
+ [],
+ fn f, acc ->
+ filepath = Path.join(dir, f)
+
+ if File.dir?(filepath) do
+ acc ++ find_all_emoji(filepath, exts)
+ else
+ acc ++ [filepath]
+ end
+ end
+ )
+ |> Enum.filter(fn f -> Path.extname(f) in exts end)
+ end
defp load_from_file(file) do
if File.exists?(file) do
@@ -182,11 +183,11 @@ defmodule Pleroma.Emoji do
|> Stream.map(&String.trim/1)
|> Stream.map(fn line ->
case String.split(line, ~r/,\s*/) do
- [name, file, tags] ->
- {name, file, tags}
-
[name, file] ->
- {name, file, to_string(match_extra(@groups, file))}
+ {name, file, [to_string(match_extra(@groups, file))]}
+
+ [name, file | tags] ->
+ {name, file, tags}
_ ->
nil
@@ -209,7 +210,7 @@ defmodule Pleroma.Emoji do
tag = match_extra(@groups, Path.join("/", Path.relative_to(path, static_path)))
shortcode = Path.basename(path, Path.extname(path))
external_path = Path.join("/", Path.relative_to(path, static_path))
- {shortcode, external_path, to_string(tag)}
+ {shortcode, external_path, [to_string(tag)]}
end)
end
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 485e79a0d..d271d3786 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -190,7 +190,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
"static_url" => url,
"visible_in_picker" => true,
"url" => url,
- "tags" => String.split(tags, ",")
+ "tags" => tags
}
end)
end
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 9441984c7..8665e058a 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -286,7 +286,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
emoji =
Emoji.get_all()
|> Enum.map(fn {short_code, path, tags} ->
- {short_code, %{image_url: path, tags: String.split(tags, ",")}}
+ {short_code, %{image_url: path, tags: tags}}
end)
|> Enum.into(%{})