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(accounts): implement birthday prop #39592

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/dav/lib/CardDAV/Converter.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ public function createCardFromUser(IUser $user): ?VCard {
case IAccountManager::PROPERTY_TWITTER:
$vCard->add(new Text($vCard, 'X-SOCIALPROFILE', $property->getValue(), ['TYPE' => 'TWITTER', 'X-NC-SCOPE' => $scope]));
break;
// TODO
case IAccountManager::PROPERTY_BIRTHDAY:
$vCard->add(new Text($vCard, 'BIRTHDAY', $property->getValue(), ['TYPE' => 'BIRTHDAY', 'X-NC-SCOPE' => $scope]));
szaimen marked this conversation as resolved.
Show resolved Hide resolved
break;
case IAccountManager::PROPERTY_ORGANISATION:
$vCard->add(new Text($vCard, 'ORG', $property->getValue(), ['X-NC-SCOPE' => $scope]));
break;
Expand Down
1 change: 1 addition & 0 deletions apps/provisioning_api/lib/Controller/AUserData.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ protected function getUserData(string $userId, bool $includeScopes = false): arr
IAccountManager::PROPERTY_ADDRESS,
IAccountManager::PROPERTY_WEBSITE,
IAccountManager::PROPERTY_TWITTER,
IAccountManager::PROPERTY_BIRTHDAY,
IAccountManager::PROPERTY_FEDIVERSE,
IAccountManager::PROPERTY_ORGANISATION,
IAccountManager::PROPERTY_ROLE,
Expand Down
6 changes: 6 additions & 0 deletions apps/provisioning_api/lib/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ public function getEditableFieldsForUser(string $userId): DataResponse {
}

$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
$permittedFields[] = IAccountManager::PROPERTY_BIRTHDAY;
$permittedFields[] = IAccountManager::PROPERTY_PHONE;
$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
Expand Down Expand Up @@ -786,6 +787,7 @@ public function editUser(string $userId, string $key, string $value): DataRespon
$permittedFields[] = self::USER_FIELD_LOCALE;
}

