Skip to content

Commit

Permalink
Merge pull request #2146 from tf/permalink-redirect
Browse files Browse the repository at this point in the history
Redirect after permalink change
  • Loading branch information
tf authored Sep 20, 2024
2 parents 8dc1eac + 853f65c commit e8fd17b
Show file tree
Hide file tree
Showing 16 changed files with 484 additions and 37 deletions.
26 changes: 23 additions & 3 deletions app/controllers/pageflow/entries_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ def index
def show
respond_to do |format|
format.html do
entry = find_by_permalink || find_by_slug
entry = find_by_permalink

return if !entry && redirect_according_to_permalink_redirect

entry ||= find_by_slug!

return if redirect_according_to_entry_redirect(entry)
return if redirect_according_to_public_https_mode
Expand Down Expand Up @@ -62,18 +66,34 @@ def find_by_permalink
)
end

def find_by_slug
def find_by_slug!
PublishedEntry.find(params[:id], entry_request_scope)
end

def entry_request_scope
Pageflow.config.public_entry_request_scope.call(Entry, request)
end

def redirect_according_to_permalink_redirect
entry = PublishedEntry.find_by_permalink_redirect(
directory: params[:directory],
slug: params[:id],
site: Site.for_request(request)
)

return false unless entry

redirect_to(EntriesHelper::PrettyUrl.build(pageflow, entry),
status: :moved_permanently,
allow_other_host: true)
true
end

def redirect_according_to_entry_redirect(entry)
return unless (redirect_location = entry_redirect(entry))
return false unless (redirect_location = entry_redirect(entry))

redirect_to(redirect_location, status: :moved_permanently, allow_other_host: true)
true
end

def entry_redirect(entry)
Expand Down
26 changes: 19 additions & 7 deletions app/helpers/pageflow/entries_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def pretty_entry_url(entry, options = {})
module PrettyUrl
extend self

def build(routes, entry, options)
def build(routes, entry, options = {})
with_custom_canonical_url_prefix(entry) ||
default(routes, entry, options)
end
Expand All @@ -24,32 +24,44 @@ def build(routes, entry, options)

def with_custom_canonical_url_prefix(entry)
return if entry.site.canonical_entry_url_prefix.blank?

entry = ensure_entry_with_revision(entry)

[
entry.site.canonical_entry_url_prefix.gsub(':locale', entry.locale),
entry.to_param,
entry_suffix(entry),
entry.site.trailing_slash_in_canonical_urls ? '/' : ''
].join
end

def entry_suffix(entry)
if entry.permalink.present?
entry_permalink_parts(entry).join
else
entry.to_param
end
end

def default(routes, entry, options)
params =
options
.reverse_merge(trailing_slash: entry.site.trailing_slash_in_canonical_urls)
.reverse_merge(Pageflow.config.site_url_options(entry.site) || {})

if entry.permalink.present?
routes.permalink_url(
entry.permalink.directory&.path || '',
entry.permalink.slug,
params
)
routes.permalink_url(*entry_permalink_parts(entry), params)
else
routes.short_entry_url(entry.to_model, params)
end
end

def entry_permalink_parts(entry)
[
entry.permalink.directory&.path || '',
entry.permalink.slug
]
end

def ensure_entry_with_revision(entry)
if entry.is_a?(EntryAtRevision)
entry
Expand Down
1 change: 1 addition & 0 deletions app/models/concerns/pageflow/permalinkable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Permalinkable

included do
belongs_to :permalink, optional: true
has_many :permalink_redirects

accepts_nested_attributes_for :permalink, update_only: true
end
Expand Down
9 changes: 9 additions & 0 deletions app/models/pageflow/permalink.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class Permalink < ApplicationRecord

validate :belongs_to_same_site_as_entry

before_update(:create_redirect,
if: -> { entry.first_published_at.present? && (slug_changed? || directory_changed?) })

private

def set_default_slug
Expand All @@ -35,5 +38,11 @@ def belongs_to_same_site_as_entry

errors.add(:directory, :invalid)
end

def create_redirect
directory.redirects.where(slug:).delete_all
entry.permalink_redirects.create!(slug: slug_was,
directory_id: directory_id_was)
end
end
end
4 changes: 4 additions & 0 deletions app/models/pageflow/permalink_directory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ class PermalinkDirectory < ApplicationRecord
validates(:path,
format: %r{\A([0-9a-zA-Z-]+/)*\z},
uniqueness: {scope: :site_id})

