mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #6093 from overleaf/jpa-last-user-activity
[web] add a last active date for denoting account activity in groups mgt GitOrigin-RevId: 32a9e5c1e8f63e794bf6d379685b6dda7e6d4d22
This commit is contained in:
parent
5eed78a3e8
commit
00e792b022
9 changed files with 47 additions and 10 deletions
|
@ -664,6 +664,12 @@ const ProjectController = {
|
|||
if (userId == null) {
|
||||
cb(null, defaultSettingsForAnonymousUser(userId))
|
||||
} else {
|
||||
User.updateOne(
|
||||
{ _id: ObjectId(userId) },
|
||||
{ $set: { lastActive: new Date() } },
|
||||
{},
|
||||
() => {}
|
||||
)
|
||||
User.findById(
|
||||
userId,
|
||||
'email first_name last_name referal_id signUpDate featureSwitches features featuresEpoch refProviders alphaProgram betaProgram isAdmin ace',
|
||||
|
|
|
@ -137,7 +137,7 @@ module.exports = {
|
|||
|
||||
exportCsv(req, res, next) {
|
||||
const { entity, entityConfig } = req
|
||||
const fields = ['email', 'last_logged_in_at']
|
||||
const fields = ['email', 'last_logged_in_at', 'last_active_at']
|
||||
|
||||
return UserMembershipHandler.getUsers(
|
||||
entity,
|
||||
|
|
|
@ -38,6 +38,7 @@ module.exports = UserMembershipViewModel = {
|
|||
first_name: 1,
|
||||
last_name: 1,
|
||||
lastLoggedIn: 1,
|
||||
lastActive: 1,
|
||||
}
|
||||
return UserGetter.getUser(userId, projection, function (error, user) {
|
||||
if (error != null || user == null) {
|
||||
|
@ -57,6 +58,7 @@ function buildUserViewModel(user, isInvite) {
|
|||
email: user.email || null,
|
||||
first_name: user.first_name || null,
|
||||
last_name: user.last_name || null,
|
||||
last_active_at: user.lastActive || user.lastLoggedIn || null,
|
||||
last_logged_in_at: user.lastLoggedIn || null,
|
||||
invite: isInvite,
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ const UserSchema = new Schema({
|
|||
return new Date()
|
||||
},
|
||||
},
|
||||
lastActive: { type: Date },
|
||||
lastLoggedIn: { type: Date },
|
||||
lastLoginIp: { type: String, default: '' },
|
||||
loginCount: { type: Number, default: 0 },
|
||||
|
|
|
@ -44,7 +44,13 @@ block content
|
|||
.col-md-4
|
||||
span.header #{translate("name")}
|
||||
.col-md-2
|
||||
span.header #{translate("last_login")}
|
||||
span.header(
|
||||
tooltip=translate('last_active_description')
|
||||
tooltip-placement="left"
|
||||
tooltip-append-to-body="true"
|
||||
)
|
||||
| #{translate("last_active")}
|
||||
sup (?)
|
||||
.col-md-2
|
||||
span.header #{translate("accepted_invite")}
|
||||
li.container-fluid(
|
||||
|
@ -62,7 +68,7 @@ block content
|
|||
.col-md-4
|
||||
span.name {{ user.first_name }} {{ user.last_name }}
|
||||
.col-md-2
|
||||
span.lastLogin {{ user.last_logged_in_at | formatDate:'Do MMM YYYY' }}
|
||||
span.lastLogin {{ user.last_active_at | formatDate:'Do MMM YYYY' }}
|
||||
.col-md-2
|
||||
span.registered
|
||||
i.fa.fa-check.text-success(ng-show="!user.invite" aria-hidden="true")
|
||||
|
|
|
@ -64,6 +64,8 @@
|
|||
"wed_love_you_to_stay": "We’d love you to stay",
|
||||
"yes_move_me_to_student_plan": "Yes, move me to the student plan",
|
||||
"last_login": "Last Login",
|
||||
"last_active": "Last Active",
|
||||
"last_active_description": "Last time a project was opened.",
|
||||
"thank_you_for_being_part_of_our_beta_program": "Thank you for being part of our Beta Program, where you can have early access to new features and help us understand your needs better",
|
||||
"you_will_be_able_to_contact_us_any_time_to_share_your_feedback": "You will be able to contact us any time to share your feedback",
|
||||
"we_may_also_contact_you_from_time_to_time_by_email_with_a_survey": "We may also contact you from time to time by email with a survey, or to see if you would like to participate in other user research initiatives",
|
||||
|
|
|
@ -58,7 +58,7 @@ describe('ProjectController', function () {
|
|||
this.LimitationsManager = { hasPaidSubscription: sinon.stub() }
|
||||
this.TagsHandler = { getAllTags: sinon.stub() }
|
||||
this.NotificationsHandler = { getUserNotifications: sinon.stub() }
|
||||
this.UserModel = { findById: sinon.stub() }
|
||||
this.UserModel = { findById: sinon.stub(), updateOne: sinon.stub() }
|
||||
this.AuthorizationManager = {
|
||||
getPrivilegeLevelForProject: sinon.stub(),
|
||||
isRestrictedUser: sinon.stub().returns(false),
|
||||
|
@ -939,7 +939,7 @@ describe('ProjectController', function () {
|
|||
brandVariationId: '12',
|
||||
}
|
||||
this.user = {
|
||||
_id: '588f3ddae8ebc1bac07c9fa4',
|
||||
_id: this.user._id,
|
||||
ace: {
|
||||
fontSize: 'massive',
|
||||
theme: 'sexy',
|
||||
|
@ -1048,6 +1048,18 @@ describe('ProjectController', function () {
|
|||
this.ProjectController.loadEditor(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should mark user as active', function (done) {
|
||||
this.res.render = (pageName, opts) => {
|
||||
expect(this.UserModel.updateOne).to.have.been.calledOnce
|
||||
expect(this.UserModel.updateOne.args[0][0]).to.deep.equal({
|
||||
_id: ObjectId(this.user._id),
|
||||
})
|
||||
expect(this.UserModel.updateOne.args[0][1].$set.lastActive).to.exist
|
||||
done()
|
||||
}
|
||||
this.ProjectController.loadEditor(this.req, this.res)
|
||||
})
|
||||
|
||||
it('should mark project as opened', function (done) {
|
||||
this.res.render = (pageName, opts) => {
|
||||
this.ProjectUpdateHandler.markAsOpened
|
||||
|
|
|
@ -46,11 +46,13 @@ describe('UserMembershipController', function () {
|
|||
_id: 'mock-member-id-1',
|
||||
email: 'mock-email-1@foo.com',
|
||||
last_logged_in_at: '2020-08-09T12:43:11.467Z',
|
||||
last_active_at: '2021-08-09T12:43:11.467Z',
|
||||
},
|
||||
{
|
||||
_id: 'mock-member-id-2',
|
||||
email: 'mock-email-2@foo.com',
|
||||
last_logged_in_at: '2020-05-20T10:41:11.407Z',
|
||||
last_active_at: '2021-05-20T10:41:11.407Z',
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -307,7 +309,7 @@ describe('UserMembershipController', function () {
|
|||
it('should export the correct csv', function () {
|
||||
return assertCalledWith(
|
||||
this.res.send,
|
||||
'"email","last_logged_in_at"\n"mock-email-1@foo.com","2020-08-09T12:43:11.467Z"\n"mock-email-2@foo.com","2020-05-20T10:41:11.407Z"'
|
||||
'"email","last_logged_in_at","last_active_at"\n"mock-email-1@foo.com","2020-08-09T12:43:11.467Z","2021-08-09T12:43:11.467Z"\n"mock-email-2@foo.com","2020-05-20T10:41:11.407Z","2021-05-20T10:41:11.407Z"'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -48,6 +48,7 @@ describe('UserMembershipViewModel', function () {
|
|||
return expect(viewModel).to.deep.equal({
|
||||
email: this.email,
|
||||
invite: true,
|
||||
last_active_at: null,
|
||||
last_logged_in_at: null,
|
||||
first_name: null,
|
||||
last_name: null,
|
||||
|
@ -57,10 +58,15 @@ describe('UserMembershipViewModel', function () {
|
|||
|
||||
it('build user', function () {
|
||||
const viewModel = this.UserMembershipViewModel.build(this.user)
|
||||
expect(viewModel._id).to.equal(this.user._id)
|
||||
expect(viewModel.email).to.equal(this.user.email)
|
||||
expect(viewModel.last_logged_in_at).to.equal(this.user.lastLoggedIn)
|
||||
return expect(viewModel.invite).to.equal(false)
|
||||
expect(viewModel).to.deep.equal({
|
||||
email: this.user.email,
|
||||
invite: false,
|
||||
last_active_at: this.user.lastLoggedIn,
|
||||
last_logged_in_at: this.user.lastLoggedIn,
|
||||
first_name: this.user.first_name,
|
||||
last_name: null,
|
||||
_id: this.user._id,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in a new issue