mirror of
https://github.com/sismics/docs.git
synced 2024-12-22 11:23:48 +01:00
Closes #309: store onboarding status server side
This commit is contained in:
parent
8b1c41ae1e
commit
61b12bdebd
@ -171,6 +171,26 @@ public class UserDao {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the onboarding status.
|
||||
*
|
||||
* @param user User to update
|
||||
* @return Updated user
|
||||
*/
|
||||
public User updateOnboarding(User user) {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
|
||||
// Get the user
|
||||
Query q = em.createQuery("select u from User u where u.id = :id and u.deleteDate is null");
|
||||
q.setParameter("id", user.getId());
|
||||
User userDb = (User) q.getSingleResult();
|
||||
|
||||
// Update the user
|
||||
userDb.setOnboarding(user.isOnboarding());
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a user by its ID.
|
||||
*
|
||||
|
@ -46,7 +46,13 @@ public class User implements Loggable {
|
||||
*/
|
||||
@Column(name = "USE_PRIVATEKEY_C", nullable = false, length = 100)
|
||||
private String privateKey;
|
||||
|
||||
|
||||
/**
|
||||
* False when the user passed the onboarding.
|
||||
*/
|
||||
@Column(name = "USE_ONBOARDING_B", nullable = false)
|
||||
private boolean onboarding;
|
||||
|
||||
/**
|
||||
* TOTP secret key.
|
||||
*/
|
||||
@ -198,6 +204,15 @@ public class User implements Loggable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isOnboarding() {
|
||||
return onboarding;
|
||||
}
|
||||
|
||||
public User setOnboarding(boolean onboarding) {
|
||||
this.onboarding = onboarding;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
|
@ -1 +1 @@
|
||||
db.version=22
|
||||
db.version=23
|
@ -0,0 +1,2 @@
|
||||
alter table T_USER add column USE_ONBOARDING_B bit not null default 1;
|
||||
update T_CONFIG set CFG_VALUE_C = '23' where CFG_ID_C = 'DB_VERSION';
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=22
|
||||
db.version=23
|
@ -101,6 +101,7 @@ public class UserResource extends BaseResource {
|
||||
user.setPassword(password);
|
||||
user.setEmail(email);
|
||||
user.setStorageQuota(storageQuota);
|
||||
user.setOnboarding(true);
|
||||
|
||||
// Create the user
|
||||
UserDao userDao = new UserDao();
|
||||
@ -622,6 +623,7 @@ public class UserResource extends BaseResource {
|
||||
* @apiGroup User
|
||||
* @apiSuccess {Boolean} anonymous True if no user is connected
|
||||
* @apiSuccess {Boolean} is_default_password True if the admin has the default password
|
||||
* @apiSuccess {Boolean} onboarding True if the UI needs to display the onboarding
|
||||
* @apiSuccess {String} username Username
|
||||
* @apiSuccess {String} email E-mail
|
||||
* @apiSuccess {Number} storage_quota Storage quota (in bytes)
|
||||
@ -665,8 +667,9 @@ public class UserResource extends BaseResource {
|
||||
.add("email", user.getEmail())
|
||||
.add("storage_quota", user.getStorageQuota())
|
||||
.add("storage_current", user.getStorageCurrent())
|
||||
.add("totp_enabled", user.getTotpKey() != null);
|
||||
|
||||
.add("totp_enabled", user.getTotpKey() != null)
|
||||
.add("onboarding", user.isOnboarding());
|
||||
|
||||
// Base functions
|
||||
JsonArrayBuilder baseFunctions = Json.createArrayBuilder();
|
||||
for (String baseFunction : ((UserPrincipal) principal).getBaseFunctionSet()) {
|
||||
@ -898,6 +901,39 @@ public class UserResource extends BaseResource {
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the onboarding experience as passed.
|
||||
*
|
||||
* @api {post} /user/onboarded Mark the onboarding experience as passed
|
||||
* @apiDescription Once the onboarding experience has been passed by the user, this resource prevent it from being displayed again.
|
||||
* @apiName PostUserOnboarded
|
||||
* @apiGroup User
|
||||
* @apiSuccess {String} status Status OK
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiPermission user
|
||||
* @apiVersion 1.7.0
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
@POST
|
||||
@Path("onboarded")
|
||||
public Response onboarded() {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
|
||||
// Save it
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(principal.getName());
|
||||
user.setOnboarding(false);
|
||||
userDao.updateOnboarding(user);
|
||||
|
||||
// Always return OK
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
.add("status", "ok");
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable time-based one-time password.
|
||||
|
@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Document default controller.
|
||||
*/
|
||||
angular.module('docs').controller('DocumentDefault', function ($scope, $rootScope, $state, Restangular, Upload, $translate, $uibModal, $dialog) {
|
||||
angular.module('docs').controller('DocumentDefault', function ($scope, $rootScope, $state, Restangular, Upload, $translate, $uibModal, $dialog, User) {
|
||||
// Load user audit log
|
||||
Restangular.one('auditlog').get().then(function (data) {
|
||||
$scope.logs = data.logs;
|
||||
@ -145,48 +145,51 @@ angular.module('docs').controller('DocumentDefault', function ($scope, $rootScop
|
||||
|
||||
// Onboarding
|
||||
$translate('onboarding.step1.title').then(function () {
|
||||
if (localStorage.onboardingDisplayed || $(window).width() < 1000) {
|
||||
return;
|
||||
}
|
||||
localStorage.onboardingDisplayed = true;
|
||||
|
||||
$rootScope.onboardingEnabled = true;
|
||||
|
||||
$rootScope.onboardingSteps = [
|
||||
{
|
||||
title: $translate.instant('onboarding.step1.title'),
|
||||
description: $translate.instant('onboarding.step1.description'),
|
||||
position: 'centered',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step2.title'),
|
||||
description: $translate.instant('onboarding.step2.description'),
|
||||
attachTo: '#document-add-btn',
|
||||
position: 'right',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step3.title'),
|
||||
description: $translate.instant('onboarding.step3.description'),
|
||||
attachTo: '#quick-upload-zone',
|
||||
position: 'left',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step4.title'),
|
||||
description: $translate.instant('onboarding.step4.description'),
|
||||
attachTo: '#search-box',
|
||||
position: 'right',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step5.title'),
|
||||
description: $translate.instant('onboarding.step5.description'),
|
||||
attachTo: '#navigation-tag',
|
||||
position: "right",
|
||||
width: 300
|
||||
User.userInfo().then(function(userData) {
|
||||
if (!userData.onboarding || $(window).width() < 1000) {
|
||||
return;
|
||||
}
|
||||
];
|
||||
Restangular.one('user').post('onboarded');
|
||||
$rootScope.userInfo.onboarding = false;
|
||||
|
||||
$rootScope.onboardingEnabled = true;
|
||||
|
||||
$rootScope.onboardingSteps = [
|
||||
{
|
||||
title: $translate.instant('onboarding.step1.title'),
|
||||
description: $translate.instant('onboarding.step1.description'),
|
||||
position: 'centered',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step2.title'),
|
||||
description: $translate.instant('onboarding.step2.description'),
|
||||
attachTo: '#document-add-btn',
|
||||
position: 'right',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step3.title'),
|
||||
description: $translate.instant('onboarding.step3.description'),
|
||||
attachTo: '#quick-upload-zone',
|
||||
position: 'left',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step4.title'),
|
||||
description: $translate.instant('onboarding.step4.description'),
|
||||
attachTo: '#search-box',
|
||||
position: 'right',
|
||||
width: 300
|
||||
},
|
||||
{
|
||||
title: $translate.instant('onboarding.step5.title'),
|
||||
description: $translate.instant('onboarding.step5.description'),
|
||||
attachTo: '#navigation-tag',
|
||||
position: "right",
|
||||
width: 300
|
||||
}
|
||||
];
|
||||
});
|
||||
});
|
||||
});
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=22
|
||||
db.version=23
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=22
|
||||
db.version=23
|
@ -172,7 +172,7 @@ public class TestUserResource extends BaseJerseyTest {
|
||||
.get();
|
||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||
json = response.readEntity(JsonObject.class);
|
||||
Assert.assertEquals(true, json.getBoolean("anonymous"));
|
||||
Assert.assertTrue(json.getBoolean("anonymous"));
|
||||
|
||||
// Check alice user information
|
||||
json = target().path("/user").request()
|
||||
@ -187,8 +187,20 @@ public class TestUserResource extends BaseJerseyTest {
|
||||
json = target().path("/user").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, bobToken)
|
||||
.get(JsonObject.class);
|
||||
Assert.assertTrue(json.getBoolean("onboarding"));
|
||||
Assert.assertEquals("bob@docs.com", json.getString("email"));
|
||||
|
||||
|
||||
// Pass onboarding
|
||||
target().path("/user/onboarded").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, bobToken)
|
||||
.post(Entity.form(new Form()), JsonObject.class);
|
||||
|
||||
// Check bob user information
|
||||
json = target().path("/user").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, bobToken)
|
||||
.get(JsonObject.class);
|
||||
Assert.assertFalse(json.getBoolean("onboarding"));
|
||||
|
||||
// Test login KO (user not found)
|
||||
response = target().path("/user/login").request()
|
||||
.post(Entity.form(new Form()
|
||||
|
Loading…
Reference in New Issue
Block a user