diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000000..d268476fef --- /dev/null +++ b/NOTES.md @@ -0,0 +1,15 @@ +## 0.4x Refactor Notes + +Please remove this file after 0.4x (or perhaps organize it so that we can see the history of breaking changes) + +### Immediate Deprecation Notices + +* `action:ajaxifying` is no longer triggered on body but on window instead, in line with other similar hooks. +* `filter:server.create_routes` and `filter:admin.create_routes` will have limited support (ajaxify works, but first-load will not). Please have a look at [this plugin](https://github.com/psychobunny/nodebb-plugin-kitchen-sink/blob/master/library.js#L16-L22) for an example on how to create routes in plugins from now on. + +### Upcoming Deprecation Warnings + +* `filter:footer.build` will be deprecated for 0.4x in favour of the widget system (WIP) +* templates.setGlobal (server-side only) deprecated in favour of using res.locals +* `plugins/fireHook` route will be deprecated for 0.4x +* synchronous hooks will be deprecated for 0.4x - we're reducing complexity by removing the `callbacked: true` property in `plugin.json` - just use callbacks. \ No newline at end of file diff --git a/app.js b/app.js index c38b06e262..097734cd25 100644 --- a/app.js +++ b/app.js @@ -95,13 +95,13 @@ function loadConfig() { nconf.set('themes_path', path.resolve(__dirname, nconf.get('themes_path'))); } - function start() { loadConfig(); nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path')); nconf.set('upload_url', path.join(path.sep, nconf.get('relative_path'), 'uploads', path.sep)); nconf.set('base_dir', __dirname); + nconf.set('views_dir', path.join(__dirname, 'public/templates')); winston.info('Time: ' + new Date()); winston.info('Initializing NodeBB v' + pkg.version); @@ -118,7 +118,6 @@ function start() { require('./src/database').init(function(err) { meta.configs.init(function () { - var templates = require('./public/src/templates'), translator = require('./public/src/translator'), webserver = require('./src/webserver'), @@ -131,25 +130,28 @@ function start() { upgrade.check(function(schema_ok) { if (schema_ok || nconf.get('check-schema') === false) { - sockets.init(webserver.server); - plugins.init(); - translator.loadServer(); - var customTemplates = meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : false; - - utils.walk(path.join(__dirname, 'public/templates'), function (err, tplsToLoad) { - templates.init(tplsToLoad, customTemplates); - }); + nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-vanilla/templates')); + nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path')); plugins.ready(function() { - templates.ready(webserver.init); + webserver.init(); }); // Temporarily removed until ncb000gt/node-cron/issues/81 and ncb000gt/node-cron/issues/83 are fixed // notifications.init(); + + process.on('SIGTERM', shutdown); + process.on('SIGINT', shutdown); + process.on('SIGHUP', restart); + process.on('uncaughtException', function(err) { + winston.error('[app] Encountered Uncaught Exception: ' + err.message); + console.log(err.stack); + restart(); + }); } else { winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); winston.warn(' node app --upgrade'); @@ -238,6 +240,25 @@ function reset() { }); } +function shutdown(code) { + winston.info('[app] Shutdown (SIGTERM/SIGINT) Initialised.'); + require('./src/database').close(); + winston.info('[app] Database connection closed.'); + + winston.info('[app] Shutdown complete.'); + process.exit(); +} + +function restart() { + if (process.send) { + winston.info('[app] Restarting...'); + process.send('nodebb:restart'); + } else { + winston.error('[app] Could not restart server. Shutting down.'); + shutdown(); + } +} + function displayHelp() { winston.info('Usage: node app [options] [arguments]'); winston.info(' [NODE_ENV=development | NODE_ENV=production] node app [--start] [arguments]'); diff --git a/nodebb b/nodebb index 26ec782725..6f69fc6dba 100755 --- a/nodebb +++ b/nodebb @@ -56,7 +56,7 @@ case "$1" in echo "Launching NodeBB in \"development\" mode." echo "To run the production build of NodeBB, please use \"forever\"." echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB" - NODE_ENV=development supervisor -q --extensions 'node|js|tpl' -- app "$@" + NODE_ENV=development supervisor -q --ignore public/templates --extensions 'node|js|tpl' -- app "$@" ;; *) diff --git a/package.json b/package.json index 79d2d26c33..d48f6e5df0 100644 --- a/package.json +++ b/package.json @@ -42,12 +42,14 @@ "nodebb-plugin-mentions": "~0.4", "nodebb-plugin-markdown": "~0.4", "nodebb-widget-essentials": "~0.0", - "nodebb-theme-vanilla": "~0.0.14", + "nodebb-theme-vanilla": "~0.0.15", "nodebb-theme-cerulean": "~0.0.13", - "nodebb-theme-lavender": "~0.0.22", + "nodebb-theme-lavender": "~0.0.23", "less": "^1.6.3", "daemon": "~1.1.0", - "underscore": "^1.6.0" + "underscore": "^1.6.0", + "mkdirp": "~0.3.5", + "rimraf": "~2.2.6" }, "optionalDependencies": { "redis": "0.8.3", diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 47a6a0b2a0..ef9fc7ce7d 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -31,14 +31,12 @@ var ajaxify = {}; ajaxify.initialLoad = false; ajaxify.go = function (url, callback, quiet) { - // "quiet": If set to true, will not call pushState app.enterRoom('global'); $(window).off('scroll'); - $(window).trigger('action:ajaxify.start', { url: url }); - $('body').trigger('action:ajaxifying', {url: url}); // Deprecated as of v0.4.0 + $(window).trigger('action:ajaxify.start', {url: url}); if ($('#content').hasClass('ajaxifying')) { templates.cancelRequest(); @@ -50,19 +48,7 @@ var ajaxify = {}; if (url.indexOf(RELATIVE_PATH.slice(1)) !== -1) { url = url.slice(RELATIVE_PATH.length); } - - var tpl_url = templates.get_custom_map(url.split('?')[0]); - - if (tpl_url == false && !templates[url]) { - if (url === '' || url === '/') { - tpl_url = 'home'; - } else { - tpl_url = url.split('/')[0].split('?')[0]; - } - - } else if (templates[url]) { - tpl_url = url; - } + var tpl_url = ajaxify.getTemplateMapping(url); var hash = ''; if(ajaxify.initialLoad) { @@ -93,16 +79,11 @@ var ajaxify = {}; translator.load(tpl_url); - $('#footer, #content').removeClass('hide').addClass('ajaxifying'); + ajaxify.fadeOut(); templates.flush(); templates.load_template(function () { - - require(['forum/' + tpl_url], function(script) { - if (script && script.init) { - script.init(); - } - }); + ajaxify.loadScript(tpl_url); if (typeof callback === 'function') { callback(); @@ -110,38 +91,12 @@ var ajaxify = {}; app.processPage(); - var widgetLocations = []; + ajaxify.renderWidgets(tpl_url, url, function(err) { + ajaxify.fadeIn(); + ajaxify.initialLoad = false; - require(['vendor/async'], function(async) { - $('#content [widget-area]').each(function() { - widgetLocations.push($(this).attr('widget-area')); - }); - - async.each(widgetLocations, function(location, next) { - var area = $('#content [widget-area="' + location + '"]'); - - socket.emit('widgets.render', {template: tpl_url + '.tpl', url: url, location: location}, function(err, renderedWidgets) { - area.html(templates.prepare(area.html()).parse({ - widgets: renderedWidgets - })).removeClass('hidden'); - - if (!renderedWidgets.length) { - $('body [no-widget-class]').each(function() { - var $this = $(this); - $this.removeClass(); - $this.addClass($this.attr('no-widget-class')); - }); - } - - next(err); - }); - }, function(err) { - $('#content, #footer').stop(true, true).removeClass('ajaxifying'); - ajaxify.initialLoad = false; - - app.refreshTitle(url); - $(window).trigger('action:ajaxify.end', { url: url }); - }); + app.refreshTitle(url); + $(window).trigger('action:ajaxify.end', {url: url}); }); }, url); @@ -151,6 +106,91 @@ var ajaxify = {}; return false; }; + ajaxify.loadScript = function(tpl_url, callback) { + require(['forum/' + tpl_url], function(script) { + if (script && script.init) { + script.init(); + } + + if (callback) { + callback(); + } + }); + }; + + ajaxify.fadeIn = function() { + $('#content, #footer').stop(true, true).removeClass('ajaxifying'); + }; + + ajaxify.fadeOut = function() { + $('#footer, #content').removeClass('hide').addClass('ajaxifying'); + }; + + ajaxify.getTemplateMapping = function(url) { + var tpl_url = templates.get_custom_map(url.split('?')[0]); + + if (tpl_url == false && !templates[url]) { + if (url === '' || url === '/') { + tpl_url = 'home'; + } else if (url === 'admin' || url === 'admin/') { + tpl_url = 'admin/index'; + } else { + tpl_url = url.split('/'); + + while(tpl_url.length) { + if (templates.is_available(tpl_url.join('/'))) { + tpl_url = tpl_url.join('/'); + break; + } + tpl_url.pop(); + } + + if (!tpl_url.length) { + tpl_url = url.split('/')[0].split('?')[0]; + } + } + } else if (templates[url]) { + tpl_url = url; + } + + return tpl_url; + } + + ajaxify.renderWidgets = function(tpl_url, url, callback) { + var widgetLocations = []; + + require(['vendor/async'], function(async) { + $('#content [widget-area]').each(function() { + widgetLocations.push($(this).attr('widget-area')); + }); + + async.each(widgetLocations, function(location, next) { + var area = $('#content [widget-area="' + location + '"]'); + + socket.emit('widgets.render', {template: tpl_url + '.tpl', url: url, location: location}, function(err, renderedWidgets) { + area.html(templates.prepare(area.html()).parse({ + widgets: renderedWidgets + })).removeClass('hidden'); + + if (!renderedWidgets.length) { + $('body [no-widget-class]').each(function() { + var $this = $(this); + $this.removeClass(); + $this.addClass($this.attr('no-widget-class')); + }); + } + + next(err); + }); + }, function(err) { + if (callback) { + callback(err); + } + }); + }); + }; + + ajaxify.refresh = function() { ajaxify.go(ajaxify.currentPage); }; diff --git a/public/src/app.js b/public/src/app.js index 90f6e63743..554a6ad4f3 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -601,27 +601,6 @@ var socket, }); }; - $('document').ready(function () { - $('#search-form').on('submit', function () { - var input = $(this).find('input'); - ajaxify.go("search/" + input.val()); - input.val(''); - return false; - }); - - $(window).blur(function(){ - app.isFocused = false; - }); - - $(window).focus(function(){ - app.isFocused = true; - - app.alternatingTitle(''); - }); - - createHeaderTooltips(); - }); - function exposeConfigToTemplates() { $(document).ready(function() { templates.setGlobal('relative_path', RELATIVE_PATH); @@ -650,6 +629,45 @@ var socket, }); } + + $('document').ready(function () { + var url = window.location.pathname.slice(1), + tpl_url = ajaxify.getTemplateMapping(url); + + $(window).trigger('action:ajaxify.start', { + url: url + }); + + $('#search-form').on('submit', function () { + var input = $(this).find('input'); + ajaxify.go("search/" + input.val()); + input.val(''); + return false; + }); + + $(window).blur(function(){ + app.isFocused = false; + }); + + $(window).focus(function(){ + app.isFocused = true; + + app.alternatingTitle(''); + }); + + createHeaderTooltips(); + templates.parseTemplateVariables(); + app.processPage(); + + ajaxify.renderWidgets(tpl_url, url); + + ajaxify.loadScript(tpl_url, function() { + $(window).trigger('action:ajaxify.end', { + url: url + }); + }); + }); + showWelcomeMessage = location.href.indexOf('loggedin') !== -1; app.loadConfig(); diff --git a/public/src/forum/account.js b/public/src/forum/account.js index 41f1310b62..dfe3c9ed11 100644 --- a/public/src/forum/account.js +++ b/public/src/forum/account.js @@ -74,9 +74,12 @@ define(['forum/accountheader'], function(header) { socket.emit('user.isOnline', theirid, Account.handleUserOnline); socket.on('event:new_post', function(data) { - var html = templates.prepare(templates['account'].blocks['posts']).parse(data); - $('.user-recent-posts').prepend(html); - $('.user-recent-posts span.timeago').timeago(); + templates.preload_template('account', function() { + templates['account'].parse({posts:[]}); + var html = templates.prepare(templates['account'].blocks['posts']).parse(data); + $('.user-recent-posts').prepend(html); + $('.user-recent-posts span.timeago').timeago(); + }); }); }); diff --git a/public/src/forum/accountposts.js b/public/src/forum/accountposts.js index 7ca5c37ee9..4f6e09307e 100644 --- a/public/src/forum/accountposts.js +++ b/public/src/forum/accountposts.js @@ -34,20 +34,23 @@ define(['forum/accountheader'], function(header) { } function onTopicsLoaded(posts) { - var html = templates.prepare(templates['accountposts'].blocks['posts']).parse({ - posts: posts - }); + templates.preload_template('accountposts', function() { + templates['accountposts'].parse(posts: []); + var html = templates.prepare(templates['accountposts'].blocks['posts']).parse({ + posts: posts + }); - translator.translate(html, function(translatedHTML) { + translator.translate(html, function(translatedHTML) { - $('#category-no-topics').remove(); + $('#category-no-topics').remove(); - html = $(translatedHTML); - html.find('img').addClass('img-responsive'); - $('.user-favourite-posts').append(html); - $('span.timeago').timeago(); - app.createUserTooltips(); - app.makeNumbersHumanReadable(html.find('.human-readable-number')); + html = $(translatedHTML); + html.find('img').addClass('img-responsive'); + $('.user-favourite-posts').append(html); + $('span.timeago').timeago(); + app.createUserTooltips(); + app.makeNumbersHumanReadable(html.find('.human-readable-number')); + }); }); } diff --git a/public/src/forum/admin/categories.js b/public/src/forum/admin/categories.js index 144adfd26a..417da007bf 100644 --- a/public/src/forum/admin/categories.js +++ b/public/src/forum/admin/categories.js @@ -117,14 +117,17 @@ define(['uploader'], function(uploader) { timeout: 2000 }); - var html = templates.prepare(templates['admin/categories'].blocks['categories']).parse({ - categories: [data] - }); - html = $(html); - html.find('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); + templates.preload_template('admin/categories', function() { + templates['admin/categories'].parse({categories:[]}); + var html = templates.prepare(templates['admin/categories'].blocks['categories']).parse({ + categories: [data] + }); + html = $(html); + html.find('[data-name="bgColor"], [data-name="color"]').each(enableColorPicker); - $('#entry-container').append(html); - $('#new-category-modal').modal('hide'); + $('#entry-container').append(html); + $('#new-category-modal').modal('hide'); + }); }); } diff --git a/public/src/forum/admin/groups.js b/public/src/forum/admin/groups.js index 811bc3c17b..45903afa59 100644 --- a/public/src/forum/admin/groups.js +++ b/public/src/forum/admin/groups.js @@ -66,7 +66,6 @@ define(function() { bootbox.confirm('Are you sure you wish to delete this group?', function(confirm) { if (confirm) { socket.emit('admin.groups.delete', gid, function(err, data) { - console.log(err, data); if(err) { return app.alertError(err.message); } diff --git a/public/src/forum/admin/topics.js b/public/src/forum/admin/topics.js index 545fc6b2a3..bcd4a2f4ec 100644 --- a/public/src/forum/admin/topics.js +++ b/public/src/forum/admin/topics.js @@ -62,20 +62,23 @@ define(function() { var btnEl = $('#topics_loadmore'); if (topics.length > 0) { - var html = templates.prepare(templates['admin/topics'].blocks['topics']).parse({ - topics: topics - }), - topicsListEl = $('.topics'); + templates.preload_template('admin/topics', function() { + templates['admin/topics'].parse({topics:[]}); + var html = templates.prepare(templates['admin/topics'].blocks['topics']).parse({ + topics: topics + }), + topicsListEl = $('.topics'); - // Fix relative paths - html = html.replace(/\{relative_path\}/g, RELATIVE_PATH); + // Fix relative paths + html = html.replace(/\{relative_path\}/g, RELATIVE_PATH); - topicsListEl.html(topicsListEl.html() + html); + topicsListEl.html(topicsListEl.html() + html); - Topics.resolveButtonStates(); + Topics.resolveButtonStates(); - btnEl.html('Load More Topics'); - $('span.timeago').timeago(); + btnEl.html('Load More Topics'); + $('span.timeago').timeago(); + }); } else { // Exhausted all topics btnEl.addClass('disabled'); diff --git a/public/src/forum/admin/users.js b/public/src/forum/admin/users.js index 25e6aee335..1ffabcb4c4 100644 --- a/public/src/forum/admin/users.js +++ b/public/src/forum/admin/users.js @@ -190,27 +190,30 @@ define(function() { return app.alertError(err.message); } - var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ - users: data.users - }), - userListEl = document.querySelector('.users'); + templates.preload_template('admin/users', function() { + templates['admin/users'].parse({users:[]}); + var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ + users: data.users + }), + userListEl = document.querySelector('.users'); - userListEl.innerHTML = html; - $('.fa-spinner').addClass('none'); + userListEl.innerHTML = html; + $('.fa-spinner').addClass('none'); - if (data && data.users.length === 0) { - $('#user-notfound-notify').html('User not found!') - .show() - .addClass('label-danger') - .removeClass('label-success'); - } else { - $('#user-notfound-notify').html(data.users.length + ' user' + (data.users.length > 1 ? 's' : '') + ' found! Search took ' + data.timing + ' ms.') - .show() - .addClass('label-success') - .removeClass('label-danger'); - } + if (data && data.users.length === 0) { + $('#user-notfound-notify').html('User not found!') + .show() + .addClass('label-danger') + .removeClass('label-success'); + } else { + $('#user-notfound-notify').html(data.users.length + ' user' + (data.users.length > 1 ? 's' : '') + ' found! Search took ' + data.timing + ' ms.') + .show() + .addClass('label-success') + .removeClass('label-danger'); + } - updateButtons(); + updateButtons(); + }); }); }, 250); }); @@ -220,14 +223,17 @@ define(function() { handleUserCreate(); function onUsersLoaded(users) { - var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ - users: users - }); - html = $(html); - $('#users-container').append(html); + templates.preload_template('admin/users', function() { + templates['admin/users'].parse({users:[]}); + var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ + users: users + }); + html = $(html); + $('#users-container').append(html); - updateUserBanButtons(html.find('.ban-btn')); - updateUserAdminButtons(html.find('.admin-btn')); + updateUserBanButtons(html.find('.ban-btn')); + updateUserAdminButtons(html.find('.admin-btn')); + }); } function loadMoreUsers() { diff --git a/public/src/forum/category.js b/public/src/forum/category.js index 80231bc931..7f94f0b33d 100644 --- a/public/src/forum/category.js +++ b/public/src/forum/category.js @@ -162,45 +162,47 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { Category.onNewTopic = function(data) { $(window).trigger('filter:categories.new_topic', data); + templates.preload_template('category', function() { + templates['category'].parse({topics:[]}); + var html = templates.prepare(templates['category'].blocks['topics']).parse({ + topics: [data] + }); - var html = templates.prepare(templates['category'].blocks['topics']).parse({ - topics: [data] - }); + translator.translate(html, function(translatedHTML) { + var topic = $(translatedHTML), + container = $('#topics-container'), + topics = $('#topics-container').children('.category-item'), + numTopics = topics.length; - translator.translate(html, function(translatedHTML) { - var topic = $(translatedHTML), - container = $('#topics-container'), - topics = $('#topics-container').children('.category-item'), - numTopics = topics.length; + $('#topics-container, .category-sidebar').removeClass('hidden'); + $('#category-no-topics').remove(); - $('#topics-container, .category-sidebar').removeClass('hidden'); - $('#category-no-topics').remove(); - - if (numTopics > 0) { - for (var x = 0; x < numTopics; x++) { - if ($(topics[x]).find('.fa-thumb-tack').length) { - if(x === numTopics - 1) { - topic.insertAfter(topics[x]); + if (numTopics > 0) { + for (var x = 0; x < numTopics; x++) { + if ($(topics[x]).find('.fa-thumb-tack').length) { + if(x === numTopics - 1) { + topic.insertAfter(topics[x]); + } + continue; } - continue; + topic.insertBefore(topics[x]); + break; } - topic.insertBefore(topics[x]); - break; + } else { + container.append(topic); } - } else { - container.append(topic); - } - topic.hide().fadeIn('slow'); + topic.hide().fadeIn('slow'); - socket.emit('categories.getPageCount', templates.get('category_id'), function(err, newPageCount) { - pagination.recreatePaginationLinks(newPageCount); + socket.emit('categories.getPageCount', templates.get('category_id'), function(err, newPageCount) { + pagination.recreatePaginationLinks(newPageCount); + }); + + topic.find('span.timeago').timeago(); + app.createUserTooltips(); + + $(window).trigger('action:categories.new_topic.loaded'); }); - - topic.find('span.timeago').timeago(); - app.createUserTooltips(); - - $(window).trigger('action:categories.new_topic.loaded'); }); }; @@ -239,36 +241,39 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { findInsertionPoint(); - var html = templates.prepare(templates['category'].blocks['topics']).parse({ - topics: topics - }); + templates.preload_template('category', function() { + templates['category'].parse({topics:[]}); + var html = templates.prepare(templates['category'].blocks['topics']).parse({ + topics: topics + }); - translator.translate(html, function(translatedHTML) { - var container = $('#topics-container'), - html = $(translatedHTML); + translator.translate(html, function(translatedHTML) { + var container = $('#topics-container'), + html = $(translatedHTML); - $('#topics-container, .category-sidebar').removeClass('hidden'); - $('#category-no-topics').remove(); + $('#topics-container, .category-sidebar').removeClass('hidden'); + $('#category-no-topics').remove(); - if(config.usePagination) { - container.empty().append(html); - } else { - if(after) { - html.insertAfter(after); - } else if(before) { - html.insertBefore(before); + if(config.usePagination) { + container.empty().append(html); } else { - container.append(html); + if(after) { + html.insertAfter(after); + } else if(before) { + html.insertBefore(before); + } else { + container.append(html); + } } - } - html.find('span.timeago').timeago(); - app.createUserTooltips(); - app.makeNumbersHumanReadable(html.find('.human-readable-number')); + html.find('span.timeago').timeago(); + app.createUserTooltips(); + app.makeNumbersHumanReadable(html.find('.human-readable-number')); - if (typeof callback === 'function') { - callback(topics); - } + if (typeof callback === 'function') { + callback(topics); + } + }); }); }; diff --git a/public/src/forum/favourites.js b/public/src/forum/favourites.js index 81fe7fda5e..6bfc07da4b 100644 --- a/public/src/forum/favourites.js +++ b/public/src/forum/favourites.js @@ -33,20 +33,23 @@ define(['forum/accountheader'], function(header) { } function onTopicsLoaded(posts) { - var html = templates.prepare(templates['favourites'].blocks['posts']).parse({ - posts: posts - }); + templates.preload_template('favourites', function() { + templates['favourites'].parse({posts:[]}); + var html = templates.prepare(templates['favourites'].blocks['posts']).parse({ + posts: posts + }); - translator.translate(html, function(translatedHTML) { + translator.translate(html, function(translatedHTML) { - $('#category-no-topics').remove(); + $('#category-no-topics').remove(); - html = $(translatedHTML); - html.find('img').addClass('img-responsive'); - $('.user-favourite-posts').append(html); - $('span.timeago').timeago(); - app.createUserTooltips(); - app.makeNumbersHumanReadable(html.find('.human-readable-number')); + html = $(translatedHTML); + html.find('img').addClass('img-responsive'); + $('.user-favourite-posts').append(html); + $('span.timeago').timeago(); + app.createUserTooltips(); + app.makeNumbersHumanReadable(html.find('.human-readable-number')); + }); }); } diff --git a/public/src/forum/recent.js b/public/src/forum/recent.js index 8c7d6c3b32..e75ecad06b 100644 --- a/public/src/forum/recent.js +++ b/public/src/forum/recent.js @@ -116,19 +116,22 @@ define(function() { } Recent.onTopicsLoaded = function(template, topics) { - var html = templates.prepare(templates[template].blocks['topics']).parse({ - topics: topics - }); + templates.preload_template(template, function() { + templates[template].parse({topics:[]}); + var html = templates.prepare(templates[template].blocks['topics']).parse({ + topics: topics + }); - translator.translate(html, function(translatedHTML) { + translator.translate(html, function(translatedHTML) { - $('#category-no-topics').remove(); + $('#category-no-topics').remove(); - html = $(translatedHTML); - $('#topics-container').append(html); - $('span.timeago').timeago(); - app.createUserTooltips(); - app.makeNumbersHumanReadable(html.find('.human-readable-number')); + html = $(translatedHTML); + $('#topics-container').append(html); + $('span.timeago').timeago(); + app.createUserTooltips(); + app.makeNumbersHumanReadable(html.find('.human-readable-number')); + }); }); } diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js index 2280bb728f..c261bd3c12 100644 --- a/public/src/forum/topic.js +++ b/public/src/forum/topic.js @@ -1207,6 +1207,9 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { findInsertionPoint(); + data.title = templates.get('topic_name'); + data.viewcount = templates.get('viewcount'); + parseAndTranslatePosts(data, function(translatedHTML) { var translated = $(translatedHTML); @@ -1233,8 +1236,11 @@ define(['composer', 'forum/pagination'], function(composer, pagination) { } function parseAndTranslatePosts(data, callback) { - var html = templates.prepare(templates['topic'].blocks['posts']).parse(data); - translator.translate(html, callback); + templates.preload_template('topic', function() { + templates['topic'].parse({posts: []}); + var html = templates.prepare(templates['topic'].blocks['posts']).parse(data); + translator.translate(html, callback); + }); } diff --git a/public/src/forum/users.js b/public/src/forum/users.js index e64b29ec00..4106ebde34 100644 --- a/public/src/forum/users.js +++ b/public/src/forum/users.js @@ -59,22 +59,24 @@ define(function() { return; } - var html = templates.prepare(templates['users'].blocks['users']).parse({ - users: data.users - }), - userListEl = $('#users-container'); + templates.preload_template('users', function() { + templates['users'].parse({users:[]}); + var html = templates.prepare(templates['users'].blocks['users']).parse({ + users: data.users + }), + userListEl = $('#users-container'); - userListEl.html(html); + userListEl.html(html); - if (data && data.users.length === 0) { - $('#user-notfound-notify').html('User not found!'); - $('#user-notfound-notify').parent().addClass('btn-warning label-warning'); - } else { - $('#user-notfound-notify').html(data.users.length + ' user' + (data.users.length > 1 ? 's' : '') + ' found! Search took ' + data.timing + ' ms.'); - $('#user-notfound-notify').parent().addClass('btn-success label-success'); - } - + if (data && data.users.length === 0) { + $('#user-notfound-notify').html('User not found!'); + $('#user-notfound-notify').parent().addClass('btn-warning label-warning'); + } else { + $('#user-notfound-notify').html(data.users.length + ' user' + (data.users.length > 1 ? 's' : '') + ' found! Search took ' + data.timing + ' ms.'); + $('#user-notfound-notify').parent().addClass('btn-success label-success'); + } + }); }); }, 500); //replace this with global throttling function/constant @@ -108,17 +110,20 @@ define(function() { } function onUsersLoaded(users, emptyContainer) { - var html = templates.prepare(templates['users'].blocks['users']).parse({ - users: users - }); + templates.preload_template('users', function() { + templates['useres'].parse({users:[]}); + var html = templates.prepare(templates['users'].blocks['users']).parse({ + users: users + }); - translator.translate(html, function(translated) { - if(emptyContainer) { - $('#users-container .registered-user').remove(); - } + translator.translate(html, function(translated) { + if(emptyContainer) { + $('#users-container .registered-user').remove(); + } - $('#users-container').append(translated); - $('#users-container .anon-user').appendTo($('#users-container')); + $('#users-container').append(translated); + $('#users-container .anon-user').appendTo($('#users-container')); + }); }); } diff --git a/public/src/templates.js b/public/src/templates.js index cf12ba9a09..46a77e5052 100644 --- a/public/src/templates.js +++ b/public/src/templates.js @@ -17,43 +17,27 @@ path = require('path'); } catch (e) {} - templates.force_refresh = function (tpl) { + templates.force_refresh = function(tpl) { return !!config.force_refresh[tpl]; }; - templates.get_custom_map = function (tpl) { - if (config['custom_mapping'] && tpl) { - for (var pattern in config['custom_mapping']) { + templates.get_custom_map = function(tpl) { + if (config.custom_mapping && tpl) { + for (var pattern in config.custom_mapping) { if (tpl.match(pattern)) { - return (config['custom_mapping'][pattern]); + return (config.custom_mapping[pattern]); } } } return false; - } - - templates.is_available = function (tpl) { - return $.inArray(tpl, available_templates) !== -1; }; - templates.ready = function (callback) { - if (callback == null) { - if (this.ready_callback) { - this.ready_callback(); - } else { - this.loaded = true; - } - } else { - if (this.loaded == true) { - callback(); - } else { - this.ready_callback = callback; - } - } + templates.is_available = function(tpl) { + return $.inArray(tpl + '.tpl', available_templates) !== -1; }; - templates.prepare = function (raw_tpl) { + templates.prepare = function(raw_tpl) { var template = {}; template.html = raw_tpl; template.parse = parse; @@ -62,73 +46,6 @@ return template; }; - function loadTemplates(templatesToLoad, customTemplateDir) { - function loadServer() { - var loaded = templatesToLoad.length, - templatesPath = __dirname + '/../templates'; - - function getTemplates(directory) { - for (var t in templatesToLoad) { - (function (file) { - function loadFile(html) { - var template = function () { - this.toString = function () { - return this.html; - }; - } - - template.prototype.file = file; - template.prototype.parse = parse; - template.prototype.html = String(html); - - templates[file] = new template; - - loaded--; - if (loaded === 0) { - templates.ready(); - } - } - - fs.readFile(directory + '/' + file + '.tpl', function (err, html) { - if (err && directory !== templatesPath) { - fs.readFile(templatesPath + '/' + file + '.tpl', function (err, html) { - loadFile(html); - }); - } else { - loadFile(html); - } - }); - }(templatesToLoad[t])); - } - } - if (customTemplateDir) { - fs.exists(customTemplateDir, function (exists) { - var directory = (exists ? customTemplateDir : templatesPath); - getTemplates(directory); - }); - } else { - getTemplates(templatesPath); - } - - } - - function loadClient() { - $.when($.getJSON(RELATIVE_PATH + '/templates/config.json'), $.getJSON(RELATIVE_PATH + '/api/get_templates_listing')).done(function (config_data, templates_data) { - config = config_data[0]; - available_templates = templates_data[0]; - templates.ready(); - }); - } - - if (fs === null) loadClient(); - else loadServer(); - } - - - templates.init = function (templates_to_load, custom_templates) { - loadTemplates(templates_to_load || [], custom_templates || false); - }; - templates.render = function(filename, options, fn) { if ('function' === typeof options) { fn = options, options = false; @@ -139,9 +56,8 @@ .replace('.' + options.settings['view engine'], ''); if (!templates[tpl]) { - fs.readFile(filename, function (err, html) { - templates[tpl] = html.toString(); - templates.prepare(templates[tpl]); + fs.readFile(filename, function(err, html) { + templates[tpl] = templates.prepare(html.toString()); return fn(err, templates[tpl].parse(options)); }); @@ -150,19 +66,7 @@ } }; - templates.getTemplateNameFromUrl = function (url) { - var parts = url.split('?')[0].split('/'); - - for (var i = 0; i < parts.length; ++i) { - if (templates.is_available(parts[i])) { - return parts[i]; - } - } - return ''; - }; - templates.preload_template = function(tpl_name, callback) { - if(templates[tpl_name]) { return callback(); } @@ -171,9 +75,9 @@ // should be named something else // TODO: The "Date.now()" in the line below is only there for development purposes. // It should be removed at some point. - $.get(RELATIVE_PATH + '/templates/' + tpl_name + '.tpl?v=' + Date.now(), function (html) { - var template = function () { - this.toString = function () { + $.get(RELATIVE_PATH + '/templates/' + tpl_name + '.tpl?v=' + Date.now(), function(html) { + var template = function() { + this.toString = function() { return this.html; }; }; @@ -188,13 +92,13 @@ }); }; - templates.load_template = function (callback, url, template) { + templates.load_template = function(callback, url, template) { var location = document.location || window.location, api_url = (url === '' || url === '/') ? 'home' : url, tpl_url = templates.get_custom_map(api_url.split('?')[0]); if (!tpl_url) { - tpl_url = templates.getTemplateNameFromUrl(api_url); + tpl_url = ajaxify.getTemplateMapping(api_url); } var template_data = null; @@ -210,7 +114,7 @@ apiXHR = $.ajax({ url: RELATIVE_PATH + '/api/' + api_url, cache: false, - success: function (data) { + success: function(data) { if (!data) { ajaxify.go('404'); return; @@ -219,7 +123,7 @@ template_data = data; parse_template(); }, - error: function (data, textStatus) { + error: function(data, textStatus) { $('#content, #footer').stop(true, true).removeClass('ajaxifying'); if (data && data.status == 404) { return ajaxify.go('404'); @@ -234,40 +138,45 @@ function parse_template() { if (!templates[tpl_url] || !template_data) return; - if (typeof global !== "undefined") + if (typeof global !== "undefined") { template_data['relative_path'] = nconf.get('relative_path'); - else + } else { template_data['relative_path'] = RELATIVE_PATH; + } - translator.translate(templates[tpl_url].parse(template_data), function (translatedTemplate) { + var template = templates[tpl_url].parse(template_data) + translator.translate(template, function(translatedTemplate) { $('#content').html(translatedTemplate); - $('#content [template-variable]').each(function (index, element) { - var value = null; - - switch ($(element).attr('template-type')) { - case 'boolean': - value = ($(element).val() === 'true' || $(element).val() === '1') ? true : false; - break; - case 'int': // Intentional fall-through - case 'integer': - value = parseInt($(element).val()); - break; - default: - value = $(element).val(); - break; - } - - templates.set($(element).attr('template-variable'), value); - }); + templates.parseTemplateVariables(); if (callback) { callback(true); } }); } + }; + templates.parseTemplateVariables = function() { + $('#content [template-variable]').each(function(index, element) { + var value = null; + + switch ($(element).attr('template-type')) { + case 'boolean': + value = ($(element).val() === 'true' || $(element).val() === '1') ? true : false; + break; + case 'int': + case 'integer': + value = parseInt($(element).val()); + break; + default: + value = $(element).val(); + break; + } + + templates.set($(element).attr('template-variable'), value); + }); }; templates.cancelRequest = function() { @@ -276,15 +185,15 @@ } }; - templates.flush = function () { + templates.flush = function() { parsed_variables = {}; }; - templates.get = function (key) { + templates.get = function(key) { return parsed_variables[key]; }; - templates.set = function (key, value) { + templates.set = function(key, value) { parsed_variables[key] = value; }; @@ -293,7 +202,7 @@ }; //modified from https://github.com/psychobunny/dcp.templates - var parse = function (data) { + var parse = function(data) { var self = this; function replace(key, value, template) { @@ -332,7 +241,6 @@ var template = this.html, regex, block; - // registering globals for (var g in templates.globals) { if (templates.globals.hasOwnProperty(g)) { data[g] = data[g] || templates.globals[g]; @@ -444,7 +352,13 @@ if ('undefined' !== typeof window) { window.templates = module.exports; - templates.init(); + + window.onload = function() { + $.when($.getJSON(RELATIVE_PATH + '/templates/config.json'), $.getJSON(RELATIVE_PATH + '/api/get_templates_listing')).done(function (config_data, templates_data) { + config = config_data[0]; + available_templates = templates_data[0]; + }); + } } })('undefined' === typeof module ? { diff --git a/public/src/translator.js b/public/src/translator.js index 265365ce5e..80542e94cb 100644 --- a/public/src/translator.js +++ b/public/src/translator.js @@ -1,13 +1,10 @@ (function (module) { "use strict"; - /*global RELATIVE_PATH*/ + /*global RELATIVE_PATH, config*/ /* * TODO: - * - * 1. recursion needed when parsing language keys (ex. topics:modal.delete.title), right now json is all one level deep - * 2. user side settings for preferred language - * + * user side settings for preferred language */ var translator = {}, @@ -98,8 +95,9 @@ }; translator.translate = function (data, callback) { - var keys = data.match(/\[\[.*?\]\]/g), - loading = 0; + if (!data) { + return callback(data); + } function insertLanguage(text, key, value, variables) { if (value) { @@ -117,6 +115,9 @@ return text; } + var keys = data.match(/\[\[.*?\]\]/g), + loading = 0; + for (var key in keys) { if (keys.hasOwnProperty(key)) { keys[key] = '' + keys[key]; diff --git a/public/src/utils.js b/public/src/utils.js index 1ebd7e475b..c37479547d 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -28,9 +28,7 @@ //Adapted from http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search walk: function(dir, done) { - var results = [], - path = require('path'), - main_dir = path.join(__dirname, '..', 'templates'); + var results = []; fs.readdir(dir, function(err, list) { if (err) { @@ -51,7 +49,7 @@ } }); } else { - results.push(file.replace(main_dir + '/', '').replace('.tpl', '')); + results.push(file); if (!--pending) { done(null, results); } diff --git a/public/templates/.gitignore b/public/templates/.gitignore new file mode 100644 index 0000000000..db3bf754ea --- /dev/null +++ b/public/templates/.gitignore @@ -0,0 +1 @@ +*.tpl \ No newline at end of file diff --git a/public/templates/403.tpl b/public/templates/403.tpl deleted file mode 100644 index 46478a224c..0000000000 --- a/public/templates/403.tpl +++ /dev/null @@ -1,4 +0,0 @@ -
[[global:403.message]]
-[[global:404.message]]
-[[global:500.message]]
-{errorMessage}
-{posts.content}
- - - [[global:posted]] - [[global:in]] - - {posts.category.name} - - - - -{posts.content}
- -{raw}
-{raw}
-
-{eventdata}
-
\ No newline at end of file
diff --git a/public/templates/admin/footer.tpl b/public/templates/admin/footer.tpl
deleted file mode 100644
index c8fdc1117c..0000000000
--- a/public/templates/admin/footer.tpl
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-