From c0a0dd8bb9010c73e35b76df0f40c7d413ac532f Mon Sep 17 00:00:00 2001 From: Alex Brazier Date: Sun, 2 Apr 2017 23:41:06 +0100 Subject: [PATCH 1/4] Add shield.svg api route --- packages/rocketchat-api/server/v1/misc.js | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/rocketchat-api/server/v1/misc.js b/packages/rocketchat-api/server/v1/misc.js index 38176417f88b..f279e1bb1fb3 100644 --- a/packages/rocketchat-api/server/v1/misc.js +++ b/packages/rocketchat-api/server/v1/misc.js @@ -31,3 +31,54 @@ RocketChat.API.v1.addRoute('me', { authRequired: true }, { ])); } }); + +RocketChat.API.v1.addRoute('shield.svg', { authRequired: false }, { + get() { + const { type, channel, name } = this.queryParams; + let text; + switch (type) { + case 'online': + const count = RocketChat.models.Users.findUsersNotOffline().count(); + text = `${ count } Online`; + break; + case 'channel': + if (!channel) { + return RocketChat.API.v1.failure('Shield channel is required for type "channel"'); + } + text = `#${ channel }`; + break; + default: + text = 'JOIN CHAT'; + } + const leftSize = name ? name.length * 6 + 32 : 24; + const rightSize = text.length * 6 + 20; + const width = leftSize + rightSize; + const height = 20; + return { + headers: { 'Content-Type': 'image/svg+xml;charset=utf-8' }, + body: ` + + + + + + + + + + + + + + + + ${ name ? `${ name } + ${ name }` : '' } + ${ text } + ${ text } + + + `.trim().replace(/\>[\s]+\<') + }; + } +}); From 14f550928a8fe1ee9316cefacdd2b37a6d4f4784 Mon Sep 17 00:00:00 2001 From: Alex Brazier Date: Mon, 3 Apr 2017 10:30:50 +0100 Subject: [PATCH 2/4] Add admin option to control which shields are available --- packages/rocketchat-api/server/settings.js | 2 ++ packages/rocketchat-api/server/v1/misc.js | 22 +++++++++++++++++----- packages/rocketchat-i18n/i18n/en.i18n.json | 4 ++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/rocketchat-api/server/settings.js b/packages/rocketchat-api/server/settings.js index c70198246deb..53f021f789c7 100644 --- a/packages/rocketchat-api/server/settings.js +++ b/packages/rocketchat-api/server/settings.js @@ -4,6 +4,8 @@ RocketChat.settings.addGroup('General', function() { this.add('API_Default_Count', 50, { type: 'int', public: false }); this.add('API_Allow_Infinite_Count', true, { type: 'boolean', public: false }); this.add('API_Enable_Direct_Message_History_EndPoint', false, { type: 'boolean', public: false }); + this.add('API_Enable_Shields', true, { type: 'boolean', public: false }); + this.add('API_Shield_Types', '*', { type: 'string', public: false, enableQuery: { _id: 'API_Enable_Shields', value: true } }); this.add('API_Enable_CORS', false, { type: 'boolean', public: false }); this.add('API_CORS_Origin', '*', { type: 'string', public: false, enableQuery: { _id: 'API_Enable_CORS', value: true } }); }); diff --git a/packages/rocketchat-api/server/v1/misc.js b/packages/rocketchat-api/server/v1/misc.js index f279e1bb1fb3..a870b012edf4 100644 --- a/packages/rocketchat-api/server/v1/misc.js +++ b/packages/rocketchat-api/server/v1/misc.js @@ -34,7 +34,18 @@ RocketChat.API.v1.addRoute('me', { authRequired: true }, { RocketChat.API.v1.addRoute('shield.svg', { authRequired: false }, { get() { - const { type, channel, name } = this.queryParams; + const { type, channel, name, icon } = this.queryParams; + if (!RocketChat.settings.get('API_Enable_Shields')) { + throw new Meteor.Error('error-endpoint-disabled', 'This endpoint is disabled', { route: '/api/v1/shields.svg' }); + } + const types = RocketChat.settings.get('API_Shield_Types'); + if (type && (types !== '*' && !types.split(',').map((t) => t.trim()).includes(type))) { + throw new Meteor.Error('error-shield-disabled', 'This shield type is disabled', { route: '/api/v1/shields.svg' }); + } + const hideIcon = icon === 'false'; + if (hideIcon && (!name || !name.trim())) { + return RocketChat.API.v1.failure('Name cannot be empty when icon is hidden'); + } let text; switch (type) { case 'online': @@ -50,7 +61,8 @@ RocketChat.API.v1.addRoute('shield.svg', { authRequired: false }, { default: text = 'JOIN CHAT'; } - const leftSize = name ? name.length * 6 + 32 : 24; + const iconSize = hideIcon ? 7 : 24; + const leftSize = name ? name.length * 6 + 7 + iconSize : iconSize; const rightSize = text.length * 6 + 20; const width = leftSize + rightSize; const height = 20; @@ -70,10 +82,10 @@ RocketChat.API.v1.addRoute('shield.svg', { authRequired: false }, { - + ${ hideIcon ? '' : '' } - ${ name ? `${ name } - ${ name }` : '' } + ${ name ? `${ name } + ${ name }` : '' } ${ text } ${ text } diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 6057e02758c1..13bddef7d37c 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -182,9 +182,13 @@ "API_Enable_CORS": "Enable CORS", "API_Enable_Direct_Message_History_EndPoint": "Enable Direct Message History Endpoint", "API_Enable_Direct_Message_History_EndPoint_Description": "This enables the `/api/v1/im.history.others` which allows the viewing of direct messages sent by other users that the caller is not part of.", + "API_Enable_Shields": "Enable Shields", + "API_Enable_Shields_Description": "Enable shields available at `/api/v1/shields.svg`", "API_GitHub_Enterprise_URL": "Server URL", "API_GitHub_Enterprise_URL_Description": "Example: http://domain.com (excluding trailing slash)", "API_Gitlab_URL": "GitLab URL", + "API_Shield_Types": "Shield Types", + "API_Shield_Types_Description": "Types of shields to enable as a comma separated list, choose from `online`, `channel` or `*` for all", "API_Token": "API Token", "API_Upper_Count_Limit": "Max Record Amount", "API_Upper_Count_Limit_Description": "What is the maximum number of records the REST API should return (when not unlimited)?", From 6555fe42cf11138239f343b30c54920be373f2e4 Mon Sep 17 00:00:00 2001 From: Alex Brazier Date: Mon, 3 Apr 2017 10:54:36 +0100 Subject: [PATCH 3/4] Add translation for Join Chat shield --- packages/rocketchat-api/server/v1/misc.js | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/rocketchat-api/server/v1/misc.js b/packages/rocketchat-api/server/v1/misc.js index a870b012edf4..0e86a7e532b0 100644 --- a/packages/rocketchat-api/server/v1/misc.js +++ b/packages/rocketchat-api/server/v1/misc.js @@ -59,7 +59,7 @@ RocketChat.API.v1.addRoute('shield.svg', { authRequired: false }, { text = `#${ channel }`; break; default: - text = 'JOIN CHAT'; + text = TAPi18n.__('Join_Chat').toUpperCase(); } const iconSize = hideIcon ? 7 : 24; const leftSize = name ? name.length * 6 + 7 + iconSize : iconSize; diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 13bddef7d37c..c602c8be7e43 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -786,6 +786,7 @@ "Jitsi_Enable_Channels": "Enable in Channels", "join": "Join", "Join_audio_call": "Join audio call", + "Join_Chat": "Join Chat", "Join_default_channels": "Join default channels", "Join_the_Community": "Join the Community", "Join_the_given_channel": "Join the given channel", From 00b8f16ebc258684ad6186dbebbaff33d0d8a0c5 Mon Sep 17 00:00:00 2001 From: Alex Brazier Date: Mon, 3 Apr 2017 22:32:15 +0100 Subject: [PATCH 4/4] Only update online count every minute for shields --- packages/rocketchat-api/server/v1/misc.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/rocketchat-api/server/v1/misc.js b/packages/rocketchat-api/server/v1/misc.js index 0e86a7e532b0..4a2f5359cc31 100644 --- a/packages/rocketchat-api/server/v1/misc.js +++ b/packages/rocketchat-api/server/v1/misc.js @@ -32,6 +32,9 @@ RocketChat.API.v1.addRoute('me', { authRequired: true }, { } }); +let onlineCache = 0; +let onlineCacheDate = 0; +const cacheInvalid = 60000; // 1 minute RocketChat.API.v1.addRoute('shield.svg', { authRequired: false }, { get() { const { type, channel, name, icon } = this.queryParams; @@ -49,8 +52,11 @@ RocketChat.API.v1.addRoute('shield.svg', { authRequired: false }, { let text; switch (type) { case 'online': - const count = RocketChat.models.Users.findUsersNotOffline().count(); - text = `${ count } Online`; + if (Date.now() - onlineCacheDate > cacheInvalid) { + onlineCache = RocketChat.models.Users.findUsersNotOffline().count(); + onlineCacheDate = Date.now(); + } + text = `${ onlineCache } ${ TAPi18n.__('Online') }`; break; case 'channel': if (!channel) {