mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-30 14:57:59 +01:00
uploading works
This commit is contained in:
parent
56db3807aa
commit
84abf98b30
9 changed files with 168 additions and 18 deletions
|
@ -14,7 +14,8 @@ config :philomena,
|
||||||
otp_secret_key: "Wn7O/8DD+qxL0X4X7bvT90wOkVGcA90bIHww4twR03Ci//zq7PnMw8ypqyyT/b/C",
|
otp_secret_key: "Wn7O/8DD+qxL0X4X7bvT90wOkVGcA90bIHww4twR03Ci//zq7PnMw8ypqyyT/b/C",
|
||||||
image_url_root: "/img",
|
image_url_root: "/img",
|
||||||
avatar_url_root: "/avatars",
|
avatar_url_root: "/avatars",
|
||||||
badge_url_root: "/media"
|
badge_url_root: "/media",
|
||||||
|
image_file_root: "priv/static/system/images"
|
||||||
|
|
||||||
config :philomena, :pow,
|
config :philomena, :pow,
|
||||||
user: Philomena.Users.User,
|
user: Philomena.Users.User,
|
||||||
|
|
|
@ -62,7 +62,7 @@ defmodule Philomena.Conversations.Conversation do
|
||||||
|
|
||||||
defp put_recipient(changeset) do
|
defp put_recipient(changeset) do
|
||||||
recipient = changeset |> get_field(:recipient)
|
recipient = changeset |> get_field(:recipient)
|
||||||
user = Repo.get_by(User, name: recipient) |> IO.inspect()
|
user = Repo.get_by(User, name: recipient)
|
||||||
|
|
||||||
changeset
|
changeset
|
||||||
|> put_change(:to, user)
|
|> put_change(:to, user)
|
||||||
|
|
|
@ -20,5 +20,6 @@ defmodule Philomena.ImageIntensities.ImageIntensity do
|
||||||
image_intensity
|
image_intensity
|
||||||
|> cast(attrs, [:nw, :ne, :sw, :se])
|
|> cast(attrs, [:nw, :ne, :sw, :se])
|
||||||
|> validate_required([:image_id, :nw, :ne, :sw, :se])
|
|> validate_required([:image_id, :nw, :ne, :sw, :se])
|
||||||
|
|> unique_constraint(:image_id, name: :index_image_intensities_on_image_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,6 +13,7 @@ defmodule Philomena.Images do
|
||||||
alias Philomena.TagChanges.TagChange
|
alias Philomena.TagChanges.TagChange
|
||||||
alias Philomena.Tags
|
alias Philomena.Tags
|
||||||
alias Philomena.Tags.Tag
|
alias Philomena.Tags.Tag
|
||||||
|
alias Philomena.Processors
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Gets a single image.
|
Gets a single image.
|
||||||
|
@ -44,10 +45,23 @@ defmodule Philomena.Images do
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_image(attrs \\ %{}) do
|
def create_image(attribution, attrs \\ %{}) do
|
||||||
|
tags = Tags.get_or_create_tags(attrs["tag_input"])
|
||||||
|
|
||||||
|
image =
|
||||||
%Image{}
|
%Image{}
|
||||||
|> Image.changeset(attrs)
|
|> Image.creation_changeset(attrs, attribution)
|
||||||
|> Repo.insert()
|
|> Image.tag_changeset(attrs, [], tags)
|
||||||
|
|> Processors.after_upload(attrs)
|
||||||
|
|
||||||
|
Multi.new
|
||||||
|
|> Multi.insert(:image, image)
|
||||||
|
|> Multi.run(:after, fn _repo, %{image: image} ->
|
||||||
|
Processors.after_insert(image)
|
||||||
|
|
||||||
|
{:ok, nil}
|
||||||
|
end)
|
||||||
|
|> Repo.transaction()
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -109,8 +109,11 @@ defmodule Philomena.Images.Image do
|
||||||
end
|
end
|
||||||
|
|
||||||
def creation_changeset(image, attrs, attribution) do
|
def creation_changeset(image, attrs, attribution) do
|
||||||
|
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||||
|
|
||||||
image
|
image
|
||||||
|> cast(attrs, [:source_url, :description])
|
|> cast(attrs, [:source_url, :description])
|
||||||
|
|> change(first_seen_at: now)
|
||||||
|> change(attribution)
|
|> change(attribution)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -119,12 +122,14 @@ defmodule Philomena.Images.Image do
|
||||||
|> cast(attrs, [
|
|> cast(attrs, [
|
||||||
:image, :image_name, :image_width, :image_height, :image_size,
|
:image, :image_name, :image_width, :image_height, :image_size,
|
||||||
:image_format, :image_mime_type, :image_aspect_ratio,
|
:image_format, :image_mime_type, :image_aspect_ratio,
|
||||||
:image_orig_sha512_hash, :image_sha512_hash, :uploaded_image
|
:image_orig_sha512_hash, :image_sha512_hash, :uploaded_image,
|
||||||
|
:is_animated
|
||||||
])
|
])
|
||||||
|> validate_required([
|
|> validate_required([
|
||||||
:image, :image_width, :image_height, :image_size,
|
:image, :image_width, :image_height, :image_size,
|
||||||
:image_format, :image_mime_type, :image_aspect_ratio,
|
:image_format, :image_mime_type, :image_aspect_ratio,
|
||||||
:image_orig_sha512_hash, :image_sha512_hash, :uploaded_image
|
:image_orig_sha512_hash, :image_sha512_hash, :uploaded_image,
|
||||||
|
:is_animated
|
||||||
])
|
])
|
||||||
|> validate_number(:image_size, greater_than: 0, less_than_or_equal_to: 26214400)
|
|> validate_number(:image_size, greater_than: 0, less_than_or_equal_to: 26214400)
|
||||||
|> validate_number(:image_width, greater_than: 0, less_than_or_equal_to: 32767)
|
|> validate_number(:image_width, greater_than: 0, less_than_or_equal_to: 32767)
|
||||||
|
|
|
@ -32,10 +32,22 @@ defmodule Philomena.Processors do
|
||||||
"video/webm" => Philomena.Processors.Webm
|
"video/webm" => Philomena.Processors.Webm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@versions [
|
||||||
|
thumb_tiny: {50, 50},
|
||||||
|
thumb_small: {150, 150},
|
||||||
|
thumb: {250, 250},
|
||||||
|
small: {320, 240},
|
||||||
|
medium: {800, 600},
|
||||||
|
large: {1280, 1024},
|
||||||
|
tall: {1024, 4096},
|
||||||
|
full: nil
|
||||||
|
]
|
||||||
|
|
||||||
def after_upload(image, params) do
|
def after_upload(image, params) do
|
||||||
with upload when not is_nil(upload) <- params["image"],
|
with upload when not is_nil(upload) <- params["image"],
|
||||||
file <- upload.path,
|
file <- upload.path,
|
||||||
mime <- @mimes[Mime.file(file)],
|
{:ok, mime} <- Mime.file(file),
|
||||||
|
mime <- @mimes[mime],
|
||||||
analyzer when not is_nil(analyzer) <- @analyzers[mime],
|
analyzer when not is_nil(analyzer) <- @analyzers[mime],
|
||||||
analysis <- analyzer.analyze(file),
|
analysis <- analyzer.analyze(file),
|
||||||
changes <- analysis_to_changes(analysis, file, upload.filename)
|
changes <- analysis_to_changes(analysis, file, upload.filename)
|
||||||
|
@ -50,7 +62,10 @@ defmodule Philomena.Processors do
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_insert(image) do
|
def after_insert(image) do
|
||||||
File.cp!(image.uploaded_image, Path.join([image_file_root(), image.image]))
|
file = image_file(image)
|
||||||
|
dir = Path.dirname(file)
|
||||||
|
File.mkdir_p!(dir)
|
||||||
|
File.cp!(image.uploaded_image, file)
|
||||||
ImageProcessor.cast(self(), image.id)
|
ImageProcessor.cast(self(), image.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -62,7 +77,7 @@ defmodule Philomena.Processors do
|
||||||
analyzer = @analyzers[mime]
|
analyzer = @analyzers[mime]
|
||||||
analysis = analyzer.analyze(file)
|
analysis = analyzer.analyze(file)
|
||||||
processor = @processors[mime]
|
processor = @processors[mime]
|
||||||
process = processor.process(analysis, file)
|
process = processor.process(analysis, file, @versions)
|
||||||
|
|
||||||
apply_edit_script(image, process)
|
apply_edit_script(image, process)
|
||||||
sha512 = Sha512.file(file)
|
sha512 = Sha512.file(file)
|
||||||
|
@ -102,22 +117,27 @@ defmodule Philomena.Processors do
|
||||||
|
|
||||||
defp apply_thumbnail(_image, thumb_dir, {:copy, new_file, destination}) do
|
defp apply_thumbnail(_image, thumb_dir, {:copy, new_file, destination}) do
|
||||||
new_destination = Path.join([thumb_dir, destination])
|
new_destination = Path.join([thumb_dir, destination])
|
||||||
|
dir = Path.dirname(new_destination)
|
||||||
|
|
||||||
File.cp(new_file, new_destination)
|
File.mkdir_p!(dir)
|
||||||
File.chmod(new_destination, 0o755)
|
File.cp!(new_file, new_destination)
|
||||||
|
File.chmod!(new_destination, 0o755)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp apply_thumbnail(image, thumb_dir, {:symlink_original, destination}) do
|
defp apply_thumbnail(image, thumb_dir, {:symlink_original, destination}) do
|
||||||
file = image_file(image)
|
file = Path.absname(image_file(image))
|
||||||
new_destination = Path.join([thumb_dir, destination])
|
new_destination = Path.join([thumb_dir, destination])
|
||||||
|
dir = Path.dirname(new_destination)
|
||||||
|
|
||||||
File.ln_s(file, new_destination)
|
File.mkdir_p!(dir)
|
||||||
File.chmod(new_destination, 0o755)
|
File.rm(new_destination)
|
||||||
|
File.ln_s!(file, new_destination)
|
||||||
|
File.chmod!(new_destination, 0o755)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp analysis_to_changes(analysis, file, upload_name) do
|
defp analysis_to_changes(analysis, file, upload_name) do
|
||||||
{width, height} = analysis.dimensions
|
{width, height} = analysis.dimensions
|
||||||
%{size: size} = File.stat(file)
|
{:ok, %{size: size}} = File.stat(file)
|
||||||
sha512 = Sha512.file(file)
|
sha512 = Sha512.file(file)
|
||||||
filename = build_filename(analysis.extension)
|
filename = build_filename(analysis.extension)
|
||||||
|
|
||||||
|
@ -132,6 +152,7 @@ defmodule Philomena.Processors do
|
||||||
"image_aspect_ratio" => aspect_ratio(width, height),
|
"image_aspect_ratio" => aspect_ratio(width, height),
|
||||||
"image_orig_sha512_hash" => sha512,
|
"image_orig_sha512_hash" => sha512,
|
||||||
"image_sha512_hash" => sha512,
|
"image_sha512_hash" => sha512,
|
||||||
|
"is_animated" => analysis.animated?,
|
||||||
"uploaded_image" => file
|
"uploaded_image" => file
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,10 @@ defmodule PhilomenaWeb.ImageController do
|
||||||
|
|
||||||
plug :load_and_authorize_resource, model: Image, only: :show, preload: [:tags, user: [awards: :badge]]
|
plug :load_and_authorize_resource, model: Image, only: :show, preload: [:tags, user: [awards: :badge]]
|
||||||
|
|
||||||
|
plug PhilomenaWeb.FilterBannedUsersPlug when action in [:new, :create]
|
||||||
|
plug PhilomenaWeb.UserAttributionPlug when action in [:create]
|
||||||
|
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||||
|
|
||||||
def index(conn, _params) do
|
def index(conn, _params) do
|
||||||
query = conn.assigns.compiled_filter
|
query = conn.assigns.compiled_filter
|
||||||
|
|
||||||
|
@ -77,4 +81,29 @@ defmodule PhilomenaWeb.ImageController do
|
||||||
layout_class: "layout--wide"
|
layout_class: "layout--wide"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new(conn, _params) do
|
||||||
|
changeset =
|
||||||
|
%Image{}
|
||||||
|
|> Images.change_image()
|
||||||
|
|
||||||
|
render(conn, "new.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(conn, %{"image" => image_params}) do
|
||||||
|
attributes = conn.assigns.attributes
|
||||||
|
|
||||||
|
case Images.create_image(attributes, image_params) do
|
||||||
|
{:ok, %{image: image}} ->
|
||||||
|
Images.reindex_image(image)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, "Image created successfully.")
|
||||||
|
|> redirect(to: Routes.image_path(conn, :show, image))
|
||||||
|
|
||||||
|
{:error, :image, changeset, _} ->
|
||||||
|
conn
|
||||||
|
|> render("new.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -83,7 +83,7 @@ defmodule PhilomenaWeb.Router do
|
||||||
get "/", ActivityController, :index
|
get "/", ActivityController, :index
|
||||||
|
|
||||||
resources "/activity", ActivityController, only: [:index]
|
resources "/activity", ActivityController, only: [:index]
|
||||||
resources "/images", ImageController, only: [:index, :show] do
|
resources "/images", ImageController, only: [:index, :show, :new, :create] do
|
||||||
resources "/comments", Image.CommentController, only: [:index, :show, :create]
|
resources "/comments", Image.CommentController, only: [:index, :show, :create]
|
||||||
resources "/tags", Image.TagController, only: [:update], singleton: true
|
resources "/tags", Image.TagController, only: [:update], singleton: true
|
||||||
resources "/sources", Image.SourceController, only: [:update], singleton: true
|
resources "/sources", Image.SourceController, only: [:update], singleton: true
|
||||||
|
|
79
lib/philomena_web/templates/image/new.html.slime
Normal file
79
lib/philomena_web/templates/image/new.html.slime
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
= form_for @changeset, Routes.image_path(@conn, :create), [multipart: true], fn f ->
|
||||||
|
|
||||||
|
.dnp-warning
|
||||||
|
h4
|
||||||
|
' Read the
|
||||||
|
a> href="/pages/rules" site rules
|
||||||
|
' and check our
|
||||||
|
a> href="/dnp" do-not-post list
|
||||||
|
p
|
||||||
|
' Don't post content the artist doesn't want here (or shared in general),
|
||||||
|
strong including commercial content.
|
||||||
|
|
||||||
|
= if !@conn.assigns.current_user do
|
||||||
|
p
|
||||||
|
strong<> Sorry, but due to spam, anonymous uploaders need to fill this out.
|
||||||
|
| If you're logged in, you can still post anonymously and won't have to deal with captchas!
|
||||||
|
|
||||||
|
.field
|
||||||
|
= checkbox f, :captcha, class: "js-captcha", value: 0
|
||||||
|
= label f, :captcha, "I am not a robot!"
|
||||||
|
|
||||||
|
p
|
||||||
|
strong
|
||||||
|
' Please check it isn't already here with
|
||||||
|
a href="/search/reverse" reverse search.
|
||||||
|
|
||||||
|
/ todo: extract this
|
||||||
|
h4 Select an image
|
||||||
|
.image-other
|
||||||
|
#js-image-upload-previews
|
||||||
|
p Upload a file from your computer
|
||||||
|
.field
|
||||||
|
= file_input f, :image, class: "input js-scraper"
|
||||||
|
= error_tag f, :image_size
|
||||||
|
= error_tag f, :image_width
|
||||||
|
= error_tag f, :image_height
|
||||||
|
= error_tag f, :image_name
|
||||||
|
= error_tag f, :image_mime_type
|
||||||
|
|
||||||
|
.field-error-js.hidden.js-scraper
|
||||||
|
|
||||||
|
h4 About this image
|
||||||
|
.field
|
||||||
|
= label f, :source_url, "The page you found this image on"
|
||||||
|
= url_input f, :source_url, class: "input input--wide js-image-input", placeholder: "Source URL"
|
||||||
|
|
||||||
|
.field
|
||||||
|
label for="image[tag_input]"
|
||||||
|
' Describe with
|
||||||
|
strong> 3+
|
||||||
|
' tags, including ratings and applicable artist tags
|
||||||
|
|
||||||
|
= render PhilomenaWeb.TagView, "_tag_editor.html", f: f, name: :tag_input, type: :upload
|
||||||
|
|
||||||
|
button.button.button--state-success.button--separate-left.button--bold id="tagsinput-save" type="button" title="This button saves the tags listed above to your browser, allowing you to retrieve them again by clicking the Load button" Save
|
||||||
|
button.button.button--state-warning.button--separate-left.button--bold id="tagsinput-load" type="button" title="This button loads any saved tags from your browser" Load
|
||||||
|
button.button.button--state-danger.button--separate-left.button--bold id="tagsinput-clear" type="button" title="This button will clear the list of tags above" Clear
|
||||||
|
|
||||||
|
br
|
||||||
|
|
||||||
|
.field
|
||||||
|
.block
|
||||||
|
.block__header.block__header--js-tabbed
|
||||||
|
= link "Description", to: "#", class: "selected", data: [click_tab: "write"]
|
||||||
|
= link "Preview", to: "#", data: [click_tab: "preview"]
|
||||||
|
|
||||||
|
.block__tab.selected data-tab="write"
|
||||||
|
/= render partial: 'layouts/textile_toolbar'
|
||||||
|
= textarea f, :description, class: "input input--wide input--text js-preview-description js-image-input js-toolbar-input", placeholder: "Describe this image in plain words - this should generally be info about the image that doesn't belong in the tags or source."
|
||||||
|
.block__tab.hidden data-tab="preview"
|
||||||
|
| Loading preview...
|
||||||
|
|
||||||
|
= if @conn.assigns.current_user do
|
||||||
|
.field
|
||||||
|
= label f, :anonymous, "Post anonymously"
|
||||||
|
= checkbox f, :anonymous, class: "checkbox"
|
||||||
|
|
||||||
|
.actions
|
||||||
|
= submit "Upload", class: "button", autocomplete: "off", data: [disable_with: "Please wait..."]
|
Loading…
Reference in a new issue