-
-
+
+
-
{{/if}}
- {{#each webdavNodes}}
-
- {{#with iconType}}
-
- {{>icon icon=icon}}
-
{{extension}}
-
- {{/with}}
-
-
-
{{this.basename}}
-
{{this.lastmod}}
-
-
-
- {{/each}}
+ {{#if isLoading}}
+
+ {{> loading}}
+
+ {{else}}
+ {{#each webdavNodes}}
+
+ {{#with iconType}}
+
+ {{>icon icon=icon}}
+
{{extension}}
+
+ {{/with}}
+
+
+
{{this.basename}}
+
{{this.lastmod}}
+
+
+
+ {{/each}}
+ {{/if}}
diff --git a/app/webdav/client/webdavFilePicker.js b/app/webdav/client/webdavFilePicker.js
index fc6edd3edfde..ccbd3919177c 100644
--- a/app/webdav/client/webdavFilePicker.js
+++ b/app/webdav/client/webdavFilePicker.js
@@ -4,6 +4,7 @@ import _ from 'underscore';
import toastr from 'toastr';
import { Session } from 'meteor/session';
import { Handlebars } from 'meteor/ui';
+import { ReactiveVar } from 'meteor/reactive-var';
import { modal, call } from '../../ui-utils';
import { t } from '../../utils';
@@ -18,10 +19,13 @@ Template.webdavFilePicker.rendered = async function() {
return toastr.error(t(response.message));
}
Session.set('webdavNodes', response.data);
+ this.isLoading.set(false);
};
+
Template.webdavFilePicker.destroyed = function() {
Session.set('webdavNodes', []);
};
+
Template.webdavFilePicker.helpers({
iconType() {
// add icon for different types
@@ -52,6 +56,9 @@ Template.webdavFilePicker.helpers({
}
return { icon, type, extension };
},
+ isLoading() {
+ return Template.instance().isLoading.get();
+ },
webdavNodes() {
return Session.get('webdavNodes');
},
@@ -59,9 +66,12 @@ Template.webdavFilePicker.helpers({
return Session.get('webdavCurrentFolder');
},
});
+
Template.webdavFilePicker.events({
async 'click #webdav-go-back'() {
- const { accountId } = Template.instance().data;
+ const instance = Template.instance();
+ const { accountId } = instance.data;
+ instance.isLoading.set(true);
let currentFolder = Session.get('webdavCurrentFolder');
// determine parent directory to go back
@@ -75,16 +85,20 @@ Template.webdavFilePicker.events({
Session.set('webdavCurrentFolder', parentFolder);
Session.set('webdavNodes', []);
const response = await call('getWebdavFileList', accountId, parentFolder);
+ instance.isLoading.set(false);
if (!response.success) {
return toastr.error(t(response.message));
}
Session.set('webdavNodes', response.data);
},
async 'click .webdav_directory'() {
- const { accountId } = Template.instance().data;
+ const instance = Template.instance();
+ const { accountId } = instance.data;
+ instance.isLoading.set(true);
Session.set('webdavCurrentFolder', this.filename);
Session.set('webdavNodes', []);
const response = await call('getWebdavFileList', accountId, this.filename);
+ instance.isLoading.set(false);
if (!response.success) {
modal.close();
return toastr.error(t(response.message));
@@ -93,10 +107,12 @@ Template.webdavFilePicker.events({
},
async 'click .webdav_file'() {
const roomId = Session.get('openedRoom');
- const { accountId } = Template.instance().data;
+ const instance = Template.instance();
+ const { accountId } = instance.data;
+ instance.isLoading.set(true);
const file = this;
const response = await call('getFileFromWebdav', accountId, file);
-
+ instance.isLoading.set(false);
if (!response.success) {
modal.close();
return toastr.error(t('Failed_to_get_webdav_file'));
@@ -106,15 +122,14 @@ Template.webdavFilePicker.events({
blob.lastModified = file.lastmod;
blob.name = file.basename;
const text = `
-
`;
-
+
`;
return modal.open({
title: t('Upload_file_question'),
text,
@@ -200,3 +215,7 @@ Template.webdavFilePicker.events({
});
},
});
+
+Template.webdavFilePicker.onCreated(function() {
+ this.isLoading = new ReactiveVar(true);
+});
diff --git a/client/importPackages.js b/client/importPackages.js
index 8277767b6a28..8ff3715321b9 100644
--- a/client/importPackages.js
+++ b/client/importPackages.js
@@ -98,6 +98,7 @@ import '../app/lazy-load';
import '../app/discussion/client';
import '../app/threads/client';
import '../app/mail-messages/client';
+import '../app/user-status';
import '../app/utils';
import '../app/settings';
import '../app/models';
diff --git a/client/startup/usersObserve.js b/client/startup/usersObserve.js
index ef705bc2e3d5..d9456fd2eb62 100644
--- a/client/startup/usersObserve.js
+++ b/client/startup/usersObserve.js
@@ -4,13 +4,15 @@ import { Session } from 'meteor/session';
import { RoomManager } from '../../app/ui-utils';
Meteor.startup(function() {
- Meteor.users.find({}, { fields: { name: 1, username: 1, status: 1, utcOffset: 1 } }).observe({
+ Meteor.users.find({}, { fields: { name: 1, username: 1, status: 1, utcOffset: 1, statusText: 1 } }).observe({
added(user) {
Session.set(`user_${ user.username }_status`, user.status);
+ Session.set(`user_${ user.username }_status_text`, user.statusText);
RoomManager.updateUserStatus(user, user.status, user.utcOffset);
},
changed(user) {
Session.set(`user_${ user.username }_status`, user.status);
+ Session.set(`user_${ user.username }_status_text`, user.statusText);
RoomManager.updateUserStatus(user, user.status, user.utcOffset);
},
removed(user) {
diff --git a/imports/startup/client/listenActiveUsers.js b/imports/startup/client/listenActiveUsers.js
index bddc19c97133..19a33ff66632 100644
--- a/imports/startup/client/listenActiveUsers.js
+++ b/imports/startup/client/listenActiveUsers.js
@@ -25,6 +25,7 @@ const saveUser = (user, force = false) => {
// name: user.name,
// utcOffset: user.utcOffset,
status: user.status,
+ statusText: user.statusText,
},
});
}
diff --git a/package-lock.json b/package-lock.json
index 672f1a1c6ad8..d39f8545b291 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "Rocket.Chat",
- "version": "1.1.0-develop",
+ "version": "1.2.0-develop",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -4749,7 +4749,7 @@
},
"colors": {
"version": "1.1.2",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
"dev": true
},
@@ -7974,7 +7974,7 @@
},
"get-stream": {
"version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"get-value": {
@@ -11975,7 +11975,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
@@ -11997,7 +11997,7 @@
},
"string_decoder": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
@@ -12146,7 +12146,7 @@
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -12922,7 +12922,7 @@
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
- "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+ "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=",
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
@@ -13589,7 +13589,7 @@
},
"tough-cookie": {
"version": "2.3.4",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
+ "resolved": "http://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
"integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
"dev": true,
"requires": {
@@ -14742,7 +14742,7 @@
},
"pify": {
"version": "2.3.0",
- "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
}
diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json
index a77af632509b..910541eefa67 100644
--- a/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -38,6 +38,7 @@
"Accounts_AllowUserAvatarChange": "Allow User Avatar Change",
"Accounts_AllowUsernameChange": "Allow Username Change",
"Accounts_AllowUserProfileChange": "Allow User Profile Change",
+ "Accounts_AllowUserStatusMessageChange": "Allow Custom Status Message",
"Accounts_AvatarBlockUnauthenticatedAccess": "Block Unauthenticated Access to Avatars",
"Accounts_AvatarCacheTime": "Avatar cache time",
"Accounts_AvatarCacheTime_description": "Number of seconds the http protocol is told to cache the avatar images.",
@@ -973,6 +974,15 @@
"Custom_Sounds": "Custom Sounds",
"Custom_Translations": "Custom Translations",
"Custom_Translations_Description": "Should be a valid JSON where keys are languages containing a dictionary of key and translations. Example:
{\n \"en\": {\n \"Channels\": \"Rooms\"\n },\n \"pt\": {\n \"Channels\": \"Salas\"\n }\n}
",
+ "Custom_User_Status": "Custom User Status",
+ "Custom_User_Status_Add": "Add Custom User Status",
+ "Custom_User_Status_Added_Successfully" : "Custom User Status Added Successfully",
+ "Custom_User_Status_Delete_Warning": "Deleting a Custom User Status cannot be undone.",
+ "Custom_User_Status_Error_Invalid_User_Status": "Invalid User Status",
+ "Custom_User_Status_Error_Name_Already_In_Use": "The Custom User Status Name is already in use.",
+ "Custom_User_Status_Has_Been_Deleted": "Custom User Status Has Been Deleted",
+ "Custom_User_Status_Info": "Custom User Status Info",
+ "Custom_User_Status_Updated_Successfully": "Custom User Status Updated Successfully",
"Customize": "Customize",
"CustomSoundsFilesystem": "Custom Sounds Filesystem",
"Dashboard": "Dashboard",
@@ -1112,6 +1122,7 @@
"E2E_password_reveal_text": "You can now create encrypted private groups and direct messages. You may also change existing private groups or DMs to encrypted.
This is end to end encryption so the key to encode/decode your messages will not be saved on the server. For that reason you need to store this password somewhere safe. You will be required to enter it on other devices you wish to use e2e encryption on.
Learn more here!Your password is:
%sThis is an auto generated password, you can setup a new password for your encryption key any time from any browser you have entered the existing password.
This password is only stored on this browser until you store the password and dismiss this message.",
"E2E_password_request_text": "To access your encrypted private groups and direct messages, enter your encryption password.
You need to enter this password to encode/decode your messages on every client you use, since the key is not stored on the server.",
"Edit": "Edit",
+ "Edit_Status": "Edit Status",
"edit-message": "Edit Message",
"edit-message_description": "Permission to edit a message within a room",
"edit-other-user-active-status": "Edit Other User Active Status",
@@ -1480,6 +1491,7 @@
"Group_favorites": "Group favorites",
"Group_mentions_disabled_x_members": "Group mentions `@all` and `@here` have been disabled for rooms with more than __total__ members.",
"Group_mentions_only": "Group mentions only",
+ "Group_subscriptions": "Group subscriptions",
"Guest_Pool": "Guest Pool",
"Hash": "Hash",
"Header": "Header",
@@ -1941,6 +1953,8 @@
"manage-own-integrations_description": "Permition to allow users to create and edit their own integration or webhooks",
"manage-sounds": "Manage Sounds",
"manage-sounds_description": "Permission to manage the server sounds",
+ "manage-user-status": "Manage User Status",
+ "manage-user-status_description": "Permission to manage the server custom user statuses",
"Manage_Apps": "Manage Apps",
"Manage_the_App": "Manage the App",
"Manager_added": "Manager added",
@@ -2321,6 +2335,7 @@
"Preparing_list_of_channels": "Preparing list of channels",
"Preparing_list_of_messages": "Preparing list of messages",
"Preparing_list_of_users": "Preparing list of users",
+ "Presence": "Presence",
"preview-c-room": "Preview Public Channel",
"preview-c-room_description": "Permission to view the contents of a public channel before joining",
"Previous_month": "Previous Month",
@@ -2590,6 +2605,7 @@
"Search_Page_Size": "Page Size",
"Search_Private_Groups": "Search Private Groups",
"Search_Provider": "Search Provider",
+ "Search_ServiceAccounts": "Search Service Accounts",
"Search_Users": "Search Users",
"seconds": "seconds",
"Secret_token": "Secret Token",
@@ -2634,6 +2650,7 @@
"Server_Type": "Server Type",
"Service": "Service",
"Service_account": "Service Account",
+ "Service_accounts": "Service Accounts",
"Service_account_key": "Service account key",
"Service_account_applied": "Service Accounts approval applications",
"Service_account_created_successfully": "Service Account created successfully",
@@ -2641,6 +2658,7 @@
"Service_account_description": "Service Accounts are an upgrade to existing user accounts. You can connect to a large number of users using service account with exclusive features such as broadcast message to all your subscribers at once",
"Service_account_limit": "Service Accounts per user",
"Service_account_login": "Service Account login",
+ "Service_Accounts_SearchFields": "Fields to consider for service account search",
"Service_account_name_placeholder": "Service Account name",
"Service_account_username_placeholder": "Service Account username",
"Service_account_title": "Create a new Service Account",
@@ -2709,6 +2727,8 @@
"Slash_Shrug_Description": "Displays ¯\\_(ツ)_/¯ after your message",
"Slash_Tableflip_Description": "Displays (╯°□°)╯︵ ┻━┻",
"Slash_TableUnflip_Description": "Displays ┬─┬ ノ( ゜-゜ノ)",
+ "Slash_Status_Description": "Set your status message",
+ "Slash_Status_Params": "Status message",
"Slash_Topic_Description": "Set topic",
"Slash_Topic_Params": "Topic message",
"Smarsh_Email": "Smarsh Email",
@@ -2783,6 +2803,11 @@
"Stats_Total_Uploads": "Total Uploads",
"Stats_Total_Uploads_Size": "Total Uploads Size",
"Status": "Status",
+ "StatusMessage": "Status Message",
+ "StatusMessage_Change_Disabled": "Your Rocket.Chat administrator has disabled the changing of status messages",
+ "StatusMessage_Changed_Successfully": "Status message changed successfully.",
+ "StatusMessage_Placeholder": "What are you doing right now?",
+ "StatusMessage_Too_Long": "Status message must be shorter than 120 characters.",
"Step": "Step",
"Stop_Recording": "Stop Recording",
"Store_Last_Message": "Store Last Message",
@@ -3098,6 +3123,7 @@
"UserData_ProcessingFrequency": "Processing Frequency (Minutes)",
"UserDataDownload": "User Data Download",
"UserDataDownload_CompletedRequestExisted_Text": "Your data file was already generated. Check your email account for the download link.",
+ "UserDataDownload_CompletedRequestExistedWithLink_Text": "Your data file was already generated. Click
here to download it.",
"UserDataDownload_EmailBody": "Your data file is now ready to download. Click
here to download it.",
"UserDataDownload_EmailSubject": "Your Data File is Ready to Download",
"UserDataDownload_Requested": "Download File Requested",
diff --git a/packages/rocketchat-i18n/i18n/es.i18n.json b/packages/rocketchat-i18n/i18n/es.i18n.json
index af4322965026..454970fdead0 100644
--- a/packages/rocketchat-i18n/i18n/es.i18n.json
+++ b/packages/rocketchat-i18n/i18n/es.i18n.json
@@ -38,6 +38,7 @@
"Accounts_AllowUserAvatarChange": "Permitir al Usuario modificar su Avatar",
"Accounts_AllowUsernameChange": "Permitir el Cambio de Nombre de Usuario",
"Accounts_AllowUserProfileChange": "Permitir al Usuario modificar su Perfil",
+ "Accounts_AllowUserStatusMessageChange": "Permitir cambio de mensaje de estado",
"Accounts_AvatarCacheTime": "Tiempo de caché de Avatar",
"Accounts_AvatarCacheTime_description": "Número de segundos que se le dice al protocolo http para almacenar en caché las imágenes de avatar.",
"Accounts_AvatarResize": "Cambiar el Tamaño de los Avatars",
@@ -2377,6 +2378,8 @@
"Slash_Shrug_Description": "Muestra ¯ \\ _ (ツ) _ / ¯ después de su mensaje",
"Slash_Tableflip_Description": "Muestra ° (╯ ° □ °) ╯( ┻━┻",
"Slash_TableUnflip_Description": "Muestra ┬─┬ ノ (゜ - ゜ ノ)",
+ "Slash_Status_Description": "Configura tu mensaje de estado",
+ "Slash_Status_Params": "Mensaje de estado",
"Slash_Topic_Description": "Establecer tema",
"Slash_Topic_Params": "Mensaje del tema",
"Smarsh_Email": "Smarsh Email",
@@ -2444,6 +2447,11 @@
"Stats_Total_Rooms": "Total de Salas",
"Stats_Total_Users": "Total de Usuarios",
"Status": "Estado",
+ "StatusMessage": "Mensaje de estado",
+ "StatusMessage_Change_Disabled": "Tu administrador de Rocket.Chat ha desactivado el cambio de mensajes de estado",
+ "StatusMessage_Changed_Successfully": "Mensaje de estado cambiado correctamente.",
+ "StatusMessage_Placeholder": "¿Qué estás haciendo ahora?",
+ "StatusMessage_Too_Long": "El mensaje de estado debe tener menos de 120 caracteres.",
"Step": "Paso",
"Stop_Recording": "Detener Grabacion",
"Store_Last_Message": "Almacenar el último mensaje",
diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
index 6b293a90110d..ea5ec5fb0564 100644
--- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
+++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
@@ -2280,6 +2280,7 @@
"Preparing_list_of_channels": "Preparando lista de canais",
"Preparing_list_of_messages": "Preparando lista de mensagens",
"Preparing_list_of_users": "Preparando lista de usuários",
+ "Presence": "Presença",
"preview-c-room": "Pré-visualizar Canal público",
"preview-c-room_description": "Permissão para visualizar o conteúdo de um canal público antes de se juntar",
"Previous_month": "Mês anterior",
diff --git a/server/importPackages.js b/server/importPackages.js
index 95dd7e535d7d..41f928f7a50c 100644
--- a/server/importPackages.js
+++ b/server/importPackages.js
@@ -82,6 +82,7 @@ import '../app/slashcommands-leave';
import '../app/slashcommands-me';
import '../app/slashcommands-msg';
import '../app/slashcommands-mute';
+import '../app/slashcommands-status';
import '../app/slashcommands-topic/server';
import '../app/slashcommands-unarchiveroom/server';
import '../app/smarsh-connector';
@@ -106,6 +107,7 @@ import '../app/chatpal-search/server';
import '../app/discussion/server';
import '../app/bigbluebutton';
import '../app/mail-messages/server';
+import '../app/user-status';
import '../app/utils';
import '../app/settings';
import '../app/models';
diff --git a/server/methods/browseChannels.js b/server/methods/browseChannels.js
index 2d8d9ffb3c7f..53b09e32755a 100644
--- a/server/methods/browseChannels.js
+++ b/server/methods/browseChannels.js
@@ -5,6 +5,7 @@ import s from 'underscore.string';
import { hasPermission } from '../../app/authorization';
import { Rooms, Users } from '../../app/models';
import { Federation } from '../../app/federation/server';
+import { settings } from '../../app/settings/server';
const sortChannels = function(field, direction) {
switch (field) {
@@ -28,11 +29,20 @@ const sortUsers = function(field, direction) {
}
};
+const sortServiceAccounts = function(field, direction) {
+ switch (field) {
+ default:
+ return {
+ [field]: direction === 'asc' ? 1 : -1,
+ };
+ }
+};
+
Meteor.methods({
browseChannels({ text = '', workspace = '', type = 'channels', sortBy = 'name', sortDirection = 'asc', page, offset, limit = 10 }) {
const regex = new RegExp(s.trim(s.escapeRegExp(text)), 'i');
- if (!['channels', 'users'].includes(type)) {
+ if (!['channels', 'users', 'serviceAccounts'].includes(type)) {
return;
}
@@ -57,11 +67,13 @@ Meteor.methods({
limit,
};
+ const canViewAnonymous = settings.get('Accounts_AllowAnonymousRead') === true;
+
const user = Meteor.user();
if (type === 'channels') {
const sort = sortChannels(sortBy, sortDirection);
- if (!hasPermission(user._id, 'view-c-room')) {
+ if ((!user && !canViewAnonymous) || (user && !hasPermission(user._id, 'view-c-room'))) {
return;
}
@@ -85,6 +97,45 @@ Meteor.methods({
};
}
+ if (type === 'serviceAccounts') {
+ const options = {
+ ...pagination,
+ sort: sortServiceAccounts(sortBy, sortDirection),
+ fields: {
+ username: 1,
+ name: 1,
+ createdAt: 1,
+ description: 1,
+ federation: 1,
+ },
+ };
+
+ const exceptions = [user.username];
+ const forcedSearchFields = workspace === 'all' && ['username', 'name', 'description'];
+
+ let result;
+ if (workspace === 'all') {
+ result = Users.findByActiveServiceAccountsExcept(text, exceptions, forcedSearchFields, options);
+ } else if (workspace === 'external') {
+ result = Users.findByActiveExternalServiceAccountsExcept(text, exceptions, options, forcedSearchFields, Federation.localIdentifier);
+ } else {
+ result = Users.findByActiveLocalServiceAccountsExcept(text, exceptions, options, forcedSearchFields, Federation.localIdentifier);
+ }
+ const total = result.count();
+ const results = result.fetch();
+ results.forEach((account) => {
+ account.subscribers = Rooms.findDirectRoomContainingUsername(account.username).count();
+ });
+ return {
+ total,
+ results,
+ };
+ }
+ // non-logged id user
+ if (!user) {
+ return;
+ }
+
// type === users
if (!hasPermission(user._id, 'view-outside-room') || !hasPermission(user._id, 'view-d-room')) {
return;
diff --git a/server/methods/createDirectMessage.js b/server/methods/createDirectMessage.js
index 2fb895435984..e4f022033686 100644
--- a/server/methods/createDirectMessage.js
+++ b/server/methods/createDirectMessage.js
@@ -109,6 +109,10 @@ Meteor.methods({
upsertSubscription.$set.archived = true;
}
+ if (to.u !== undefined) {
+ upsertSubscription.$set.sa = true;
+ }
+
Subscriptions.upsert({
rid,
$and: [{ 'u._id': me._id }], // work around to solve problems with upsert and dot
diff --git a/server/methods/requestDataDownload.js b/server/methods/requestDataDownload.js
index 604d68c72ec9..255def67976a 100644
--- a/server/methods/requestDataDownload.js
+++ b/server/methods/requestDataDownload.js
@@ -1,9 +1,10 @@
import fs from 'fs';
import path from 'path';
+import mkdirp from 'mkdirp';
import { Meteor } from 'meteor/meteor';
-import { ExportOperations } from '../../app/models';
+import { ExportOperations, UserDataFiles } from '../../app/models';
import { settings } from '../../app/settings';
let tempFolder = '/tmp/userData';
@@ -25,30 +26,42 @@ Meteor.methods({
yesterday.setUTCDate(yesterday.getUTCDate() - 1);
if (lastOperation.createdAt > yesterday) {
+ if (lastOperation.status === 'completed') {
+ const lastFile = UserDataFiles.findLastFileByUser(userId);
+ if (lastFile) {
+ return {
+ requested: false,
+ exportOperation: lastOperation,
+ url: lastFile.url,
+ };
+ }
+ }
+
return {
requested: false,
exportOperation: lastOperation,
+ url: null,
};
}
}
if (!fs.existsSync(tempFolder)) {
- fs.mkdirSync(tempFolder);
+ mkdirp.sync(tempFolder);
}
const subFolderName = fullExport ? 'full' : 'partial';
const baseFolder = path.join(tempFolder, userId);
if (!fs.existsSync(baseFolder)) {
- fs.mkdirSync(baseFolder);
+ mkdirp.sync(baseFolder);
}
const folderName = path.join(baseFolder, subFolderName);
if (!fs.existsSync(folderName)) {
- fs.mkdirSync(folderName);
+ mkdirp.sync(folderName);
}
const assetsFolder = path.join(folderName, 'assets');
if (!fs.existsSync(assetsFolder)) {
- fs.mkdirSync(assetsFolder);
+ mkdirp.sync(assetsFolder);
}
const exportOperation = {
@@ -67,6 +80,7 @@ Meteor.methods({
return {
requested: true,
exportOperation,
+ url: null,
};
},
});
diff --git a/server/methods/saveUserPreferences.js b/server/methods/saveUserPreferences.js
index f9e0d6c97683..cc3fc86a8f08 100644
--- a/server/methods/saveUserPreferences.js
+++ b/server/methods/saveUserPreferences.js
@@ -38,6 +38,7 @@ Meteor.methods({
sidebarHideAvatar: Match.Optional(Boolean),
sidebarGroupByType: Match.Optional(Boolean),
sidebarShowDiscussion: Match.Optional(Boolean),
+ sidebarShowServiceAccounts: Match.Optional(Boolean),
muteFocusedConversations: Match.Optional(Boolean),
};
check(settings, Match.ObjectIncluding(keys));
diff --git a/server/methods/saveUserProfile.js b/server/methods/saveUserProfile.js
index 788ee9281d04..2fdf5e5fafdb 100644
--- a/server/methods/saveUserProfile.js
+++ b/server/methods/saveUserProfile.js
@@ -49,6 +49,10 @@ Meteor.methods({
Meteor.call('setUsername', settings.username);
}
+ if (settings.statusText || settings.statusText === '') {
+ Meteor.call('setUserStatus', null, settings.statusText);
+ }
+
if (settings.email) {
if (!checkPassword(user, settings.typedPassword)) {
throw new Meteor.Error('error-invalid-password', 'Invalid password', {
diff --git a/server/publications/activeUsers.js b/server/publications/activeUsers.js
index c6e33e283517..0256c5a37215 100644
--- a/server/publications/activeUsers.js
+++ b/server/publications/activeUsers.js
@@ -13,6 +13,7 @@ Meteor.publish('activeUsers', function() {
name: 1,
status: 1,
utcOffset: 1,
+ statusText: 1,
},
});
});
diff --git a/server/publications/subscription.js b/server/publications/subscription.js
index 182de0eaf38b..aead3afcbea9 100644
--- a/server/publications/subscription.js
+++ b/server/publications/subscription.js
@@ -18,6 +18,7 @@ const fields = {
roles: 1,
unread: 1,
prid: 1,
+ sa: 1,
userMentions: 1,
groupMentions: 1,
archived: 1,
diff --git a/tests/end-to-end/api/00-miscellaneous.js b/tests/end-to-end/api/00-miscellaneous.js
index 3a6773fa2bac..91e09e3ca510 100644
--- a/tests/end-to-end/api/00-miscellaneous.js
+++ b/tests/end-to-end/api/00-miscellaneous.js
@@ -141,6 +141,7 @@ describe('miscellaneous', function() {
'sidebarGroupByType',
'muteFocusedConversations',
'sidebarShowDiscussion',
+ 'sidebarShowServiceAccounts',
];
expect(res.body).to.have.property('success', true);
diff --git a/tests/end-to-end/api/01-users.js b/tests/end-to-end/api/01-users.js
index b63ad391ff47..b16f0a15ca5d 100644
--- a/tests/end-to-end/api/01-users.js
+++ b/tests/end-to-end/api/01-users.js
@@ -495,6 +495,7 @@ describe('[Users]', function() {
updateSetting('Accounts_AllowUserProfileChange', true)
.then(() => updateSetting('Accounts_AllowUsernameChange', true))
.then(() => updateSetting('Accounts_AllowRealNameChange', true))
+ .then(() => updateSetting('Accounts_AllowUserStatusMessageChange', true))
.then(() => updateSetting('Accounts_AllowEmailChange', true))
.then(() => updateSetting('Accounts_AllowPasswordChange', true))
.then(done);
@@ -503,6 +504,7 @@ describe('[Users]', function() {
updateSetting('Accounts_AllowUserProfileChange', true)
.then(() => updateSetting('Accounts_AllowUsernameChange', true))
.then(() => updateSetting('Accounts_AllowRealNameChange', true))
+ .then(() => updateSetting('Accounts_AllowUserStatusMessageChange', true))
.then(() => updateSetting('Accounts_AllowEmailChange', true))
.then(() => updateSetting('Accounts_AllowPasswordChange', true))
.then(done);
@@ -662,6 +664,50 @@ describe('[Users]', function() {
});
});
+ it('should return an error when trying update user status message and it is not allowed', (done) => {
+ updatePermission('edit-other-user-info', ['user']).then(() => {
+ updateSetting('Accounts_AllowUserStatusMessageChange', false)
+ .then(() => {
+ request.post(api('users.update'))
+ .set(credentials)
+ .send({
+ userId: targetUser._id,
+ data: {
+ statusMessage: 'a new status',
+ },
+ })
+ .expect('Content-Type', 'application/json')
+ .expect(400)
+ .expect((res) => {
+ expect(res.body).to.have.property('success', false);
+ })
+ .end(done);
+ });
+ });
+ });
+
+ it('should update user status message when the required permission is applied', (done) => {
+ updatePermission('edit-other-user-info', ['admin']).then(() => {
+ updateSetting('Accounts_AllowUserStatusMessageChange', false)
+ .then(() => {
+ request.post(api('users.update'))
+ .set(credentials)
+ .send({
+ userId: targetUser._id,
+ data: {
+ name: 'a new status',
+ },
+ })
+ .expect('Content-Type', 'application/json')
+ .expect(200)
+ .expect((res) => {
+ expect(res.body).to.have.property('success', true);
+ })
+ .end(done);
+ });
+ });
+ });
+
it('should return an error when trying update user email and it is not allowed', (done) => {
updatePermission('edit-other-user-info', ['user']).then(() => {
updateSetting('Accounts_AllowEmailChange', false)
diff --git a/tests/pageobjects/administration.page.js b/tests/pageobjects/administration.page.js
index 15a3a6746010..1629e980a9bd 100644
--- a/tests/pageobjects/administration.page.js
+++ b/tests/pageobjects/administration.page.js
@@ -364,6 +364,10 @@ class Administration extends Page {
get accountsRealNameChangeFalse() { return browser.element('label:nth-of-type(2) [name="Accounts_AllowRealNameChange"]'); }
+ get accountsUserStatusMessageChangeTrue() { return browser.element('label:nth-of-type(1) [name="Accounts_AllowUserStatusMessageChange"]'); }
+
+ get accountsUserStatusMessageChangeFalse() { return browser.element('label:nth-of-type(2) [name="Accounts_AllowUserStatusMessageChange"]'); }
+
get accountsUsernameChangeTrue() { return browser.element('label:nth-of-type(1) [name="Accounts_AllowUsernameChange"]'); }
get accountsUsernameChangeFalse() { return browser.element('label:nth-of-type(2) [name="Accounts_AllowUsernameChange"]'); }