FEATURE: Allow to modify topic-backed static pages (#15324)

A plugin API that allows customizing existing topic-backed static pages, like:
faq, tos, privacy (see: StaticController) The block passed to this
method has to return a SiteSetting name that contains a topic id.

```
add_topic_static_page("faq") do |controller|
  current_user&.locale == "pl" ? "polish_faq_topic_id" : "faq_topic_id"
end
```

You can also add new pages in a plugin, but remember to add a route,
for example:

```
get "contact" => "static#show", id: "contact"
```
This commit is contained in:
Jarek Radosz 2021-12-16 04:24:11 +01:00 committed by GitHub
parent ad4faf637c
commit 2a4df93b8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 17 deletions

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
class StaticController < ApplicationController class StaticController < ApplicationController
skip_before_action :check_xhr, :redirect_to_login_if_required skip_before_action :check_xhr, :redirect_to_login_if_required
skip_before_action :verify_authenticity_token, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset] skip_before_action :verify_authenticity_token, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset]
skip_before_action :preload_json, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset] skip_before_action :preload_json, only: [:brotli_asset, :cdn_asset, :enter, :favicon, :service_worker_asset]
@ -11,24 +10,26 @@ class StaticController < ApplicationController
PAGES_WITH_EMAIL_PARAM = ['login', 'password_reset', 'signup'] PAGES_WITH_EMAIL_PARAM = ['login', 'password_reset', 'signup']
MODAL_PAGES = ['password_reset', 'signup'] MODAL_PAGES = ['password_reset', 'signup']
DEFAULT_PAGES = {
"faq" => { redirect: "faq_url", topic_id: "guidelines_topic_id" },
"tos" => { redirect: "tos_url", topic_id: "tos_topic_id" },
"privacy" => { redirect: "privacy_policy_url", topic_id: "privacy_topic_id" },
}
CUSTOM_PAGES = {} # Add via `#add_topic_static_page` in plugin API
def show def show
return redirect_to(path '/') if current_user && (params[:id] == 'login' || params[:id] == 'signup') return redirect_to(path '/') if current_user && (params[:id] == 'login' || params[:id] == 'signup')
if SiteSetting.login_required? && current_user.nil? && ['faq', 'guidelines'].include?(params[:id]) if SiteSetting.login_required? && current_user.nil? && ['faq', 'guidelines'].include?(params[:id])
return redirect_to path('/login') return redirect_to path('/login')
end end
map = { map = DEFAULT_PAGES.deep_merge(CUSTOM_PAGES)
"faq" => { redirect: "faq_url", topic_id: "guidelines_topic_id" },
"tos" => { redirect: "tos_url", topic_id: "tos_topic_id" },
"privacy" => { redirect: "privacy_policy_url", topic_id: "privacy_topic_id" }
}
@page = params[:id] @page = params[:id]
if map.has_key?(@page) if map.has_key?(@page)
site_setting_key = map[@page][:redirect] site_setting_key = map[@page][:redirect]
url = SiteSetting.get(site_setting_key) url = SiteSetting.get(site_setting_key) if site_setting_key
return redirect_to(url) if url.present? return redirect_to(url) if url.present?
end end
@ -39,8 +40,12 @@ class StaticController < ApplicationController
@page = @page.gsub(/[^a-z0-9\_\-]/, '') @page = @page.gsub(/[^a-z0-9\_\-]/, '')
if map.has_key?(@page) if map.has_key?(@page)
@topic = Topic.find_by_id(SiteSetting.get(map[@page][:topic_id])) topic_id = map[@page][:topic_id]
topic_id = instance_exec(&topic_id) if topic_id.is_a?(Proc)
@topic = Topic.find_by_id(SiteSetting.get(topic_id))
raise Discourse::NotFound unless @topic raise Discourse::NotFound unless @topic
title_prefix = if I18n.exists?("js.#{@page}") title_prefix = if I18n.exists?("js.#{@page}")
I18n.t("js.#{@page}") I18n.t("js.#{@page}")
else else
@ -48,17 +53,16 @@ class StaticController < ApplicationController
end end
@title = "#{title_prefix} - #{SiteSetting.title}" @title = "#{title_prefix} - #{SiteSetting.title}"
@body = @topic.posts.first.cooked @body = @topic.posts.first.cooked
@faq_overriden = !SiteSetting.faq_url.blank? @faq_overridden = !SiteSetting.faq_url.blank?
render :show, layout: !request.xhr?, formats: [:html] render :show, layout: !request.xhr?, formats: [:html]
return return
end end
unless @title.present? @title = SiteSetting.title
@title = if SiteSetting.short_site_description.present?
"#{SiteSetting.title} - #{SiteSetting.short_site_description}" if SiteSetting.short_site_description.present?
else @title << " - #{SiteSetting.short_site_description}"
SiteSetting.title
end
end end
if I18n.exists?("static.#{@page}") if I18n.exists?("static.#{@page}")

