Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add first login timestamp of each user to oc_preferences and user:info output #49377

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions apps/provisioning_api/lib/Controller/AUserData.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ protected function getUserData(string $userId, bool $includeScopes = false): ?ar

// Find the data
$data['id'] = $targetUserObject->getUID();
$data['firstLoginTimestamp'] = $targetUserObject->getFirstLogin();
$data['lastLoginTimestamp'] = $targetUserObject->getLastLogin();
$data['lastLogin'] = $targetUserObject->getLastLogin() * 1000;
$data['backend'] = $targetUserObject->getBackendClassName();
$data['subadmin'] = $this->getUserSubAdminGroupsData($targetUserObject->getUID());
Expand Down
2 changes: 2 additions & 0 deletions apps/provisioning_api/lib/ResponseDefinitions.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
* headlineScope?: Provisioning_APIUserDetailsScope,
* id: string,
* language: string,
* firstLoginTimestamp: int,
* lastLoginTimestamp: int,
* lastLogin: int,
* locale: string,
* manager: string,
Expand Down
10 changes: 10 additions & 0 deletions apps/provisioning_api/openapi-administration.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@
"headline",
"id",
"language",
"firstLoginTimestamp",
"lastLoginTimestamp",
"lastLogin",
"locale",
"manager",
Expand Down Expand Up @@ -195,6 +197,14 @@
"language": {
"type": "string"
},
"firstLoginTimestamp": {
"type": "integer",
"format": "int64"
},
"lastLoginTimestamp": {
"type": "integer",
"format": "int64"
},
"lastLogin": {
"type": "integer",
"format": "int64"
Expand Down
10 changes: 10 additions & 0 deletions apps/provisioning_api/openapi-full.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@
"headline",
"id",
"language",
"firstLoginTimestamp",
"lastLoginTimestamp",
"lastLogin",
"locale",
"manager",
Expand Down Expand Up @@ -242,6 +244,14 @@
"language": {
"type": "string"
},
"firstLoginTimestamp": {
"type": "integer",
"format": "int64"
},
"lastLoginTimestamp": {
"type": "integer",
"format": "int64"
},
"lastLogin": {
"type": "integer",
"format": "int64"
Expand Down
10 changes: 10 additions & 0 deletions apps/provisioning_api/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@
"headline",
"id",
"language",
"firstLoginTimestamp",
"lastLoginTimestamp",
"lastLogin",
"locale",
"manager",
Expand Down Expand Up @@ -242,6 +244,14 @@
"language": {
"type": "string"
},
"firstLoginTimestamp": {
"type": "integer",
"format": "int64"
},
"lastLoginTimestamp": {
"type": "integer",
"format": "int64"
},
"lastLogin": {
"type": "integer",
"format": "int64"
Expand Down
24 changes: 21 additions & 3 deletions apps/provisioning_api/tests/Controller/UsersControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1144,9 +1144,13 @@ public function testGetUserDataAsAdmin(): void {
->method('getHome')
->willReturn('/var/www/newtcloud/data/UID');
$targetUser
->expects($this->once())
->expects($this->exactly(2))
->method('getLastLogin')
->willReturn(1521191471);
$targetUser
->expects($this->once())
->method('getFirstLogin')
->willReturn(1511191471);
$targetUser
->expects($this->once())
->method('getBackendClassName')
Expand All @@ -1169,6 +1173,8 @@ public function testGetUserDataAsAdmin(): void {
'id' => 'UID',
'enabled' => true,
'storageLocation' => '/var/www/newtcloud/data/UID',
'firstLoginTimestamp' => 1511191471,
'lastLoginTimestamp' => 1521191471,
'lastLogin' => 1521191471000,
'backend' => 'Database',
'subadmin' => ['group3'],
Expand Down Expand Up @@ -1270,9 +1276,13 @@ public function testGetUserDataAsSubAdminAndUserIsAccessible(): void {
->expects($this->never())
->method('getHome');
$targetUser
->expects($this->once())
->expects($this->exactly(2))
->method('getLastLogin')
->willReturn(1521191471);
$targetUser
->expects($this->once())
->method('getFirstLogin')
->willReturn(1511191471);
$targetUser
->expects($this->once())
->method('getBackendClassName')
Expand Down Expand Up @@ -1308,6 +1318,8 @@ public function testGetUserDataAsSubAdminAndUserIsAccessible(): void {
$expected = [
'id' => 'UID',
'enabled' => true,
'firstLoginTimestamp' => 1511191471,
'lastLoginTimestamp' => 1521191471,
'lastLogin' => 1521191471000,
'backend' => 'Database',
'subadmin' => [],
Expand Down Expand Up @@ -1452,9 +1464,13 @@ public function testGetUserDataAsSubAdminSelfLookup(): void {
->expects($this->never())
->method('getHome');
$targetUser
->expects($this->once())
->expects($this->exactly(2))
->method('getLastLogin')
->willReturn(1521191471);
$targetUser
->expects($this->once())
->method('getFirstLogin')
->willReturn(1511191471);
$targetUser
->expects($this->once())
->method('getBackendClassName')
Expand Down Expand Up @@ -1485,6 +1501,8 @@ public function testGetUserDataAsSubAdminSelfLookup(): void {

$expected = [
'id' => 'UID',
'firstLoginTimestamp' => 1511191471,
'lastLoginTimestamp' => 1521191471,
'lastLogin' => 1521191471000,
'backend' => 'Database',
'subadmin' => [],
Expand Down
6 changes: 6 additions & 0 deletions apps/settings/src/components/Users/UserListHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@
{{ t('settings', 'Storage location') }}
</span>
</th>
<th v-if="showConfig.showFirstLogin"
class="header__cell"
data-cy-user-list-header-first-login
scope="col">
<span>{{ t('settings', 'First login') }}</span>
</th>
<th v-if="showConfig.showLastLogin"
class="header__cell"
data-cy-user-list-header-last-login
Expand Down
6 changes: 6 additions & 0 deletions apps/settings/src/components/Users/UserRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,12 @@
</template>
</td>

<td v-if="showConfig.showFirstLogin"
class="row__cell"
data-cy-user-list-cell-first-login>
<span v-if="!isObfuscated">{{ userFirstLogin }}</span>
</td>

<td v-if="showConfig.showLastLogin"
:title="userLastLoginTooltip"
class="row__cell"
Expand Down
14 changes: 14 additions & 0 deletions apps/settings/src/components/Users/UserSettingsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
:checked.sync="showStoragePath">
{{ t('settings', 'Show storage path') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch type="switch"
data-test="showFirstLogin"
:checked.sync="showFirstLogin">
{{ t('settings', 'Show first login') }}
</NcCheckboxRadioSwitch>
<NcCheckboxRadioSwitch type="switch"
data-test="showLastLogin"
:checked.sync="showLastLogin">
Expand Down Expand Up @@ -164,6 +169,15 @@ export default {
},
},

showFirstLogin: {
get() {
return this.showConfig.showFirstLogin
},
set(status) {
this.setShowConfig('showFirstLogin', status)
},
},

showLastLogin: {
get() {
return this.showConfig.showLastLogin
Expand Down
25 changes: 21 additions & 4 deletions apps/settings/src/mixins/UserRowMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import { formatFileSize } from '@nextcloud/files'
import { useFormatDateTime } from '@nextcloud/vue'

export default {
props: {
Expand Down Expand Up @@ -36,6 +37,12 @@ export default {
default: () => [],
},
},
setup(props) {
const { formattedFullTime } = useFormatDateTime(props.user.firstLoginTimestamp * 1000, { relativeTime: false })
return {
formattedFullTime,
}
},
computed: {
showConfig() {
return this.$store.getters.getShowConfig
Expand Down Expand Up @@ -120,16 +127,26 @@ export default {
return userLang
},

userFirstLogin() {
if (this.user.firstLoginTimestamp > 0) {
return this.formattedFullTime
}
if (this.user.firstLoginTimestamp < 0) {
return t('settings', 'Unknown')
}
return t('settings', 'Never')
},

/* LAST LOGIN */
userLastLoginTooltip() {
if (this.user.lastLogin > 0) {
return OC.Util.formatDate(this.user.lastLogin)
if (this.user.lastLoginTimestamp > 0) {
return OC.Util.formatDate(this.user.lastLoginTimestamp * 1000)
}
return ''
},
userLastLogin() {
if (this.user.lastLogin > 0) {
return OC.Util.relativeModifiedDate(this.user.lastLogin)
if (this.user.lastLoginTimestamp > 0) {
return OC.Util.relativeModifiedDate(this.user.lastLoginTimestamp * 1000)
}
return t('settings', 'Never')
},
Expand Down
1 change: 1 addition & 0 deletions apps/settings/src/store/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const state = {
showConfig: {
showStoragePath: localStorage.getItem('account_settings__showStoragePath') === 'true',
showUserBackend: localStorage.getItem('account_settings__showUserBackend') === 'true',
showFirstLogin: localStorage.getItem('account_settings__showFirstLogin') === 'true',
showLastLogin: localStorage.getItem('account_settings__showLastLogin') === 'true',
showNewUserForm: localStorage.getItem('account_settings__showNewUserForm') === 'true',
showLanguages: localStorage.getItem('account_settings__showLanguages') === 'true',
Expand Down
13 changes: 12 additions & 1 deletion core/Command/User/Info.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,25 @@ protected function execute(InputInterface $input, OutputInterface $output): int
'groups' => $groups,
'quota' => $user->getQuota(),
'storage' => $this->getStorageInfo($user),
'last_seen' => date(\DateTimeInterface::ATOM, $user->getLastLogin()), // ISO-8601
'first_seen' => $this->formatLoginDate($user->getFirstLogin()),
'last_seen' => $this->formatLoginDate($user->getLastLogin()),
'user_directory' => $user->getHome(),
'backend' => $user->getBackendClassName()
];
$this->writeArrayInOutputFormat($input, $output, $data);
return 0;
}

private function formatLoginDate(int $timestamp): string {
if ($timestamp < 0) {
return 'unknown';
} elseif ($timestamp === 0) {
return 'never';
} else {
return date(\DateTimeInterface::ATOM, $timestamp); // ISO-8601
}
}

/**
* @param IUser $user
* @return array
Expand Down
13 changes: 12 additions & 1 deletion core/Command/User/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ private function formatUsers(array $users, bool $detailed = false): \Generator {
'enabled' => $user->isEnabled(),
'groups' => $groups,
'quota' => $user->getQuota(),
'last_seen' => date(\DateTimeInterface::ATOM, $user->getLastLogin()), // ISO-8601
'first_seen' => $this->formatLoginDate($user->getFirstLogin()),
'last_seen' => $this->formatLoginDate($user->getLastLogin()),
'user_directory' => $user->getHome(),
'backend' => $user->getBackendClassName()
];
Expand All @@ -93,4 +94,14 @@ private function formatUsers(array $users, bool $detailed = false): \Generator {
yield $user->getUID() => $value;
}
}

private function formatLoginDate(int $timestamp): string {
if ($timestamp < 0) {
return 'unknown';
} elseif ($timestamp === 0) {
return 'never';
} else {
return date(\DateTimeInterface::ATOM, $timestamp); // ISO-8601
}
}
}
4 changes: 2 additions & 2 deletions dist/6127-6127.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/6127-6127.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/comments-comments-app.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/comments-comments-app.js.map

Large diffs are not rendered by default.

Loading
Loading