diff --git a/Gruntfile.js b/Gruntfile.js
index 85ea76938f..f3c4f1723e 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,24 +1,24 @@
"use strict";
-var fork = require('child_process').fork,
- env = process.env,
- worker, updateWorker,
- incomplete = [],
- running = 0;
-
+var fork = require('child_process').fork;
+var env = process.env;
+var worker, updateWorker, initWorker;
+var incomplete = [];
+var running = 0;
module.exports = function (grunt) {
var args = [];
+ var initArgs = ['--build'];
if (!grunt.option('verbose')) {
args.push('--log-level=info');
+ initArgs.push('--log-level=info');
}
function update(action, filepath, target) {
- var updateArgs = args.slice(),
- fromFile = '',
- compiling = '',
- time = Date.now();
-
+ var updateArgs = args.slice();
+ var compiling = '';
+ var time = Date.now();
+
if (target === 'lessUpdated_Client') {
compiling = 'clientCSS';
} else if (target === 'lessUpdated_Admin') {
@@ -44,12 +44,16 @@ module.exports = function (grunt) {
if (updateWorker) {
updateWorker.kill('SIGKILL');
}
- updateWorker = fork('app.js', updateArgs, { env: env });
+ updateWorker = fork('app.js', updateArgs, {
+ env: env
+ });
++running;
updateWorker.on('exit', function () {
--running;
if (running === 0) {
- worker = fork('app.js', args, { env: env });
+ worker = fork('app.js', args, {
+ env: env
+ });
worker.on('message', function () {
if (incomplete.length) {
incomplete = [];
@@ -131,15 +135,24 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-watch');
- if (grunt.option('skip')) {
- grunt.registerTask('default', ['watch:serverUpdated']);
- } else {
- grunt.registerTask('default', ['watch']);
- }
-
-
+ grunt.registerTask('default', ['watch']);
env.NODE_ENV = 'development';
- worker = fork('app.js', args, { env: env });
+ if (grunt.option('skip')) {
+ worker = fork('app.js', args, {
+ env: env
+ });
+ } else {
+ initWorker = fork('app.js', initArgs, {
+ env: env
+ });
+
+ initWorker.on('exit', function () {
+ worker = fork('app.js', args, {
+ env: env
+ });
+ });
+ }
+
grunt.event.on('watch', update);
-};
\ No newline at end of file
+};
diff --git a/public/language/cs/admin/appearance/skins.json b/public/language/cs/admin/appearance/skins.json
index 64edb7c071..c84298d5a2 100644
--- a/public/language/cs/admin/appearance/skins.json
+++ b/public/language/cs/admin/appearance/skins.json
@@ -1,9 +1,9 @@
{
"loading": "Načítání motivů…",
- "homepage": "Homepage",
- "select-skin": "Select Skin",
- "current-skin": "Current Skin",
- "skin-updated": "Skin Updated",
+ "homepage": "Domovská stránka",
+ "select-skin": "Vyber motiv",
+ "current-skin": "Současný motiv",
+ "skin-updated": "Motiv aktualizován",
"applied-success": "%1 skin was succesfully applied",
"revert-success": "Skin reverted to base colours"
}
\ No newline at end of file
diff --git a/public/language/cs/admin/settings/user.json b/public/language/cs/admin/settings/user.json
index bdabb075e9..607a2fbd92 100644
--- a/public/language/cs/admin/settings/user.json
+++ b/public/language/cs/admin/settings/user.json
@@ -1,21 +1,21 @@
{
- "authentication": "Authentication",
- "allow-local-login": "Allow local login",
- "require-email-confirmation": "Require Email Confirmation",
+ "authentication": "Ověření",
+ "allow-local-login": "Povolit místní přihlášení",
+ "require-email-confirmation": "Vyžadovat potvrzení e-mailem",
"email-confirm-interval": "User may not resend a confirmation email until",
- "email-confirm-email2": "minutes have elapsed",
+ "email-confirm-email2": "minut uplynulo",
"allow-login-with": "Allow login with",
- "allow-login-with.username-email": "Username or Email",
- "allow-login-with.username": "Username Only",
- "allow-login-with.email": "Email Only",
- "account-settings": "Account Settings",
- "disable-username-changes": "Disable username changes",
- "disable-email-changes": "Disable email changes",
- "disable-password-changes": "Disable password changes",
- "allow-account-deletion": "Allow account deletion",
+ "allow-login-with.username-email": "Uživatelské jméno nebo e-mail",
+ "allow-login-with.username": "Pouze uživatelské jméno",
+ "allow-login-with.email": "Pouze e-mail",
+ "account-settings": "Nastavení účtu",
+ "disable-username-changes": "Zakázat změnu uživatelského jména",
+ "disable-email-changes": "Zakázat změnu e-mailu",
+ "disable-password-changes": "Zakázat změnu hesla",
+ "allow-account-deletion": "Povolit smazání účtu",
"user-info-private": "Make user info private",
- "themes": "Themes",
- "disable-user-skins": "Prevent users from choosing a custom skin",
+ "themes": "Témata",
+ "disable-user-skins": "Zabránit uživateli ve výběru vlastního vzhledu",
"account-protection": "Account Protection",
"login-attempts": "Login attempts per hour",
"login-attempts-help": "If login attempts to a user's account exceeds this threshold, that account will be locked for a pre-configured amount of time",
@@ -34,10 +34,10 @@
"registration.max-invites": "Maximum Invitations per User",
"max-invites": "Maximum Invitations per User",
"max-invites-help": "0 for no restriction. Admins get infinite invitations
Only applicable for \"Invite Only\"",
- "min-username-length": "Minimum Username Length",
- "max-username-length": "Maximum Username Length",
- "min-password-length": "Minimum Password Length",
- "max-about-me-length": "Maximum About Me Length",
+ "min-username-length": "Minimální délka uživatelského jména",
+ "max-username-length": "Maximální délka uživatelského jména",
+ "min-password-length": "Minimální délka hesla",
+ "max-about-me-length": "Maximální délka hesla",
"terms-of-use": "Forum Terms of Use (Leave blank to disable)",
"user-search": "User Search",
"user-search-results-per-page": "Number of results to display",
@@ -48,10 +48,10 @@
"outgoing-new-tab": "Open outgoing links in new tab",
"topic-search": "Enable In-Topic Searching",
"digest-freq": "Subscribe to Digest",
- "digest-freq.off": "Off",
- "digest-freq.daily": "Daily",
- "digest-freq.weekly": "Weekly",
- "digest-freq.monthly": "Monthly",
+ "digest-freq.off": "Vypnuto",
+ "digest-freq.daily": "Denně",
+ "digest-freq.weekly": "Týdně",
+ "digest-freq.monthly": "Měsíčně",
"email-chat-notifs": "Send an email if a new chat message arrives and I am not online",
"email-post-notif": "Send an email when replies are made to topics I am subscribed to",
"follow-created-topics": "Follow topics you create",
diff --git a/public/language/cs/login.json b/public/language/cs/login.json
index 8d189a2d0e..e8652b4d33 100644
--- a/public/language/cs/login.json
+++ b/public/language/cs/login.json
@@ -8,5 +8,5 @@
"failed_login_attempt": "Přihlášení neúspěšné",
"login_successful": "Přihlášení proběhlo úspěšně!",
"dont_have_account": "Nemáte účet?",
- "logged-out-due-to-inactivity": "You have been logged out of the Admin Control Panel due to inactivity"
+ "logged-out-due-to-inactivity": "Z důvodu nečinnosti jste byl odhlášen z ovládacího panelu administrátora"
}
\ No newline at end of file
diff --git a/public/language/cs/modules.json b/public/language/cs/modules.json
index 28a90319de..84afa3b052 100644
--- a/public/language/cs/modules.json
+++ b/public/language/cs/modules.json
@@ -13,7 +13,7 @@
"chat.contacts": "Kontakty",
"chat.message-history": "Historie zpráv",
"chat.pop-out": "Skrýt chat",
- "chat.minimize": "Minimize",
+ "chat.minimize": "Minimalizovat",
"chat.maximize": "Maximalizovat",
"chat.seven_days": "7 dní",
"chat.thirty_days": "30 dní",
diff --git a/public/language/cs/register.json b/public/language/cs/register.json
index fae5335f6a..fccff9ac25 100644
--- a/public/language/cs/register.json
+++ b/public/language/cs/register.json
@@ -1,6 +1,6 @@
{
"register": "Registrace",
- "cancel_registration": "Cancel Registration",
+ "cancel_registration": "Zrušit registraci",
"help.email": "Ve výchozím nastavení bude váš e-mail skrytý.",
"help.username_restrictions": "Jedinečné uživatelské jméno dlouhé %1 až %2 znaků. Ostatní uživatelé Vás mohou zmínit jako @uživatelské-jméno.",
"help.minimum_password_length": "Délka vašeho hesla musí být alespoň %1 znaků.",
diff --git a/public/language/cs/user.json b/public/language/cs/user.json
index 01cf965cc8..25b2599e43 100644
--- a/public/language/cs/user.json
+++ b/public/language/cs/user.json
@@ -94,7 +94,7 @@
"topics_per_page": "Témat na stránce",
"posts_per_page": "Příspěvků na stránce",
"notification_sounds": "Přehrát zvuk když dostanete notifikaci",
- "notifications_and_sounds": "Notifications & Sounds",
+ "notifications_and_sounds": "Upozornění a zvuky",
"incoming-message-sound": "Incoming message sound",
"outgoing-message-sound": "Outgoing message sound",
"notification-sound": "Notification sound",
diff --git a/public/language/ja/topic.json b/public/language/ja/topic.json
index 6f7cac850b..3a70be2b0f 100644
--- a/public/language/ja/topic.json
+++ b/public/language/ja/topic.json
@@ -107,10 +107,10 @@
"more_guests": "ゲストさんが%1人",
"users_and_others": "%1と他は%2",
"sort_by": "並び替え",
- "oldest_to_newest": "新しい順に",
- "newest_to_oldest": "古い順に",
- "most_votes": "最高評価",
- "most_posts": "最高投稿",
+ "oldest_to_newest": "古い\bものから新しい順",
+ "newest_to_oldest": "新しいものから古い順",
+ "most_votes": "最も投票された順",
+ "most_posts": "最も投稿された順",
"stale.title": "新しいスレッドを作りますか?",
"stale.warning": "あなたが返信しようとしてるスレッドが古いスレッドです。新しいスレッドを作って、そしてこのスレッドが参考として入れた方を勧めます。そうしますか?",
"stale.create": "新しいスレッドを作ります。",
diff --git a/public/language/sk/admin/advanced/errors.json b/public/language/sk/admin/advanced/errors.json
index 1b12ed100f..02a4a0c461 100644
--- a/public/language/sk/admin/advanced/errors.json
+++ b/public/language/sk/admin/advanced/errors.json
@@ -1,14 +1,14 @@
{
"figure-x": "Znázorniť %1",
"error-events-per-day": "%1 events per day",
- "error.404": "404 Not Found",
- "error.503": "503 Service Unavailable",
+ "error.404": "404 Nenájdené",
+ "error.503": "503 Služba nie je k dispozícií",
"manage-error-log": "Manage Error Log",
"export-error-log": "Export Error Log (CSV)",
"clear-error-log": "Clear Error Log",
"route": "Route",
"count": "Count",
- "no-routes-not-found": "Hooray! No 404 errors!",
+ "no-routes-not-found": "Hurá! Žiadne chyby 404!",
"clear404-confirm": "Are you sure you wish to clear the 404 error logs?",
- "clear404-success": "\"404 Not Found\" errors cleared"
+ "clear404-success": "Chybné hlásenia \"404 Nenájdené\" vyčistené"
}
\ No newline at end of file
diff --git a/public/language/sk/admin/advanced/events.json b/public/language/sk/admin/advanced/events.json
index cce4546e34..ef963e4ce2 100644
--- a/public/language/sk/admin/advanced/events.json
+++ b/public/language/sk/admin/advanced/events.json
@@ -1,6 +1,6 @@
{
"events": "Udalosti",
- "no-events": "There are no events",
- "control-panel": "Events Control Panel",
- "delete-events": "Delete Events"
+ "no-events": "Zatiaľ neexistujô žiadne udalosti",
+ "control-panel": "Ovládací panel udalostí",
+ "delete-events": "Odstrániť udalosť"
}
\ No newline at end of file
diff --git a/public/language/sk/admin/advanced/logs.json b/public/language/sk/admin/advanced/logs.json
index 0dd71a3f30..1c30f4dca7 100644
--- a/public/language/sk/admin/advanced/logs.json
+++ b/public/language/sk/admin/advanced/logs.json
@@ -1,7 +1,7 @@
{
- "logs": "Protokoly",
- "control-panel": "Logs Control Panel",
- "reload": "Reload Logs",
- "clear": "Clear Logs",
- "clear-success": "Logs Cleared!"
+ "logs": "Záznamy",
+ "control-panel": "Ovládací panel záznamov",
+ "reload": "Znovu načítať záznamy",
+ "clear": "Vyčistiť záznamy",
+ "clear-success": "Záznamy vyčistené!"
}
\ No newline at end of file
diff --git a/public/language/sk/admin/appearance/skins.json b/public/language/sk/admin/appearance/skins.json
index 4db6fbdd8a..f7440bfa8e 100644
--- a/public/language/sk/admin/appearance/skins.json
+++ b/public/language/sk/admin/appearance/skins.json
@@ -1,9 +1,9 @@
{
- "loading": "Loading Skins...",
- "homepage": "Homepage",
- "select-skin": "Select Skin",
- "current-skin": "Current Skin",
- "skin-updated": "Skin Updated",
- "applied-success": "%1 skin was succesfully applied",
- "revert-success": "Skin reverted to base colours"
+ "loading": "Načítať vzhľady...",
+ "homepage": "Domovska stránka",
+ "select-skin": "Vybrať vzhľad",
+ "current-skin": "Aktuálny vzhľad",
+ "skin-updated": "Vzhľad aktualizovaný",
+ "applied-success": "%1 vzhľad bol úspešne aplikovaný",
+ "revert-success": "Vzhľad bol obnovený do základných farieb"
}
\ No newline at end of file
diff --git a/public/language/sk/admin/appearance/themes.json b/public/language/sk/admin/appearance/themes.json
index 3148a01337..34dca8603e 100644
--- a/public/language/sk/admin/appearance/themes.json
+++ b/public/language/sk/admin/appearance/themes.json
@@ -1,11 +1,11 @@
{
- "checking-for-installed": "Checking for installed themes...",
- "homepage": "Homepage",
- "select-theme": "Select Theme",
- "current-theme": "Current Theme",
- "no-themes": "No installed themes found",
- "revert-confirm": "Are you sure you wish to restore the default NodeBB theme?",
- "theme-changed": "Theme Changed",
- "revert-success": "You have successfully reverted your NodeBB back to it's default theme.",
- "restart-to-activate": "Please restart your NodeBB to fully activate this theme"
+ "checking-for-installed": "Kontrola nainštalovaných motívov...",
+ "homepage": "Domovská stránka",
+ "select-theme": "Vybrať motív",
+ "current-theme": "Aktuálny motív",
+ "no-themes": "Žiadne nainštalované motívy neboli nájdené",
+ "revert-confirm": "Ste si istý, že chcete obnoviť predvolený NodeBB motív?",
+ "theme-changed": "Motív zmenený",
+ "revert-success": "Úspešne sa Vám podarilo obnoviť Váš NodeBB do predvoleného motívu.",
+ "restart-to-activate": "Prosím, reštartujte Váš NodeBB pre úplne aktivovanie tohto motívu."
}
\ No newline at end of file
diff --git a/public/language/sk/admin/general/dashboard.json b/public/language/sk/admin/general/dashboard.json
index a70dda322e..12a34ba046 100644
--- a/public/language/sk/admin/general/dashboard.json
+++ b/public/language/sk/admin/general/dashboard.json
@@ -1,20 +1,20 @@
{
- "forum-traffic": "Forum Traffic",
- "page-views": "Page Views",
- "unique-visitors": "Unique Visitors",
- "users": "Users",
- "posts": "Posts",
- "topics": "Topics",
- "page-views-last-month": "Page views Last Month",
- "page-views-this-month": "Page views This Month",
- "page-views-last-day": "Page views in last 24 hours",
+ "forum-traffic": "Prevádzka fóra",
+ "page-views": "Zobrazenia stránok",
+ "unique-visitors": "Unikátne návštevy",
+ "users": "Užívatelia",
+ "posts": "Príspevky",
+ "topics": "Témy",
+ "page-views-last-month": "Zobrazenia stránok za posledný mesiac",
+ "page-views-this-month": "Zobrazenia stránok za tento mesiac",
+ "page-views-last-day": "Zobrazenia stránok za posledných 24 hodín",
- "stats.day": "Day",
- "stats.week": "Week",
- "stats.month": "Month",
- "stats.all": "All Time",
+ "stats.day": "Deň",
+ "stats.week": "Týždeň",
+ "stats.month": "Mesiac",
+ "stats.all": "Celé obdobie",
- "updates": "Updates",
+ "updates": "Aktualizácie",
"running-version": "You are running NodeBB v%1.",
"keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.",
"up-to-date": "
You are up-to-date
", @@ -22,42 +22,42 @@ "prerelease-upgrade-available": "This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider upgrading your NodeBB.
", "prerelease-warning": "This is a pre-release version of NodeBB. Unintended bugs may occur.
", - "notices": "Notices", - "restart-not-required": "Restart not required", - "restart-required": "Restart required", - "search-plugin-installed": "Search Plugin installed", - "search-plugin-not-installed": "Search Plugin not installed", + "notices": "Upozornenie", + "restart-not-required": "Reštart nie je potrebný", + "restart-required": "Reštart je potrebný", + "search-plugin-installed": "Vyhľadávací doplnok bol nainštalovaný", + "search-plugin-not-installed": "Vyhľadávací doplnok nebol nainštalovaný", "search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality", "control-panel": "System Control", - "reload": "Reload", - "restart": "Restart", + "reload": "Obnoviť", + "restart": "Reštartovať", "restart-warning": "Reloading or Restarting your NodeBB will drop all existing connections for a few seconds.", "maintenance-mode": "Maintenance Mode", "maintenance-mode-title": "Click here to set up maintenance mode for NodeBB", "realtime-chart-updates": "Realtime Chart Updates", - "active-users": "Active Users", - "active-users.users": "Users", - "active-users.guests": "Guests", - "active-users.total": "Total", - "active-users.connections": "Connections", + "active-users": "Aktívny užívatelia", + "active-users.users": "Užívatelia", + "active-users.guests": "Hostia", + "active-users.total": "Celkovo", + "active-users.connections": "Pripojení", - "anonymous-registered-users": "Anonymous vs Registered Users", - "anonymous": "Anonymous", - "registered": "Registered", + "anonymous-registered-users": "Neznámy vs Zaregistrovaný užívatelia", + "anonymous": "Neznámy", + "registered": "Zaregistrovaný", "user-presence": "User Presence", "on-categories": "On categories list", "reading-posts": "Reading posts", "browsing-topics": "Browsing topics", - "recent": "Recent", - "unread": "Unread", + "recent": "Nedávne", + "unread": "Neprečitané", "high-presence-topics": "High Presence Topics", - "graphs.page-views": "Page Views", - "graphs.unique-visitors": "Unique Visitors", - "graphs.registered-users": "Registered Users", - "graphs.anonymous-users": "Anonymous Users" + "graphs.page-views": "Zobrazenia stránok", + "graphs.unique-visitors": "Unikátny navštevníci", + "graphs.registered-users": "Zarestrovaný užívatelia", + "graphs.anonymous-users": "Neznámy užívatelia" } diff --git a/public/language/sk/admin/manage/tags.json b/public/language/sk/admin/manage/tags.json index db40e9f098..775a9aed63 100644 --- a/public/language/sk/admin/manage/tags.json +++ b/public/language/sk/admin/manage/tags.json @@ -6,7 +6,7 @@ "description": "Select tags via clicking and/or dragging, use shift to select multiple.", "create": "Create Tag", "modify": "Modify Tags", - "delete": "Delete Selected Tags", + "delete": "Odstrániť vybraté značky", "search": "Search for tags...", "settings": "Click here to visit the tag settings page.", "name": "Tag Name", diff --git a/public/language/tr/admin/settings/uploads.json b/public/language/tr/admin/settings/uploads.json index b83ea976ce..4daa55400b 100644 --- a/public/language/tr/admin/settings/uploads.json +++ b/public/language/tr/admin/settings/uploads.json @@ -1,7 +1,7 @@ { "posts": "İletiler", "allow-files": "Allow users to upload regular files", - "private": "Make uploaded files private", + "private": "Yüklenen dosyaları gizli yap", "max-image-width": "Resize images down to specified width (in pixels)", "max-image-width-help": "(in pixels, default: 760 pixels, set to 0 to disable)", "max-file-size": "Maksimum Dosya Boyutu (KiB)", @@ -19,10 +19,10 @@ "profile-image-dimension-help": "(in pixels, default: 128 pixels)", "max-profile-image-size": "Maximum Profile Image File Size", "max-profile-image-size-help": "(in kilobytes, default: 256 KiB)", - "max-cover-image-size": "Maximum Cover Image File Size", + "max-cover-image-size": "Maksimum Kapak Görseli Dosya Boyutu", "max-cover-image-size-help": "(in kilobytes, default: 2,048 KiB)", "keep-all-user-images": "Keep old versions of avatars and profile covers on the server", - "profile-covers": "Profile Covers", - "default-covers": "Default Cover Images", + "profile-covers": "Profil Kapakları", + "default-covers": "Varsayılan Kapak Görseli", "default-covers-help": "Add comma-separated default cover images for accounts that don't have an uploaded cover image" } diff --git a/public/src/modules/pictureCropper.js b/public/src/modules/pictureCropper.js index 40be206c19..b04bc48e97 100644 --- a/public/src/modules/pictureCropper.js +++ b/public/src/modules/pictureCropper.js @@ -18,7 +18,7 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe uploadModal = $(uploadModal); uploadModal.modal('show'); - uploadModal.on('hidden.bs.modal', function () { + uploadModal.on('hidden.bs.modal', function () { uploadModal.remove(); }); @@ -31,9 +31,11 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe }); }; - module.handleImageCrop = function (data, callback) { + module.handleImageCrop = function (data, callback) { $('#crop-picture-modal').remove(); - templates.parse('modals/crop_picture', {url: data.url}, function (cropperHtml) { + templates.parse('modals/crop_picture', { + url: data.url + }, function (cropperHtml) { translator.translate(cropperHtml, function (translated) { var cropperModal = $(translated); cropperModal.modal('show'); @@ -58,28 +60,28 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe cropperModal.find('.reset').on('click', function () { cropperTool.reset(); }); - + cropperModal.find('.crop-btn').on('click', function () { $(this).addClass('disabled'); var imageData = data.imageType ? cropperTool.getCroppedCanvas().toDataURL(data.imageType) : cropperTool.getCroppedCanvas().toDataURL(); - + cropperModal.find('#upload-progress-bar').css('width', '100%'); cropperModal.find('#upload-progress-box').show().removeClass('hide'); - + var socketData = {}; socketData[data.paramName] = data.paramValue; socketData['imageData'] = imageData; - - socket.emit(data.socketMethod, socketData, function (err, imageData) { - if (err) { - cropperModal.find('#upload-progress-box').hide(); - cropperModal.find('.upload-btn').removeClass('disabled'); - cropperModal.find('.crop-btn').removeClass('disabled'); - return app.alertError(err.message); - } - callback(imageData.url); - cropperModal.modal('hide'); + socket.emit(data.socketMethod, socketData, function (err, imageData) { + if (err) { + cropperModal.find('#upload-progress-box').hide(); + cropperModal.find('.upload-btn').removeClass('disabled'); + cropperModal.find('.crop-btn').removeClass('disabled'); + return app.alertError(err.message); + } + + callback(imageData.url); + cropperModal.modal('hide'); }); }); @@ -115,8 +117,8 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe return showAlert('error', '[[uploads:select-file-to-upload]]'); } - var file = fileInput[0].files[0]; - var reader = new FileReader(); + var file = fileInput[0].files[0]; + var reader = new FileReader(); var imageUrl; var imageType = file.type; diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index 18b0b63dda..923e3cf7d8 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -59,7 +59,10 @@ function uploadAsImage(req, uploadedFile, callback) { return next(new Error('[[error:no-privileges]]')); } if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: uploadedFile, uid: req.uid}, callback); + return plugins.fireHook('filter:uploadImage', { + image: uploadedFile, + uid: req.uid + }, callback); } file.isFileTypeAllowed(uploadedFile.path, next); }, @@ -156,7 +159,10 @@ uploadsController.uploadThumb = function (req, res, next) { } if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: uploadedFile, uid: req.uid}, next); + return plugins.fireHook('filter:uploadImage', { + image: uploadedFile, + uid: req.uid + }, next); } uploadFile(req.uid, uploadedFile, next); @@ -167,11 +173,17 @@ uploadsController.uploadThumb = function (req, res, next) { uploadsController.uploadGroupCover = function (uid, uploadedFile, callback) { if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: uploadedFile, uid: uid}, callback); + return plugins.fireHook('filter:uploadImage', { + image: uploadedFile, + uid: uid + }, callback); } if (plugins.hasListeners('filter:uploadFile')) { - return plugins.fireHook('filter:uploadFile', {file: uploadedFile, uid: uid}, callback); + return plugins.fireHook('filter:uploadFile', { + file: uploadedFile, + uid: uid + }, callback); } file.isFileTypeAllowed(uploadedFile.path, function (err) { @@ -184,7 +196,10 @@ uploadsController.uploadGroupCover = function (uid, uploadedFile, callback) { function uploadFile(uid, uploadedFile, callback) { if (plugins.hasListeners('filter:uploadFile')) { - return plugins.fireHook('filter:uploadFile', {file: uploadedFile, uid: uid}, callback); + return plugins.fireHook('filter:uploadFile', { + file: uploadedFile, + uid: uid + }, callback); } if (!uploadedFile) { @@ -197,7 +212,7 @@ function uploadFile(uid, uploadedFile, callback) { if (meta.config.hasOwnProperty('allowedFileExtensions')) { var allowed = file.allowedExtensions(); - var extension = typeToExtension(uploadedFile.type); + var extension = file.typeToExtension(uploadedFile.type); if (!extension || (allowed.length > 0 && allowed.indexOf(extension) === -1)) { return callback(new Error('[[error:invalid-file-type, ' + allowed.join(', ') + ']]')); } @@ -207,7 +222,7 @@ function uploadFile(uid, uploadedFile, callback) { } function saveFileToLocal(uploadedFile, callback) { - var extension = typeToExtension(uploadedFile.type); + var extension = file.typeToExtension(uploadedFile.type); if (!extension) { return callback(new Error('[[error:invalid-extension]]')); } @@ -228,14 +243,6 @@ function saveFileToLocal(uploadedFile, callback) { }); } -function typeToExtension(type) { - var extension; - if (type) { - extension = '.' + mime.extension(type); - } - return extension; -} - function deleteTempFiles(files) { async.each(files, function (file, next) { fs.unlink(file.path, function (err) { @@ -247,6 +254,4 @@ function deleteTempFiles(files) { }); } - - module.exports = uploadsController; diff --git a/src/file.js b/src/file.js index 004643ef38..585a46dcaf 100644 --- a/src/file.js +++ b/src/file.js @@ -6,6 +6,7 @@ var path = require('path'); var winston = require('winston'); var jimp = require('jimp'); var mkdirp = require('mkdirp'); +var mime = require('mime'); var utils = require('../public/src/utils'); @@ -13,8 +14,8 @@ var file = {}; file.saveFileToLocal = function (filename, folder, tempPath, callback) { /* - * remarkable doesn't allow spaces in hyperlinks, once that's fixed, remove this. - */ + * remarkable doesn't allow spaces in hyperlinks, once that's fixed, remove this. + */ filename = filename.split('.'); filename.forEach(function (name, idx) { filename[idx] = utils.slugify(name); @@ -118,7 +119,8 @@ file.existsSync = function (path) { file.link = function link(filePath, destPath, cb) { if (process.platform === 'win32') { fs.link(filePath, destPath, cb); - } else { + } + else { fs.symlink(filePath, destPath, 'file', cb); } }; @@ -128,4 +130,12 @@ file.linkDirs = function linkDirs(sourceDir, destDir, callback) { fs.symlink(sourceDir, destDir, type, callback); }; +file.typeToExtension = function (type) { + var extension; + if (type) { + extension = '.' + mime.extension(type); + } + return extension; +}; + module.exports = file; diff --git a/src/groups/cover.js b/src/groups/cover.js index 3512a235d5..8f18c60ad7 100644 --- a/src/groups/cover.js +++ b/src/groups/cover.js @@ -10,6 +10,7 @@ var mime = require('mime'); var winston = require('winston'); var db = require('../database'); +var image = require('../image'); var uploadsController = require('../controllers/uploads'); module.exports = function (Groups) { @@ -37,7 +38,7 @@ module.exports = function (Groups) { if (tempPath) { return next(null, tempPath); } - writeImageDataToFile(data.imageData, next); + image.writeImageDataToTempFile(data.imageData, next); }, function (_tempPath, next) { tempPath = _tempPath; @@ -97,24 +98,6 @@ module.exports = function (Groups) { }); } - function writeImageDataToFile(imageData, callback) { - // Calculate md5sum of image - // This is required because user data can be private - var md5sum = crypto.createHash('md5'); - md5sum.update(imageData); - md5sum = md5sum.digest('hex'); - - // Save image - var tempPath = path.join(nconf.get('upload_path'), md5sum + '.png'); - var buffer = new Buffer(imageData.slice(imageData.indexOf('base64') + 7), 'base64'); - - fs.writeFile(tempPath, buffer, { - encoding: 'base64' - }, function (err) { - callback(err, tempPath); - }); - } - Groups.removeCover = function (data, callback) { db.deleteObjectFields('group:' + data.groupName, ['cover:url', 'cover:thumb:url', 'cover:position'], callback); }; diff --git a/src/image.js b/src/image.js index 0ab68e5a23..7b428f2331 100644 --- a/src/image.js +++ b/src/image.js @@ -1,8 +1,13 @@ 'use strict'; +var os = require('os'); var fs = require('fs'); +var path = require('path'); var Jimp = require('jimp'); var async = require('async'); +var crypto = require('crypto'); + +var file = require('./file'); var plugins = require('./plugins'); var image = module.exports; @@ -65,9 +70,6 @@ image.resizeImage = function (data, callback) { } }, function (image, next) { - if (data.write === false) { - return next(); - } image.write(data.target || data.path, next); } ], function (err) { @@ -83,7 +85,7 @@ image.normalise = function (path, extension, callback) { path: path, extension: extension }, function (err) { - callback(err); + callback(err, path + '.png'); }); } else { new Jimp(path, function (err, image) { @@ -91,7 +93,7 @@ image.normalise = function (path, extension, callback) { return callback(err); } image.write(path + '.png', function (err) { - callback(err); + callback(err, path + '.png'); }); }); } @@ -116,3 +118,28 @@ image.convertImageToBase64 = function (path, callback) { callback(err, data ? data.toString('base64') : null); }); }; + +image.mimeFromBase64 = function (imageData) { + return imageData.slice(5, imageData.indexOf('base64') - 1); +}; + +image.extensionFromBase64 = function (imageData) { + return file.typeToExtension(image.mimeFromBase64(imageData)); +}; + +image.writeImageDataToTempFile = function (imageData, callback) { + var filename = crypto.createHash('md5').update(imageData).digest('hex'); + + var type = image.mimeFromBase64(imageData); + var extension = file.typeToExtension(type); + + var filepath = path.join(os.tmpdir(), filename + extension); + + var buffer = new Buffer(imageData.slice(imageData.indexOf('base64') + 7), 'base64'); + + fs.writeFile(filepath, buffer, { + encoding: 'base64' + }, function (err) { + callback(err, filepath); + }); +}; \ No newline at end of file diff --git a/src/install.js b/src/install.js index 8c14302d80..c5ae1e214b 100644 --- a/src/install.js +++ b/src/install.js @@ -389,8 +389,8 @@ function createMenuItems(next) { if (err || exists) { return next(err); } - var navigation = require('./navigation/admin'), - data = require('../install/data/navigation.json'); + var navigation = require('./navigation/admin'); + var data = require('../install/data/navigation.json'); navigation.save(data, next); }); diff --git a/src/navigation/admin.js b/src/navigation/admin.js index 39258e6702..ab1c5aff42 100644 --- a/src/navigation/admin.js +++ b/src/navigation/admin.js @@ -1,14 +1,12 @@ "use strict"; - - var async = require('async'); var plugins = require('../plugins'); var db = require('../database'); var translator = require('../../public/src/modules/translator'); var pubsub = require('../pubsub'); -var admin = {}; +var admin = module.exports; admin.cache = null; pubsub.on('admin:navigation:save', function () { @@ -71,5 +69,3 @@ function getAvailable(callback) { plugins.fireHook('filter:navigation.available', core, callback); } - -module.exports = admin; \ No newline at end of file diff --git a/src/socket.io/topics/move.js b/src/socket.io/topics/move.js index 25f5f8a19d..938fe22792 100644 --- a/src/socket.io/topics/move.js +++ b/src/socket.io/topics/move.js @@ -23,9 +23,7 @@ module.exports = function (SocketTopics) { if (!canMove) { return next(new Error('[[error:no-privileges]]')); } - next(); - }, - function (next) { + topics.getTopicFields(tid, ['cid', 'slug'], next); }, function (_topicData, next) { diff --git a/src/socket.io/user.js b/src/socket.io/user.js index c10a510e71..060c9e7b2e 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -272,35 +272,34 @@ SocketUser.invite = function (socket, email, callback) { return callback(new Error('[[error:forum-not-invite-only]]')); } - var max = meta.config.maximumInvites; + async.waterfall([ + function (next) { + user.isAdministrator(socket.uid, next); + }, + function (isAdmin, next) { + if (registrationType === 'admin-invite-only' && !isAdmin) { + return next(new Error('[[error:no-privileges]]')); + } + + var max = parseInt(meta.config.maximumInvites, 10); + if (!max) { + return user.sendInvitationEmail(socket.uid, email, callback); + } - user.isAdministrator(socket.uid, function (err, admin) { - if (err) { - return callback(err); - } - if (registrationType === 'admin-invite-only' && !admin) { - return callback(new Error('[[error:no-privileges]]')); - } - if (max) { async.waterfall([ function (next) { user.getInvitesNumber(socket.uid, next); }, function (invites, next) { - if (!admin && invites > max) { + if (!isAdmin && invites >= max) { return next(new Error('[[error:invite-maximum-met, ' + invites + ', ' + max + ']]')); } - next(); - }, - function (next) { + user.sendInvitationEmail(socket.uid, email, next); } - ], callback); - } else { - user.sendInvitationEmail(socket.uid, email, callback); + ], next); } - }); - + ], callback); }; SocketUser.getUserByUID = function (socket, uid, callback) { diff --git a/src/start.js b/src/start.js index 0a8ed18e67..06ffc93589 100644 --- a/src/start.js +++ b/src/start.js @@ -99,6 +99,7 @@ function setupConfigs() { nconf.set('use_port', !!urlObject.port); nconf.set('relative_path', relativePath); nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); + nconf.set('upload_url', '/assets/uploads'); } function printStartupInfo() { diff --git a/src/user/invite.js b/src/user/invite.js index 1066a662ac..043bf62a7b 100644 --- a/src/user/invite.js +++ b/src/user/invite.js @@ -61,9 +61,7 @@ module.exports = function (User) { if (exists) { return next(new Error('[[error:email-taken]]')); } - next(); - }, - function (next) { + async.parallel([ function (next) { db.setAdd('invitation:uid:' + uid, email, next); @@ -131,11 +129,11 @@ module.exports = function (User) { return next(new Error('[[error:invalid-username]]')); } async.parallel([ - function deleteFromReferenceList(next) { - db.setRemove('invitation:uid:' + invitedByUid, email, next); + function (next) { + deleteFromReferenceList(invitedByUid, email, next); }, - function deleteInviteKey(next) { - db.delete('invitation:email:' + email, callback); + function (next) { + db.delete('invitation:email:' + email, next); } ], function (err) { next(err); @@ -146,7 +144,37 @@ module.exports = function (User) { User.deleteInvitationKey = function (email, callback) { callback = callback || function () {}; - db.delete('invitation:email:' + email, callback); + + async.waterfall([ + function (next) { + User.getInvitingUsers(next); + }, + function (uids, next) { + async.each(uids, function (uid, next) { + deleteFromReferenceList(uid, email, next); + }, next); + }, + function (next) { + db.delete('invitation:email:' + email, next); + } + ], callback); }; + function deleteFromReferenceList(uid, email, callback) { + async.waterfall([ + function (next) { + db.setRemove('invitation:uid:' + uid, email, next); + }, + function (next) { + db.setCount('invitation:uid:' + uid, next); + }, + function (count, next) { + if (count === 0) { + return db.setRemove('invitation:uids', uid, next); + } + setImmediate(next); + } + ], callback); + } + }; diff --git a/src/user/picture.js b/src/user/picture.js index bc0b8f597f..09b7d636e8 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -1,11 +1,8 @@ 'use strict'; var async = require('async'); -var path = require('path'); var fs = require('fs'); -var os = require('os'); var nconf = require('nconf'); -var crypto = require('crypto'); var winston = require('winston'); var request = require('request'); var mime = require('mime'); @@ -19,72 +16,7 @@ var db = require('../database'); module.exports = function (User) { User.uploadPicture = function (uid, picture, callback) { - - var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; - var extension = path.extname(picture.name); - var updateUid = uid; - var imageDimension = parseInt(meta.config.profileImageDimension, 10) || 128; - var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1; - var keepAllVersions = parseInt(meta.config['profile:keepAllUserImages'], 10) === 1; - var uploadedImage; - - if (parseInt(meta.config.allowProfileImageUploads) !== 1) { - return callback(new Error('[[error:profile-image-uploads-disabled]]')); - } - - if (picture.size > uploadSize * 1024) { - return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); - } - - if (!extension) { - return callback(new Error('[[error:invalid-image-extension]]')); - } - - async.waterfall([ - function (next) { - if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: picture, uid: updateUid}, next); - } - - var filename = updateUid + '-profileimg' + (keepAllVersions ? '-' + Date.now() : '') + (convertToPNG ? '.png' : extension); - - async.waterfall([ - function (next) { - file.isFileTypeAllowed(picture.path, next); - }, - function (next) { - image.resizeImage({ - path: picture.path, - extension: extension, - width: imageDimension, - height: imageDimension, - write: false, - }, next); - }, - function (next) { - if (!convertToPNG) { - return next(); - } - async.series([ - async.apply(image.normalise, picture.path, extension), - async.apply(fs.rename, picture.path + '.png', picture.path) - ], function (err) { - next(err); - }); - }, - function (next) { - file.saveFileToLocal(filename, 'profile', picture.path, next); - }, - ], next); - }, - function (_image, next) { - uploadedImage = _image; - User.setUserFields(updateUid, {uploadedpicture: uploadedImage.url, picture: uploadedImage.url}, next); - }, - function (next) { - next(null, uploadedImage); - } - ], callback); + User.uploadCroppedPicture({uid: uid, file: picture}, callback); }; User.uploadFromUrl = function (uid, url, callback) { @@ -92,32 +24,41 @@ module.exports = function (User) { return callback(new Error('[[error:no-plugin]]')); } - request.head(url, function (err, res) { - if (err) { - return callback(err); - } - var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; - var size = res.headers['content-length']; - var type = res.headers['content-type']; - var extension = mime.extension(type); + async.waterfall([ + function (next) { + request.head(url, next); + }, + function (res, body, next) { + var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; + var size = res.headers['content-length']; + var type = res.headers['content-type']; + var extension = mime.extension(type); - if (['png', 'jpeg', 'jpg', 'gif'].indexOf(extension) === -1) { - return callback(new Error('[[error:invalid-image-extension]]')); - } - - if (size > uploadSize * 1024) { - return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); - } - - var picture = {url: url, name: ''}; - plugins.fireHook('filter:uploadImage', {image: picture, uid: uid}, function (err, image) { - if (err) { - return callback(err); + if (['png', 'jpeg', 'jpg', 'gif'].indexOf(extension) === -1) { + return callback(new Error('[[error:invalid-image-extension]]')); } - User.setUserFields(uid, {uploadedpicture: image.url, picture: image.url}); - callback(null, image); - }); - }); + + if (size > uploadSize * 1024) { + return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); + } + + plugins.fireHook('filter:uploadImage', { + uid: uid, + image: { + url: url, + name: '' + } + }, next); + }, + function (image, next) { + User.setUserFields(uid, { + uploadedpicture: image.url, + picture: image.url + }, function (err) { + next(err, image); + }); + } + ], callback); }; User.updateCoverPosition = function (uid, position, callback) { @@ -125,8 +66,12 @@ module.exports = function (User) { }; User.updateCoverPicture = function (data, callback) { - var keepAllVersions = parseInt(meta.config['profile:keepAllUserImages'], 10) === 1; - var url, md5sum; + + var url; + var picture = { + name: 'profileCover', + uid: data.uid + }; if (!data.imageData && data.position) { return User.updateCoverPosition(data.uid, data.position, callback); @@ -145,161 +90,165 @@ module.exports = function (User) { } if (data.file) { - return next(); + return setImmediate(next, null, data.file.path); } - md5sum = crypto.createHash('md5'); - md5sum.update(data.imageData); - md5sum = md5sum.digest('hex'); - - data.file = { - path: path.join(os.tmpdir(), md5sum) - }; - - var buffer = new Buffer(data.imageData.slice(data.imageData.indexOf('base64') + 7), 'base64'); - - fs.writeFile(data.file.path, buffer, { - encoding: 'base64' - }, next); + image.writeImageDataToTempFile(data.imageData, next); }, - function (next) { - var image = { - name: 'profileCover', - path: data.file.path, - uid: data.uid - }; + function (path, next) { + picture.path = path; - if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: image, uid: data.uid}, next); - } - - var filename = data.uid + '-profilecover' + (keepAllVersions ? '-' + Date.now() : ''); - async.waterfall([ - function (next) { - file.isFileTypeAllowed(data.file.path, next); - }, - function (next) { - file.saveFileToLocal(filename, 'profile', image.path, next); - }, - function (upload, next) { - next(null, { - url: nconf.get('relative_path') + upload.url, - name: image.name - }); - } - ], next); + var extension = data.file ? file.typeToExtension(data.file.type) : image.extensionFromBase64(data.imageData); + var filename = generateProfileImageFilename(data.uid, 'profilecover', extension); + uploadProfileOrCover(filename, picture, next); }, function (uploadData, next) { url = uploadData.url; User.setUserField(data.uid, 'cover:url', uploadData.url, next); }, function (next) { - fs.unlink(data.file.path, function (err) { - if (err) { - winston.error(err); - } - next(); - }); + if (data.position) { + User.updateCoverPosition(data.uid, data.position, next); + } else { + setImmediate(next); + } } ], function (err) { - if (err) { - return fs.unlink(data.file.path, function (unlinkErr) { - if (unlinkErr) { - winston.error(unlinkErr); - } - - callback(err); // send back the original error - }); - } - - if (data.position) { - User.updateCoverPosition(data.uid, data.position, function (err) { - callback(err, {url: url}); - }); - } else { - callback(err, {url: url}); - } + deleteFile(picture.path); + callback(err, { + url: url + }); }); }; - - User.uploadCroppedPicture = function (data, callback) { - var keepAllVersions = parseInt(meta.config['profile:keepAllUserImages'], 10) === 1; - var url, md5sum; - if (!data.imageData) { + User.uploadCroppedPicture = function (data, callback) { + + if (parseInt(meta.config.allowProfileImageUploads) !== 1) { + return callback(new Error('[[error:profile-image-uploads-disabled]]')); + } + + if (!data.imageData && !data.file) { return callback(new Error('[[error:invalid-data]]')); } - + + var size = data.file ? data.file.size : data.imageData.length; + var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; + if (size > uploadSize * 1024) { + return callback(new Error('[[error:file-too-big, ' + meta.config.maximumProfileImageSize + ']]')); + } + + var type = data.file ? data.file.type : image.mimeFromBase64(data.imageData); + var extension = file.typeToExtension(type); + if (!extension) { + return callback(new Error('[[error:invalid-image-extension]]')); + } + + var uploadedImage; + + var picture = { + name: 'profileAvatar', + uid: data.uid + }; + async.waterfall([ function (next) { - var size = data.file ? data.file.size : data.imageData.length; - var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; - if (size > uploadSize * 1024) { - return next(new Error('[[error:file-too-big, ' + meta.config.maximumProfileImageSize + ']]')); + if (data.file) { + return setImmediate(next, null, data.file.path); } + image.writeImageDataToTempFile(data.imageData, next); + }, + function (path, next) { + convertToPNG(path, extension, next); + }, + function (path, next) { + picture.path = path; - md5sum = crypto.createHash('md5'); - md5sum.update(data.imageData); - md5sum = md5sum.digest('hex'); - - data.file = { - path: path.join(os.tmpdir(), md5sum) - }; - - var buffer = new Buffer(data.imageData.slice(data.imageData.indexOf('base64') + 7), 'base64'); - - fs.writeFile(data.file.path, buffer, { - encoding: 'base64' + var imageDimension = parseInt(meta.config.profileImageDimension, 10) || 128; + image.resizeImage({ + path: picture.path, + extension: extension, + width: imageDimension, + height: imageDimension }, next); }, function (next) { - var image = { - name: 'profileAvatar', - path: data.file.path, - uid: data.uid - }; - - if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: image, uid: data.uid}, next); - } - - var filename = data.uid + '-profileavatar' + (keepAllVersions ? '-' + Date.now() : ''); - async.waterfall([ - function (next) { - file.isFileTypeAllowed(data.file.path, next); - }, - function (next) { - file.saveFileToLocal(filename, 'profile', image.path, next); - }, - function (upload, next) { - next(null, { - url: nconf.get('relative_path') + upload.url, - name: image.name - }); - } - ], next); + var filename = generateProfileImageFilename(data.uid, 'profileavatar', extension); + uploadProfileOrCover(filename, picture, next); }, - function (uploadData, next) { - url = uploadData.url; - User.setUserFields(data.uid, {uploadedpicture: url, picture: url}, next); - }, - function (next) { - fs.unlink(data.file.path, function (err) { - if (err) { - winston.error(err); - } - next(); - }); + function (_uploadedImage, next) { + uploadedImage = _uploadedImage; + + User.setUserFields(data.uid, { + uploadedpicture: uploadedImage.url, + picture: uploadedImage.url + }, next); } ], function (err) { - if (err) { - callback(err); // send back the original error - } - - callback(err, {url: url}); + deleteFile(picture.path); + callback(err, uploadedImage); }); }; + function convertToPNG(path, extension, callback) { + var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1; + if (!convertToPNG) { + return setImmediate(callback, null, path); + } + + image.normalise(path, extension, function (err, newPath) { + if (err) { + return callback(err); + } + deleteFile(path); + callback(null, newPath); + }); + } + + function uploadProfileOrCover(filename, image, callback) { + if (plugins.hasListeners('filter:uploadImage')) { + return plugins.fireHook('filter:uploadImage', { + image: image, + uid: image.uid + }, callback); + } + + saveFileToLocal(filename, image, callback); + } + + function generateProfileImageFilename(uid, type, extension) { + var keepAllVersions = parseInt(meta.config['profile:keepAllUserImages'], 10) === 1; + var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1; + return uid + '-' + type + (keepAllVersions ? '-' + Date.now() : '') + (convertToPNG ? '.png' : extension); + } + + function saveFileToLocal(filename, image, callback) { + async.waterfall([ + function (next) { + file.isFileTypeAllowed(image.path, next); + }, + function (next) { + file.saveFileToLocal(filename, 'profile', image.path, next); + }, + function (upload, next) { + next(null, { + url: nconf.get('relative_path') + upload.url, + path: upload.path, + name: image.name + }); + } + ], callback); + } + + function deleteFile(path) { + if (path) { + fs.unlink(path, function (err) { + if (err) { + winston.error(err); + } + }); + } + } + User.removeCoverPicture = function (data, callback) { db.deleteObjectFields('user:' + data.uid, ['cover:url', 'cover:position'], callback); }; diff --git a/test/controllers.js b/test/controllers.js index 54578f76c3..42070e4c36 100644 --- a/test/controllers.js +++ b/test/controllers.js @@ -29,6 +29,12 @@ describe('Controllers', function () { }, user: function (next) { user.create({username: 'foo', password: 'barbar'}, next); + }, + navigation: function (next) { + var navigation = require('../src/navigation/admin'); + var data = require('../install/data/navigation.json'); + + navigation.save(data, next); } }, function (err, results) { if (err) { diff --git a/test/files/503.html b/test/files/503.html new file mode 100644 index 0000000000..119c710ab7 --- /dev/null +++ b/test/files/503.html @@ -0,0 +1,177 @@ + + ++ This forum is temporarily unavailable due to excessive load. +
++ We shouldn't be down for long. Please check back shortly. Sorry for the inconvenience! +
++ Alright. You can stop clicking... it's not going to make the site come back sooner! +
+