From b81b3668e077b7b762f0c29265efe56c9373d8ad Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Tue, 20 Feb 2018 19:33:01 -0300 Subject: [PATCH] [NEW] Alert for new versions --- .meteor/packages | 1 + .meteor/versions | 1 + packages/rocketchat-i18n/i18n/en.i18n.json | 3 ++ .../rocketchat-lib/server/models/Users.js | 20 +++++++ .../rocketchat-ui-admin/client/admin.html | 18 +++---- .../rocketchat-ui/client/views/app/alerts.js | 7 +-- .../rocketchat-version-check/client/client.js | 28 ++++++++++ packages/rocketchat-version-check/package.js | 19 +++++++ .../server/addSettings.js | 8 +++ .../server/checkUpdate.js | 14 +++++ .../server/functions/checkVersionUpdate.js | 54 +++++++++++++++++++ .../server/functions/getNewUpdates.js | 47 ++++++++++++++++ .../rocketchat-version-check/server/logger.js | 1 + .../server/methods/banner_dismiss.js | 11 ++++ .../rocketchat-version-check/server/server.js | 29 ++++++++++ server/publications/userData.js | 3 +- 16 files changed, 251 insertions(+), 13 deletions(-) create mode 100644 packages/rocketchat-version-check/client/client.js create mode 100644 packages/rocketchat-version-check/package.js create mode 100644 packages/rocketchat-version-check/server/addSettings.js create mode 100644 packages/rocketchat-version-check/server/checkUpdate.js create mode 100644 packages/rocketchat-version-check/server/functions/checkVersionUpdate.js create mode 100644 packages/rocketchat-version-check/server/functions/getNewUpdates.js create mode 100644 packages/rocketchat-version-check/server/logger.js create mode 100644 packages/rocketchat-version-check/server/methods/banner_dismiss.js create mode 100644 packages/rocketchat-version-check/server/server.js diff --git a/.meteor/packages b/.meteor/packages index d9d3e75588e1..f6da7829fd40 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -184,3 +184,4 @@ steffo:meteor-accounts-saml todda00:friendly-slugs yasaricli:slugify yasinuslu:blaze-meta +rocketchat:version-check diff --git a/.meteor/versions b/.meteor/versions index 3f36b1969eb4..fe345b2e9cbd 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -236,6 +236,7 @@ rocketchat:ui-message@0.1.0 rocketchat:ui-sidenav@0.1.0 rocketchat:ui-vrecord@0.0.1 rocketchat:version@1.0.0 +rocketchat:version-check@0.0.1 rocketchat:videobridge@0.2.0 rocketchat:webrtc@0.0.1 rocketchat:wordpress@0.0.1 diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 806f72348431..6d3ca84df074 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -420,6 +420,7 @@ "Closing_chat": "Closing chat", "Collapse_Embedded_Media_By_Default": "Collapse Embedded Media by Default", "Color": "Color", + "Contains_Security_Fixes": "Contains Security Fixes", "Commands": "Commands", "Comment_to_leave_on_closing_session": "Comment to Leave on Closing Session", "Common_Access": "Common Access", @@ -1356,6 +1357,7 @@ "New_role": "New role", "New_Room_Notification": "New Room Notification", "New_Trigger": "New Trigger", + "New_version_available_(s)": "New version available (%s)", "New_videocall_request": "New Video Call Request", "No_available_agents_to_transfer": "No available agents to transfer", "No_channel_with_name_%s_was_found": "No channel with name \"%s\" was found!", @@ -1996,6 +1998,7 @@ "Unread_Tray_Icon_Alert": "Unread Tray Icon Alert", "Unstar_Message": "Remove Star", "Updated_at": "Updated at", + "Update_your_RocketChat": "Update your Rocket.Chat", "Upload_user_avatar": "Upload avatar", "Upload_file_description": "File description", "Upload_file_name": "File name", diff --git a/packages/rocketchat-lib/server/models/Users.js b/packages/rocketchat-lib/server/models/Users.js index bf80fd4f7a20..41ed1fe2926a 100644 --- a/packages/rocketchat-lib/server/models/Users.js +++ b/packages/rocketchat-lib/server/models/Users.js @@ -532,6 +532,26 @@ class ModelUsers extends RocketChat.models._Base { return this.update(_id, update); } + addBannerById(_id, banner) { + const update = { + $set: { + [`banners.${ banner.id }`]: banner + } + }; + + return this.update({ _id }, update); + } + + removeBannerById(_id, banner) { + const update = { + $unset: { + [`banners.${ banner.id }`]: true + } + }; + + return this.update({ _id }, update); + } + // INSERT create(data) { const user = { diff --git a/packages/rocketchat-ui-admin/client/admin.html b/packages/rocketchat-ui-admin/client/admin.html index c506bf3d9218..caeb39260959 100644 --- a/packages/rocketchat-ui-admin/client/admin.html +++ b/packages/rocketchat-ui-admin/client/admin.html @@ -54,9 +54,9 @@
{{#if $eq type 'string'}} {{#if multiline}} - + {{else}} - + {{/if}} {{/if}} @@ -65,20 +65,20 @@ {{/if}} {{#if $eq type 'password'}} - + {{/if}} {{#if $eq type 'int'}} - + {{/if}} {{#if $eq type 'boolean'}} - - + + {{/if}} {{#if $eq type 'select'}} - {{#each values}} {{/each}} @@ -86,7 +86,7 @@ {{/if}} {{#if $eq type 'language'}} - {{#each languages}} {{/each}} @@ -118,7 +118,7 @@ {{/if}} {{#if $eq type 'font'}} - + {{/if}} {{#if $eq type 'code'}} diff --git a/packages/rocketchat-ui/client/views/app/alerts.js b/packages/rocketchat-ui/client/views/app/alerts.js index a6849394372f..1b2d465d17ed 100644 --- a/packages/rocketchat-ui/client/views/app/alerts.js +++ b/packages/rocketchat-ui/client/views/app/alerts.js @@ -2,7 +2,7 @@ this.alerts = { renderedAlert: null, open(config) { - this.close(); + this.close(false); config.closable = typeof(config.closable) === typeof(true) ? config.closable : true; @@ -12,7 +12,7 @@ this.alerts = { this.renderedAlert = Blaze.renderWithData(Template.alerts, config, document.body, document.body.querySelector('#alert-anchor')); }, - close() { + close(dismiss = true) { if (this.timer) { clearTimeout(this.timer); delete this.timer; @@ -27,6 +27,8 @@ this.alerts = { if (activeElement) { $(activeElement).removeClass('active'); } + + dismiss && this.renderedAlert.dataVar.curValue.onClose && this.renderedAlert.dataVar.curValue.onClose(); } }; @@ -49,7 +51,6 @@ Template.alerts.onDestroyed(function() { if (this.data.onDestroyed) { this.data.onDestroyed(); } - this.data.onClose && this.data.onClose(); }); Template.alerts.events({ diff --git a/packages/rocketchat-version-check/client/client.js b/packages/rocketchat-version-check/client/client.js new file mode 100644 index 000000000000..6a8226bb921e --- /dev/null +++ b/packages/rocketchat-version-check/client/client.js @@ -0,0 +1,28 @@ +/* globals alerts */ + +Meteor.startup(function() { + Tracker.autorun(() => { + const user = Meteor.user(); + + if (user && Object.keys(user.banners || {}).length > 0) { + const firstBanner = Object.values(user.banners).sort((a, b) => b.priority - a.priority)[0]; + firstBanner.textArguments = firstBanner.textArguments || []; + + alerts.open({ + title: TAPi18n.__(firstBanner.title), + text: TAPi18n.__(firstBanner.text, ...firstBanner.textArguments), + modifiers: firstBanner.modifiers, + action() { + if (firstBanner.link) { + window.open(firstBanner.link, '_system'); + } + }, + onClose() { + Meteor.call('banner/dismiss', { + id: firstBanner.id + }); + } + }); + } + }); +}); diff --git a/packages/rocketchat-version-check/package.js b/packages/rocketchat-version-check/package.js new file mode 100644 index 000000000000..db482523b0ed --- /dev/null +++ b/packages/rocketchat-version-check/package.js @@ -0,0 +1,19 @@ +Package.describe({ + name: 'rocketchat:version-check', + version: '0.0.1', + summary: 'Check for new avaiable versions', + git: '' +}); + +Package.onUse(function(api) { + api.use([ + 'mongo', + 'ecmascript', + 'rocketchat:lib', + 'rocketchat:logger', + 'percolate:synced-cron' + ]); + + api.mainModule('client/client.js', 'client'); + api.mainModule('server/server.js', 'server'); +}); diff --git a/packages/rocketchat-version-check/server/addSettings.js b/packages/rocketchat-version-check/server/addSettings.js new file mode 100644 index 000000000000..90f07d56b46e --- /dev/null +++ b/packages/rocketchat-version-check/server/addSettings.js @@ -0,0 +1,8 @@ +RocketChat.settings.addGroup('General', function() { + this.section('Update', function() { + this.add('Update_LatestAvailableVersion', '0.0.0', { + type: 'string', + readonly: true + }); + }); +}); diff --git a/packages/rocketchat-version-check/server/checkUpdate.js b/packages/rocketchat-version-check/server/checkUpdate.js new file mode 100644 index 000000000000..b1c7528021b7 --- /dev/null +++ b/packages/rocketchat-version-check/server/checkUpdate.js @@ -0,0 +1,14 @@ +export default { + versions: [{ + version: '0.61.2', + security: true, + infoUrl: 'https://rocket.chat' + }], + alerts: [{ + timestamp: '2018-02-19T22:56:23.290Z', + title: 'Alert title', + description: 'Alert description', + infoUrl: 'https://rocket.chat', + infoTitle: 'More information' + }] +}; diff --git a/packages/rocketchat-version-check/server/functions/checkVersionUpdate.js b/packages/rocketchat-version-check/server/functions/checkVersionUpdate.js new file mode 100644 index 000000000000..3d52f1c07c1c --- /dev/null +++ b/packages/rocketchat-version-check/server/functions/checkVersionUpdate.js @@ -0,0 +1,54 @@ +import semver from 'semver'; +import getNewUpdates from './getNewUpdates'; +import logger from '../logger'; + +export default () => { + logger.info('Checking for version updates'); + + const { versions } = getNewUpdates(); + + const update = { + exists: false, + lastestVersion: null, + security: false + }; + + const lastCheckedVersion = RocketChat.settings.get('Update_LatestAvailableVersion'); + versions.forEach(version => { + if (semver.lte(version.version, lastCheckedVersion)) { + return; + } + + if (semver.lte(version.version, RocketChat.Info.version)) { + return; + } + + update.exists = true; + update.lastestVersion = version; + + if (version.security === true) { + update.security = true; + } + }); + + if (update.exists) { + RocketChat.settings.updateById('Update_LatestAvailableVersion', update.lastestVersion.version); + RocketChat.models.Roles.findUsersInRole('admin').forEach(adminUser => { + const msg = { + msg: `*${ TAPi18n.__('Update_your_RocketChat', adminUser.language) }*\n${ TAPi18n.__('New_version_available_(s)', update.lastestVersion.version, adminUser.language) }\n${ update.lastestVersion.infoUrl }`, + rid: [adminUser._id, 'rocket.cat'].sort().join('') + }; + + Meteor.runAsUser('rocket.cat', () => Meteor.call('sendMessage', msg)); + + RocketChat.models.Users.addBannerById(adminUser._id, { + id: 'versionUpdate', + priority: 10, + title: 'Update_your_RocketChat', + text: 'New_version_available_(s)', + textArguments: [update.lastestVersion.version], + link: update.lastestVersion.infoUrl + }); + }); + } +}; diff --git a/packages/rocketchat-version-check/server/functions/getNewUpdates.js b/packages/rocketchat-version-check/server/functions/getNewUpdates.js new file mode 100644 index 000000000000..7cac0bd94a96 --- /dev/null +++ b/packages/rocketchat-version-check/server/functions/getNewUpdates.js @@ -0,0 +1,47 @@ +/* global MongoInternals */ +import os from 'os'; +import { HTTP } from 'meteor/http'; +import logger from '../logger'; +// import checkUpdate from '../checkUpdate'; + +export default () => { + try { + const uniqueID = RocketChat.models.Settings.findOne('uniqueID'); + const _oplogHandle = MongoInternals.defaultRemoteCollectionDriver().mongo._oplogHandle; + const oplogEnabled = _oplogHandle && _oplogHandle.onOplogEntry && RocketChat.settings.get('Force_Disable_OpLog_For_Cache') !== true; + + const data = { + uniqueId: uniqueID.value, + installedAt: uniqueID.createdAt, + version: RocketChat.Info.version, + oplogEnabled, + osType: os.type(), + osPlatform: os.platform(), + osArch: os.arch(), + osRelease: os.release(), + nodeVersion: process.version, + deployMethod: process.env.DEPLOY_METHOD || 'tar', + deployPlatform: process.env.DEPLOY_PLATFORM || 'selfinstall' + }; + + const result = HTTP.get('https://releases.rocket.chat/updates/check', { + params: data + }); + + // const result = { + // data: { + // ...checkUpdate + // } + // }; + + return result.data; + } catch (error) { + // console.log(error); + logger.error('Failed to get version updates', error); + + return { + versions: [], + alerts: [] + }; + } +}; diff --git a/packages/rocketchat-version-check/server/logger.js b/packages/rocketchat-version-check/server/logger.js new file mode 100644 index 000000000000..e1157e5c833e --- /dev/null +++ b/packages/rocketchat-version-check/server/logger.js @@ -0,0 +1 @@ +export default new Logger('VersionCheck'); diff --git a/packages/rocketchat-version-check/server/methods/banner_dismiss.js b/packages/rocketchat-version-check/server/methods/banner_dismiss.js new file mode 100644 index 000000000000..fb7dde6cd686 --- /dev/null +++ b/packages/rocketchat-version-check/server/methods/banner_dismiss.js @@ -0,0 +1,11 @@ +Meteor.methods({ + 'banner/dismiss'({ id }) { + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'banner/dismiss' }); + } + + RocketChat.models.Users.removeBannerById(this.userId, { + id + }); + } +}); diff --git a/packages/rocketchat-version-check/server/server.js b/packages/rocketchat-version-check/server/server.js new file mode 100644 index 000000000000..2529553437e2 --- /dev/null +++ b/packages/rocketchat-version-check/server/server.js @@ -0,0 +1,29 @@ +/* globals SyncedCron */ + +import checkVersionUpdate from './functions/checkVersionUpdate'; +import './methods/banner_dismiss'; +import './addSettings'; + +const jobName = 'version_check'; + +if (SyncedCron.nextScheduledAtDate(jobName)) { + SyncedCron.remove(jobName); +} + +SyncedCron.add({ + name: jobName, + schedule: parser => parser.text('at 2:00 am'), + job() { + checkVersionUpdate(); + } +}); + +SyncedCron.start(); + +Meteor.startup(() => { + checkVersionUpdate(); +}); + +// Send email to admins +// Save latest alert +// ENV var to disable the check for update for our cloud diff --git a/server/publications/userData.js b/server/publications/userData.js index bea7a2c30396..953cc1e7ed8d 100644 --- a/server/publications/userData.js +++ b/server/publications/userData.js @@ -27,7 +27,8 @@ Meteor.publish('userData', function() { requirePasswordChangeReason: 1, 'services.password.bcrypt': 1, 'services.totp.enabled': 1, - statusLivechat: 1 + statusLivechat: 1, + banners: 1 } }); });