has_many(:redirects,
class_name: 'PermalinkRedirect',
foreign_key: :directory_id)
end
end
7 changes: 7 additions & 0 deletions app/models/pageflow/permalink_redirect.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Pageflow
# @api private
class PermalinkRedirect < ApplicationRecord
belongs_to :entry
belongs_to :directory, class_name: 'PermalinkDirectory'
end
end
14 changes: 13 additions & 1 deletion app/models/pageflow/published_entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def self.find(id, scope = Entry)
PublishedEntry.new(scope.published.find(id))
end

def self.find_by_permalink(directory: nil, slug:, scope:)
def self.find_by_permalink(slug:, scope:, directory: nil)
wrap(
scope.published.includes(permalink: :directory).where(
pageflow_permalink_directories: {path: directory || ''},
Expand All @@ -75,6 +75,18 @@ def self.find_by_permalink(directory: nil, slug:, scope:)
)
end

def self.find_by_permalink_redirect(site:, slug:, directory: nil)
wrap(
PermalinkRedirect.joins(:entry, :directory).merge(Entry.published).where(
slug:,
pageflow_permalink_directories: {
path: directory || '',
site:
}
).first&.entry
)
end

def self.wrap_all(entries)
entries = entries.includes(:published_revision) unless entries.loaded?
entries.map { |entry| new(entry) }
Expand Down
6 changes: 4 additions & 2 deletions app/views/admin/entries/_permalink_inputs.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<%= form.input(:permalink,
as: :pageflow_permalink,
base_url: pretty_site_url(entry.site),
base_url: entry.site.canonical_entry_url_prefix.presence ||
pretty_site_url(entry.site),
site: entry.site,
slug_placeholder: entry.default_permalink_slug) %>
slug_placeholder: entry.default_permalink_slug,
hint: entry.new_record? ? nil : t('pageflow.admin.entries.permalink_hint'))%>
8 changes: 8 additions & 0 deletions config/locales/new/permalink_redirects.de.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
de:
pageflow:
admin:
entries:
permalink_hint: |-
Wenn der Permalink geändert wird, nachdem der Beitrag
bereits veröffentlicht wurde, wird automatisch eine
Weiterleitung von der alten URL zur neuen URL eingerichtet.
8 changes: 8 additions & 0 deletions config/locales/new/permalink_redirects.en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
en:
pageflow:
admin:
entries:
permalink_hint: |-
Changing the permalink after the story has already been
published automatically creates a redirect from the old URL
to the new URL
13 changes: 13 additions & 0 deletions db/migrate/20240918084059_create_pageflow_permalink_redirects.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class CreatePageflowPermalinkRedirects < ActiveRecord::Migration[5.2]
def change
create_table :pageflow_permalink_redirects do |t|
t.belongs_to :entry, index: true
t.string :slug
t.belongs_to :directory, index: true

t.timestamps

t.index ['slug', 'directory_id'], name: 'index_pageflow_permalink_redirects_on_slug_and_dir', unique: true
end
end
end
34 changes: 34 additions & 0 deletions spec/controllers/admin/entries_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,40 @@ def self.name
expect(response.body)
.not_to have_selector('input[name="entry[permalink_attributes][slug]"]')
end

it 'displays host of site in permalink input' do
user = create(:user)
account = create(:account, with_publisher: user)
account.default_site.update(cname: 'some.example.com')
create(
:permalink_directory,
path: '',
site: account.default_site
)

sign_in(user, scope: :user)
get :new

expect(response.body)
.to have_selector('.permalink_base_url', text: 'some.example.com/')
end

it 'prefers cononical entry url prefix in permalink input' do
user = create(:user)
account = create(:account, with_publisher: user)
account.default_site.update(canonical_entry_url_prefix: 'https://some.example.com/foo/')
create(
:permalink_directory,
path: '',
site: account.default_site
)

sign_in(user, scope: :user)
get :new

expect(response.body)
.to have_selector('.permalink_base_url', text: 'some.example.com/foo/')
end
end

describe '#create' do
Expand Down
Loading

0 comments on commit e8fd17b

Please sign in to comment.