feat(controllers): extend posts_controller with publish and unpublish

This commit is contained in:
dualfroz committed 2026-09-04 16:26:21 +02:00
1 parent 985e87fb68
commit 2226e46cd6
3 files changed
+155

No files matched your search

@@ -287,4 +287,65 @@ class PostsController < ApplicationController
flash[:notice] = t('posts.restored_notice', id: post.id) flash[:notice] = t('posts.restored_notice', id: post.id)
redirect url_for(:trash_posts) redirect url_for(:trash_posts)
rescue Prism::Orm::RecordNotFound rescue Prism::Orm::RecordNotFound
respond_not_found(t('posts.not_found'))
end
# `published!`/`draft!` come straight from Post's `enum :status` (see the
# model) - they set the column and save() in one call, so this action is
# nothing but "find, authorize like an edit, flip the status".
def publish
post = Post.find_by!(slug: params['slug'])
authorize(post, :update?)
post.published!
flash[:notice] = t('posts.published_notice', id: post.id)
redirect url_for(:post, slug: post.to_param)
rescue Prism::Orm::RecordNotFound
respond_not_found(t('posts.not_found'))
end
def unpublish
post = Post.find_by!(slug: params['slug'])
authorize(post, :update?)
post.draft!
flash[:notice] = t('posts.unpublished_notice', id: post.id)
redirect url_for(:post, slug: post.to_param)
rescue Prism::Orm::RecordNotFound
respond_not_found(t('posts.not_found'))
end
# Post#acts_as_list is scoped by `featured` (see the model) - moving a
# post that ISN'T currently featured is harmless (it just reorders it
# inside the unused non-featured group) but pointless, so both actions
# redirect back to the SAME featured=1 view the reorder links only ever
# appear on, regardless of which group the post actually belongs to.
def move_up
post = Post.find_by!(slug: params['slug'])
authorize(post, :update?)
post.move_higher
redirect "#{url_for(:posts)}?featured=1"
rescue Prism::Orm::RecordNotFound
respond_not_found(t('posts.not_found'))
end
def move_down
post = Post.find_by!(slug: params['slug'])
authorize(post, :update?)
post.move_lower
redirect "#{url_for(:posts)}?featured=1"
rescue Prism::Orm::RecordNotFound
respond_not_found(t('posts.not_found'))
end
private
def store_cover(upload)
return nil unless upload.is_a?(Prism::UploadedFile)
return nil if upload.original_filename.to_s.empty?
uploads_dir = File.join(APP_ROOT, 'public', 'uploads')
FileUtils.mkdir_p(uploads_dir)
safe_name = "#{Time.now.strftime('%Y%m%d%H%M%S')}-#{upload.original_filename.gsub(/[^a-zA-Z0-9_.-]/, '_')}"
File.binwrite(File.join(uploads_dir, safe_name), upload.read)
safe_name
end
end end
@@ -0,0 +1,47 @@
class SessionController < ApplicationController
def new
render(:new, locals: { errors: [] })
end
def create
credentials = params.require('user').permit(:email, :password, :remember_me).to_h
user = User.find_by(email: credentials['email'])
if user && user.authenticate(credentials['password'])
sign_in(user, remember: credentials['remember_me'] == '1')
respond_to do |format|
format.html do
flash[:notice] = t('sessions.signed_in_notice')
redirect after_authentication_url
end
format.json { json(user.as_json(methods: [:email])) }
end
else
respond_to do |format|
format.html do
status(422)
render(:new, locals: { errors: [t('sessions.invalid_credentials')] })
end
# 401, not 422 like the HTML branch above: there is no resource
# here whose validation failed (no User object at all when the
# email does not match anyone), just a rejected authentication
# attempt - the JSON:API status a client should actually branch on.
format.json do
json({ errors: [jsonapi_error(status: 401, pointer: '/data/attributes/password',
title: 'invalid credentials', detail: t('sessions.invalid_credentials'))] }, 401)
end
end
end
end
def destroy
sign_out
respond_to do |format|
format.html do
flash[:notice] = t('sessions.signed_out_notice')
redirect url_for(:root)
end
format.json { json({ message: t('sessions.signed_out_notice') }) }
end
end
end
@@ -0,0 +1,47 @@
class UsersController < ApplicationController
def new
render(:new, locals: { user: User.new, errors: [] })
end
def create
attrs = params.require('user').permit(:email, :password, :password_confirmation).to_h
user = User.new(attrs)
if user.save
sign_in(user)
respond_to do |format|
format.html do
flash[:notice] = t('users.signed_up_notice')
redirect url_for(:root)
end
format.json { json(user.as_json(methods: [:email]), 201) }
end
else
respond_to do |format|
format.html do
status(422)
render(:new, locals: { user: user, errors: user.errors.full_messages })
end
format.json { render_errors(user) }
end
end
# The unique index on users.email (see db/migrate) is a storage-level
# constraint, not just the app-level `validates :email, uniqueness: true`
# above - it still fires for anything that reaches Store#insert without
# going through Model#valid? first (a racing concurrent signup that read
# the table before the other one committed, a future admin script, a bug
# elsewhere that skips validation). #create can't tell those apart from a
# normal duplicate, so it reports the same friendly message either way.
rescue Prism::Orm::Store::UniqueConstraintViolation
respond_to do |format|
format.html do
status(422)
render(:new, locals: { user: user, errors: [t('users.email_taken_notice')] })
end
format.json do
json({ errors: [jsonapi_error(status: 422, pointer: '/data/attributes/email',
title: 'is taken', detail: t('users.email_taken_notice'))] }, 422)
end
end
end
end