Skip to content

Commit 7a2310b

Browse files
authored
Merge pull request #2244 from tf/custom-404
Custom 404 pages
2 parents 175a9f4 + 8b2d496 commit 7a2310b

File tree

14 files changed

+316
-12
lines changed

14 files changed

+316
-12
lines changed

admins/pageflow/sites.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ module Pageflow
3030
:copyright_link_label,
3131
:privacy_link_url,
3232
:home_url,
33-
:cutoff_mode_name
33+
:cutoff_mode_name,
34+
:custom_404_entry_id
3435
] + permitted_admin_form_input_params
3536
end
3637

app/controllers/concerns/pageflow/controller_delegation.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module ControllerDelegation
88
# the response.
99
def delegate_to_rack_app!(app)
1010
result = app.call(request.env)
11-
yield(*result) if block_given?
11+
result = yield(result) if block_given?
1212

1313
throw(:delegate, result)
1414
end

app/controllers/pageflow/application_controller.rb

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,9 @@ class ApplicationController < ActionController::Base
2222

2323
rescue_from ActiveRecord::RecordNotFound do |exception|
2424
debug_log_with_backtrace(exception)
25+
2526
respond_to do |format|
26-
format.html do
27-
begin
28-
render file: Rails.public_path.join('pageflow', 'error_pages', '404.html'), status: :not_found
29-
rescue ActionView::MissingTemplate => exception
30-
debug_log_with_backtrace(exception)
31-
head :not_found
32-
end
33-
end
27+
format.html { render_static_404_error_page }
3428
format.any { head :not_found }
3529
end
3630
end
@@ -61,6 +55,14 @@ def locale_from_accept_language_header
6155
http_accept_language.compatible_language_from(I18n.available_locales)
6256
end
6357

58+
def render_static_404_error_page
59+
render file: Rails.public_path.join('pageflow', 'error_pages', '404.html'),
60+
status: :not_found
61+
rescue ActionView::MissingTemplate => e
62+
debug_log_with_backtrace(e)
63+
head :not_found
64+
end
65+
6466
def debug_log_with_backtrace(exception)
6567
Rails.logger.debug exception
6668
backtrace = ''

app/controllers/pageflow/entries_controller.rb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ def show
2525
return unless check_entry_password_protection(entry)
2626

2727
delegate_to_entry_type_frontend_app!(entry)
28+
rescue ActiveRecord::RecordNotFound
29+
render_custom_or_static_404_error_page
2830
end
2931
end
3032
end
@@ -100,15 +102,18 @@ def entry_redirect(entry)
100102
Pageflow.config.public_entry_redirect.call(entry, request)
101103
end
102104

103-
def delegate_to_entry_type_frontend_app!(entry)
105+
def delegate_to_entry_type_frontend_app!(entry, override_status: nil)
104106
EntriesControllerEnvHelper.add_entry_info_to_env(request.env, entry: entry, mode: :published)
105107

106-
delegate_to_rack_app!(entry.entry_type.frontend_app) do |_status, headers, _body|
108+
delegate_to_rack_app!(entry.entry_type.frontend_app) do |result|
109+
status, headers, body = result
107110
config = Pageflow.config_for(entry)
108111

109112
allow_iframe_for_embed(headers)
110113
apply_additional_headers(entry, config, headers)
111114
apply_cache_control(entry, config, headers)
115+
116+
[override_status || status, headers, body]
112117
end
113118
end
114119

@@ -128,5 +133,17 @@ def apply_additional_headers(entry, config, headers)
128133
config.additional_public_entry_headers.for(entry, request)
129134
)
130135
end
136+
137+
def render_custom_or_static_404_error_page
138+
site = Site.for_request(request).first
139+
140+
if site&.custom_404_entry&.published_without_password_protection?
141+
entry = PublishedEntry.new(site.custom_404_entry)
142+
delegate_to_entry_type_frontend_app!(entry, override_status: 404)
143+
else
144+
# Fallback to ApplicationController's handler method
145+
render_static_404_error_page
146+
end
147+
end
131148
end
132149
end

app/models/concerns/pageflow/entry_publication_states.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def published_with_password_protection?
3838
published? && published_revision.password_protected?
3939
end
4040

41+
def published_without_password_protection?
42+
published? && !published_revision.password_protected?
43+
end
44+
4145
def published?
4246
published_revision.present?
4347
end

app/models/pageflow/site.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Pageflow
22
class Site < ApplicationRecord
33
belongs_to :account
4+
belongs_to :custom_404_entry, class_name: 'Entry', optional: true
45

56
has_many :entry_templates, dependent: :destroy
67
has_many :entries
@@ -11,6 +12,7 @@ class Site < ApplicationRecord
1112

1213
validates :account, presence: true
1314
validates_inclusion_of :cutoff_mode_name, in: :available_cutoff_mode_names, allow_blank: true
15+
validate :custom_404_entry_belongs_to_same_site
1416

1517
delegate :enabled_feature_names, to: :account
1618

@@ -74,5 +76,11 @@ def self.ransackable_associations(_auth_object = nil)
7476
def available_cutoff_mode_names
7577
Pageflow.config_for(account).cutoff_modes.names
7678
end
79+
80+
def custom_404_entry_belongs_to_same_site
81+
return unless custom_404_entry.present?
82+
83+
errors.add(:custom_404_entry, :must_belong_to_same_site) if custom_404_entry.site_id != id
84+
end
7785
end
7886
end

app/views/admin/sites/_fields.html.erb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
<%= f.input :sitemap_enabled, hint: t('pageflow.admin.sites.sitemap_hint',
2424
site_host: @site&.persisted? ? @site.host : '<host>') %>
2525

26+
<%= f.input :custom_404_entry_id,
27+
as: :select,
28+
collection: @site&.persisted? ? @site.entries.published_without_password_protection.pluck(:title, :id) : [],
29+
include_blank: t('pageflow.admin.sites.custom_404_entry.none'),
30+
hint: t('pageflow.admin.sites.custom_404_entry.hint') %>
31+
2632
<% account_config.admin_form_inputs.find_all_for(:site).each do |form_input| %>
2733
<%= form_input.build(f) %>
2834
<% end %>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
de:
2+
pageflow:
3+
admin:
4+
sites:
5+
custom_404_entry:
6+
none: "(Standard 404-Seite verwenden)"
7+
hint: "Wählen Sie einen Eintrag aus, der als benutzerdefinierte 404-Seite angezeigt werden soll. Nur veröffentlichte Einträge ohne Passwortschutz sind verfügbar."
8+
activerecord:
9+
attributes:
10+
pageflow/site:
11+
custom_404_entry_id: "Benutzerdefinierte 404-Seite"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
en:
2+
pageflow:
3+
admin:
4+
sites:
5+
custom_404_entry:
6+
none: "(Use default 404 page)"
7+
hint: "Select an entry to display as the custom 404 page. Only published entries without password protection are available."
8+
activerecord:
9+
attributes:
10+
pageflow/site:
11+
custom_404_entry_id: "Custom 404 page"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddCustom404EntryToSites < ActiveRecord::Migration[7.1]
2+
def change
3+
add_reference :pageflow_sites, :custom_404_entry, null: true
4+
end
5+
end

0 commit comments

Comments
 (0)