View File

@ -3,7 +3,7 @@
<ul class='nav-pills' role='navigation' itemscope itemtype='http://schema.org/SiteNavigationElement'> <ul class='nav-pills' role='navigation' itemscope itemtype='http://schema.org/SiteNavigationElement'>
<% unless SiteSetting.login_required? && current_user.nil? %> <% unless SiteSetting.login_required? && current_user.nil? %>
<li class="nav-item-about"><%= link_to t('js.about.simple_title'), about_index_path %></a></li> <li class="nav-item-about"><%= link_to t('js.about.simple_title'), about_index_path %></a></li>
<% if @faq_overriden %> <% if @faq_overridden %>
<li class='nav-item-guidelines'><a class='<%= @page == 'faq' ? 'active' : '' %>' href='<%= guidelines_path %>'><%= t 'js.guidelines' %></a></li> <li class='nav-item-guidelines'><a class='<%= @page == 'faq' ? 'active' : '' %>' href='<%= guidelines_path %>'><%= t 'js.guidelines' %></a></li>
<li class='nav-item-faq'><a href='<%= faq_path %>'><%= t 'js.faq' %></a></li> <li class='nav-item-faq'><a href='<%= faq_path %>'><%= t 'js.faq' %></a></li>
<% else %> <% else %>

View File

@ -994,6 +994,22 @@ class Plugin::Instance
DiscoursePluginRegistry.register_notification_consolidation_plan(plan, self) DiscoursePluginRegistry.register_notification_consolidation_plan(plan, self)
end end
# Allows customizing existing topic-backed static pages, like:
# faq, tos, privacy (see: StaticController) The block passed to this
# method has to return a SiteSetting name that contains a topic id.
#
# add_topic_static_page("faq") do |controller|
# current_user&.locale == "pl" ? "polish_faq_topic_id" : "faq_topic_id"
# end
#
# You can also add new pages in a plugin, but remember to add a route,
# for example:
#
# get "contact" => "static#show", id: "contact"
def add_topic_static_page(page, options = {}, &blk)
StaticController::CUSTOM_PAGES[page] = blk ? { topic_id: blk } : options
end
protected protected
def self.js_path def self.js_path

View File

@ -277,6 +277,52 @@ describe StaticController do
expect(response.body).to include("<title>FAQ - Discourse</title>") expect(response.body).to include("<title>FAQ - Discourse</title>")
end end
end end
context "plugin api extensions" do
after do
Rails.application.reload_routes!
StaticController::CUSTOM_PAGES.clear
end
it "adds new topic-backed pages" do
routes = Proc.new do
get "contact" => "static#show", id: "contact"
end
Discourse::Application.routes.send(:eval_block, routes)
topic_id = Fabricate(:post, cooked: "contact info").topic_id
SiteSetting.setting(:contact_topic_id, topic_id)
Plugin::Instance.new.add_topic_static_page("contact", topic_id: "contact_topic_id")
get "/contact"
expect(response.status).to eq(200)
expect(response.body).to include("contact info")
end
it "replaces existing topic-backed pages" do
topic_id = Fabricate(:post, cooked: "Regular FAQ").topic_id
SiteSetting.setting(:faq_topic_id, topic_id)
polish_topic_id = Fabricate(:post, cooked: "Polish FAQ").topic_id
SiteSetting.setting(:polish_faq_topic_id, polish_topic_id)
Plugin::Instance.new.add_topic_static_page("faq") do
current_user&.locale == "pl" ? "polish_faq_topic_id" : "faq_topic_id"
end
get "/faq"
expect(response.status).to eq(200)
expect(response.body).to include("Regular FAQ")
sign_in(Fabricate(:user, locale: "pl"))
get "/faq"
expect(response.status).to eq(200)
expect(response.body).to include("Polish FAQ")
end
end
end end
describe '#enter' do describe '#enter' do