FEATURE: Allow the user to select a custom home page (#5268)

* Add user_home configuration option

* Use the new user_home preference to actually show the right home page

* Fix trailing whitespace

* Update user_option_serializer.rb

* Fix JavaScript default homepage tests

* Use an object instead of a giant switch

* Remove trailing whitespace

* Make the default `user_home` set to `null` instead of `0`

* Rename user_home to homepage_id
This commit is contained in:
Michael Howell 2017-11-09 12:45:19 -07:00 committed by Sam
parent 162932114e
commit 38b8d68c68
14 changed files with 90 additions and 4 deletions

View File

@ -1,8 +1,11 @@
import PreferencesTabController from "discourse/mixins/preferences-tab-controller"; import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
import { setDefaultHomepage } from "discourse/lib/utilities";
import { default as computed, observes } from "ember-addons/ember-computed-decorators"; import { default as computed, observes } from "ember-addons/ember-computed-decorators";
import { currentThemeKey, listThemes, previewTheme, setLocalTheme } from 'discourse/lib/theme-selector'; import { currentThemeKey, listThemes, previewTheme, setLocalTheme } from 'discourse/lib/theme-selector';
import { popupAjaxError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
const USER_HOMES = { 1: "latest", 2: "categories", 3: "unread", 4: "new", 5: "top" };
export default Ember.Controller.extend(PreferencesTabController, { export default Ember.Controller.extend(PreferencesTabController, {
@computed("makeThemeDefault") @computed("makeThemeDefault")
@ -14,6 +17,8 @@ export default Ember.Controller.extend(PreferencesTabController, {
'enable_quoting', 'enable_quoting',
'disable_jump_reply', 'disable_jump_reply',
'automatically_unpin_topics', 'automatically_unpin_topics',
'allow_private_messages',
'homepage_id',
]; ];
if (makeDefault) { if (makeDefault) {
@ -51,6 +56,19 @@ export default Ember.Controller.extend(PreferencesTabController, {
previewTheme(key); previewTheme(key);
}, },
homeChanged() {
const siteHome = Discourse.SiteSettings.top_menu.split("|")[0].split(",")[0];
const userHome = USER_HOMES[this.get('model.user_option.homepage_id')];
setDefaultHomepage(userHome || siteHome);
},
@computed()
userSelectableHome() {
return _.map(USER_HOMES, (name, num) => {
return {name: I18n.t('filters.' + name + '.title'), value: num};
});
},
actions: { actions: {
save() { save() {
this.set('saved', false); this.set('saved', false);
@ -66,6 +84,8 @@ export default Ember.Controller.extend(PreferencesTabController, {
setLocalTheme(this.get('themeKey'), this.get('model.user_option.theme_key_seq')); setLocalTheme(this.get('themeKey'), this.get('model.user_option.theme_key_seq'));
} }
this.homeChanged();
}).catch(popupAjaxError); }).catch(popupAjaxError);
} }
} }

View File

@ -1,5 +1,7 @@
import { escape } from 'pretty-text/sanitizer'; import { escape } from 'pretty-text/sanitizer';
const homepageSelector = 'meta[name=discourse_current_homepage]';
export function translateSize(size) { export function translateSize(size) {
switch (size) { switch (size) {
case 'tiny': return 20; case 'tiny': return 20;
@ -349,8 +351,22 @@ export function displayErrorForUpload(data) {
} }
export function defaultHomepage() { export function defaultHomepage() {
// the homepage is the first item of the 'top_menu' site setting let homepage = null;
return Discourse.SiteSettings.top_menu.split("|")[0].split(",")[0]; let elem = _.first($(homepageSelector));
if (elem) {
homepage = elem.content;
}
if (!homepage) {
homepage = Discourse.SiteSettings.top_menu.split("|")[0].split(",")[0];
}
return homepage;
}
export function setDefaultHomepage(homepage) {
let elem = _.first($(homepageSelector));
if (elem) {
elem.content = homepage;
}
} }
export function determinePostReplaceSelection({ selection, needle, replacement }) { export function determinePostReplaceSelection({ selection, needle, replacement }) {

View File

@ -249,6 +249,7 @@ const User = RestModel.extend({
'include_tl0_in_digests', 'include_tl0_in_digests',
'theme_key', 'theme_key',
'allow_private_messages', 'allow_private_messages',
'homepage_id',
]; ];
if (fields) { if (fields) {

View File

@ -23,6 +23,13 @@
{{/if}} {{/if}}
<div class="control-group home">
<label class="control-label">{{i18n 'user.home'}}</label>
<div class="controls">
{{combo-box content=userSelectableHome valueAttribute="value" value=model.user_option.homepage_id}}
</div>
</div>
<div class="control-group other"> <div class="control-group other">
<label class="control-label">{{i18n 'user.other_settings'}}</label> <label class="control-label">{{i18n 'user.other_settings'}}</label>

View File

@ -308,7 +308,7 @@ class ApplicationController < ActionController::Base
end end
def current_homepage def current_homepage
current_user ? SiteSetting.homepage : SiteSetting.anonymous_homepage current_user&.user_option&.homepage || SiteSetting.anonymous_homepage
end end
def serialize_data(obj, serializer, opts = nil) def serialize_data(obj, serializer, opts = nil)

View File

@ -347,6 +347,10 @@ module ApplicationHelper
end end
end end
def current_homepage
current_user&.user_option&.homepage || SiteSetting.anonymous_homepage
end
def build_plugin_html(name) def build_plugin_html(name)
return "" unless allow_plugins? return "" unless allow_plugins?
DiscoursePluginRegistry.build_html(name, controller) || "" DiscoursePluginRegistry.build_html(name, controller) || ""

View File

@ -128,6 +128,17 @@ class UserOption < ActiveRecord::Base
times.max times.max
end end
def homepage
case homepage_id
when 1 then "latest"
when 2 then "categories"
when 3 then "unread"
when 4 then "new"
when 5 then "top"
else SiteSetting.homepage
end
end
private private
def update_tracked_topics def update_tracked_topics
@ -165,6 +176,7 @@ end
# theme_key :string # theme_key :string
# theme_key_seq :integer default(0), not null # theme_key_seq :integer default(0), not null
# allow_private_messages :boolean default(TRUE), not null # allow_private_messages :boolean default(TRUE), not null
# homepage_id :integer default(null)
# #
# Indexes # Indexes
# #

View File

@ -22,6 +22,7 @@ class UserOptionSerializer < ApplicationSerializer
:theme_key, :theme_key,
:theme_key_seq, :theme_key_seq,
:allow_private_messages, :allow_private_messages,
:homepage_id,
def auto_track_topics_after_msecs def auto_track_topics_after_msecs
object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs

View File

@ -36,6 +36,7 @@ class UserUpdater
:include_tl0_in_digests, :include_tl0_in_digests,
:theme_key, :theme_key,
:allow_private_messages, :allow_private_messages,
:homepage_id,
] ]
def initialize(actor, user) def initialize(actor, user)

View File

@ -5,6 +5,7 @@
<title><%= content_for?(:title) ? yield(:title) : SiteSetting.title %></title> <title><%= content_for?(:title) ? yield(:title) : SiteSetting.title %></title>
<meta name="description" content="<%= @description_meta || SiteSetting.site_description %>"> <meta name="description" content="<%= @description_meta || SiteSetting.site_description %>">
<meta name="discourse_theme_key" content="<%= theme_key %>"> <meta name="discourse_theme_key" content="<%= theme_key %>">
<meta name="discourse_current_homepage" content="<%= current_homepage %>">
<%= render partial: "layouts/head" %> <%= render partial: "layouts/head" %>
<%= discourse_csrf_tags %> <%= discourse_csrf_tags %>

View File

@ -658,6 +658,7 @@ en:
undo_revoke_access: "Undo Revoke Access" undo_revoke_access: "Undo Revoke Access"
api_approved: "Approved:" api_approved: "Approved:"
theme: "Theme" theme: "Theme"
home: "Default Home Page"
staff_counters: staff_counters:
flags_given: "helpful flags" flags_given: "helpful flags"

View File

@ -0,0 +1,5 @@
class AddUserOptionHome < ActiveRecord::Migration[5.1]
def change
add_column :user_options, :homepage_id, :integer, null: true, default: nil
end
end

View File

@ -7,7 +7,7 @@ class HomePageConstraint
return @filter == 'finish_installation' if SiteSetting.has_login_hint? return @filter == 'finish_installation' if SiteSetting.has_login_hint?
provider = Discourse.current_user_provider.new(request.env) provider = Discourse.current_user_provider.new(request.env)
homepage = provider.current_user ? SiteSetting.homepage : SiteSetting.anonymous_homepage homepage = provider&.current_user&.user_option&.homepage || SiteSetting.anonymous_homepage
homepage == @filter homepage == @filter
rescue Discourse::InvalidAccess rescue Discourse::InvalidAccess
false false

View File

@ -10,6 +10,7 @@ import {
getRawSize, getRawSize,
avatarImg, avatarImg,
defaultHomepage, defaultHomepage,
setDefaultHomepage,
validateUploadedFiles, validateUploadedFiles,
getUploadMarkdown, getUploadMarkdown,
caretRowCol, caretRowCol,
@ -204,6 +205,22 @@ QUnit.test("allowsAttachments", assert => {
QUnit.test("defaultHomepage", assert => { QUnit.test("defaultHomepage", assert => {
Discourse.SiteSettings.top_menu = "latest|top|hot"; Discourse.SiteSettings.top_menu = "latest|top|hot";
assert.equal(defaultHomepage(), "latest", "default homepage is the first item in the top_menu site setting"); assert.equal(defaultHomepage(), "latest", "default homepage is the first item in the top_menu site setting");
var meta = document.createElement("meta");
meta.name = "discourse_current_homepage";
meta.content = "hot";
document.body.appendChild(meta);
assert.equal(defaultHomepage(), "hot", "default homepage is pulled from <meta name=discourse_current_homepage>");
document.body.removeChild(meta);
});
QUnit.test("setDefaultHomepage", assert => {
var meta = document.createElement("meta");
meta.name = "discourse_current_homepage";
meta.content = "hot";
document.body.appendChild(meta);
setDefaultHomepage("top");
assert.equal(meta.content, "top", "default homepage set by setDefaultHomepage");
document.body.removeChild(meta);
}); });
QUnit.test("caretRowCol", assert => { QUnit.test("caretRowCol", assert => {