diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index 605093cf42..f717ade836 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/services/web/app/src/Features/Project/ProjectController.js @@ -36,7 +36,6 @@ const BrandVariationsHandler = require('../BrandVariations/BrandVariationsHandle const { getUserAffiliations } = require('../Institutions/InstitutionsAPI') const V1Handler = require('../V1/V1Handler') const UserController = require('../User/UserController') -const SystemMessageManager = require('../SystemMessages/SystemMessageManager') const _ssoAvailable = (affiliation, session, linkedInstitutionIds) => { if (!affiliation.institution) return false @@ -357,9 +356,6 @@ const ProjectController = { let noV1Connection = false async.parallel( { - systemMessages(cb) { - SystemMessageManager.getMessages(cb) - }, tags(cb) { TagsHandler.getAllTags(userId, cb) }, @@ -547,7 +543,6 @@ const ProjectController = { const viewModel = { title: 'your_projects', priority_title: true, - systemMessages: results.systemMessages, projects, tags, notifications: notifications || [], diff --git a/services/web/app/src/Features/SystemMessages/SystemMessageController.js b/services/web/app/src/Features/SystemMessages/SystemMessageController.js new file mode 100644 index 0000000000..dace617725 --- /dev/null +++ b/services/web/app/src/Features/SystemMessages/SystemMessageController.js @@ -0,0 +1,26 @@ +const Settings = require('settings-sharelatex') +const SystemMessageManager = require('./SystemMessageManager') + +const ProjectController = { + getMessages(req, res, next) { + SystemMessageManager.getMessages((err, messages) => { + if (err) { + next(err) + } else { + if (!Settings.siteIsOpen) { + // Override all messages with notice for admins when site is closed. + messages = [ + { + content: + 'SITE IS CLOSED TO PUBLIC. OPEN ONLY FOR SITE ADMINS. DO NOT EDIT PROJECTS.', + _id: 'protected' // prevents hiding message in frontend + } + ] + } + res.json(messages || []) + } + }) + } +} + +module.exports = ProjectController diff --git a/services/web/app/src/router.js b/services/web/app/src/router.js index 0634a9d0a6..1b543f4210 100644 --- a/services/web/app/src/router.js +++ b/services/web/app/src/router.js @@ -49,6 +49,7 @@ const LinkedFilesRouter = require('./Features/LinkedFiles/LinkedFilesRouter') const TemplatesRouter = require('./Features/Templates/TemplatesRouter') const InstitutionsController = require('./Features/Institutions/InstitutionsController') const UserMembershipRouter = require('./Features/UserMembership/UserMembershipRouter') +const SystemMessageController = require('./Features/SystemMessages/SystemMessageController') const logger = require('logger-sharelatex') const _ = require('underscore') @@ -108,6 +109,12 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) { webRouter.get('/user/activate', UserPagesController.activateAccountPage) AuthenticationController.addEndpointToLoginWhitelist('/user/activate') + webRouter.get( + '/system/messages', + AuthenticationController.requireLogin(), + SystemMessageController.getMessages + ) + webRouter.get( '/user/settings', AuthenticationController.requireLogin(), diff --git a/services/web/app/views/project/editor.pug b/services/web/app/views/project/editor.pug index 0c3207bc72..56f89451ce 100644 --- a/services/web/app/views/project/editor.pug +++ b/services/web/app/views/project/editor.pug @@ -3,7 +3,6 @@ extends ../layout block vars - var suppressNavbar = true - var suppressFooter = true - - var suppressSystemMessages = true - metadata.robotsNoindexNofollow = true block content @@ -49,6 +48,17 @@ block content .div(ng-controller="SavingNotificationController") .alert.alert-warning.small(ng-repeat="(doc_id, state) in docSavingStatus" ng-if="state.unsavedSeconds > 8") #{translate("saving_notification_with_seconds", {docname:"{{ state.doc.name }}", seconds:"{{ state.unsavedSeconds }}"})} + .div(ng-controller="SystemMessagesController") + .alert.alert-warning.system-message( + ng-repeat="message in messages" + ng-controller="SystemMessageController" + ng-hide="hidden" + ) + button(ng-hide="protected",ng-click="hide()").close.pull-right + span(aria-hidden="true") × + span.sr-only #{translate("close")} + .system-message-content(ng-bind-html="htmlContent") + include ./editor/left-menu #chat-wrapper.full-size( diff --git a/services/web/app/views/project/importing.pug b/services/web/app/views/project/importing.pug index e9282fdee0..4674b7b3e2 100644 --- a/services/web/app/views/project/importing.pug +++ b/services/web/app/views/project/importing.pug @@ -3,7 +3,6 @@ extends ../layout block vars - var suppressNavbar = true - var suppressFooter = true - - var suppressSystemMessages = true - metadata.robotsNoindexNofollow = true block content diff --git a/services/web/app/views/project/list.pug b/services/web/app/views/project/list.pug index 3ff4e764b1..9b2a4d754c 100644 --- a/services/web/app/views/project/list.pug +++ b/services/web/app/views/project/list.pug @@ -20,26 +20,19 @@ block content ng-controller="ProjectPageController" role="main" ) - - if(typeof(suppressSystemMessages) == "undefined") - script. - window.systemMessages = !{StringHelper.stringifyJsonForScript(systemMessages)}; - - if(settings.siteIsOpen == false && getSessionUser() && getSessionUser().isAdmin) - script. - window.systemMessages.push({content: "SITE IS CLOSED TO PUBLIC. OPEN ONLY FOR SITE ADMINS", _id:"siteclosed"}) - - .system-messages( - ng-cloak - ng-controller="SystemMessagesController" + .system-messages( + ng-cloak + ng-controller="SystemMessagesController" + ) + .system-message( + ng-repeat="message in messages" + ng-controller="SystemMessageController" + ng-hide="hidden" ) - .system-message( - ng-repeat="message in messages" - ng-controller="SystemMessageController" - ng-hide="hidden" - ) - button(ng-click="hide()").close.pull-right - span(aria-hidden="true") × - span.sr-only #{translate("close")} - .system-message-content(ng-bind-html="htmlContent") + button(ng-hide="protected",ng-click="hide()").close.pull-right + span(aria-hidden="true") × + span.sr-only #{translate("close")} + .system-message-content(ng-bind-html="htmlContent") include ../translations/translation_message diff --git a/services/web/frontend/js/ide.js b/services/web/frontend/js/ide.js index 151b89252c..02ec401726 100644 --- a/services/web/frontend/js/ide.js +++ b/services/web/frontend/js/ide.js @@ -59,6 +59,7 @@ define([ 'main/event', 'main/account-upgrade', 'main/exposed-settings', + 'main/system-messages', '../../modules/modules-ide.js' ], function( App, diff --git a/services/web/frontend/js/main/system-messages.js b/services/web/frontend/js/main/system-messages.js index f029b8dcca..9007636485 100644 --- a/services/web/frontend/js/main/system-messages.js +++ b/services/web/frontend/js/main/system-messages.js @@ -11,18 +11,36 @@ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ define(['base'], function(App) { - App.controller( - 'SystemMessagesController', - $scope => ($scope.messages = window.systemMessages) - ) + const MESSAGE_POLL_INTERVAL = 15 * 60 * 1000 + // Controller for messages (array) + App.controller('SystemMessagesController', ($http, $scope) => { + $scope.messages = window.systemMessages + var pollSystemMessages = function() { + $http + .get('/system/messages') + .then(response => { + $scope.messages = response.data + }) + .catch(() => { + // ignore errors + }) + } + pollSystemMessages() + setInterval(pollSystemMessages, MESSAGE_POLL_INTERVAL) + }) + // Controller for individual message (show/hide) return App.controller('SystemMessageController', function($scope, $sce) { $scope.hidden = $.localStorage(`systemMessage.hide.${$scope.message._id}`) + $scope.protected = $scope.message._id === 'protected' $scope.htmlContent = $scope.message.content return ($scope.hide = function() { - $scope.hidden = true - return $.localStorage(`systemMessage.hide.${$scope.message._id}`, true) + if (!$scope.protected) { + // do not allow protected messages to be hidden + $scope.hidden = true + return $.localStorage(`systemMessage.hide.${$scope.message._id}`, true) + } }) }) }) diff --git a/services/web/frontend/stylesheets/app/base.less b/services/web/frontend/stylesheets/app/base.less index c1e574ab21..7f1e8b35b8 100644 --- a/services/web/frontend/stylesheets/app/base.less +++ b/services/web/frontend/stylesheets/app/base.less @@ -3,6 +3,10 @@ background-color: @sys-msg-background; color: @sys-msg-color; border-bottom: @sys-msg-border; + a { + color: @sidebar-link-color; + text-decoration: underline; + } } .system-message .close when (@is-overleaf = true) { diff --git a/services/web/test/unit/src/Project/ProjectControllerTests.js b/services/web/test/unit/src/Project/ProjectControllerTests.js index eb93ee32fd..57ecc4f646 100644 --- a/services/web/test/unit/src/Project/ProjectControllerTests.js +++ b/services/web/test/unit/src/Project/ProjectControllerTests.js @@ -130,10 +130,6 @@ describe('ProjectController', function() { } ]) - this.SystemMessageManager = { - getMessages: sinon.stub().callsArgWith(0, null, []) - } - this.ProjectController = SandboxedModule.require(MODULE_PATH, { globals: { console: console @@ -151,7 +147,6 @@ describe('ProjectController', function() { inc() {} }, '@overleaf/o-error/http': HttpErrors, - '../SystemMessages/SystemMessageManager': this.SystemMessageManager, './ProjectDeleter': this.ProjectDeleter, './ProjectDuplicator': this.ProjectDuplicator, './ProjectCreationHandler': this.ProjectCreationHandler, @@ -377,14 +372,6 @@ describe('ProjectController', function() { describe('projectListPage', function() { beforeEach(function() { - this.systemMessages = [ - { _id: '42', content: 'Hello from the other side!' }, - { _id: '1337', content: 'Can you read this?' } - ] - this.SystemMessageManager.getMessages = sinon - .stub() - .callsArgWith(0, null, this.systemMessages) - this.tags = [ { name: 1, project_ids: ['1', '2', '3'] }, { name: 2, project_ids: ['a', '1'] }, @@ -456,14 +443,6 @@ describe('ProjectController', function() { this.ProjectController.projectListPage(this.req, this.res) }) - it('should send the systemMessages', function(done) { - this.res.render = (pageName, opts) => { - opts.systemMessages.should.deep.equal(this.systemMessages) - done() - } - this.ProjectController.projectListPage(this.req, this.res) - }) - it('should create trigger ip matcher notifications', function(done) { this.settings.overleaf = true this.res.render = (pageName, opts) => {