From 54186d9db15a1eff8cfd58f3da64faeb4d4fd799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Alby?= Date: Tue, 23 Apr 2019 10:19:52 -0400 Subject: [PATCH] Add linked accounts section to user settings page (#1705) Add linked accounts section to user settings page GitOrigin-RevId: d2bb26a3bfb3946144a05b98f58d50a2c57f3040 --- .../AuthenticationController.coffee | 8 ++++ .../app/coffee/infrastructure/Features.coffee | 2 + services/web/app/views/user/settings.pug | 10 ++-- .../app/views/user/settings/user-oauth.pug | 35 ++++++++++++++ services/web/public/src/main.js | 2 + .../web/public/src/main/account-settings.js | 4 +- .../oauth/controllers/UserOauthController.js | 48 +++++++++++++++++++ .../oauth/factories/UserOauthDataService.js | 20 ++++++++ .../stylesheets/_ol_style_includes.less | 1 + .../public/stylesheets/components/lists.less | 22 +++++++++ 10 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 services/web/app/views/user/settings/user-oauth.pug create mode 100644 services/web/public/src/main/oauth/controllers/UserOauthController.js create mode 100644 services/web/public/src/main/oauth/factories/UserOauthDataService.js create mode 100644 services/web/public/stylesheets/components/lists.less diff --git a/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee b/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee index 5bd8d256e6..84fce3e1f7 100644 --- a/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee +++ b/services/web/app/coffee/Features/Authentication/AuthenticationController.coffee @@ -31,6 +31,7 @@ module.exports = AuthenticationController = session_created: (new Date()).toISOString() ip_address: user._login_req_ip must_reconfirm: user.must_reconfirm + v1_id: user.overleaf?.id callback(null, lightUser) deserializeUser: (user, cb) -> @@ -160,6 +161,13 @@ module.exports = AuthenticationController = else return null + getLoggedInUserV1Id: (req) -> + user = AuthenticationController.getSessionUser(req) + if user?.v1_id? + return user.v1_id + else + return null + getSessionUser: (req) -> if req?.session?.user? return req.session.user diff --git a/services/web/app/coffee/infrastructure/Features.coffee b/services/web/app/coffee/infrastructure/Features.coffee index 0f95ed40d1..7e53cbd29b 100644 --- a/services/web/app/coffee/infrastructure/Features.coffee +++ b/services/web/app/coffee/infrastructure/Features.coffee @@ -18,6 +18,8 @@ module.exports = Features = return Settings.accountMerge? and Settings.overleaf? and !Settings.forceImportToV2 when 'custom-togglers' return Settings.overleaf? + when 'oauth' + return Settings.oauth? when 'publish-templates' return true when 'view-templates' diff --git a/services/web/app/views/user/settings.pug b/services/web/app/views/user/settings.pug index 4ec4d042a3..92788e1b21 100644 --- a/services/web/app/views/user/settings.pug +++ b/services/web/app/views/user/settings.pug @@ -9,9 +9,10 @@ block content .page-header h1 #{translate("account_settings")} .account-settings(ng-controller="AccountSettingsController", ng-cloak) + if hasFeature('affiliations') include settings/user-affiliations - + form-messages(for="settingsForm") .alert.alert-success(ng-show="settingsForm.response.success") | #{translate("thanks_settings_updated")} @@ -145,7 +146,7 @@ block content | !{moduleIncludes("userSettings", locals)} hr - + h3 | #{translate("sharelatex_beta_program")} @@ -164,13 +165,16 @@ block content div a(id="sessions-link", href="/user/sessions") #{translate("manage_sessions")} - if settings.overleaf + if settings.overleaf && !hasFeature('oauth') hr p | To manage your account's connection to Google, Twitter, ORCID and IEEE, please | a(href="/sign_in_to_v1?return_to=/users/edit#linked-accounts") click here | . + else if hasFeature('oauth') + hr + include settings/user-oauth hr diff --git a/services/web/app/views/user/settings/user-oauth.pug b/services/web/app/views/user/settings/user-oauth.pug new file mode 100644 index 0000000000..32e7b77994 --- /dev/null +++ b/services/web/app/views/user/settings/user-oauth.pug @@ -0,0 +1,35 @@ +form.row( + ng-controller="UserOauthController" + name="oauthForm" + ng-cloak +) + .col-xs-12 + h3.text-capitalize #{translate("linked_accounts")} + p.small #{translate("linked_accounts_explained", {appName:'{{settings.appName}}'})} + + div.text-center(ng-if="ui.isLoadingProviders") + i.fa.fa-fw.fa-spin.fa-refresh(aria-hidden="true") + |  #{translate("loading")}... + + ul( + class="list-like-table" + ng-if="ui.isLoadingProviders == false && ui.hasError == false" + ) + li(ng-repeat="provider in providers") + .row + .col-xs-12.col-sm-8.col-md-10 + h4 {{provider.name}} + p.small(ng-if="provider.key != 'orcid'") + | #{translate("login_with_service", {service:'{{provider.name}}'})} + p.small(ng-if="provider.key == 'orcid'") + | !{translate('oauth_orcid_description', {link: '/blog/434'})} + .col-xs-2.col-sm-4.col-md-2.text-right + button.btn.btn-default(ng-if="userProviders[provider.key]") #{translate("unlink")} + button.btn.btn-primary(ng-if="!userProviders[provider.key]") #{translate("link")} + + .alert.alert-danger( + ng-if="ui.hasError" + ) + i.fa.fa-fw.fa-exclamation-triangle(aria-hidden="true") + span(ng-if="!ui.errorMessage")  #{translate("error_performing_request")} + span(ng-if="ui.errorMessage")  {{ui.errorMessage}} \ No newline at end of file diff --git a/services/web/public/src/main.js b/services/web/public/src/main.js index 4f7e17ab33..c3993d0214 100644 --- a/services/web/public/src/main.js +++ b/services/web/public/src/main.js @@ -33,6 +33,8 @@ define([ 'main/affiliations/components/affiliationForm', 'main/affiliations/controllers/UserAffiliationsController', 'main/affiliations/factories/UserAffiliationsDataService', + 'main/oauth/factories/UserOauthDataService', + 'main/oauth/controllers/UserOauthController', 'main/keys', 'main/cms/blog', 'main/cms/index', diff --git a/services/web/public/src/main/account-settings.js b/services/web/public/src/main/account-settings.js index db898f2b17..b9eda22730 100644 --- a/services/web/public/src/main/account-settings.js +++ b/services/web/public/src/main/account-settings.js @@ -20,12 +20,14 @@ define(['base'], function(App) { '$modal', 'event_tracking', 'UserAffiliationsDataService', + 'UserOauthDataService', function( $scope, $http, $modal, event_tracking, - UserAffiliationsDataService + UserAffiliationsDataService, + UserOauthDataService ) { $scope.subscribed = true diff --git a/services/web/public/src/main/oauth/controllers/UserOauthController.js b/services/web/public/src/main/oauth/controllers/UserOauthController.js new file mode 100644 index 0000000000..9404850b54 --- /dev/null +++ b/services/web/public/src/main/oauth/controllers/UserOauthController.js @@ -0,0 +1,48 @@ +define(['base'], App => + App.controller('UserOauthController', [ + '$scope', + '$q', + '_', + 'UserOauthDataService', + function($scope, $q, _, UserOauthDataService) { + $scope.providers = [ + { key: 'google', name: 'Google' }, + { key: 'orcid', name: 'Orcid' }, + { key: 'twitter', name: 'Twitter' } + ] + const _monitorRequest = function(promise) { + $scope.ui.hasError = false + $scope.ui.isLoadingProviders = true + promise + .catch(response => { + $scope.ui.hasError = true + $scope.ui.errorMessage = + response && response.data && response.data.message + ? response.data.message + : 'error' + }) + .finally(() => { + $scope.ui.isLoadingProviders = false + }) + return promise + } + const _reset = function() { + $scope.ui = { + hasError: false, + errorMessage: '', + isLoadingProviders: false + } + $scope.userProviders = {} + } + const _getUserV1OauthProviders = () => { + $scope.ui.isLoadingProviders = true + return _monitorRequest(UserOauthDataService.getUserOauthV1()).then( + userProviders => { + $scope.userProviders = userProviders + } + ) + } + _reset() + return _getUserV1OauthProviders() + } + ])) diff --git a/services/web/public/src/main/oauth/factories/UserOauthDataService.js b/services/web/public/src/main/oauth/factories/UserOauthDataService.js new file mode 100644 index 0000000000..52b8204e1a --- /dev/null +++ b/services/web/public/src/main/oauth/factories/UserOauthDataService.js @@ -0,0 +1,20 @@ +define(['base'], function(App) { + return App.factory('UserOauthDataService', [ + '$http', + function($http) { + const getUserOauthV1 = () => { + if (window.ExposedSettings.isOverleaf) { + return $http.get('/user/v1-oauth-uids').then(response => { + return response.data + }) + } else { + return {} + } + } + + return { + getUserOauthV1 + } + } + ]) +}) diff --git a/services/web/public/stylesheets/_ol_style_includes.less b/services/web/public/stylesheets/_ol_style_includes.less index ff300fc458..2f40ddf464 100644 --- a/services/web/public/stylesheets/_ol_style_includes.less +++ b/services/web/public/stylesheets/_ol_style_includes.less @@ -8,4 +8,5 @@ @import "app/publisher-hub.less"; @import "app/admin-hub.less"; @import "app/import.less"; +@import "components/lists.less"; @import "components/overbox.less"; \ No newline at end of file diff --git a/services/web/public/stylesheets/components/lists.less b/services/web/public/stylesheets/components/lists.less new file mode 100644 index 0000000000..02b6eedbb0 --- /dev/null +++ b/services/web/public/stylesheets/components/lists.less @@ -0,0 +1,22 @@ +.list-like-table { + border: 1px solid @hr-border; + border-radius: @border-radius-base; + list-style: none; + margin: 0; + padding: @padding-sm; + li { + border-top: 1px solid @hr-border; + div { + display: table-cell; + float: none; + vertical-align: middle; + } + .row { + display: table; + width: 100%; + } + &:first-child { + border-top: 0; + } + } +} \ No newline at end of file