A mucher saner API for updating the title of routes, even when nested.

Properly sends the title of the page to google analytics
This commit is contained in:
Robin Ward 2014-10-09 14:37:23 -04:00
parent e8637344c3
commit 5fc150e057
17 changed files with 106 additions and 72 deletions

View File

@ -1,17 +1,9 @@
/**
The base admin route
@class AdminRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminRoute = Discourse.Route.extend({ Discourse.AdminRoute = Discourse.Route.extend({
renderTemplate: function() { renderTemplate: function() {
this.render('admin/templates/admin'); this.render('admin/templates/admin');
}, },
activate: function() { titleToken: function() {
Discourse.set('title', I18n.t('admin_title')); return I18n.t('admin_title');
} }
}); });

View File

@ -10,6 +10,7 @@ var DiscourseResolver = require('discourse/ember/resolver').default;
window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
rootElement: '#main', rootElement: '#main',
_docTitle: null,
getURL: function(url) { getURL: function(url) {
// If it's a non relative URL, return it. // If it's a non relative URL, return it.
@ -25,13 +26,8 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
Resolver: DiscourseResolver, Resolver: DiscourseResolver,
titleChanged: function() { _titleChanged: function() {
var title = ""; var title = this.get('_docTitle') || Discourse.SiteSettings.title;
if (this.get('title')) {
title += "" + (this.get('title')) + " - ";
}
title += Discourse.SiteSettings.title;
// if we change this we can trigger changes on document.title // if we change this we can trigger changes on document.title
// only set if changed. // only set if changed.
@ -51,7 +47,7 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
document.title = title; document.title = title;
}, 200); }, 200);
} }
}.observes('title', 'hasFocus', 'notifyCount'), }.observes('_docTitle', 'hasFocus', 'notifyCount'),
faviconChanged: function() { faviconChanged: function() {
if(Discourse.User.currentProp('dynamic_favicon')) { if(Discourse.User.currentProp('dynamic_favicon')) {

View File

@ -15,6 +15,16 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, {
this.set('controllers.search.searchContext', this.get('model.searchContext')); this.set('controllers.search.searchContext', this.get('model.searchContext'));
}.observes('topic'), }.observes('topic'),
_titleChanged: function() {
var title = this.get('title');
if (!Em.empty(title)) {
// Note normally you don't have to trigger this, but topic titles can be updated
// and are sometimes lazily loaded.
this.send('refreshTitle');
}
}.observes('title'),
termChanged: function() { termChanged: function() {
var dropdown = this.get('controllers.header.visibleDropdown'); var dropdown = this.get('controllers.header.visibleDropdown');
var term = this.get('controllers.search.term'); var term = this.get('controllers.search.term');

View File

@ -12,16 +12,18 @@ export default {
// Out of the box, Discourse tries to track google analytics // Out of the box, Discourse tries to track google analytics
// if it is present // if it is present
if (typeof window._gaq !== 'undefined') { if (typeof window._gaq !== 'undefined') {
pageTracker.on('change', function() { pageTracker.on('change', function(url, title) {
window._gaq.push(['_trackPageview', window.location.pathname+window.location.search]); window._gaq.push(["_set", "title", title]);
window._gaq.push(['_trackPageview', url]);
}); });
return; return;
} }
// Also use Universal Analytics if it is present // Also use Universal Analytics if it is present
if (typeof window.ga !== 'undefined') { if (typeof window.ga !== 'undefined') {
pageTracker.on('change', function() { pageTracker.on('change', function(url, title) {
window.ga('send', 'pageview', window.location.pathname+window.location.search); window.ga('send', 'pageview', {page: url, title: title});
}); });
} }
} }

View File

@ -22,8 +22,8 @@ Discourse.PageTracker = Ember.Object.extend(Ember.Evented, {
self = this; self = this;
router.on('didTransition', function() { router.on('didTransition', function() {
var router = this; this.send('refreshTitle');
self.trigger('change', router.get('url')); self.trigger('change', this.get('url'), Discourse.get('_docTitle'));
}); });
this.set('started', true); this.set('started', true);
} }

View File

@ -5,9 +5,7 @@ export default Discourse.Route.extend({
}); });
}, },
setupController: function(controller, model) { titleToken: function() {
controller.set('model', model); return I18n.t('about.simple_title');
Discourse.set('title', I18n.t('about.simple_title'));
} }
}); });

View File

@ -1,6 +1,13 @@
var ApplicationRoute = Em.Route.extend({ var ApplicationRoute = Em.Route.extend({
siteTitle: Discourse.computed.setting('title'),
actions: { actions: {
_collectTitleTokens: function(tokens) {
tokens.push(this.get('siteTitle'));
Discourse.set('_docTitle', tokens.join(' - '));
},
showTopicEntrance: function(data) { showTopicEntrance: function(data) {
this.controllerFor('topic-entrance').send('show', data); this.controllerFor('topic-entrance').send('show', data);
}, },

View File

@ -1,5 +1,4 @@
export default Discourse.Route.extend({ export default Discourse.Route.extend({
model: function() { model: function() {
if (PreloadStore.get('badges')) { if (PreloadStore.get('badges')) {
return PreloadStore.getAndRemove('badges').then(function(json) { return PreloadStore.getAndRemove('badges').then(function(json) {
@ -10,8 +9,7 @@ export default Discourse.Route.extend({
} }
}, },
setupController: function(controller, model) { titleToken: function() {
controller.set('model', model); return I18n.t('badges.title');
Discourse.set('title', I18n.t('badges.title'));
} }
}); });

View File

@ -1,4 +1,4 @@
export default Ember.Route.extend({ export default Discourse.Route.extend({
serialize: function(model) { serialize: function(model) {
return {id: model.get('id'), slug: model.get('name').replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase()}; return {id: model.get('id'), slug: model.get('name').replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase()};
}, },
@ -13,12 +13,18 @@ export default Ember.Route.extend({
} }
}, },
titleToken: function() {
var model = this.modelFor('badges.show');
if (model) {
return model.get('displayName');
}
},
setupController: function(controller, model) { setupController: function(controller, model) {
Discourse.UserBadge.findByBadgeId(model.get('id')).then(function(userBadges) { Discourse.UserBadge.findByBadgeId(model.get('id')).then(function(userBadges) {
controller.set('userBadges', userBadges); controller.set('userBadges', userBadges);
controller.set('userBadgesLoaded', true); controller.set('userBadgesLoaded', true);
}); });
controller.set('model', model); controller.set('model', model);
Discourse.set('title', model.get('displayName'));
} }
}); });

View File

@ -61,13 +61,17 @@ export default function(filter, params) {
}); });
}, },
titleToken: function() {
var filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title', {count: 0}),
model = this.currentModel;
return I18n.t('filters.with_category', { filter: filterText, category: model.get('name') });
},
setupController: function(controller, model) { setupController: function(controller, model) {
var topics = this.get('topics'), var topics = this.get('topics'),
periods = this.controllerFor('discovery').get('periods'), periods = this.controllerFor('discovery').get('periods'),
periodId = topics.get('for_period') || (filter.indexOf('/') > 0 ? filter.split('/')[1] : ''), periodId = topics.get('for_period') || (filter.indexOf('/') > 0 ? filter.split('/')[1] : '');
filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title', {count: 0});
Discourse.set('title', I18n.t('filters.with_category', { filter: filterText, category: model.get('name') }));
this.controllerFor('navigation/category').set('canCreateTopic', topics.get('can_create_topic')); this.controllerFor('navigation/category').set('canCreateTopic', topics.get('can_create_topic'));
this.controllerFor('discovery/topics').setProperties({ this.controllerFor('discovery/topics').setProperties({

View File

@ -32,6 +32,13 @@ export default function(filter, extras) {
return Discourse.TopicList.list(filter, findOpts, extras); return Discourse.TopicList.list(filter, findOpts, extras);
}, },
titleToken: function() {
if (filter === Discourse.Utilities.defaultHomepage()) { return; }
var filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title', {count: 0});
return I18n.t('filters.with_topics', {filter: filterText});
},
setupController: function(controller, model, trans) { setupController: function(controller, model, trans) {
controller.setProperties(Em.getProperties(trans, _.keys(queryParams).map(function(v){ controller.setProperties(Em.getProperties(trans, _.keys(queryParams).map(function(v){
@ -39,14 +46,7 @@ export default function(filter, extras) {
}))); })));
var periods = this.controllerFor('discovery').get('periods'), var periods = this.controllerFor('discovery').get('periods'),
periodId = model.get('for_period') || (filter.indexOf('/') > 0 ? filter.split('/')[1] : ''), periodId = model.get('for_period') || (filter.indexOf('/') > 0 ? filter.split('/')[1] : '');
filterText = I18n.t('filters.' + filter.replace('/', '.') + '.title', {count: 0});
if (filter === Discourse.Utilities.defaultHomepage()) {
Discourse.set('title', '');
} else {
Discourse.set('title', I18n.t('filters.with_topics', {filter: filterText}));
}
this.controllerFor('discovery/topics').setProperties({ this.controllerFor('discovery/topics').setProperties({
model: model, model: model,

View File

@ -21,6 +21,29 @@ Discourse.Route = Em.Route.extend({
Em.run.scheduleOnce('afterRender', Discourse.Route, 'cleanDOM'); Em.run.scheduleOnce('afterRender', Discourse.Route, 'cleanDOM');
}, },
actions: {
_collectTitleTokens: function(tokens) {
// If there's a title token method, call it and get the token
if (this.titleToken) {
var t = this.titleToken();
if (t && t.length) {
if (t instanceof Array) {
t.forEach(function(ti) {
tokens.push(ti);
});
} else {
tokens.push(t);
}
}
}
return true;
},
refreshTitle: function() {
this.send('_collectTitleTokens', []);
}
},
redirectIfLoginRequired: function() { redirectIfLoginRequired: function() {
var app = this.controllerFor('application'); var app = this.controllerFor('application');
if (app.get('loginRequired')) { if (app.get('loginRequired')) {

View File

@ -1,11 +1,3 @@
/**
The route for handling the "Categories" view
@class DiscoveryCategoriesRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(Discourse.OpenComposer, { Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(Discourse.OpenComposer, {
renderTemplate: function() { renderTemplate: function() {
this.render('navigation/categories', { outlet: 'navigation-bar' }); this.render('navigation/categories', { outlet: 'navigation-bar' });
@ -31,16 +23,18 @@ Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(Discourse.OpenCompos
}); });
}, },
titleToken: function() {
return I18n.t('filters.categories.title');
},
setupController: function(controller, model) { setupController: function(controller, model) {
controller.set('model', model); controller.set('model', model);
Discourse.set('title', I18n.t('filters.categories.title'));
// Only show either the Create Category or Create Topic button // Only show either the Create Category or Create Topic button
this.controllerFor('navigation/categories').set('canCreateCategory', model.get('can_create_category')); this.controllerFor('navigation/categories').set('canCreateCategory', model.get('can_create_category'));
this.controllerFor('navigation/categories').set('canCreateTopic', model.get('can_create_topic') && !model.get('can_create_category')); this.controllerFor('navigation/categories').set('canCreateTopic', model.get('can_create_topic') && !model.get('can_create_category'));
this.openTopicDraft(model); this.openTopicDraft(model);
}, },
actions: { actions: {

View File

@ -12,7 +12,15 @@ Discourse.TopicRoute = Discourse.Route.extend({
show_deleted: { replace: true } show_deleted: { replace: true }
}, },
titleToken: function() {
var model = this.modelFor('topic');
if (model) {
return model.get('title');
}
},
actions: { actions: {
showTopicAdminMenu: function() { showTopicAdminMenu: function() {
this.controllerFor("topic-admin-menu").send("show"); this.controllerFor("topic-admin-menu").send("show");
}, },

View File

@ -1,5 +1,13 @@
export default Discourse.Route.extend({ export default Discourse.Route.extend({
titleToken: function() {
var model = this.modelFor('user');
var username = model.get('username');
if (username) {
return [I18n.t("user.profile"), username];
}
},
actions: { actions: {
logout: function() { logout: function() {
Discourse.logout(); Discourse.logout();

View File

@ -17,11 +17,6 @@ export default Discourse.View.extend(AddCategoryClass, Discourse.Scrolling, {
postStream: Em.computed.alias('controller.postStream'), postStream: Em.computed.alias('controller.postStream'),
_updateTitle: function() {
var title = this.get('topic.title');
if (title) return Discourse.set('title', _.unescape(title));
}.observes('topic.loaded', 'topic.title'),
_composeChanged: function() { _composeChanged: function() {
var composerController = Discourse.get('router.composerController'); var composerController = Discourse.get('router.composerController');
composerController.clearState(); composerController.clearState();

View File

@ -1,11 +1,4 @@
export default Ember.View.extend(Discourse.ScrollTop, { export default Ember.View.extend(Discourse.ScrollTop, {
templateName: 'user/user', templateName: 'user/user',
userBinding: 'controller.content', userBinding: 'controller.content'
updateTitle: function() {
var username = this.get('user.username');
if (username) {
Discourse.set('title', "" + (I18n.t("user.profile")) + " - " + username);
}
}.observes('user.loaded', 'user.username')
}); });