From 1668b5eab2e218817b8c49b0f94bc2d73b9f97e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9gis=20Hanol?= <regis@hanol.fr>
Date: Thu, 28 Mar 2013 14:01:13 +0100
Subject: [PATCH] FIX: allows the selection of the default landing tab

---
 .../discourse/controllers/list_controller.js  | 21 +++----
 .../javascripts/discourse/models/nav_item.js  | 60 +++++++------------
 .../discourse/routes/application_routes.js    | 18 +++---
 app/controllers/list_controller.rb            |  6 +-
 app/models/site_setting.rb                    |  9 +++
 config/routes.rb                              | 28 ++++-----
 lib/homepage_constraint.rb                    | 10 ++++
 spec/controllers/list_controller_spec.rb      | 17 ++++--
 8 files changed, 81 insertions(+), 88 deletions(-)
 create mode 100644 lib/homepage_constraint.rb

diff --git a/app/assets/javascripts/discourse/controllers/list_controller.js b/app/assets/javascripts/discourse/controllers/list_controller.js
index 22d50bb01..046df79dc 100644
--- a/app/assets/javascripts/discourse/controllers/list_controller.js
+++ b/app/assets/javascripts/discourse/controllers/list_controller.js
@@ -14,7 +14,7 @@ Discourse.ListController = Discourse.Controller.extend({
   canCreateTopic: false,
   needs: ['composer', 'modal', 'listTopics'],
 
-  availableNavItems: (function() {
+  availableNavItems: function() {
     var hasCategories, loggedOn, summary;
     summary = this.get('filterSummary');
     loggedOn = !!Discourse.get('currentUser');
@@ -28,7 +28,7 @@ Discourse.ListController = Discourse.Controller.extend({
     }).filter(function(i) {
       return i !== null;
     });
-  }).property('filterSummary'),
+  }.property('filterSummary'),
 
   /**
     Load a list based on a filter
@@ -63,26 +63,23 @@ Discourse.ListController = Discourse.Controller.extend({
   },
 
   // Put in the appropriate page title based on our view
-  updateTitle: (function() {
+  updateTitle: function() {
     if (this.get('filterMode') === 'categories') {
       return Discourse.set('title', Em.String.i18n('categories_list'));
     } else {
       if (this.present('category')) {
-        return Discourse.set('title', "" + (this.get('category.name').capitalize()) + " " + (Em.String.i18n('topic.list')));
+        return Discourse.set('title', this.get('category.name').capitalize() + " " + Em.String.i18n('topic.list'));
       } else {
         return Discourse.set('title', Em.String.i18n('topic.list'));
       }
     }
-  }).observes('filterMode', 'category'),
+  }.observes('filterMode', 'category'),
 
   // Create topic button
   createTopic: function() {
-    var topicList;
-    topicList = this.get('controllers.listTopics.content');
-    if (!topicList) {
-      return;
-    }
-    return this.get('controllers.composer').open({
+    var topicList = this.get('controllers.listTopics.content');
+    if (!topicList) return;
+    this.get('controllers.composer').open({
       categoryName: this.get('category.name'),
       action: Discourse.Composer.CREATE_TOPIC,
       draftKey: topicList.get('draft_key'),
@@ -100,5 +97,3 @@ Discourse.ListController = Discourse.Controller.extend({
 Discourse.ListController.reopenClass({
   filters: ['latest', 'hot', 'favorited', 'read', 'unread', 'new', 'posted']
 });
-
-
diff --git a/app/assets/javascripts/discourse/models/nav_item.js b/app/assets/javascripts/discourse/models/nav_item.js
index 0e5d0e9db..802b47e0f 100644
--- a/app/assets/javascripts/discourse/models/nav_item.js
+++ b/app/assets/javascripts/discourse/models/nav_item.js
@@ -6,47 +6,35 @@
   @namespace Discourse
   @module Discourse
 **/
-var validNavNames = ['read', 'latest', 'hot', 'categories', 'favorited', 'category', 'unread', 'new', 'posted'];
-var validAnon = ['latest', 'hot', 'category', 'categories'];
+var validNavNames = ['latest', 'hot', 'categories', 'category', 'favorited', 'unread', 'new', 'read', 'posted'];
+var validAnon     = ['latest', 'hot', 'categories', 'category'];
 
 Discourse.NavItem = Discourse.Model.extend({
-  categoryName: (function() {
-    var split;
-    split = this.get('name').split('/');
-    if (split[0] === 'category') {
-      return split[1];
-    } else {
-      return null;
-    }
-  }).property(),
-  href: (function() {
-    /* href from this item
-    */
+  categoryName: function() {
+    var split = this.get('name').split('/');
+    return split[0] === 'category' ? split[1] : null;
+  }.property('name'),
 
-    var name;
-    name = this.get('name');
-    if (name === 'category') {
-      return Discourse.getURL("/") + name + "/" + (this.get('categoryName'));
-    } else {
-      return Discourse.getURL("/") + name;
-    }
-  }).property()
+  // href from this item
+  href: function() {
+    var name = this.get('name'),
+        href = Discourse.getURL("/") + name;
+    if (name === 'category') href += "/" + this.get('categoryName');
+    return href;
+  }.property('name')
 });
 
 Discourse.NavItem.reopenClass({
 
   // create a nav item from the text, will return null if there is not valid nav item for this particular text
   fromText: function(text, opts) {
-    var countSummary, hasCategories, loggedOn, name, split, testName;
-    countSummary = opts.countSummary;
-    loggedOn = opts.loggedOn;
-    hasCategories = opts.hasCategories;
-    split = text.split(",");
-    name = split[0];
-    testName = name.split("/")[0];
+    var countSummary = opts.countSummary,
+        split = text.split(","),
+        name = split[0],
+        testName = name.split("/")[0];
 
-    if (!loggedOn && !validAnon.contains(testName)) return null;
-    if (!hasCategories && testName === "categories") return null;
+    if (!opts.loggedOn && !validAnon.contains(testName)) return null;
+    if (!opts.hasCategories && testName === "categories") return null;
     if (!validNavNames.contains(testName)) return null;
 
     opts = {
@@ -54,14 +42,10 @@ Discourse.NavItem.reopenClass({
       hasIcon: name === "unread" || name === "favorited",
       filters: split.splice(1)
     };
-    if (countSummary) {
-      if (countSummary && countSummary[name]) {
-        opts.count = countSummary[name];
-      }
-    }
+
+    if (countSummary && countSummary[name]) opts.count = countSummary[name];
+
     return Discourse.NavItem.create(opts);
   }
 
 });
-
-
diff --git a/app/assets/javascripts/discourse/routes/application_routes.js b/app/assets/javascripts/discourse/routes/application_routes.js
index 8d5cb265b..9d8e487be 100644
--- a/app/assets/javascripts/discourse/routes/application_routes.js
+++ b/app/assets/javascripts/discourse/routes/application_routes.js
@@ -19,22 +19,20 @@ Discourse.Route.buildRoutes(function() {
     router.route(p, { path: "/" + p });
   });
 
-  this.route('faq', { path: '/faq' });
-  this.route('tos', { path: '/tos' });
-  this.route('privacy', { path: '/privacy' });
-
   // List routes
   this.resource('list', { path: '/' }, function() {
     router = this;
 
     // Generate routes for all our filters
-    Discourse.ListController.filters.forEach(function(r) {
-      router.route(r, { path: "/" + r });
-      router.route(r, { path: "/" + r + "/more" });
+    Discourse.ListController.filters.forEach(function(filter) {
+      router.route(filter, { path: "/" + filter });
+      router.route(filter, { path: "/" + filter + "/more" });
     });
 
-    this.route('latest', { path: '/' });
-    this.route('hot', { path: '/hot' });
+    // the homepage is the first item of the 'top_menu' site setting
+    var homepage = PreloadStore.get('siteSettings').top_menu.split("|")[0];
+    this.route(homepage, { path: '/' });
+
     this.route('categories', { path: '/categories' });
     this.route('category', { path: '/category/:slug/more' });
     this.route('category', { path: '/category/:slug' });
@@ -51,5 +49,3 @@ Discourse.Route.buildRoutes(function() {
     this.route('invited', { path: 'invited' });
   });
 });
-
-
diff --git a/app/controllers/list_controller.rb b/app/controllers/list_controller.rb
index 253faa65a..773e05c89 100644
--- a/app/controllers/list_controller.rb
+++ b/app/controllers/list_controller.rb
@@ -1,12 +1,11 @@
 class ListController < ApplicationController
 
-  before_filter :ensure_logged_in, except: [:index, :hot, :category, :category_feed]
+  before_filter :ensure_logged_in, except: [:latest, :hot, :category, :category_feed]
   skip_before_filter :check_xhr
 
   # Create our filters
   [:latest, :hot, :favorited, :read, :posted, :unread, :new].each do |filter|
     define_method(filter) do
-
       list_opts = {page: params[:page]}
 
       # html format means we need to farm exclude from the site options
@@ -14,7 +13,7 @@ class ListController < ApplicationController
         #TODO objectify this stuff
         SiteSetting.top_menu.split('|').each do |f|
           s = f.split(",")
-          if s[0] == action_name || (action_name == "index" && s[0] == "latest")
+          if s[0] == action_name || (action_name == "index" && s[0] == SiteSetting.homepage)
             list_opts[:exclude_category] = s[1][1..-1] if s.length == 2
           end
         end
@@ -27,7 +26,6 @@ class ListController < ApplicationController
       respond(list)
     end
   end
-  alias_method :index, :latest
 
   def category
 
diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb
index a7982efab..03f1d535a 100644
--- a/app/models/site_setting.rb
+++ b/app/models/site_setting.rb
@@ -190,4 +190,13 @@ class SiteSetting < ActiveRecord::Base
   def self.post_length
     min_post_length..max_post_length
   end
+
+  def self.homepage
+    top_menu.split('|')[0]
+  end
+
+  def self.anonymous_homepage
+    top_menu.split('|').select{ |f| ['latest', 'hot', 'categories', 'category'].include? f }[0]
+  end
+
 end
diff --git a/config/routes.rb b/config/routes.rb
index 81de68fe7..f6bac25af 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,6 +1,7 @@
 require 'sidekiq/web'
 
 require_dependency 'admin_constraint'
+require_dependency 'homepage_constraint'
 
 # This used to be User#username_format, but that causes a preload of the User object
 # and makes Guard not work properly.
@@ -154,20 +155,10 @@ Discourse::Application.routes.draw do
   get 'popular' => 'list#popular_redirect'
   get 'popular/more' => 'list#popular_redirect'
 
-  get 'latest' => 'list#index'
-  get 'latest/more' => 'list#index'
-  get 'hot' => 'list#hot'
-  get 'hot/more' => 'list#hot'
-  get 'favorited' => 'list#favorited'
-  get 'favorited/more' => 'list#favorited'
-  get 'read' => 'list#read'
-  get 'read/more' => 'list#read'
-  get 'unread' => 'list#unread'
-  get 'unread/more' => 'list#unread'
-  get 'new' => 'list#new'
-  get 'new/more' => 'list#new'
-  get 'posted' => 'list#posted'
-  get 'posted/more' => 'list#posted'
+  [:latest, :hot, :favorited, :read, :posted, :unread, :new].each do |filter|
+    get "#{filter}" => "list##{filter}"
+    get "#{filter}/more" => "list##{filter}"
+  end
 
   get 'search' => 'search#query'
 
@@ -222,11 +213,12 @@ Discourse::Application.routes.draw do
   post 'draft' => 'draft#update'
   delete 'draft' => 'draft#destroy'
 
-
   get 'robots.txt' => 'robots_txt#index'
 
-  # You can have the root of your site routed with "root"
-  # just remember to delete public/index.html.
-  root to: 'list#index'
+  [:latest, :hot, :unread, :new, :favorited, :read, :posted].each do |filter|
+    root to: "list##{filter}", constraints: HomePageConstraint.new("#{filter}")
+  end
+  # special case for categories
+  root to: "categories#index", constraints: HomePageConstraint.new("categories")
 
 end
diff --git a/lib/homepage_constraint.rb b/lib/homepage_constraint.rb
new file mode 100644
index 000000000..bbac573ea
--- /dev/null
+++ b/lib/homepage_constraint.rb
@@ -0,0 +1,10 @@
+class HomePageConstraint
+  def initialize(filter)
+    @filter = filter
+  end
+
+  def matches?(request)
+    homepage = request.session[:current_user_id].present? ? SiteSetting.homepage : SiteSetting.anonymous_homepage
+    homepage == @filter
+  end
+end
\ No newline at end of file
diff --git a/spec/controllers/list_controller_spec.rb b/spec/controllers/list_controller_spec.rb
index 0725c0bbf..6a39ec4c7 100644
--- a/spec/controllers/list_controller_spec.rb
+++ b/spec/controllers/list_controller_spec.rb
@@ -8,12 +8,21 @@ describe ListController do
     @post = Fabricate(:post, user: @user)
   end
 
-  context 'index' do
-    before do
-      xhr :get, :index
+  describe 'indexes' do
+
+    [:latest, :hot].each do |filter|
+      context '#{filter}' do
+        before { xhr :get, filter }
+        it { should respond_with(:success) }
+      end
+    end
+
+    [:favorited, :read, :posted, :unread, :new].each do |filter|
+      context '#{filter}' do
+        it { expect { xhr :get, filter }.to raise_error(Discourse::NotLoggedIn) }
+      end
     end
 
-    it { should respond_with(:success) }
   end
 
   context 'category' do