Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
22 changes: 21 additions & 1 deletion apps/profile/lib/Controller/ProfilePageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace OCA\Profile\Controller;

use OCA\Settings\ConfigLexicon;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\AnonRateLimit;
use OCP\AppFramework\Http\Attribute\BruteForceProtection;
Expand All @@ -17,11 +18,14 @@
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\UserRateLimit;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IAppConfig;
use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\Profile\BeforeTemplateRenderedEvent;
Expand All @@ -43,6 +47,8 @@ public function __construct(
private IUserStatusManager $userStatusManager,
private INavigationManager $navigationManager,
private IEventDispatcher $eventDispatcher,
private IAppConfig $appConfig,
private IURLGenerator $urlGenerator,
) {
parent::__construct($appName, $request);
}
Expand All @@ -53,14 +59,28 @@ public function __construct(
#[BruteForceProtection(action: 'user')]
#[UserRateLimit(limit: 30, period: 120)]
#[AnonRateLimit(limit: 30, period: 120)]
public function index(string $targetUserId): TemplateResponse {
public function index(string $targetUserId): TemplateResponse|RedirectResponse {
$profileNotFoundTemplate = new TemplateResponse(
'profile',
'404-profile',
[],
TemplateResponse::RENDER_AS_GUEST,
);

if (
$this->appConfig->getValueBool('settings', ConfigLexicon::PROFILE_PRIVATE)
&& !$this->userSession->isLoggedIn()
) {
return new RedirectResponse(
$this->urlGenerator->linkToRoute('core.login.showLoginForm', [
'redirect_url' => $this->urlGenerator->linkToRoute(
'profile.ProfilePage.index',
['targetUserId' => $targetUserId]
),
])
);
}

$targetUser = $this->userManager->get($targetUserId);
if ($targetUser === null) {
$profileNotFoundTemplate->throttle();
Expand Down
39 changes: 5 additions & 34 deletions apps/profile/tests/Controller/ProfilePageControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,25 @@

namespace OCA\Profile\Tests\Controller;

use OC\Profile\ProfileManager;
use OC\UserStatus\Manager;
use OCA\Profile\Controller\ProfilePageController;
use OCP\AppFramework\Services\IInitialState;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\Share\IManager;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;

class ProfilePageControllerTest extends TestCase {

private IUserManager&MockObject $userManager;
private ProfilePageController $controller;

protected function setUp(): void {
parent::setUp();

$request = $this->createMock(IRequest::class);
$initialStateService = $this->createMock(IInitialState::class);
$profileManager = $this->createMock(ProfileManager::class);
$shareManager = $this->createMock(IManager::class);
$this->userManager = $this->createMock(IUserManager::class);
$userSession = $this->createMock(IUserSession::class);
$userStatusManager = $this->createMock(Manager::class);
$navigationManager = $this->createMock(INavigationManager::class);
$eventDispatcher = $this->createMock(IEventDispatcher::class);

$this->controller = new ProfilePageController(
'profile',
$request,
$initialStateService,
$profileManager,
$shareManager,
$this->userManager,
$userSession,
$userStatusManager,
$navigationManager,
$eventDispatcher,
$this->controller = $this->createInstanceWithMocks(
ProfilePageController::class,
[ 'appName' => 'profile'],
);
}

public function testUserNotFound(): void {
$this->userManager->method('get')
$this->getAutoMock(IUserManager::class)->method('get')
->willReturn(null);

$response = $this->controller->index('bob');
Expand All @@ -69,7 +40,7 @@ public function testUserDisabled(): void {
$user->method('isEnabled')
->willReturn(false);

$this->userManager->method('get')
$this->getAutoMock(IUserManager::class)->method('get')
->willReturn($user);

$response = $this->controller->index('bob');
Expand Down
9 changes: 9 additions & 0 deletions apps/settings/lib/ConfigLexicon.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class ConfigLexicon implements ILexicon {
public const USER_LIST_SHOW_NEW_USER_FORM = 'user_list_show_new_user_form';
public const USER_LIST_SHOW_LANGUAGES = 'user_list_show_languages';

public const PROFILE_PRIVATE = 'profile_private';

#[\Override]
public function getStrictness(): Strictness {
return Strictness::IGNORE;
Expand Down Expand Up @@ -94,6 +96,13 @@ public function getUserConfigs(): array {
definition: 'Show languages in user list',
lazy: true,
),
new Entry(
key: self::PROFILE_PRIVATE,
type: ValueType::BOOL,
defaultRaw: false,
definition: 'Set profiles as private to the instance',
lazy: false,
),
];
}
}
2 changes: 2 additions & 0 deletions apps/settings/lib/Settings/Admin/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use OC\Profile\ProfileManager;
use OC\Profile\TProfileHelper;
use OCA\Settings\ConfigLexicon;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\AppFramework\Utility\ITimeFactory;
Expand Down Expand Up @@ -56,6 +57,7 @@ public function getForm() {
$this->initialStateService->provideInitialState('profileEnabledGlobally', $this->profileManager->isProfileEnabled());
$this->initialStateService->provideInitialState('profileEnabledByDefault', $this->isProfileEnabledByDefault($this->config));
$this->initialStateService->provideInitialState('profilePickerEnabled', $this->isProfilePickerEnabled($this->config));
$this->initialStateService->provideInitialState('profilePrivate', $this->appConfig->getValueBool('settings', ConfigLexicon::PROFILE_PRIVATE));

// Basic settings
$this->initialStateService->provideInitialState('restrictSystemTagsCreationToAdmin', $this->appConfig->getValueBool('systemtags', 'restrict_creation_to_admin', false));
Expand Down
41 changes: 36 additions & 5 deletions apps/settings/src/components/BasicSettings/ProfileSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,26 @@
v-model="initialProfileEnabledByDefault"
type="switch"
@update:modelValue="onProfileDefaultChange">
{{ t('settings', 'Enable') }}
{{ t('settings', 'Enable by default') }}
</NcCheckboxRadioSwitch>

<NcNoteCard type="info">
{{ t('settings', 'Enable or disable the profile picker in the Smart Picker and the profile link previews.') }}
</NcNoteCard>

<NcCheckboxRadioSwitch
v-model="initialProfilePickerEnabled"
type="switch"
@update:modelValue="onProfilePickerChange">
{{ t('settings', 'Enable the profile picker') }}
</NcCheckboxRadioSwitch>

<NcNoteCard type="info">
{{ t('settings', 'Enable or disable the profile picker in the Smart Picker and the profile link previews.') }}
</NcNoteCard>
<NcCheckboxRadioSwitch
v-model="initialProfilePrivate"
type="switch"
@update:modelValue="onProfilePrivateChange">
{{ t('settings', 'Prevent unauthenticated users from seeing user profiles') }}
</NcCheckboxRadioSwitch>
</div>
</template>

Expand All @@ -41,11 +48,12 @@ import { loadState } from '@nextcloud/initial-state'
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import logger from '../../logger.ts'
import { saveProfileDefault, saveProfilePicker } from '../../service/ProfileService.js'
import { saveProfileDefault, saveProfilePicker, saveProfilePrivate } from '../../service/ProfileService.js'
import { validateBoolean } from '../../utils/validate.js'

const profileEnabledByDefault = loadState('settings', 'profileEnabledByDefault', true)
const profilePickerEnabled = loadState('settings', 'profilePickerEnabled', true)
const profilePrivate = loadState('settings', 'profilePrivate', true)

export default {
name: 'ProfileSettings',
Expand All @@ -59,6 +67,7 @@ export default {
return {
initialProfileEnabledByDefault: profileEnabledByDefault,
initialProfilePickerEnabled: profilePickerEnabled,
initialProfilePrivate: profilePrivate,
}
},

Expand Down Expand Up @@ -107,6 +116,28 @@ export default {
}
},

async onProfilePrivateChange(isEnabled) {
if (validateBoolean(isEnabled)) {
await this.updateProfilePrivate(isEnabled)
}
},

async updateProfilePrivate(isEnabled) {
try {
const responseData = await saveProfilePrivate(isEnabled)
this.handleResponse({
isEnabled,
key: 'initialProfilePrivate',
status: responseData.ocs?.meta?.status,
})
} catch (e) {
this.handleResponse({
errorMessage: t('settings', 'Unable to update profile private setting'),
error: e,
})
}
},

handleResponse({ isEnabled, key, status, errorMessage, error }) {
if (status === 'ok') {
this[key] = isEnabled
Expand Down
24 changes: 24 additions & 0 deletions apps/settings/src/service/ProfileService.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,27 @@ export async function saveProfilePicker(isEnabled) {

return res.data
}

/**
* Save profile private
*
* @param {boolean} isEnabled the default
* @return {object}
*/
export async function saveProfilePrivate(isEnabled) {
// Convert to string for compatibility
isEnabled = isEnabled ? '1' : '0'

const url = generateOcsUrl('/apps/provisioning_api/api/v1/config/apps/{appId}/{key}', {
appId: 'settings',
key: 'profile_private',
})

await confirmPassword()

const res = await axios.post(url, {
value: isEnabled,
})

return res.data
}
Loading