Skip to content

Commit eb99182

Browse files
authored
Make human readable short urls (#92)
* Add album image model * Add image controller * fix image metadata issue * upgrade data migration rake task * update controllers to use new model * run rubocop * update migration task * Update model slug creation * update rubocop * fix public image not avalable outside application bug
1 parent 0cbd8bd commit eb99182

25 files changed

+369
-57
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@
3535
yarn-debug.log*
3636
.yarn-integrity
3737
.env
38+
39+
.idea

.rubocop.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ AllCops:
1111
- 'vendor/**/*'
1212
- '.git/**/*'
1313
- '.github/**/*'
14+
- 'lib/tasks/data_migration.rake'
1415
# Cop specific settings
1516
Style/GuardClause:
1617
MinBodyLength: 3

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ gem 'jbuilder', '~> 2.7'
2525
# Use Active Storage variant
2626
gem 'image_processing', '~> 1.2'
2727

28+
# Use for shorter urls
29+
gem 'friendly_id', '~> 5.4.0'
30+
2831
# Reduces boot times through caching; required in config/boot.rb
2932
gem 'bootsnap', '>= 1.4.2', require: false
3033

Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ GEM
8787
faraday (1.0.1)
8888
multipart-post (>= 1.2, < 3)
8989
ffi (1.15.5)
90+
friendly_id (5.4.2)
91+
activerecord (>= 4.0.0)
9092
globalid (0.4.2)
9193
activesupport (>= 4.2.0)
9294
hashie (4.1.0)
@@ -262,6 +264,7 @@ DEPENDENCIES
262264
byebug
263265
capybara (>= 2.15)
264266
dotenv-rails
267+
friendly_id (~> 5.4.0)
265268
i18n_generators (~> 2.2, >= 2.2.2)
266269
image_processing (~> 1.2)
267270
jbuilder (~> 2.7)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Place all the styles related to the AlbumImage controller here.
2+
// They will automatically be included in application.css.
3+
// You can use Sass (SCSS) here: https://sass-lang.com/
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
class AlbumImageController < ApplicationController
2+
before_action :set_image
3+
4+
def show
5+
http_cache_forever public: true do
6+
content_headers_from @image.file.blob
7+
stream @image.file.blob
8+
end
9+
end
10+
11+
private
12+
13+
def set_image
14+
@image = AlbumImage.friendly.find(params[:id])
15+
authorize_image
16+
end
17+
18+
def authorize_image
19+
return if @image.album.public?
20+
21+
unless current_user.present? && (current_user.site_admin? || logged_in_as_admin_of?(album.circle) || album.shared?)
22+
redirect_to '/', notice: I18n.t('unauthorized', scope: 'album_images.errors')
23+
end
24+
end
25+
26+
def content_headers_from(blob)
27+
response.headers['Content-Type'] = blob.content_type_for_serving
28+
response.headers['Content-Disposition'] = ActionDispatch::Http::ContentDisposition.format \
29+
disposition: blob.forced_disposition_for_serving || params[:disposition] || 'inline',
30+
filename: blob.filename.sanitized
31+
end
32+
33+
def stream(blob)
34+
blob.download do |chunk|
35+
response.stream.write chunk
36+
end
37+
ensure
38+
response.stream.close
39+
end
40+
end

app/controllers/albums_controller.rb

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class AlbumsController < ApplicationController
2-
before_action :set_album, only: %i[edit update destroy delete_image add_image]
2+
before_action :set_album, only: %i[edit update destroy delete_image add_image image]
33
before_action :login_required
44
before_action :admin_or_owner_required, only: %i[edit update destroy]
55
before_action :admin_or_owner_or_shared_required, only: %i[delete_image add_image]
@@ -10,24 +10,24 @@ class AlbumsController < ApplicationController
1010
# GET /albums
1111
def index
1212
@albums = Album.all.order(created_at: :desc)
13-
@title = 'Minden album'
13+
@title = 'Minden album'
1414
end
1515

1616
# GET /albums/myalbums
1717
def myalbums
1818
@albums = Album.where(user: current_user).order(created_at: :desc)
19-
@title = 'Albumaim'
19+
@title = 'Albumaim'
2020
render :index
2121
end
2222

2323
# GET /albums/1
2424
def show
25-
@album = Album.includes(:images_attachments).find(params[:id])
25+
@album = Album.includes(:album_images).find(params[:id])
2626
end
2727

2828
# GET /albums/new
2929
def new
30-
@album = Album.new
30+
@album = Album.new
3131
@circles = current_user.memberships.where(accepted: true).map(&:circle)
3232

3333
redirect_to circles_path, notice: 'Nincs körtagsága, nem hozhat létre kört!' if @circles.empty?
@@ -41,7 +41,7 @@ def create
4141
@render_target = :new
4242

4343
@album = Album.new(album_params)
44-
@album.images = params[:album][:images]
44+
@album.build_images params[:album][:images]
4545
@album.user = current_user
4646

4747
if @album.save
@@ -70,24 +70,27 @@ def destroy
7070

7171
# GET /albums/1/image?image_id=1
7272
def image
73+
image = @album.album_images.find_by(id: params[:image_id])
74+
return render json: { status: 404 }, status: :not_found if image.blank?
75+
7376
render json: {
74-
url: url_for(ActiveStorage::Attachment.find(params[:image_id])),
75-
filename: ActiveStorage::Attachment.find(params[:image_id]).blob.filename
77+
url: url_for(image),
78+
filename: image.file.blob.filename
7679
}
7780
end
7881

7982
# DELETE one image of the album
8083
def delete_image
81-
image = ActiveStorage::Attachment.find(params[:image_id])
82-
image.purge
84+
image = AlbumImage.find(params[:image_id])
85+
image.destroy!
8386
redirect_to @album
8487
end
8588

8689
# POST add image(s) to the album
8790
def add_image
8891
images = params[:images]
89-
@album.images.attach(images) if images.present?
90-
92+
@album.build_images(images) if images.present?
93+
@album.save
9194
redirect_to @album, notice: 'Album sikeresen módosítva.'
9295
end
9396

@@ -100,7 +103,7 @@ def set_album
100103

101104
# Analyze all the images for their width and height
102105
def analyze_album
103-
@album.images.each { |i| i.analyze unless i.analyzed? }
106+
@album.images.map(&:file).each { |i| i.analyze unless i.analyzed? }
104107
end
105108

106109
# Allow only owner or admin

app/controllers/application_controller.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,42 @@
11
class ApplicationController < ActionController::Base
2+
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
3+
24
protected
35

46
def logged_in?
57
session[:user_id]
68
end
9+
710
helper_method :logged_in?
811

912
def in_circle?(circle)
1013
Membership.exists?(user: current_user, circle: circle)
1114
end
15+
1216
helper_method :in_circle?
1317

1418
def accepted_in_circle?(circle)
1519
Membership.exists?(user: current_user, circle: circle, accepted: true)
1620
end
21+
1722
helper_method :accepted_in_circle?
1823

1924
def logged_in_as_site_admin?
2025
current_user&.site_admin?
2126
end
27+
2228
helper_method :logged_in_as_site_admin?
2329

2430
def logged_in_as_admin_of?(circle)
2531
Membership.exists?(user: current_user, circle: circle, admin: true)
2632
end
33+
2734
helper_method :logged_in_as_admin_of?
2835

2936
def current_user
3037
@current_user ||= User.find(session[:user_id]) if logged_in?
3138
end
39+
3240
helper_method :current_user
3341

3442
# Function used in derived classes as before actions
@@ -40,4 +48,8 @@ def login_required
4048
def site_admin_required
4149
redirect_to root_path, notice: 'Nincs jogosultságod az oldalhoz!' unless logged_in_as_site_admin?
4250
end
51+
52+
def record_not_found
53+
redirect_to root_url, notice: 'Erőforrás nem találva'
54+
end
4355
end

app/extensions/secure_blob_extension.rb

Lines changed: 0 additions & 32 deletions
This file was deleted.

app/helpers/album_image_helper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module AlbumImageHelper
2+
end

app/models/album.rb

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
class Album < ApplicationRecord
2-
has_many_attached :images
32
validates :title, presence: true, length: { minimum: 3, maximum: 128 }
43
validates :desc, length: { maximum: 255 }
54
belongs_to :user
65
belongs_to :circle
6+
has_many :album_images, dependent: :destroy, autosave: true
77

88
def thumbnail
9-
if images.empty?
9+
if album_images.empty?
1010
ActionController::Base.helpers.image_url('album-blank.jpg')
1111
else
12-
images.first.variant gravity: 'Center', resize: '300x200^', crop: '300x200+0+0'
12+
album_images.first.file.variant gravity: 'Center', resize: '300x200^', crop: '300x200+0+0'
1313
end
1414
end
1515

16-
def self.find_blob_owner(blob_id)
17-
Album.joins(:images_blobs).find_by(active_storage_blobs: { id: blob_id })
16+
def images
17+
album_images
18+
end
19+
20+
def build_images(images)
21+
return if images.blank?
22+
23+
images.each do |image|
24+
album_images.build(file: image)
25+
end
1826
end
1927
end

app/models/album_image.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
class AlbumImage < ApplicationRecord
2+
extend FriendlyId
3+
belongs_to :album
4+
has_one_attached :file
5+
friendly_id :slug_candidates, use: :slugged
6+
7+
private
8+
9+
def slug_candidates
10+
[
11+
:filename,
12+
%i[filename extension],
13+
%i[album_name filename extension],
14+
%i[circle_name album_name filename extension]
15+
]
16+
end
17+
18+
def filename
19+
file.filename.base.to_s
20+
end
21+
22+
def extension
23+
file.filename.extension_without_delimiter.to_s
24+
end
25+
26+
def circle_name
27+
album.circle.name
28+
end
29+
30+
def album_name
31+
album.title
32+
end
33+
end

app/views/albums/show.html.erb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
</span>
1111
<% end %>
1212
<% unless @album.public? %>
13-
<span
14-
class="tag is-warning is-medium ml-1"
13+
<span
14+
class="tag is-warning is-medium ml-1"
1515
title="Az album képei nem elérhetőek a weboldalon kívülről"
1616
>
1717
Nem publikus<sup class="is-align-self-flex-start">?</sup>
@@ -26,7 +26,7 @@
2626
<%= @album.desc %>
2727
</p>
2828
<p class="is-size-6 mt-5">
29-
Használd a
29+
Használd a
3030
<svg xmlns="http://www.w3.org/2000/svg" height="20" fill="none" viewBox="0 2 24 20" stroke="currentColor">
3131
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
3232
</svg>
@@ -110,7 +110,7 @@
110110

111111
<script>
112112
images = <%= @album.images.map { |i|
113-
{ src: url_for(i), w: i.metadata["width"], h: i.metadata["height"] }
113+
{ src: url_for(i), w: i.file.metadata["width"], h: i.file.metadata["height"] }
114114
}.to_json.html_safe %>
115115
</script>
116116
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/masonry.pkgd.min.js"></script>

0 commit comments

Comments
 (0)