diff --git a/config/config.exs b/config/config.exs index ed213b64..0e099c5a 100644 --- a/config/config.exs +++ b/config/config.exs @@ -15,6 +15,7 @@ config :philomena, tumblr_api_key: "fuiKNFp9vQFvjLNvx4sUwti4Yb5yGutBN4Xh10LXZhhRKjWlV4", image_url_root: "/img", avatar_url_root: "/avatars", + advert_url_root: "/spns", badge_url_root: "/media", image_file_root: "priv/static/system/images", cdn_host: "", diff --git a/config/prod.secret.exs b/config/prod.secret.exs index a612dfab..2146cdaa 100644 --- a/config/prod.secret.exs +++ b/config/prod.secret.exs @@ -18,6 +18,7 @@ config :philomena, anonymous_name_salt: System.get_env("ANONYMOUS_NAME_SALT"), password_pepper: System.get_env("PASSWORD_PEPPER"), avatar_url_root: System.get_env("AVATAR_URL_ROOT"), + advert_url_root: System.get_env("ADVERT_URL_ROOT"), image_file_root: System.get_env("IMAGE_FILE_ROOT"), tumblr_api_key: System.get_env("TUMBLR_API_KEY"), otp_secret_key: System.get_env("OTP_SECRET_KEY"), diff --git a/lib/philomena/adverts.ex b/lib/philomena/adverts.ex index b3561b37..4f05872d 100644 --- a/lib/philomena/adverts.ex +++ b/lib/philomena/adverts.ex @@ -8,17 +8,71 @@ defmodule Philomena.Adverts do alias Philomena.Adverts.Advert - @doc """ - Returns the list of adverts. - ## Examples + def random_live do + now = DateTime.utc_now() - iex> list_adverts() - [%Advert{}, ...] + Advert + |> where(live: true, restrictions: "none") + |> where([a], a.start_date < ^now and a.finish_date > ^now) + |> order_by(asc: fragment("random()")) + |> limit(1) + |> Repo.one() + |> record_impression() + end - """ - def list_adverts do - Repo.all(Advert) + def random_live_for(image) do + image = Repo.preload(image, :tags) + now = DateTime.utc_now() + + Advert + |> where(live: true) + |> where([a], a.restrictions in ^restrictions(image)) + |> where([a], a.start_date < ^now and a.finish_date > ^now) + |> order_by(asc: fragment("random()")) + |> limit(1) + |> Repo.one() + |> record_impression() + end + + def click(%Advert{} = advert) do + spawn fn -> + query = where(Advert, id: ^advert.id) + Repo.update_all(query, inc: [clicks: 1]) + end + end + + defp record_impression(nil), do: nil + defp record_impression(advert) do + spawn fn -> + query = where(Advert, id: ^advert.id) + Repo.update_all(query, inc: [impressions: 1]) + end + + advert + end + + defp sfw?(image) do + image_tags = MapSet.new(image.tags |> Enum.map(& &1.name)) + sfw_tags = MapSet.new(["safe", "suggestive"]) + intersect = MapSet.intersection(image_tags, sfw_tags) + + MapSet.size(intersect) > 0 + end + + defp nsfw?(image) do + image_tags = MapSet.new(image.tags |> Enum.map(& &1.name)) + nsfw_tags = MapSet.new(["questionable", "explicit"]) + intersect = MapSet.intersection(image_tags, nsfw_tags) + + MapSet.size(intersect) > 0 + end + + defp restrictions(image) do + restrictions = ["none"] + restrictions = if nsfw?(image), do: ["nsfw" | restrictions], else: restrictions + restrictions = if sfw?(image), do: ["sfw" | restrictions], else: restrictions + restrictions end @doc """ diff --git a/lib/philomena_web/controllers/advert_controller.ex b/lib/philomena_web/controllers/advert_controller.ex new file mode 100644 index 00000000..dfd5f0d0 --- /dev/null +++ b/lib/philomena_web/controllers/advert_controller.ex @@ -0,0 +1,17 @@ +defmodule PhilomenaWeb.AdvertController do + use PhilomenaWeb, :controller + + alias Philomena.Adverts + alias Philomena.Adverts.Advert + + plug :load_resource, model: Advert + + def show(conn, _params) do + advert = conn.assigns.advert + + Adverts.click(advert) + + conn + |> redirect(external: advert.link) + end +end \ No newline at end of file diff --git a/lib/philomena_web/controllers/image_controller.ex b/lib/philomena_web/controllers/image_controller.ex index 83b22866..2dac0d25 100644 --- a/lib/philomena_web/controllers/image_controller.ex +++ b/lib/philomena_web/controllers/image_controller.ex @@ -15,6 +15,7 @@ defmodule PhilomenaWeb.ImageController do plug PhilomenaWeb.UserAttributionPlug when action in [:create] plug PhilomenaWeb.CaptchaPlug when action in [:create] plug PhilomenaWeb.ScraperPlug, [params_name: "image", params_key: "image"] when action in [:create] + plug PhilomenaWeb.AdvertPlug when action in [:show] def index(conn, _params) do query = conn.assigns.compiled_filter diff --git a/lib/philomena_web/controllers/topic_controller.ex b/lib/philomena_web/controllers/topic_controller.ex index bed611b4..ec99ef50 100644 --- a/lib/philomena_web/controllers/topic_controller.ex +++ b/lib/philomena_web/controllers/topic_controller.ex @@ -9,6 +9,7 @@ defmodule PhilomenaWeb.TopicController do plug PhilomenaWeb.FilterBannedUsersPlug when action in [:new, :create] plug PhilomenaWeb.UserAttributionPlug when action in [:new, :create] + plug PhilomenaWeb.AdvertPlug when action in [:show] plug :load_and_authorize_resource, model: Forum, id_name: "forum_id", id_field: "short_name", persisted: true def show(conn, %{"id" => slug} = params) do diff --git a/lib/philomena_web/plugs/advert_plug.ex b/lib/philomena_web/plugs/advert_plug.ex new file mode 100644 index 00000000..09246743 --- /dev/null +++ b/lib/philomena_web/plugs/advert_plug.ex @@ -0,0 +1,27 @@ +defmodule PhilomenaWeb.AdvertPlug do + alias Philomena.Adverts + alias Plug.Conn + + def init([]), do: [] + + def call(conn, _opts) do + user = conn.assigns.current_user + image = conn.assigns[:image] + show_ads? = show_ads?(user) + + conn + |> maybe_assign_ad(image, show_ads?) + end + + defp maybe_assign_ad(conn, nil, true), + do: Conn.assign(conn, :advert, Adverts.random_live()) + defp maybe_assign_ad(conn, image, true), + do: Conn.assign(conn, :advert, Adverts.random_live_for(image)) + defp maybe_assign_ad(conn, _image, _false), + do: conn + + defp show_ads?(%{hide_advertisements: false}), + do: true + defp show_ads?(_user), + do: true +end \ No newline at end of file diff --git a/lib/philomena_web/router.ex b/lib/philomena_web/router.ex index 6e2d5db6..9e9498bf 100644 --- a/lib/philomena_web/router.ex +++ b/lib/philomena_web/router.ex @@ -122,6 +122,7 @@ defmodule PhilomenaWeb.Router do resources "/posts", PostController, only: [:index] resources "/commissions", CommissionController, only: [:index, :show] resources "/galleries", GalleryController, only: [:index, :show] + resources "/adverts", AdvertController, only: [:show] get "/:id", ImageController, :show # get "/:forum_id", ForumController, :show # impossible to do without constraints diff --git a/lib/philomena_web/templates/advert/_box.html.slime b/lib/philomena_web/templates/advert/_box.html.slime new file mode 100644 index 00000000..dcbff54f --- /dev/null +++ b/lib/philomena_web/templates/advert/_box.html.slime @@ -0,0 +1,14 @@ +.block#imagespns + .spnstxt + ' Interested in advertising on Derpibooru? + = link "Click here", to: "/pages/advertising" + ' for information! + + a href=Routes.advert_path(@conn, :show, @advert) rel="nofollow" title=@advert.title + img src=advert_image_url(@advert) alt=@advert.title width="700" height="80" + + p + strong + ' Derpibooru costs over $25 a day to operate - + = link "help support us financially", to: "/pages/donations" + ' ! \ No newline at end of file diff --git a/lib/philomena_web/templates/image/show.html.slime b/lib/philomena_web/templates/image/show.html.slime index 9eb384ab..aab52c14 100644 --- a/lib/philomena_web/templates/image/show.html.slime +++ b/lib/philomena_web/templates/image/show.html.slime @@ -2,6 +2,9 @@ = render PhilomenaWeb.ImageView, "_image_page.html", image: @image, conn: @conn .layout--narrow + = if @conn.assigns.advert do + = render PhilomenaWeb.Advertview, "_box.html", advert: @conn.assigns.advert, conn: @conn + .image-description = render PhilomenaWeb.ImageView, "_description.html", image: @image, body: @description, conn: @conn = render PhilomenaWeb.Image.DescriptionView, "_form.html", image: @image, changeset: @image_changeset, conn: @conn diff --git a/lib/philomena_web/templates/topic/show.html.slime b/lib/philomena_web/templates/topic/show.html.slime index 1c8dee3d..6b8f3daa 100644 --- a/lib/philomena_web/templates/topic/show.html.slime +++ b/lib/philomena_web/templates/topic/show.html.slime @@ -38,7 +38,8 @@ h1 = @topic.title = for {post, body} <- @posts, !post.destroyed_content do = render PhilomenaWeb.PostView, "_post.html", conn: @conn, post: post, body: body - /include ../adverts/_box.html.slim + = if @conn.assigns.advert do + = render PhilomenaWeb.Advertview, "_box.html", advert: @conn.assigns.advert, conn: @conn / Post editability data for JS /.js-editable-posts data-editable=editable_communications(@posts).to_json diff --git a/lib/philomena_web/views/advert_view.ex b/lib/philomena_web/views/advert_view.ex new file mode 100644 index 00000000..1d995249 --- /dev/null +++ b/lib/philomena_web/views/advert_view.ex @@ -0,0 +1,11 @@ +defmodule PhilomenaWeb.AdvertView do + use PhilomenaWeb, :view + + def advert_image_url(%{image: image}) do + advert_url_root() <> image + end + + defp advert_url_root do + Application.get_env(:philomena, :advert_url_root) + end +end