Commit 6b3842cf authored by Haelwenn's avatar Haelwenn
Browse files

Merge branch 'remove/mastofe' into 'develop'

Remove MastoFE from Pleroma, fixes #2625

Closes #2625

See merge request !3392
parents 6b1282a8 1841bd83
Pipeline #37447 passed with stages
in 90 minutes and 50 seconds

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.
......@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
### Removed
- MastoFE
### Changed
### Added
......
......@@ -321,9 +321,6 @@
subjectLineBehavior: "email",
theme: "pleroma-dark",
webPushNotifications: false
},
masto_fe: %{
showInstanceSpecificPanel: true
}
config :pleroma, :assets,
......
......@@ -1164,7 +1164,7 @@
type: :group,
description:
"This form can be used to configure a keyword list that keeps the configuration data for any " <>
"kind of frontend. By default, settings for pleroma_fe and masto_fe are configured. If you want to " <>
"kind of frontend. By default, settings for pleroma_fe are configured. If you want to " <>
"add your own configuration your settings all fields must be complete.",
children: [
%{
......@@ -1364,25 +1364,6 @@
suggestions: ["pleroma-dark"]
}
]
},
%{
key: :masto_fe,
label: "Masto FE",
type: :map,
description: "Settings for Masto FE",
suggestions: [
%{
showInstanceSpecificPanel: true
}
],
children: [
%{
key: :showInstanceSpecificPanel,
label: "Show instance specific panel",
type: :boolean,
description: "Whenether to show the instance's specific panel"
}
]
}
]
},
......
......@@ -247,7 +247,7 @@ Notes:
### :frontend_configurations
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` and `masto_fe` are configured. You can find the documentation for `pleroma_fe` configuration into [Pleroma-FE configuration and customization for instance administrators](/frontend/CONFIGURATION/#options).
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` are configured. You can find the documentation for `pleroma_fe` configuration into [Pleroma-FE configuration and customization for instance administrators](/frontend/CONFIGURATION/#options).
Frontends can access these settings at `/api/v1/pleroma/frontend_configurations`
......@@ -258,10 +258,7 @@ config :pleroma, :frontend_configurations,
pleroma_fe: %{
theme: "pleroma-dark",
# ... see /priv/static/static/config.json for the available keys.
},
masto_fe: %{
showInstanceSpecificPanel: true
}
}
```
These settings **need to be complete**, they will override the defaults.
......
......@@ -16,11 +16,4 @@ Installation instructions can be found in the installation section of these docs
Great! Now you can explore the fediverse! Open the login page for your Pleroma instance (e.g. <https://pleroma.soykaf.com>) and login with your username and password. (If you don't have an account yet, click on Register)
### Pleroma-FE
The default front-end used by Pleroma is Pleroma-FE. You can find more information on what it is and how to use it in the [Introduction to Pleroma-FE](../frontend).
### Mastodon interface
If the Pleroma interface isn't your thing, or you're just trying something new but you want to keep using the familiar Mastodon interface, we got that too!
Just add a "/web" after your instance url (e.g. <https://pleroma.soykaf.com/web>) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC!
The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation.
Remember, what you see is only the frontend part of Mastodon, the backend is still Pleroma.
The default front-end used by Pleroma is Pleroma-FE. You can find more information on what it is and how to use it in the [Introduction to Pleroma-FE](../frontend).
\ No newline at end of file
#!/bin/sh
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
project_id="74"
project_branch="rebase/glitch-soc"
static_dir="instance/static"
# For bundling:
# project_branch="pleroma"
# static_dir="priv/static"
if [ ! -d "${static_dir}" ]
then
echo "Error: ${static_dir} directory is missing, are you sure you are running this script at the root of pleroma’s repository?"
exit 1
fi
last_modified="$(curl --fail -s -I 'https://git.pleroma.social/api/v4/projects/'${project_id}'/jobs/artifacts/'${project_branch}'/download?job=build' | grep '^Last-Modified:' | cut -d: -f2-)"
echo "branch:${project_branch}"
echo "Last-Modified:${last_modified}"
artifact="mastofe.zip"
if [ "${last_modified}x" = "x" ]
then
echo "ERROR: Couldn't get the modification date of the latest build archive, maybe it expired, exiting..."
exit 1
fi
if [ -e mastofe.timestamp ] && [ "$(cat mastofe.timestamp)" = "${last_modified}" ]
then
echo "MastoFE is up-to-date, exiting..."
exit 0
fi
curl --fail -c - "https://git.pleroma.social/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=build" -o "${artifact}" || exit
# TODO: Update the emoji as well
rm -fr "${static_dir}/sw.js" "${static_dir}/packs" || exit
unzip -q "${artifact}" || exit
cp public/assets/sw.js "${static_dir}/sw.js" || exit
cp -r public/packs "${static_dir}/packs" || exit
echo "${last_modified}" > mastofe.timestamp
rm -fr public
rm -i "${artifact}"
......@@ -124,7 +124,6 @@ defmodule Pleroma.User do
field(:is_moderator, :boolean, default: false)
field(:is_admin, :boolean, default: false)
field(:show_role, :boolean, default: true)
field(:mastofe_settings, :map, default: nil)
field(:uri, ObjectValidators.Uri, default: nil)
field(:hide_followers_count, :boolean, default: false)
field(:hide_follows_count, :boolean, default: false)
......@@ -1713,7 +1712,6 @@ def purge_user_changeset(user) do
ap_enabled: false,
is_moderator: false,
is_admin: false,
mastofe_settings: nil,
mascot: nil,
emoji: %{},
pleroma_settings_store: %{},
......@@ -2331,13 +2329,6 @@ def mascot_update(user, url) do
|> update_and_set_cache()
end
def mastodon_settings_update(user, settings) do
user
|> cast(%{mastofe_settings: settings}, [:mastofe_settings])
|> validate_required([:mastofe_settings])
|> update_and_set_cache()
end
@spec confirmation_changeset(User.t(), keyword()) :: Changeset.t()
def confirmation_changeset(user, set_confirmation: confirmed?) do
params =
......
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastoFEController do
use Pleroma.Web, :controller
alias Pleroma.User
alias Pleroma.Web.MastodonAPI.AuthController
alias Pleroma.Web.OAuth.Token
alias Pleroma.Web.Plugs.OAuthScopesPlug
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings)
# Note: :index action handles attempt of unauthenticated access to private instance with redirect
plug(:skip_public_check when action == :index)
plug(
OAuthScopesPlug,
%{scopes: ["read"], fallback: :proceed_unauthenticated}
when action == :index
)
plug(:skip_auth when action == :manifest)
@doc "GET /web/*path"
def index(conn, _params) do
with %{assigns: %{user: %User{} = user, token: %Token{app_id: token_app_id} = token}} <- conn,
{:ok, %{id: ^token_app_id}} <- AuthController.local_mastofe_app() do
conn
|> put_layout(false)
|> render("index.html",
token: token.token,
user: user,
custom_emojis: Pleroma.Emoji.get_all()
)
else
_ ->
conn
|> put_session(:return_to, conn.request_path)
|> redirect(to: "/web/login")
end
end
@doc "GET /web/manifest.json"
def manifest(conn, _params) do
render(conn, "manifest.json")
end
@doc "PUT /api/web/settings: Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere"
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
with {:ok, _} <- User.mastodon_settings_update(user, settings) do
json(conn, %{})
else
e ->
conn
|> put_status(:internal_server_error)
|> json(%{error: inspect(e)})
end
end
end
......@@ -21,8 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
plug(Pleroma.Web.ApiSpec.CastAndValidate)
@local_mastodon_name "Mastodon-Local"
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AppOperation
@doc "POST /api/v1/apps"
......@@ -35,7 +33,6 @@ def create(%{body_params: params} = conn, _params) do
|> Map.put(:scopes, scopes)
with cs <- App.register_changeset(%App{}, app_attrs),
false <- cs.changes[:client_name] == @local_mastodon_name,
{:ok, app} <- Repo.insert(cs) do
render(conn, "show.json", app: app)
end
......
......@@ -7,77 +7,12 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
alias Pleroma.Helpers.AuthHelper
alias Pleroma.Helpers.UriHelper
alias Pleroma.User
alias Pleroma.Web.OAuth.App
alias Pleroma.Web.OAuth.Authorization
alias Pleroma.Web.OAuth.Token
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
alias Pleroma.Web.TwitterAPI.TwitterAPI
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
plug(Pleroma.Web.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset)
@local_mastodon_name "Mastodon-Local"
@doc "GET /web/login"
# Local Mastodon FE login callback action
def login(conn, %{"code" => auth_token} = params) do
with {:ok, app} <- local_mastofe_app(),
{:ok, auth} <- Authorization.get_by_token(app, auth_token),
{:ok, oauth_token} <- Token.exchange_token(app, auth) do
redirect_to =
conn
|> local_mastodon_post_login_path()
|> UriHelper.modify_uri_params(%{"access_token" => oauth_token.token})
conn
|> AuthHelper.put_session_token(oauth_token.token)
|> redirect(to: redirect_to)
else
_ -> redirect_to_oauth_form(conn, params)
end
end
def login(conn, params) do
with %{assigns: %{user: %User{}, token: %Token{app_id: app_id}}} <- conn,
{:ok, %{id: ^app_id}} <- local_mastofe_app() do
redirect(conn, to: local_mastodon_post_login_path(conn))
else
_ -> redirect_to_oauth_form(conn, params)
end
end
defp redirect_to_oauth_form(conn, _params) do
with {:ok, app} <- local_mastofe_app() do
path =
Routes.o_auth_path(conn, :authorize,
response_type: "code",
client_id: app.client_id,
redirect_uri: ".",
scope: Enum.join(app.scopes, " ")
)
redirect(conn, to: path)
end
end
@doc "DELETE /auth/sign_out"
def logout(conn, _) do
conn =
with %{assigns: %{token: %Token{} = oauth_token}} <- conn,
session_token = AuthHelper.get_session_token(conn),
{:ok, %Token{token: ^session_token}} <- RevokeToken.revoke(oauth_token) do
AuthHelper.delete_session_token(conn)
else
_ -> conn
end
redirect(conn, to: "/")
end
@doc "POST /auth/password"
def password_reset(conn, params) do
nickname_or_email = params["email"] || params["nickname"]
......@@ -86,23 +21,4 @@ def password_reset(conn, params) do
json_response(conn, :no_content, "")
end
defp local_mastodon_post_login_path(conn) do
case get_session(conn, :return_to) do
nil ->
Routes.masto_fe_path(conn, :index, ["getting-started"])
return_to ->
delete_session(conn, :return_to)
return_to
end
end
@spec local_mastofe_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
def local_mastofe_app do
App.get_or_make(
%{client_name: @local_mastodon_name, redirect_uris: "."},
["read", "write", "follow", "push", "admin"]
)
end
end
......@@ -597,9 +597,6 @@ def login(%User{} = user, %App{} = app, requested_scopes) when is_list(requested
end
end
# Special case: Local MastodonFE
defp redirect_uri(%Plug.Conn{} = conn, "."), do: Routes.auth_url(conn, :login)
defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri
defp get_session_registration_id(%Plug.Conn{} = conn), do: get_session(conn, :registration_id)
......
......@@ -104,12 +104,6 @@ defmodule Pleroma.Web.Router do
plug(Pleroma.Web.Plugs.UserIsAdminPlug)
end
pipeline :mastodon_html do
plug(:browser)
plug(:authenticate)
plug(:after_auth)
end
pipeline :pleroma_html do
plug(:browser)
plug(:authenticate)
......@@ -546,13 +540,6 @@ defmodule Pleroma.Web.Router do
get("/timelines/list/:list_id", TimelineController, :list)
end
scope "/api/web", Pleroma.Web do
pipe_through(:authenticated_api)
# Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere
put("/settings", MastoFEController, :put_settings)
end
scope "/api/v1", Pleroma.Web.MastodonAPI do
pipe_through(:app_api)
......@@ -748,25 +735,6 @@ defmodule Pleroma.Web.Router do
get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
end
scope "/", Pleroma.Web do
pipe_through(:api)
get("/web/manifest.json", MastoFEController, :manifest)
end
scope "/", Pleroma.Web do
pipe_through(:mastodon_html)
get("/web/login", MastodonAPI.AuthController, :login)
delete("/auth/sign_out", MastodonAPI.AuthController, :logout)
post("/auth/password", MastodonAPI.AuthController, :password_reset)
get("/web/*path", MastoFEController, :index)
get("/embed/:id", EmbedController, :show)
end
scope "/proxy/", Pleroma.Web do
get("/preview/:sig/:url", MediaProxy.MediaProxyController, :preview)
get("/preview/:sig/:url/:filename", MediaProxy.MediaProxyController, :preview)
......
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta content='width=device-width, initial-scale=1' name='viewport'>
<title>
<%= Config.get([:instance, :name]) %>
</title>
<link rel="icon" type="image/png" href="/favicon.png"/>
<link rel="manifest" type="applicaton/manifest+json" href="<%= Routes.masto_fe_path(Pleroma.Web.Endpoint, :manifest) %>" />
<meta name="theme-color" content="<%= Config.get([:manifest, :theme_color]) %>" />
<script crossorigin='anonymous' src="/packs/locales.js"></script>
<script crossorigin='anonymous' src="/packs/locales/glitch/en.js"></script>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/getting_started.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/compose.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/home_timeline.js'>
<link rel='preload' as='script' crossorigin='anonymous' href='/packs/features/notifications.js'>
<script id='initial-state' type='application/json'><%= initial_state(@token, @user, @custom_emojis) %></script>
<script src="/packs/core/common.js"></script>
<link rel="stylesheet" media="all" href="/packs/core/common.css" />
<script src="/packs/flavours/glitch/common.js"></script>
<link rel="stylesheet" media="all" href="/packs/flavours/glitch/common.css" />
<script src="/packs/flavours/glitch/home.js"></script>
</head>
<body class='app-body no-reduce-motion system-font'>
<div class='app-holder' data-props='{&quot;locale&quot;:&quot;en&quot;}' id='mastodon'>
</div>
</body>
</html>
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastoFEView do
use Pleroma.Web, :view
alias Pleroma.Config
alias Pleroma.User
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.CustomEmojiView
def initial_state(token, user, custom_emojis) do
limit = Config.get([:instance, :limit])
%{
meta: %{
streaming_api_base_url: Pleroma.Web.Endpoint.websocket_url(),
access_token: token,
locale: "en",
domain: Pleroma.Web.Endpoint.host(),
admin: "1",
me: "#{user.id}",
unfollow_modal: false,
boost_modal: false,
delete_modal: true,
auto_play_gif: false,
display_sensitive_media: false,
reduce_motion: false,
max_toot_chars: limit,
mascot: User.get_mascot(user)["url"]
},
poll_limits: Config.get([:instance, :poll_limits]),
rights: %{
delete_others_notice: present?(user.is_moderator),
admin: present?(user.is_admin)
},
compose: %{
me: "#{user.id}",
default_privacy: user.default_scope,
default_sensitive: false,
allow_content_types: Config.get([:instance, :allowed_post_formats])
},
media_attachments: %{
accept_content_types: [
".jpg",
".jpeg",
".png",
".gif",
".webm",
".mp4",
".m4v",
"image\/jpeg",
"image\/png",
"image\/gif",
"video\/webm",
"video\/mp4"
]
},
settings: user.mastofe_settings || %{},
push_subscription: nil,
accounts: %{user.id => render(AccountView, "show.json", user: user, for: user)},
custom_emojis: render(CustomEmojiView, "index.json", custom_emojis: custom_emojis),
char_limit: limit
}
|> Jason.encode!()
|> Phoenix.HTML.raw()
end
defp present?(nil), do: false
defp present?(false), do: false
defp present?(_), do: true
def render("manifest.json", _params) do
%{
name: Config.get([:instance, :name]),
description: Config.get([:instance, :description]),
icons: Config.get([:manifest, :icons]),
theme_color: Config.get([:manifest, :theme_color]),
background_color: Config.get([:manifest, :background_color]),
display: "standalone",
scope: Pleroma.Web.Endpoint.url(),
start_url: Routes.masto_fe_path(Pleroma.Web.Endpoint, :index, ["getting-started"]),
categories: [
"social"
],
serviceworker: %{
src: "/sw.js"
}
}
end
end
defmodule Pleroma.Repo.Migrations.RemoveMastofeSettingsFromUsers do
use Ecto.Migration
def change do
alter table(:users) do
remove_if_exists(:mastofe_settings, :map)
end
end
end
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#E6E7E8" d="M32 32c0 2.209-1.791 4-4 4H8c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h20c2.209 0 4 1.791 4 4v28z"/><path fill="#BE1931" d="M8.397 11.891c.53 0 .996.078 1.392.232.244.066.453.143.63.231.796-.044 1.612-.122 2.453-.231 1.104-.134 2.463-.297 4.076-.497V9.307c0-1.613-.188-2.948-.563-4.01-.155-.332-.233-.552-.233-.662 0-.331.133-.498.398-.498.706 0 1.359.089 1.954.266.686.198 1.028.453 1.028.761 0 .111-.042.342-.134.696-.265.685-.395 1.955-.395 3.811v1.723c1.299-.132 2.723-.276 4.271-.43.706-.045 1.381-.166 2.023-.366.463-.088.739-.132.83-.132.264 0 .837.222 1.72.662.928.553 1.394.984 1.394 1.293 0 .242-.12.486-.362.729-.819.881-1.408 1.809-1.759 2.782l-.798 1.691c-.13.329-.276.583-.426.76l.128.067c.508.311.761.586.761.829 0 .198-.183.309-.56.332-1.699 0-3.236.088-4.606.266l-2.614.196-.034 4.442c0 2.009-.069 3.62-.198 4.838-.133 1.368-.333 2.341-.598 2.914-.245.488-.445.733-.598.733-.089 0-.232-.266-.431-.798-.132-.617-.198-1.687-.198-3.214v-8.749l-1.789.167c-1.194.111-2.111.168-2.75.168-.266 0-.488-.024-.664-.069-.044.2-.11.376-.198.533-.133.287-.277.43-.432.43-.198 0-.385-.154-.562-.465-.287-.438-.442-.892-.464-1.358l-.398-2.585c-.155-1.348-.332-2.286-.531-2.818-.111-.551-.409-1.081-.894-1.59-.177-.11-.266-.186-.266-.23 0-.354.133-.531.397-.531zm3.347 7.221c1.812-.085 3.545-.196 5.203-.328v-6.132c-1.702.199-3.347.464-4.937.796-.31.111-.597.188-.862.232 0 .265.022.53.067.793.265 2.013.441 3.561.529 4.639zm7.259-6.69v6.163c.728-.087 1.435-.179 2.119-.265 1.326-.178 2.286-.286 2.879-.331.048-.11.103-.232.168-.363.199-.641.463-1.646.798-3.016.264-1.084.394-1.768.394-2.056 0-.353-.339-.529-1.024-.529-1.041 0-2.696.121-4.973.363-.13-.001-.254.012-.361.034z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#E6E7E8" d="M32 32c0 2.209-1.791 4-4 4H8c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h20c2.209 0 4 1.791 4 4v28z"/><path fill="#DD2E44" d="M11 7c-2.519 0-4.583 1.87-4.929 4.293C6.802 10.503 7.839 10 9 10c2.209 0 4 1.791 4 4 0 2 1.497 2.198.707 2.929C16.13 16.583 16 14.519 16 12c0-2.761-2.239-5-5-5z"/><path fill="#55ACEE" d="M23 14c0-2.209 1.791-4 4-4 1.161 0 2.198.503 2.929 1.293C29.583 8.87 27.52 7 25 7c-2.762 0-5 2.239-5 5 0 2.519-.131 4.583 2.293 4.929C21.503 16.198 23 16 23 14z"/><path fill="#FFAC33" d="M14 12c0-4.971 4-9 4-9s4 4.029 4 9-1.791 9-4 9-4-4.029-4-9z"/><path fill="#553788" d="M11.707 21.071C12.497 21.802 13 22.839 13 24c0 2.209-1.791 4-4 4-1.161 0-2.198-.503-2.929-1.293C6.417 29.131 8.481 31 11 31c2.761 0 5-2.238 5-5 0-2.52-1.87-4.583-4.293-4.929zM27 28c-2.209 0-4-1.791-4-4 0-1.161.503-2.198 1.293-2.929C21.869 21.417 20 23.48 20 26c0 2.762 2.238 5 5 5 2.52 0 4.583-1.869 4.929-4.293C29.198 27.497 28.161 28 27 28z"/><path fill="#9266CC" d="M14 24c0 4.971 3 9 4 9s4-4.029 4-9c0-.874-.055-1.719-.159-2.519C21.357 17.737 19.82 15 18 15c-1.82 0-3.357 2.737-3.841 6.481-.104.8-.159 1.645-.159 2.519z"/><path fill="#EDBB9F" d="M13 17c0-3.866 3-4 5-4s5 .134 5 4c0 3.865-2.238 7-5 7-2.761 0-5-3.135-5-7z"/><circle fill="#662113" cx="16" cy="17" r="1"/><circle fill="#662113" cx="20" cy="17" r="1"/><path fill="#662113" d="M18 22c1.104 0 2-.896 2-2h-4c0 1.104.896 2 2 2z"/><circle fill="#A0041E" cx="6" cy="11" r="1"/><circle fill="#269" cx="30" cy="11" r="1"/><circle fill="#DD2E44" cx="18" cy="3" r="1"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#DD2E44" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M14.747 9.125c.527-1.426 1.736-2.573 3.317-2.573 1.643 0 2.792 1.085 3.318 2.573l6.077 16.867c.186.496.248.931.248 1.147 0 1.209-.992 2.046-2.139 2.046-1.303 0-1.954-.682-2.264-1.611l-.931-2.915h-8.62l-.93 2.884c-.31.961-.961 1.642-2.232 1.642-1.24 0-2.294-.93-2.294-2.17 0-.496.155-.868.217-1.023l6.233-16.867zm.34 11.256h5.891l-2.883-8.992h-.062l-2.946 8.992z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#DD2E44" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M10.498 9.249c0-1.488 1.023-2.325 2.449-2.325H18.9c3.224 0 5.83 2.17 5.83 5.457 0 2.17-.9 3.628-2.885 4.558v.062c2.637.372 4.713 2.573 4.713 5.271 0 4.372-2.914 6.729-7.193 6.729h-6.386c-1.427 0-2.481-.899-2.481-2.356V9.249zm4.651 6.418h2.419c1.519 0 2.511-.899 2.511-2.45 0-1.457-1.147-2.201-2.511-2.201h-2.419v4.651zm0 9.24h3.659c1.674 0 2.915-.961 2.915-2.697 0-1.458-1.117-2.45-3.287-2.45h-3.287v5.147z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#DD2E44" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M6.993 18.001c0-6.656 4.48-11.776 11.007-11.776 6.432 0 11.008 5.28 11.008 11.776 0 6.623-4.449 11.774-11.008 11.774-6.496 0-11.007-5.151-11.007-11.774zm17.023 0c0-3.872-2.016-7.36-6.016-7.36s-6.015 3.488-6.015 7.36c0 3.903 1.952 7.359 6.015 7.359 4.065 0 6.016-3.456 6.016-7.359z"/></svg>
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment