Skip to content

Commit

Permalink
[WIP] Vuetiful emails
Browse files Browse the repository at this point in the history
Signed-off-by: Christopher Ng <chrng8@gmail.com>
  • Loading branch information
Pytal committed Jun 8, 2021
1 parent 745543d commit 58c1e89
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 11 deletions.
15 changes: 11 additions & 4 deletions apps/provisioning_api/lib/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -740,12 +740,19 @@ public function editUser(string $userId, string $key, string $value): DataRespon
$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
break;
case IAccountManager::PROPERTY_EMAIL:
if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
$targetUser->setEMailAddress($value);
} else {
throw new OCSException('', 102);
$userAccount = $this->accountManager->getAccount($targetUser);
$userProperty = $userAccount->getProperty($key);
if ($userProperty->getValue() !== $value) {
try {
$userProperty->setValue($value);
$this->accountManager->updateAccount($userAccount);
} catch (\InvalidArgumentException $e) {
throw new OCSException('Invalid ' . $e->getMessage(), 102);
}
}
break;
case IAccountManager::COLLECTION_EMAIL:
break;
case IAccountManager::PROPERTY_PHONE:
case IAccountManager::PROPERTY_ADDRESS:
case IAccountManager::PROPERTY_WEBSITE:
Expand Down
2 changes: 1 addition & 1 deletion apps/settings/js/federationsettingsview.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
this._inputFields = [
'displayname',
'phone',
'email',
// 'email',
'website',
'twitter',
'address',
Expand Down
26 changes: 25 additions & 1 deletion apps/settings/lib/Settings/Personal/PersonalInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Settings\ISettings;
use OCP\Accounts\IAccountProperty;
use OCP\AppFramework\Services\IInitialState;

class PersonalInfo implements ISettings {

Expand All @@ -65,6 +67,8 @@ class PersonalInfo implements ISettings {
private $l10nFactory;
/** @var IL10N */
private $l;
/** @var IInitialState */
private $initialStateService;

public function __construct(
IConfig $config,
Expand All @@ -73,7 +77,8 @@ public function __construct(
IAccountManager $accountManager,
IAppManager $appManager,
IFactory $l10nFactory,
IL10N $l
IL10N $l,
IInitialState $initialStateService
) {
$this->config = $config;
$this->userManager = $userManager;
Expand All @@ -82,6 +87,7 @@ public function __construct(
$this->appManager = $appManager;
$this->l10nFactory = $l10nFactory;
$this->l = $l;
$this->initialStateService = $initialStateService;
}

public function getForm(): TemplateResponse {
Expand Down Expand Up @@ -138,6 +144,24 @@ public function getForm(): TemplateResponse {
'groups' => $this->getGroups($user),
] + $messageParameters + $languageParameters + $localeParameters;

$this->initialStateService->provideInitialState(
'emails',
[
[
'value' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getValue(),
'scope' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getScope(),
'verified' => $account->getProperty(IAccountManager::PROPERTY_EMAIL)->getVerified(),
],
...array_map(
fn(IAccountProperty $property) => [
'value' => $property->getValue(),
'scope' => $property->getScope(),
'verified' => $property->getVerified(),
],
$account->getPropertyCollection(IAccountManager::COLLECTION_EMAIL)->getProperties()
)
]
);

return new TemplateResponse('settings', 'settings/personal/personal.info', $parameters, '');
}
Expand Down
110 changes: 110 additions & 0 deletions apps/settings/src/components/PersonalInfo/EmailSection.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<!--
- @copyright 2021 Christopher Ng <chrng8@gmail.com>
-
- @author 2021 Christopher Ng <chrng8@gmail.com>
-
- @license GNU AGPL version 3 or any later version
-
- 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>
<form id="emailform" class="section">
<h3>
<label for="email">{{ t('settings', 'Emails') }}</label>
<a href="#" class="federation-menu" :aria-label="t('settings', 'Change privacy level of email')">
<span class="icon-federation-menu icon-password">
<span class="icon-triangle-s"></span>
</span>
</a>
<Actions>
<ActionButton icon="icon-add" @click.stop.prevent="addEmails(email, additionalEmails)">
{{ t('settings', 'Add email address') }}
</ActionButton>
</Actions>
</h3>
<input
type="email"
name="email"
id="email"
v-model="email"
:placeholder="t('settings', 'Your email address')" />
<input
v-for="(email, index) in additionalEmails"
type="email"
name="additionalEmail[]"
:id="`additionalEmail-${index}`"
v-model="email.value"
:placeholder="t('settings', `Additional email address ${index+1}`)"
:key="index" />
</form>
</template>

<script>
import { Actions, ActionButton } from '@nextcloud/vue'
import axios from '@nextcloud/axios'
import * as auth from '@nextcloud/auth'
import * as router from '@nextcloud/router'
import ErrorHandler from '../../mixins/ErrorHandler'

export default {
name: 'EmailSection',
components: {
Actions,
ActionButton,
},
mixins: [ErrorHandler],
props: {
initialEmails: {
type: Array,
required: true,
},
},
data() {
/* eslint-disable */
console.log(this.initialEmails)
return {
email: this.initialEmails[0].value,
additionalEmails: this.initialEmails.slice(1),
}
},
methods: {
async addEmails(email, additionalEmails) {
const userId = auth.getCurrentUser().uid
// TODO upgrade @nextcloud/router to v2.0 so we can remove the .slice() trailing slash hack
const url = router.generateOcsUrl(`cloud/users/${userId}`, 2).slice(0, -1)

// Set the primary email
// try catch
const res = await this.handleError(axios.put(url, {
key: 'email',
value: email,
}))
// console.log(res.data)

// Set additional emails
const resp = await this.handleError(axios.put(url, {
key: 'additional_mail',
value: additionalEmails.map(({ value }) => value),
}))
// console.log(res.data)

additionalEmails.push({ value: '' })
}
}
}
</script>

<style scoped>
</style>
39 changes: 39 additions & 0 deletions apps/settings/src/main-personal-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @copyright 2021, Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* 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/>.
*
*/

import Vue from 'vue'
import { loadState } from '@nextcloud/initial-state'

import EmailSection from './components/PersonalInfo/EmailSection'

// eslint-disable-next-line camelcase
__webpack_nonce__ = btoa(OC.requestToken)

Vue.prototype.t = t

const View = Vue.extend(EmailSection)
new View({
propsData: {
initialEmails: loadState('settings', 'emails'),
// ...more initial props
},
}).$mount('#vue-emailform')
31 changes: 31 additions & 0 deletions apps/settings/src/mixins/ErrorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @copyright Copyright (c) 2021 Christopher Ng <chrng8@gmail.com>
*
* @author Christopher Ng <chrng8@gmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* 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/>.
*
*/

export default {
methods: {
handleError(promise) {
promise
.then(response => response)
.catch(error => ({ data: null, error }))
}
}
}
4 changes: 4 additions & 0 deletions apps/settings/templates/settings/personal/personal.info.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
'federationsettingsview',
'federationscopemenu',
'settings/personalInfo',
'vue-settings-personal-info',
]);
?>

Expand Down Expand Up @@ -125,6 +126,9 @@
<input type="hidden" id="displaynamescope" value="<?php p($_['displayNameScope']) ?>">
</form>
</div>
<div class="personal-settings-setting-box">
<div id="vue-emailform" class="section"></div>
</div>
<div class="personal-settings-setting-box">
<form id="emailform" class="section">
<h3>
Expand Down
1 change: 1 addition & 0 deletions apps/settings/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
'settings-personal-security': path.join(__dirname, 'src', 'main-personal-security'),
'settings-personal-webauthn': path.join(__dirname, 'src', 'main-personal-webauth'),
'settings-nextcloud-pdf': path.join(__dirname, 'src', 'main-nextcloud-pdf'),
'settings-personal-info': path.join(__dirname, 'src', 'main-personal-info'),
},
output: {
path: path.resolve(__dirname, './js'),
Expand Down
19 changes: 17 additions & 2 deletions lib/private/Accounts/AccountManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,12 @@ protected function addMissingDefaultValues(array $userData) {
}
}

foreach ($this::COLLECTION_PROPERTIES as $property) {
if (!isset($userData[$property])) {
$userData[$property] = new AccountPropertyCollection($property);
}
}

return $userData;
}

Expand Down Expand Up @@ -566,6 +572,11 @@ protected function buildDefaultUserRecord(IUser $user) {
'scope' => self::SCOPE_FEDERATED,
'verified' => self::NOT_VERIFIED,
],
self::COLLECTION_EMAIL =>
[
// TODO implement the correct way
'properties' => [],
],
self::PROPERTY_AVATAR =>
[
'scope' => self::SCOPE_FEDERATED
Expand All @@ -588,7 +599,11 @@ protected function buildDefaultUserRecord(IUser $user) {
private function parseAccountData(IUser $user, $data): Account {
$account = new Account($user);
foreach ($data as $property => $accountData) {
$account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED);
if (!$this->isCollection($property)) {
$account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED);
} else {
$account->setPropertyCollection(new AccountPropertyCollection($property));
}
}
return $account;
}
Expand All @@ -600,7 +615,7 @@ public function getAccount(IUser $user): IAccount {
public function updateAccount(IAccount $account): void {
$data = [];

foreach ($account->getProperties() as $property) {
foreach ($account->getAllProperties() as $property) {
$data[$property->getName()] = [
'value' => $property->getValue(),
'scope' => $property->getScope(),
Expand Down
4 changes: 1 addition & 3 deletions lib/private/Accounts/TAccountsHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@
trait TAccountsHelper {
protected function isCollection(string $propertyName): bool {
return in_array($propertyName,
[
IAccountManager::COLLECTION_EMAIL,
],
IAccountManager::COLLECTION_PROPERTIES,
true
);
}
Expand Down
4 changes: 4 additions & 0 deletions lib/public/Accounts/IAccountManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ interface IAccountManager {

public const COLLECTION_EMAIL = 'additional_mail';

public const COLLECTION_PROPERTIES = [
self::COLLECTION_EMAIL,
];

public const NOT_VERIFIED = '0';
public const VERIFICATION_IN_PROGRESS = '1';
public const VERIFIED = '2';
Expand Down

0 comments on commit 58c1e89

Please sign in to comment.