$permittedFields[] = IAccountManager::PROPERTY_BIRTHDAY;
$permittedFields[] = IAccountManager::PROPERTY_PHONE;
$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
Expand All @@ -796,6 +798,7 @@ public function editUser(string $userId, string $key, string $value): DataRespon
$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
$permittedFields[] = IAccountManager::PROPERTY_BIRTHDAY . self::SCOPE_SUFFIX;
$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
Expand Down Expand Up @@ -835,6 +838,7 @@ public function editUser(string $userId, string $key, string $value): DataRespon
$permittedFields[] = self::USER_FIELD_PASSWORD;
$permittedFields[] = self::USER_FIELD_LANGUAGE;
$permittedFields[] = self::USER_FIELD_LOCALE;
$permittedFields[] = IAccountManager::PROPERTY_BIRTHDAY;
$permittedFields[] = IAccountManager::PROPERTY_PHONE;
$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
Expand Down Expand Up @@ -968,6 +972,7 @@ public function editUser(string $userId, string $key, string $value): DataRespon
throw new OCSException('', 102);
}
break;
case IAccountManager::PROPERTY_BIRTHDAY:
case IAccountManager::PROPERTY_PHONE:
case IAccountManager::PROPERTY_ADDRESS:
case IAccountManager::PROPERTY_WEBSITE:
Expand Down Expand Up @@ -1011,6 +1016,7 @@ public function editUser(string $userId, string $key, string $value): DataRespon
}
$this->accountManager->updateAccount($userAccount);
break;
case IAccountManager::PROPERTY_BIRTHDAY . self::SCOPE_SUFFIX:
case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
Expand Down
9 changes: 8 additions & 1 deletion apps/settings/lib/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ protected function canAdminChangeUserPasswords(): bool {
* @param string|null $twitterScope
* @param string|null $fediverse
* @param string|null $fediverseScope
* @param string|null $birthday
* @param string|null $birthdayScope
*
* @return DataResponse
*/
Expand All @@ -380,7 +382,9 @@ public function setUserSettings(?string $avatarScope = null,
?string $twitter = null,
?string $twitterScope = null,
?string $fediverse = null,
?string $fediverseScope = null
?string $fediverseScope = null,
?string $birthday = null,
?string $birthdayScope = null
) {
$user = $this->userSession->getUser();
if (!$user instanceof IUser) {
Expand Down Expand Up @@ -420,6 +424,7 @@ public function setUserSettings(?string $avatarScope = null,
IAccountManager::PROPERTY_PHONE => ['value' => $phone, 'scope' => $phoneScope],
IAccountManager::PROPERTY_TWITTER => ['value' => $twitter, 'scope' => $twitterScope],
IAccountManager::PROPERTY_FEDIVERSE => ['value' => $fediverse, 'scope' => $fediverseScope],
IAccountManager::PROPERTY_BIRTHDAY => ['value' => $birthday, 'scope' => $birthdayScope],
];
$allowUserToChangeDisplayName = $this->config->getSystemValueBool('allow_user_to_change_display_name', true);
foreach ($updatable as $property => $data) {
Expand Down Expand Up @@ -461,6 +466,8 @@ public function setUserSettings(?string $avatarScope = null,
'twitterScope' => $userAccount->getProperty(IAccountManager::PROPERTY_TWITTER)->getScope(),
'fediverse' => $userAccount->getProperty(IAccountManager::PROPERTY_FEDIVERSE)->getValue(),
'fediverseScope' => $userAccount->getProperty(IAccountManager::PROPERTY_FEDIVERSE)->getScope(),
'birthday' => $userAccount->getProperty(IAccountManager::PROPERTY_BIRTHDAY)->getValue(),
'birthdayScope' => $userAccount->getProperty(IAccountManager::PROPERTY_BIRTHDAY)->getScope(),
'message' => $this->l10n->t('Settings saved'),
],
],
Expand Down
1 change: 1 addition & 0 deletions apps/settings/lib/Settings/Personal/PersonalInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ public function getForm(): TemplateResponse {
'role' => $this->getProperty($account, IAccountManager::PROPERTY_ROLE),
'headline' => $this->getProperty($account, IAccountManager::PROPERTY_HEADLINE),
'biography' => $this->getProperty($account, IAccountManager::PROPERTY_BIOGRAPHY),
'birthday' => $this->getProperty($account, IAccountManager::PROPERTY_BIRTHDAY),
];

$accountParameters = [
Expand Down
150 changes: 150 additions & 0 deletions apps/settings/src/components/PersonalInfo/BirthdaySection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<!--
- @copyright 2022 Christopher Ng <chrng8@gmail.com>
-
- @author Christopher Ng <chrng8@gmail.com>
-
- @license AGPL-3.0-or-later
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-->

<template>
<section>
<HeaderBar :input-id="inputId"
:readable="propertyReadable" />

<template>
<NcDateTimePickerNative :id="inputId"
type="date"
:label="t('settings', 'Your birthday')"
:value="birthdayValue"
@input="onInput" />
</template>
</section>
</template>

<script>
import { loadState } from '@nextcloud/initial-state'

import HeaderBar from './shared/HeaderBar.vue'
import AccountPropertySection from './shared/AccountPropertySection.vue'

import {
NAME_READABLE_ENUM,
ACCOUNT_SETTING_PROPERTY_READABLE_ENUM,
ACCOUNT_SETTING_PROPERTY_ENUM,
} from '../../constants/AccountPropertyConstants.js'

import { NcDateTimePickerNative } from '@nextcloud/vue'
import debounce from 'debounce'
import { savePrimaryAccountProperty } from '../../service/PersonalInfo/PersonalInfoService'
import { handleError } from '../../utils/handlers'

const { birthday } = loadState('settings', 'personalInfoParameters', {})

export default {
name: 'BirthdaySection',

components: {
AccountPropertySection,
NcDateTimePickerNative,
HeaderBar,
},

data() {
return {
propertyReadable: 'Birthday', // ACCOUNT_SETTING_PROPERTY_READABLE_ENUM.BIRTHDAY,
readable: 'Birthday',
birthday: { ...birthday, readable: NAME_READABLE_ENUM[birthday.name] },
birthdayValue: new Date(),
name: 'birthday',
}
},

computed: {
inputId() {
return `account-setting-${ACCOUNT_SETTING_PROPERTY_ENUM.BIRTHDAY}`
},
},

mounted() {
console.log('birthday', this, ACCOUNT_SETTING_PROPERTY_READABLE_ENUM)
},

methods: {
onInput(e) {
console.log('onInput', e)
this.birtdayValue = e
this.debouncePropertyChange(this.birthdayValue.toString())
},

debouncePropertyChange: debounce(async function(value) {
Copy link
Contributor

@szaimen szaimen Jul 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: should not duplicate the debounce logic

this.helperText = null
if (this.$refs.input && this.$refs.input.validationMessage) {
this.helperText = this.$refs.input.validationMessage
return
}
if (this.onValidate && !this.onValidate(value)) {
return
}
await this.updateProperty(value)
}, 500),

async updateProperty(value) {
try {
const responseData = await savePrimaryAccountProperty(
this.name,
value,
)
this.handleResponse({
value,
status: responseData.ocs?.meta?.status,
})
} catch (e) {
this.handleResponse({
errorMessage: t('settings', 'Unable to update {property}', { property: this.readable.toLocaleLowerCase() }),
error: e,
})
}
},

handleResponse({ value, status, errorMessage, error }) {
if (status === 'ok') {
this.initialValue = value
if (this.onSave) {
this.onSave(value)
}
this.showCheckmarkIcon = true
setTimeout(() => { this.showCheckmarkIcon = false }, 2000)
} else {
this.$emit('update:value', this.initialValue)
handleError(error, errorMessage)
this.showErrorIcon = true
setTimeout(() => { this.showErrorIcon = false }, 2000)
}
},
},
}
</script>

<style lang="scss" scoped>
section {
padding: 10px 10px;

&::v-deep button:disabled {
cursor: default;
}
}
</style>
5 changes: 5 additions & 0 deletions apps/settings/src/constants/AccountPropertyConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const ACCOUNT_PROPERTY_ENUM = Object.freeze({
ROLE: 'role',
TWITTER: 'twitter',
WEBSITE: 'website',
BIRTHDAY: 'birthday',
})

/** Enum of account properties to human readable account property names */
Expand All @@ -61,6 +62,7 @@ export const ACCOUNT_PROPERTY_READABLE_ENUM = Object.freeze({
TWITTER: t('settings', 'Twitter'),
FEDIVERSE: t('settings', 'Fediverse (e.g. Mastodon)'),
WEBSITE: t('settings', 'Website'),
BIRTHDAY: t('settings', 'Birthday'),
})

export const NAME_READABLE_ENUM = Object.freeze({
Expand All @@ -78,6 +80,7 @@ export const NAME_READABLE_ENUM = Object.freeze({
[ACCOUNT_PROPERTY_ENUM.TWITTER]: ACCOUNT_PROPERTY_READABLE_ENUM.TWITTER,
[ACCOUNT_PROPERTY_ENUM.FEDIVERSE]: ACCOUNT_PROPERTY_READABLE_ENUM.FEDIVERSE,
[ACCOUNT_PROPERTY_ENUM.WEBSITE]: ACCOUNT_PROPERTY_READABLE_ENUM.WEBSITE,
[ACCOUNT_PROPERTY_ENUM.BIRTHDAY]: ACCOUNT_PROPERTY_READABLE_ENUM.BIRTHDAY,
})

/** Enum of profile specific sections to human readable names */
Expand All @@ -101,6 +104,7 @@ export const PROPERTY_READABLE_KEYS_ENUM = Object.freeze({
[ACCOUNT_PROPERTY_READABLE_ENUM.TWITTER]: ACCOUNT_PROPERTY_ENUM.TWITTER,
[ACCOUNT_PROPERTY_READABLE_ENUM.FEDIVERSE]: ACCOUNT_PROPERTY_ENUM.FEDIVERSE,
[ACCOUNT_PROPERTY_READABLE_ENUM.WEBSITE]: ACCOUNT_PROPERTY_ENUM.WEBSITE,
[ACCOUNT_PROPERTY_READABLE_ENUM.BIRTHDAY]: ACCOUNT_PROPERTY_ENUM.BIRTHDAY,
})

/**
Expand Down Expand Up @@ -143,6 +147,7 @@ export const PROPERTY_READABLE_SUPPORTED_SCOPES_ENUM = Object.freeze({
[ACCOUNT_PROPERTY_READABLE_ENUM.TWITTER]: [SCOPE_ENUM.LOCAL, SCOPE_ENUM.PRIVATE],
[ACCOUNT_PROPERTY_READABLE_ENUM.FEDIVERSE]: [SCOPE_ENUM.LOCAL, SCOPE_ENUM.PRIVATE],
[ACCOUNT_PROPERTY_READABLE_ENUM.WEBSITE]: [SCOPE_ENUM.LOCAL, SCOPE_ENUM.PRIVATE],
[ACCOUNT_PROPERTY_READABLE_ENUM.BIRTHDAY]: [SCOPE_ENUM.LOCAL, SCOPE_ENUM.PRIVATE],
})

/** List of readable account properties which aren't published to the lookup server */
Expand Down
3 changes: 3 additions & 0 deletions apps/settings/src/main-personal-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import RoleSection from './components/PersonalInfo/RoleSection.vue'
import HeadlineSection from './components/PersonalInfo/HeadlineSection.vue'
import BiographySection from './components/PersonalInfo/BiographySection.vue'
import ProfileVisibilitySection from './components/PersonalInfo/ProfileVisibilitySection/ProfileVisibilitySection.vue'
import BirthdaySection from './components/PersonalInfo/BirthdaySection.vue'

__webpack_nonce__ = btoa(getRequestToken())

Expand All @@ -65,6 +66,7 @@ const TwitterView = Vue.extend(TwitterSection)
const FediverseView = Vue.extend(FediverseSection)
const LanguageView = Vue.extend(LanguageSection)
const LocaleView = Vue.extend(LocaleSection)
const BirthdayView = Vue.extend(BirthdaySection)

new AvatarView().$mount('#vue-avatar-section')
new DetailsView().$mount('#vue-details-section')
Expand All @@ -77,6 +79,7 @@ new TwitterView().$mount('#vue-twitter-section')
new FediverseView().$mount('#vue-fediverse-section')
new LanguageView().$mount('#vue-language-section')
new LocaleView().$mount('#vue-locale-section')
new BirthdayView().$mount('#vue-birthday-section')

if (profileEnabledGlobally) {
const ProfileView = Vue.extend(ProfileSection)
Expand Down
3 changes: 3 additions & 0 deletions apps/settings/templates/settings/personal/personal.info.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@
<div class="personal-settings-setting-box">
<div id="vue-location-section"></div>
</div>
<div class="personal-settings-setting-box">
<div id="vue-birthday-section"></div>
</div>
<div class="personal-settings-setting-box personal-settings-language-box">
<div id="vue-language-section"></div>
</div>
Expand Down
1 change: 1 addition & 0 deletions core/Db/ProfileConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class ProfileConfig extends Entity implements JsonSerializable {
IAccountManager::PROPERTY_EMAIL => self::VISIBILITY_SHOW_USERS_ONLY,
IAccountManager::PROPERTY_PHONE => self::VISIBILITY_SHOW_USERS_ONLY,
IAccountManager::PROPERTY_TWITTER => self::VISIBILITY_SHOW,
IAccountManager::PROPERTY_BIRTHDAY => self::VISIBILITY_SHOW,
IAccountManager::PROPERTY_WEBSITE => self::VISIBILITY_SHOW,
];

Expand Down
7 changes: 7 additions & 0 deletions lib/private/Accounts/AccountManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class AccountManager implements IAccountManager {
self::PROPERTY_ROLE => self::SCOPE_LOCAL,
self::PROPERTY_HEADLINE => self::SCOPE_LOCAL,
self::PROPERTY_BIOGRAPHY => self::SCOPE_LOCAL,
self::PROPERTY_BIRTHDAY => self::SCOPE_LOCAL,
];

public function __construct(
Expand Down Expand Up @@ -668,6 +669,12 @@ protected function buildDefaultUserRecord(IUser $user): array {
'verified' => self::NOT_VERIFIED,
],

[
'name' => self::PROPERTY_BIRTHDAY,
'value' => '',
'scope' => $scopes[self::PROPERTY_BIRTHDAY],
],

[
'name' => self::PROPERTY_FEDIVERSE,
'value' => '',
Expand Down
7 changes: 7 additions & 0 deletions lib/public/Accounts/IAccountManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
public const PROPERTY_WEBSITE = 'website';
public const PROPERTY_ADDRESS = 'address';
public const PROPERTY_TWITTER = 'twitter';
public const PROPERTY_BIRTHDAY = 'birthday';
public const PROPERTY_FEDIVERSE = 'fediverse';

/**
Expand Down Expand Up @@ -140,6 +141,11 @@
*/
public const PROPERTY_PROFILE_ENABLED = 'profile_enabled';

/**
* @since 28.0.0
*/
public const PROPERTY_BIRTHDAY = 'birthday';

Check failure on line 147 in lib/public/Accounts/IAccountManager.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

DuplicateConstant

lib/public/Accounts/IAccountManager.php:147:15: DuplicateConstant: Constant names should be unique (see https://psalm.dev/302)

Check failure on line 147 in lib/public/Accounts/IAccountManager.php

View workflow job for this annotation

GitHub Actions / static-code-analysis-ocp

DuplicateConstant

lib/public/Accounts/IAccountManager.php:147:15: DuplicateConstant: Constant names should be unique (see https://psalm.dev/302)

Check failure

Code scanning / Psalm

DuplicateConstant Error

Constant names should be unique

/**
* The list of allowed properties
*
Expand All @@ -159,6 +165,7 @@
self::PROPERTY_HEADLINE,
self::PROPERTY_BIOGRAPHY,
self::PROPERTY_PROFILE_ENABLED,
self::PROPERTY_BIRTHDAY,
];

public const COLLECTION_EMAIL = 'additional_mail';
Expand Down
Loading