diff --git a/app/assets/javascripts/discourse/controllers/header_controller.js b/app/assets/javascripts/discourse/controllers/header_controller.js index f5a37fe1053..54a845de200 100644 --- a/app/assets/javascripts/discourse/controllers/header_controller.js +++ b/app/assets/javascripts/discourse/controllers/header_controller.js @@ -11,22 +11,10 @@ Discourse.HeaderController = Discourse.Controller.extend({ showExtraInfo: null, notifications: null, - categories: function() { - return Discourse.Category.list(); - }.property(), - showFavoriteButton: function() { return Discourse.User.current() && !this.get('topic.isPrivateMessage'); }.property('topic.isPrivateMessage'), - mobileView: function() { - return Discourse.Mobile.mobileView; - }.property(), - - showMobileToggle: function() { - return Discourse.SiteSettings.enable_mobile_theme; - }.property(), - actions: { toggleStar: function() { var topic = this.get('topic'); @@ -34,10 +22,6 @@ Discourse.HeaderController = Discourse.Controller.extend({ return false; }, - toggleMobileView: function() { - Discourse.Mobile.toggleMobileView(); - }, - showNotifications: function(headerView) { var self = this; diff --git a/app/assets/javascripts/discourse/controllers/site_map_category_controller.js b/app/assets/javascripts/discourse/controllers/site_map_category_controller.js new file mode 100644 index 00000000000..47cb886f43c --- /dev/null +++ b/app/assets/javascripts/discourse/controllers/site_map_category_controller.js @@ -0,0 +1,5 @@ +Discourse.SiteMapCategoryController = Ember.ObjectController.extend({ + showBadges: function() { + return !!Discourse.User.current(); + }.property().volatile() +}); diff --git a/app/assets/javascripts/discourse/controllers/site_map_controller.js b/app/assets/javascripts/discourse/controllers/site_map_controller.js new file mode 100644 index 00000000000..f8ca13fc068 --- /dev/null +++ b/app/assets/javascripts/discourse/controllers/site_map_controller.js @@ -0,0 +1,33 @@ +Discourse.SiteMapController = Ember.ArrayController.extend(Discourse.HasCurrentUser, { + itemController: "siteMapCategory", + + showAdminLinks: function() { + return this.get("currentUser.staff"); + }.property("currentUser.staff"), + + flaggedPostsCount: function() { + return this.get("currentUser.site_flagged_posts_count"); + }.property("currentUser.site_flagged_posts_count"), + + faqUrl: function() { + return Discourse.SiteSettings.faq_url ? Discourse.SiteSettings.faq_url : Discourse.getURL('/faq'); + }.property(), + + showMobileToggle: function() { + return Discourse.SiteSettings.enable_mobile_theme; + }.property(), + + mobileViewLinkTextKey: function() { + return Discourse.Mobile.mobileView ? "desktop_view" : "mobile_view"; + }.property(), + + categories: function() { + return Discourse.Category.list(); + }.property(), + + actions: { + toggleMobileView: function() { + Discourse.Mobile.toggleMobileView(); + } + } +}); diff --git a/app/assets/javascripts/discourse/helpers/application_helpers.js b/app/assets/javascripts/discourse/helpers/application_helpers.js index adb5cc5751d..132fbbff305 100644 --- a/app/assets/javascripts/discourse/helpers/application_helpers.js +++ b/app/assets/javascripts/discourse/helpers/application_helpers.js @@ -317,17 +317,3 @@ Handlebars.registerHelper('date', function(property, options) { } }); - -/** - Produces a link to the FAQ - - @method faqLink - @for Handlebars -**/ -Handlebars.registerHelper('faqLink', function(property, options) { - return new Handlebars.SafeString( - "" + I18n.t('faq') + "" - ); -}); diff --git a/app/assets/javascripts/discourse/templates/components/header-category-info.js.handlebars b/app/assets/javascripts/discourse/templates/components/header-category-info.js.handlebars deleted file mode 100644 index feec6de8f90..00000000000 --- a/app/assets/javascripts/discourse/templates/components/header-category-info.js.handlebars +++ /dev/null @@ -1,13 +0,0 @@ -{{categoryLink category allowUncategorized=true}} -{{#if currentUser}} - {{#with view.category}} - {{#if unreadTopics}} - {{unreadTopics}} - {{/if}} - {{#if newTopics}} - {{newTopics}} - {{/if}} - {{/with}} -{{else}} -{{unbound category.topic_count}} -{{/if}} diff --git a/app/assets/javascripts/discourse/templates/header.js.handlebars b/app/assets/javascripts/discourse/templates/header.js.handlebars index a41630353a6..506d98d710d 100644 --- a/app/assets/javascripts/discourse/templates/header.js.handlebars +++ b/app/assets/javascripts/discourse/templates/header.js.handlebars @@ -99,49 +99,7 @@ {{render notifications notifications}} {{#if view.renderSiteMap}} -
- - - {{#if categories}} - - {{/if}} - -
+ {{render siteMap}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/site_map.js.handlebars b/app/assets/javascripts/discourse/templates/site_map.js.handlebars new file mode 100644 index 00000000000..04cf99eab05 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/site_map.js.handlebars @@ -0,0 +1,25 @@ +
+ + + {{#if categories}} + + {{/if}} +
\ No newline at end of file diff --git a/app/assets/javascripts/discourse/templates/site_map/_admin_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_admin_link.js.handlebars new file mode 100644 index 00000000000..3dd4760d03d --- /dev/null +++ b/app/assets/javascripts/discourse/templates/site_map/_admin_link.js.handlebars @@ -0,0 +1 @@ +{{i18n admin_title}} \ No newline at end of file diff --git a/app/assets/javascripts/discourse/templates/site_map/_all_categories_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_all_categories_link.js.handlebars new file mode 100644 index 00000000000..237f84204df --- /dev/null +++ b/app/assets/javascripts/discourse/templates/site_map/_all_categories_link.js.handlebars @@ -0,0 +1 @@ +{{#link-to "list.categories"}}{{i18n filters.categories.title}}{{/link-to}} \ No newline at end of file diff --git a/app/assets/javascripts/discourse/templates/site_map/_category.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_category.js.handlebars new file mode 100644 index 00000000000..4ce98c6c0d9 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/site_map/_category.js.handlebars @@ -0,0 +1,11 @@ +{{categoryLink this allowUncategorized=true}} +{{#if showBadges}} + {{#if unreadTopics}} + {{unreadTopics}} + {{/if}} + {{#if newTopics}} + {{newTopics}} + {{/if}} +{{else}} + {{unbound topic_count}} +{{/if}} \ No newline at end of file diff --git a/app/assets/javascripts/discourse/templates/site_map/_faq_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_faq_link.js.handlebars new file mode 100644 index 00000000000..be58ebbbb0c --- /dev/null +++ b/app/assets/javascripts/discourse/templates/site_map/_faq_link.js.handlebars @@ -0,0 +1 @@ +{{i18n faq}} \ No newline at end of file diff --git a/app/assets/javascripts/discourse/templates/site_map/_flagged_posts_links.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_flagged_posts_links.js.handlebars new file mode 100644 index 00000000000..6222ac2e7c1 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/site_map/_flagged_posts_links.js.handlebars @@ -0,0 +1,4 @@ +{{i18n flags_title}} +{{#if flaggedPostsCount}} + {{flaggedPostsCount}} +{{/if}} \ No newline at end of file diff --git a/app/assets/javascripts/discourse/templates/site_map/_latest_topics_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_latest_topics_link.js.handlebars new file mode 100644 index 00000000000..b99291c3211 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/site_map/_latest_topics_link.js.handlebars @@ -0,0 +1 @@ +{{#titledLinkTo "list.latest" titleKey="filters.latest.help" class="latest-topics-link"}}{{i18n filters.latest.title}}{{/titledLinkTo}} \ No newline at end of file diff --git a/app/assets/javascripts/discourse/templates/site_map/_mobile_toggle_link.js.handlebars b/app/assets/javascripts/discourse/templates/site_map/_mobile_toggle_link.js.handlebars new file mode 100644 index 00000000000..f230716bb68 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/site_map/_mobile_toggle_link.js.handlebars @@ -0,0 +1 @@ +{{boundI18n mobileViewLinkTextKey}} \ No newline at end of file diff --git a/test/javascripts/controllers/site_map_category_controller_test.js b/test/javascripts/controllers/site_map_category_controller_test.js new file mode 100644 index 00000000000..63e5e92dcdb --- /dev/null +++ b/test/javascripts/controllers/site_map_category_controller_test.js @@ -0,0 +1,17 @@ +var controller; + +module("Discourse.SiteMapCategoryController", { + setup: function() { + controller = Discourse.SiteMapCategoryController.create(); + } +}); + +test("showBadges", function() { + this.stub(Discourse.User, "current"); + + Discourse.User.current.returns(null); + ok(!controller.get("showBadges"), "returns false when no user is logged in"); + + Discourse.User.current.returns({}); + ok(controller.get("showBadges"), "returns true when an user is logged in"); +}); diff --git a/test/javascripts/controllers/site_map_controller_test.js b/test/javascripts/controllers/site_map_controller_test.js new file mode 100644 index 00000000000..b8cce189fd1 --- /dev/null +++ b/test/javascripts/controllers/site_map_controller_test.js @@ -0,0 +1,83 @@ +var controller, oldMobileView; + +module("Discourse.SiteMapController", { + setup: function() { + oldMobileView = Discourse.Mobile.mobileView; + + controller = Discourse.SiteMapController.create(); + }, + + teardown: function() { + Discourse.Mobile.mobileView = oldMobileView; + } +}); + +test("itemController", function() { + equal(controller.get("itemController"), "siteMapCategory", "defaults to SiteMapCategoryController"); +}); + +test("showAdminLinks", function() { + var currentUserStub = Ember.Object.create(); + this.stub(Discourse.User, "current").returns(currentUserStub); + + currentUserStub.set("staff", true); + equal(controller.get("showAdminLinks"), true, "is true when current user is a staff member"); + + currentUserStub.set("staff", false); + equal(controller.get("showAdminLinks"), false, "is false when current user is not a staff member"); +}); + +test("flaggedPostsCount", function() { + var currentUserStub = Ember.Object.create(); + this.stub(Discourse.User, "current").returns(currentUserStub); + + currentUserStub.set("site_flagged_posts_count", 5); + equal(controller.get("flaggedPostsCount"), 5, "returns current user's flagged posts count"); + + currentUserStub.set("site_flagged_posts_count", 0); + equal(controller.get("flaggedPostsCount"), 0, "is bound (reacts to change of current user's flagged posts count)"); +}); + +test("faqUrl returns faq url configured in site settings if it is set", function() { + Discourse.SiteSettings.faq_url = "faq-url"; + equal(controller.get("faqUrl"), "faq-url"); +}); + +test("faqUrl returns default '/faq' url when there is no corresponding site setting set", function() { + Discourse.SiteSettings.faq_url = null; + equal(controller.get("faqUrl"), "/faq"); +}); + +test("showMoblieToggle returns true when mobile theme is enabled in site settings", function() { + Discourse.SiteSettings.enable_mobile_theme = true; + equal(controller.get("showMobileToggle"), true); +}); + +test("showMoblieToggle returns false when mobile theme is disabled in site settings", function() { + Discourse.SiteSettings.enable_mobile_theme = false; + equal(controller.get("showMobileToggle"), false); +}); + +test("mobileViewLinkTextKey returns translation key for a desktop view if the current view is mobile view", function() { + Discourse.Mobile.mobileView = true; + equal(controller.get("mobileViewLinkTextKey"), "desktop_view"); +}); + +test("mobileViewLinkTextKey returns translation key for a mobile view if the current view is desktop view", function() { + Discourse.Mobile.mobileView = false; + equal(controller.get("mobileViewLinkTextKey"), "mobile_view"); +}); + +test("categories", function() { + var categoryListStub = ["category1", "category2"]; + this.stub(Discourse.Category, "list").returns(categoryListStub); + + equal(controller.get("categories"), categoryListStub, "returns the list of categories"); +}); + +test("toggleMobleView", function() { + this.stub(Discourse.Mobile, "toggleMobileView"); + + controller.send("toggleMobileView"); + ok(Discourse.Mobile.toggleMobileView.calledOnce, "switches between desktop and mobile views"); +}); diff --git a/test/javascripts/templates/site_map_test.js b/test/javascripts/templates/site_map_test.js new file mode 100644 index 00000000000..8c0f715a0d0 --- /dev/null +++ b/test/javascripts/templates/site_map_test.js @@ -0,0 +1,273 @@ +var controller; + +var setUpController = function(properties) { + Ember.run(function() { + controller.setProperties(properties); + }); +}; + +var appendView = function() { + Ember.run(function() { + Discourse.advanceReadiness(); + Ember.View.create({ + container: Discourse.__container__, + controller: controller, + templateName: "siteMap" + }).appendTo(fixture()); + }); +}; + +var locationLinksSelector = ".location-links"; +var categoryLinksSelector = ".category-links"; + +module("Template: site_map", { + setup: function() { + sinon.stub(I18n, "t", function(scope, options) { + if (options) { + if (options.count) { + return [scope, options.count].join(" "); + } else { + return [scope, options.username, options.link].join(" ").trim(); + } + } + return scope; + }); + + controller = Ember.ArrayController.create({ + container: Discourse.__container__ + }); + }, + + teardown: function() { + I18n.t.restore(); + } +}); + +test("location links part is rendered correctly", function() { + setUpController({ + showAdminLinks: true, + flaggedPostsCount: 2, + faqUrl: "faq-url", + showMobileToggle: true, + mobileViewLinkTextKey: "mobile_view_link_text_key" + }); + + appendView(); + + var $locationLinks = fixture(locationLinksSelector); + + var $adminLink = $locationLinks.find(".admin-link"); + ok(exists($adminLink), "a link to the admin section is present"); + equal($adminLink.attr("href"), "/admin", "the link to the admin section points to a correct URL"); + notEqual($adminLink.text().indexOf("admin_title"), -1, "the link to the admin section contains correct text"); + ok(exists($adminLink.find(".icon-wrench")), "the link to the admin section contains correct icon"); + + var $flaggedPostsLink = $locationLinks.find(".flagged-posts-link"); + ok(exists($flaggedPostsLink), "link to the flagged posts list is present"); + equal($flaggedPostsLink.attr("href"), "/admin/flags/active", "the link to the flagged posts list points to a correct URL"); + notEqual($flaggedPostsLink.text().indexOf("flags_title"), -1, "the link to the flagged posts list contains correct text"); + ok(exists($flaggedPostsLink.find(".icon-flag")), "the link to the flagged posts list contains correct icon"); + + var $flaggedPostsBadge = $locationLinks.find(".flagged-posts.badge-notification"); + ok(exists($flaggedPostsBadge), "a flagged posts badge is present"); + equal($flaggedPostsBadge.attr("href"), "/admin/flags/active", "the flagged posts badge points to a correct URL"); + equal($flaggedPostsBadge.attr("title"), "notifications.total_flagged", "the flagged posts badge has correct title attr"); + equal($flaggedPostsBadge.text(), "2", "the flagged posts badge has correct text"); + + var $latestTopicsLink = $locationLinks.find(".latest-topics-link"); + ok(exists($latestTopicsLink), "the latest topics link is present"); + equal($latestTopicsLink.attr("href"), "/", "the latest topics link points to a correct URL"); + equal($latestTopicsLink.attr("title"), "filters.latest.help", "the latest topics link has correct title attr"); + equal($latestTopicsLink.text(), "filters.latest.title", "the latest topics link has correct text"); + + var $faqLink = $locationLinks.find(".faq-link"); + ok(exists($faqLink), "the FAQ link is present"); + equal($faqLink.attr("href"), "faq-url", "the FAQ link points to a correct URL"); + equal($faqLink.text(), "faq", "the FAQ link has correct text"); + + var $mobileToggleLink = $locationLinks.find(".mobile-toggle-link"); + ok(exists($mobileToggleLink), "the mobile theme toggle link is present"); + equal($mobileToggleLink.text().trim(), "mobile_view_link_text_key", "the mobile theme toggle link has correct text"); +}); + +test("binds mobile theme toggle link to the correct controller action", function() { + this.stub(Ember.Handlebars.helpers, "action", function(actionName) { + return new Handlebars.SafeString('data-test-stub-action-name="' + actionName + '"'); + }); + + setUpController({ + showMobileToggle: true + }); + + appendView(); + + equal(fixture(locationLinksSelector).find(".mobile-toggle-link").data("test-stub-action-name"), "toggleMobileView"); +}); + +test("does not show flagged posts badge when there are no flagged posts", function() { + setUpController({ + showAdminLinks: true, + flaggedPostsCount: 0 + }); + + appendView(); + + var $locationLinks = fixture(locationLinksSelector); + ok(exists($locationLinks.find(".flagged-posts-link")), "primary link to flagged posts list is still shown"); + ok(!exists($locationLinks.find(".flagged-posts.badge-notification")), "badge with the number of flagged posts is not shown"); +}); + +test("does not show any admin links when current user is not a staff member", function() { + setUpController({ + showAdminLinks: false, + flaggedPostsCount: 2 + }); + + appendView(); + + var $locationLinks = fixture(locationLinksSelector); + ok(!exists($locationLinks.find(".admin-link")), "the link to the admin section is not shown"); + ok(!exists($locationLinks.find(".flagged-posts-link")), "the link to flagged posts list is not shown"); + ok(!exists($locationLinks.find(".flagged-posts.badge-notification")), "the badge with the number of flagged posts is not shown"); +}); + +test("does not show mobile theme toggle link if mobile theme is disabled in configuration", function() { + setUpController({ + showMobileToggle: false, + mobileViewLinkTextKey: "mobile_view_link_text_key" + }); + + appendView(); + + ok(!exists(fixture(locationLinksSelector).find(".mobile-toggle-link"))); +}); + +var categoryFixture = { + showBadges: true, + name: "category name", + color: "ffffff", + text_color: "000000", + slug: "category-slug", + topic_count: 123, + description: "category description", + unreadTopics: 10, + newTopics: 20 +}; + +test("category links part is rendered correctly", function() { + setUpController({ + categories: [ + Discourse.Category.create(categoryFixture), + Discourse.Category.create(categoryFixture) + ] + }); + + appendView(); + + var $categoryLinks = fixture(categoryLinksSelector); + + var $heading = $categoryLinks.find(".heading"); + ok(exists($heading), "a categories list heading exists"); + equal($heading.attr("title"), "filters.categories.help", "categories list heading has correct title attr"); + + var $allCategoriesLink = $heading.find("a"); + ok(exists($allCategoriesLink), "an 'all categories' link exists"); + equal($allCategoriesLink.attr("href"), "/categories", "the 'all categories' link points to a correct URL"); + equal($allCategoriesLink.text(), "filters.categories.title", "the 'all categories' link has correct text"); + + var $categories = $categoryLinks.find(".category"); + equal(count($categories), 2, "the number of categories is correct"); + + var $firstCategoryLink = $categories.first().find(".badge-category"); + ok(exists($firstCategoryLink), "a category item contains a category link"); + equal($firstCategoryLink.attr("href"), "/category/category-slug", "the category link points to a correct URL"); + equal($firstCategoryLink.attr("title"), "category description", "the category link has correct title attr"); + equal($firstCategoryLink.css("color"), "rgb(0, 0, 0)", "the category link has correct color css rule set"); + equal($firstCategoryLink.css("background-color"), "rgb(255, 255, 255)", "the category link has correct background-color css rule set"); + equal($firstCategoryLink.text(), "category name", "the category link has correct text"); + + var $firstCategoryUnreadTopicsLink = $categories.first().find(".unread-posts"); + ok(exists($firstCategoryUnreadTopicsLink), "a category item contains current user unread topics link"); + equal($firstCategoryUnreadTopicsLink.attr("href"), "/category/category-slug/l/unread", "the unread topics link points to a correct URL"); + ok($firstCategoryUnreadTopicsLink.hasClass("badge") && $firstCategoryUnreadTopicsLink.hasClass("badge-notification"), "the unread topics link has correct classes"); + equal($firstCategoryUnreadTopicsLink.attr("title"), "topic.unread_topics 10", "the unread topics link has correct title"); + equal($firstCategoryUnreadTopicsLink.text(), "10", "the unread topics link has correct text"); + + var $firstCategoryNewTopicsLink = $categories.first().find(".new-posts"); + ok(exists($firstCategoryNewTopicsLink), "a category item contains current user new topics link"); + equal($firstCategoryNewTopicsLink.attr("href"), "/category/category-slug/l/new", "the new topics link points to a correct URL"); + ok($firstCategoryNewTopicsLink.hasClass("badge") && $firstCategoryNewTopicsLink.hasClass("badge-notification"), "the new topics link has correct classes"); + equal($firstCategoryNewTopicsLink.attr("title"), "topic.new_topics 20", "the new topics link has correct title"); + notEqual($firstCategoryNewTopicsLink.text().indexOf("20"), -1, "the new topics link contains correct text"); + ok(exists($firstCategoryNewTopicsLink.find(".icon-asterisk")), "the new topics link contains correct icon"); + + var $firstCategoryAllTopicsCount = $categories.first().find(".topics-count"); + ok(!exists($firstCategoryAllTopicsCount), "the count of all topics is not shown"); +}); + +test("categories show the count of all topics instead of new and unread ones when user is not logged in", function() { + var categoryWithoutBadgesFixture = _.extend({}, categoryFixture, { + showBadges: false + }); + + setUpController({ + categories: [ + Discourse.Category.create(categoryWithoutBadgesFixture) + ] + }); + + appendView(); + + var $firstCategory = fixture(categoryLinksSelector).find(".category").first(); + + var $allTopicsCountTag = $firstCategory.find(".topics-count"); + ok(exists($allTopicsCountTag), "the tag with all topics count is shown"); + equal($allTopicsCountTag.text(), "123", "the tag with all topics count has correct text"); + + ok(!exists($firstCategory.find(".unread-posts")), "the unread posts link is not shown"); + ok(!exists($firstCategory.find(".new-posts")), "the new posts link is not shown"); +}); + +test("unread topics link is not shown when there are no unread topics", function() { + var categoryWithNoUnreadTopicsFixture = _.extend({}, categoryFixture, { + unreadTopics: 0 + }); + + setUpController({ + categories: [ + Discourse.Category.create(categoryWithNoUnreadTopicsFixture) + ] + }); + + appendView(); + + var $firstCategory = fixture(categoryLinksSelector).find(".category").first(); + ok(!exists($firstCategory.find(".unread-posts"))); +}); + +test("new topics link are not shown when there are no new topics", function() { + var categoryWithNoNewTopicsFixture = _.extend({}, categoryFixture, { + newTopics: 0 + }); + + setUpController({ + categories: [ + Discourse.Category.create(categoryWithNoNewTopicsFixture) + ] + }); + + appendView(); + + var $firstCategory = fixture(categoryLinksSelector).find(".category").first(); + ok(!exists($firstCategory.find(".new-posts"))); +}); + +test("the whole categories section is hidden if there are no categories", function() { + setUpController({ + categories: [] + }); + + appendView(); + + ok(!exists(fixture(categoryLinksSelector))); +});