From 478f61dac49f947aa15b4a73b9abaf8fd411ca6a Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 10 Feb 2021 12:53:55 +0100 Subject: [PATCH 1/4] [WIP] allow superusers to join organizations unlisted --- app/controllers/UserController.scala | 1 - app/models/user/User.scala | 2 ++ conf/evolutions/065-unlisted-superusers.sql | 27 +++++++++++++++++++ .../reversions/065-unlisted-superusers.sql | 25 +++++++++++++++++ tools/postgres/schema.sql | 3 ++- 5 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 conf/evolutions/065-unlisted-superusers.sql create mode 100644 conf/evolutions/reversions/065-unlisted-superusers.sql diff --git a/app/controllers/UserController.scala b/app/controllers/UserController.scala index 693faf3f6a0..c4b06cb02c4 100755 --- a/app/controllers/UserController.scala +++ b/app/controllers/UserController.scala @@ -342,7 +342,6 @@ class UserController @Inject()(userService: UserService, _ <- ensureProperTeamAdministration(user, teamsWithUpdate) trimmedExperiences = experiences.map { case (key, value) => key.trim -> value } updatedTeams = teamsWithUpdate.map(_._1) ++ teamsWithoutUpdate - oldMultiUser <- multiUserDAO.findOne(user._multiUser) _ <- userService.update(user, firstName.trim, lastName.trim, diff --git a/app/models/user/User.scala b/app/models/user/User.scala index e5db81394df..b2fc1e71bc5 100755 --- a/app/models/user/User.scala +++ b/app/models/user/User.scala @@ -34,6 +34,7 @@ case class User( isAdmin: Boolean, isDatasetManager: Boolean, isDeactivated: Boolean, + isUnlisted: Boolean, created: Long = System.currentTimeMillis(), lastTaskTypeId: Option[ObjectId] = None, isDeleted: Boolean = false @@ -80,6 +81,7 @@ class UserDAO @Inject()(sqlClient: SQLClient)(implicit ec: ExecutionContext) r.isadmin, r.isdatasetmanager, r.isdeactivated, + r.isunlisted, r.created.getTime, r.lasttasktypeid.map(ObjectId(_)), r.isdeleted diff --git a/conf/evolutions/065-unlisted-superusers.sql b/conf/evolutions/065-unlisted-superusers.sql new file mode 100644 index 00000000000..58141c91991 --- /dev/null +++ b/conf/evolutions/065-unlisted-superusers.sql @@ -0,0 +1,27 @@ +-- https://github.com/scalableminds/webknossos/pull/TODO + +START TRANSACTION; + +DROP VIEW webknossos.userInfos; +DROP VIEW webknossos.users_; + +ALTER TABLE webknossos.users ADD COLUMN isUnlisted BOOLEAN NOT NULL DEFAULT FALSE; + +CREATE VIEW webknossos.users_ AS SELECT * FROM webknossos.users WHERE NOT isDeleted; + + +-- identical to previous (has to be dropped first because of the dependency) +CREATE VIEW webknossos.userInfos AS +SELECT +u._id AS _user, m.email, u.firstName, u.lastname, o.displayName AS organization_displayName, +u.isDeactivated, u.isDatasetManager, u.isAdmin, m.isSuperUser, +u._organization, o.name AS organization_name, u.created AS user_created, +m.created AS multiuser_created, u._multiUser, m._lastLoggedInIdentity, u.lastActivity +FROM webknossos.users_ u +JOIN webknossos.organizations_ o ON u._organization = o._id +JOIN webknossos.multiUsers_ m on u._multiUser = m._id; + + +UPDATE webknossos.releaseInformation SET schemaVersion = 65; + +COMMIT TRANSACTION; diff --git a/conf/evolutions/reversions/065-unlisted-superusers.sql b/conf/evolutions/reversions/065-unlisted-superusers.sql new file mode 100644 index 00000000000..2a716907e71 --- /dev/null +++ b/conf/evolutions/reversions/065-unlisted-superusers.sql @@ -0,0 +1,25 @@ +START TRANSACTION; + +DROP VIEW webknossos.userInfos; +DROP VIEW webknossos.users_; + +ALTER TABLE webknossos.users DROP COLUMN isUnlisted; + +CREATE VIEW webknossos.users_ AS SELECT * FROM webknossos.users WHERE NOT isDeleted; + + +-- identical to previous (has to be dropped first because of the dependency) +CREATE VIEW webknossos.userInfos AS +SELECT +u._id AS _user, m.email, u.firstName, u.lastname, o.displayName AS organization_displayName, +u.isDeactivated, u.isDatasetManager, u.isAdmin, m.isSuperUser, +u._organization, o.name AS organization_name, u.created AS user_created, +m.created AS multiuser_created, u._multiUser, m._lastLoggedInIdentity, u.lastActivity +FROM webknossos.users_ u +JOIN webknossos.organizations_ o ON u._organization = o._id +JOIN webknossos.multiUsers_ m on u._multiUser = m._id; + + +UPDATE webknossos.releaseInformation SET schemaVersion = 64; + +COMMIT TRANSACTION; diff --git a/tools/postgres/schema.sql b/tools/postgres/schema.sql index f8409cead86..19a7b0d1874 100644 --- a/tools/postgres/schema.sql +++ b/tools/postgres/schema.sql @@ -21,7 +21,7 @@ START TRANSACTION; CREATE TABLE webknossos.releaseInformation ( schemaVersion BIGINT NOT NULL ); -INSERT INTO webknossos.releaseInformation(schemaVersion) values(63); +INSERT INTO webknossos.releaseInformation(schemaVersion) values(65); COMMIT TRANSACTION; CREATE TABLE webknossos.analytics( @@ -288,6 +288,7 @@ CREATE TABLE webknossos.users( isDatasetManager BOOLEAN NOT NULL DEFAULT false, created TIMESTAMPTZ NOT NULL DEFAULT NOW(), lastTaskTypeId CHAR(24) DEFAULT NULL, + isUnlisted BOOLEAN NOT NULL DEFAULT FALSE, isDeleted BOOLEAN NOT NULL DEFAULT false, UNIQUE (_multiUser, _organization), CONSTRAINT userConfigurationIsJsonObject CHECK(jsonb_typeof(userConfiguration) = 'object') From 02f3cdd36b4c5837e0fd3f68911d5cbb1ac5e169 Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 10 Feb 2021 13:17:17 +0100 Subject: [PATCH 2/4] use isUnlisted property in scala code --- CHANGELOG.unreleased.md | 1 + app/controllers/InitialDataController.scala | 1 + app/models/user/User.scala | 10 +++++++--- app/models/user/UserService.scala | 11 ++++++++--- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index b34bdc18423..d5343ad6b6e 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -14,6 +14,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released - The "Meshes" tab was overhauled, so that it displays generated isosurfaces and imported meshes. Generated isosurfaces can be jumped to, reloaded, downloaded and removed. [#4917](https://github.com/scalableminds/webknossos/pull/4917) - Added an explicit `/signup` (or `/auth/signup`) route. [#5091](https://github.com/scalableminds/webknossos/pull/5091/files) - Added the annotation option "center new nodes" to switch whether newly created nodes should be centered or not. [#4150](https://github.com/scalableminds/webknossos/pull/5112) +- For webKnossos maintenance, superUsers can now join organizations without being listed as a user there. [#5151](https://github.com/scalableminds/webknossos/pull/5151) ### Changed - Make the isosurface feature in the meshes tab more robust. If a request fails, a retry is initiated. [#5102](https://github.com/scalableminds/webknossos/pull/5102) diff --git a/app/controllers/InitialDataController.scala b/app/controllers/InitialDataController.scala index 335b1d13deb..f123a6c55ac 100644 --- a/app/controllers/InitialDataController.scala +++ b/app/controllers/InitialDataController.scala @@ -87,6 +87,7 @@ Samplecountry userService.createLoginInfo(userId), isAdmin = true, isDatasetManager = true, + isUnlisted = false, isDeactivated = false, lastTaskTypeId = None ) diff --git a/app/models/user/User.scala b/app/models/user/User.scala index b2fc1e71bc5..81966a3a04a 100755 --- a/app/models/user/User.scala +++ b/app/models/user/User.scala @@ -106,7 +106,9 @@ class UserDAO @Inject()(sqlClient: SQLClient)(implicit ec: ExecutionContext) override def findAll(implicit ctx: DBAccessContext): Fox[List[User]] = for { accessQuery <- readAccessQuery - r <- run(sql"select #${columns} from #${existingCollectionName} where #${accessQuery}".as[UsersRow]) + r <- run( + sql"select #${columns} from #${existingCollectionName} where isUnlisted = false and #${accessQuery}" + .as[UsersRow]) parsed <- Fox.combined(r.toList.map(parse)) } yield parsed @@ -119,6 +121,7 @@ class UserDAO @Inject()(sqlClient: SQLClient)(implicit ec: ExecutionContext) r <- run(sql"""select #${columnsWithPrefix("u.")} from (select #${columns} from #${existingCollectionName} where #${accessQuery}) u join webknossos.user_team_roles on u._id = webknossos.user_team_roles._user where webknossos.user_team_roles._team in #${writeStructTupleWithQuotes(teams.map(_.id))} + and u.isUnlisted = false and (u.isDeactivated = false or u.isDeactivated = ${includeDeactivated}) order by _id""".as[UsersRow]) parsed <- Fox.combined(r.toList.map(parse)) @@ -192,11 +195,12 @@ class UserDAO @Inject()(sqlClient: SQLClient)(implicit ec: ExecutionContext) def insertOne(u: User): Fox[Unit] = for { _ <- run(sqlu"""insert into webknossos.users(_id, _multiUser, _organization, firstName, lastName, lastActivity, - userConfiguration, isDeactivated, isAdmin, isDatasetManager, created, isDeleted) + userConfiguration, isDeactivated, isAdmin, isDatasetManager, isUnlisted, created, isDeleted) values(${u._id}, ${u._multiUser}, ${u._organization}, ${u.firstName}, ${u.lastName}, ${new java.sql.Timestamp(u.lastActivity)}, '#${sanitize( Json.toJson(u.userConfiguration).toString)}', - ${u.isDeactivated}, ${u.isAdmin}, ${u.isDatasetManager}, ${new java.sql.Timestamp(u.created)}, ${u.isDeleted}) + ${u.isDeactivated}, ${u.isAdmin}, ${u.isDatasetManager}, ${u.isUnlisted}, ${new java.sql.Timestamp( + u.created)}, ${u.isDeleted}) """) } yield () diff --git a/app/models/user/UserService.scala b/app/models/user/UserService.scala index fd9c9aae57a..1aa01a03c8b 100755 --- a/app/models/user/UserService.scala +++ b/app/models/user/UserService.scala @@ -114,6 +114,7 @@ class UserService @Inject()(conf: WkConf, isAdmin, isDatasetManager = false, isDeactivated = !isActive, + isUnlisted = false, lastTaskTypeId = None ) _ <- userDAO.insertOne(user) @@ -128,12 +129,15 @@ class UserService @Inject()(conf: WkConf, .findOneByOrgaAndMultiUser(organizationId, originalUser._multiUser)(GlobalAccessContext) .futureBox _ <- if (multiUser.isSuperUser && existingIdentity.isEmpty) { - joinOrganization(originalUser, organizationId, autoActivate = true, isAdmin = true) + joinOrganization(originalUser, organizationId, autoActivate = true, isAdmin = true, isUnlisted = true) } else Fox.successful(()) } yield () - def joinOrganization(originalUser: User, organizationId: ObjectId, autoActivate: Boolean, isAdmin: Boolean = false)( - implicit ctx: DBAccessContext): Fox[User] = + def joinOrganization(originalUser: User, + organizationId: ObjectId, + autoActivate: Boolean, + isAdmin: Boolean = false, + isUnlisted: Boolean = false)(implicit ctx: DBAccessContext): Fox[User] = for { newUserId <- Fox.successful(ObjectId.generate) organizationTeamId <- organizationDAO.findOrganizationTeamId(organizationId) @@ -148,6 +152,7 @@ class UserService @Inject()(conf: WkConf, isDatasetManager = false, isDeactivated = !autoActivate, lastTaskTypeId = None, + isUnlisted = isUnlisted, created = System.currentTimeMillis() ) _ <- userDAO.insertOne(user) From 0ddcaddf0cc346a42a1e41f7af5f8dcde9b96af7 Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 10 Feb 2021 13:19:28 +0100 Subject: [PATCH 3/4] migrations guide --- MIGRATIONS.unreleased.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATIONS.unreleased.md b/MIGRATIONS.unreleased.md index 9863249ec45..859033e88a8 100644 --- a/MIGRATIONS.unreleased.md +++ b/MIGRATIONS.unreleased.md @@ -12,3 +12,4 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md). - [061-userinfos-view.sql](conf/evolutions/061-userinfos-view) - [062-dataset-uploader.sql](conf/evolutions/062-dataset-uploader.sql) - [063-novelUserExperienceinfos.sql](conf/evolutions/063-novelUserExperienceinfos.sql) +- [065-unlisted-superusers.sql](conf/evolutions/065-unlisted-superusers.sql) From 29031e5b95c3f298d0a19db5dc0ddeff7ac0326a Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 10 Feb 2021 13:54:00 +0100 Subject: [PATCH 4/4] adapt test db --- test/db/users.csv | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/db/users.csv b/test/db/users.csv index 78a42b8bcdf..1e1a3e1881e 100644 --- a/test/db/users.csv +++ b/test/db/users.csv @@ -1,6 +1,6 @@ -_id,_multiUser,_organization,firstName,lastName,lastActivity,userConfiguration,isDeactivated,isAdmin,isDatasetManager,created,isDeleted,lasttasktypeid -'570b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23ea','5ab0c6a674d0af7b003b23ac','user_A','BoyA','2016-04-11T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,t,t,'2016-04-11T12:57:49.000Z',,f -'670b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23eb','5ab0c6a674d0af7b003b23ac','user_B','BoyB','2016-04-12T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,f,t,'2016-04-11T12:57:49.000Z',,f -'770b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23ec','5ab0c6a674d0af7b003b23ac','user_C','BoyC','2016-04-13T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,f,f,'2016-04-11T12:57:49.000Z',,f -'870b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23ed','5ab0c6a674d0af7b003b23ac','user_D','BoyD','2016-04-14T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,f,f,'2016-04-11T12:57:49.000Z',,f -'970b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23ee','6bb0c6a674d0af7b003b23bd','user_E','BoyE','2016-04-06T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,t,f,'2016-04-11T12:57:49.000Z',,f +_id,_multiUser,_organization,firstName,lastName,lastActivity,userConfiguration,isDeactivated,isAdmin,isDatasetManager,created,lasttasktypeid,isUnlisted,isDeleted +'570b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23ea','5ab0c6a674d0af7b003b23ac','user_A','BoyA','2016-04-11T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,t,t,'2016-04-11T12:57:49.000Z',,f,f +'670b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23eb','5ab0c6a674d0af7b003b23ac','user_B','BoyB','2016-04-12T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,f,t,'2016-04-11T12:57:49.000Z',,f,f +'770b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23ec','5ab0c6a674d0af7b003b23ac','user_C','BoyC','2016-04-13T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,f,f,'2016-04-11T12:57:49.000Z',,f,f +'870b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23ed','5ab0c6a674d0af7b003b23ac','user_D','BoyD','2016-04-14T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,f,f,'2016-04-11T12:57:49.000Z',,f,f +'970b9f4d2a7c0e4d008da6ef','8fb0c6a674d0af7b003b23ee','6bb0c6a674d0af7b003b23bd','user_E','BoyE','2016-04-06T12:57:49.053Z','{"configuration":{"sortCommentsAsc":true,"messages":[{"success":"Your configuration got updated"}],"isosurfaceDisplay":false,"moveValue":300,"rotateValue":0.01,"particleSize":5,"scale":1,"clippingDistance":50,"mouseRotateValue":0.004,"displayCrosshair":true,"crosshairSize":0.1,"sortTreesByName":false,"keyboardDelay":200,"motionsensorActive":false,"moveValue3d":300,"keyboardActive":true,"isosurfaceResolution":80,"isosurfaceBBsize":1,"mouseActive":true,"newNodeNewTree":false,"segmentationOpacity":20,"inverseY":false,"sphericalCapRadius":140,"clippingDistanceArbitrary":64,"scaleValue":0.05,"tdViewDisplayPlanes":true,"overrideNodeRadius":true,"renderComments":false,"zoom":2,"inverseX":false,"gamepadActive":false,"dynamicSpaceDirection":true,"firstVisToggle":true}}',f,t,f,'2016-04-11T12:57:49.000Z',,f,f