From b313879ab36913ba04c4426fd37a6447863a11e2 Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Fri, 21 Nov 2025 15:24:14 +0100 Subject: [PATCH 1/5] chore(user_ldap): Delete legacy templates Not needed anymore after the vue migration Signed-off-by: Louis Chmn --- .../templates/part.settingcontrols.php | 19 --- .../templates/part.wizard-groupfilter.php | 83 ----------- .../templates/part.wizard-loginfilter.php | 85 ----------- .../templates/part.wizard-server.php | 137 ------------------ .../templates/part.wizard-userfilter.php | 86 ----------- .../templates/part.wizardcontrols.php | 25 ---- apps/user_ldap/tests/Settings/AdminTest.php | 8 - 7 files changed, 443 deletions(-) delete mode 100644 apps/user_ldap/templates/part.settingcontrols.php delete mode 100644 apps/user_ldap/templates/part.wizard-groupfilter.php delete mode 100644 apps/user_ldap/templates/part.wizard-loginfilter.php delete mode 100644 apps/user_ldap/templates/part.wizard-server.php delete mode 100644 apps/user_ldap/templates/part.wizard-userfilter.php delete mode 100644 apps/user_ldap/templates/part.wizardcontrols.php diff --git a/apps/user_ldap/templates/part.settingcontrols.php b/apps/user_ldap/templates/part.settingcontrols.php deleted file mode 100644 index 4105fa751b9e4..0000000000000 --- a/apps/user_ldap/templates/part.settingcontrols.php +++ /dev/null @@ -1,19 +0,0 @@ - -
- - - - t('Help'));?> - -
diff --git a/apps/user_ldap/templates/part.wizard-groupfilter.php b/apps/user_ldap/templates/part.wizard-groupfilter.php deleted file mode 100644 index bab08d85da8ca..0000000000000 --- a/apps/user_ldap/templates/part.wizard-groupfilter.php +++ /dev/null @@ -1,83 +0,0 @@ - -
-
-

- t('Groups meeting these criteria are available in %s:', [$theme->getName()]));?> -

-

- - - -

-

- - - - - - -

-

- t('Available groups'));?> -

- -
- -
- -

- t('Selected groups'));?> -

-

-

- -

- -

- t('The filter specifies which LDAP groups shall have access to the %s instance.', [$theme->getName()]));?> -

- -

-

-

-

-

- - -

- -
-
diff --git a/apps/user_ldap/templates/part.wizard-loginfilter.php b/apps/user_ldap/templates/part.wizard-loginfilter.php deleted file mode 100644 index 541a5c06c8c76..0000000000000 --- a/apps/user_ldap/templates/part.wizard-loginfilter.php +++ /dev/null @@ -1,85 +0,0 @@ - -
-
-

- t('When logging in, %s will find the user based on the following attributes:', [$theme->getName()]));?> -

-

- - - " - name="ldap_loginfilter_username" value="1" /> -

- t('Allows login against the LDAP/AD username, which is either "uid" or "sAMAccountName" and will be detected.'));?> -

-

-

- - - " - aria-describedby="ldap_loginfilter_email_instructions" - name="ldap_loginfilter_email" value="1" /> -

- t('Allows login against an email attribute. "mail" and "mailPrimaryAddress" allowed.'));?> -

-

-

- - - -

-

- -

- -

- t('Defines the filter to apply, when login is attempted. "%%uid" replaces the username in the login action. Example: "uid=%%uid"'));?> -

-

-

-

-

-

- -

- t('Attempts to receive a DN for the given loginname and the current login filter'));?> -

- -

- -
-
diff --git a/apps/user_ldap/templates/part.wizard-server.php b/apps/user_ldap/templates/part.wizard-server.php deleted file mode 100644 index 9fb67342247cc..0000000000000 --- a/apps/user_ldap/templates/part.wizard-server.php +++ /dev/null @@ -1,137 +0,0 @@ - -
-

- - -

- t('Add a new configuration'));?> -

- -

- t('Copy current configuration into new directory binding'));?> -

- -

- t('Delete the current configuration'));?> -

-

- -
-
-
-
- -

- t('You can omit the protocol, unless you require SSL. If so, start with ldaps://'));?> -

- - - - -
-
-
-
 
-
- -

- t('The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty.'));?> -

-
- -
- -

- t('For anonymous access, leave DN and Password empty.'));?> -

- -
-
 
- -
- -

- t('You can specify Base DN for users and groups in the Advanced tab'));?> -

- - -
- -
- -

- t('Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge.'));?> -

- -
- -
- -
-
- -
diff --git a/apps/user_ldap/templates/part.wizard-userfilter.php b/apps/user_ldap/templates/part.wizard-userfilter.php deleted file mode 100644 index ab505c65d4405..0000000000000 --- a/apps/user_ldap/templates/part.wizard-userfilter.php +++ /dev/null @@ -1,86 +0,0 @@ - -
-
-

- t('Listing and searching for users is constrained by these criteria:'));?> -

-

- - - -

-

- - t('The most common object classes for users are organizationalPerson, person, user, and inetOrgPerson. If you are not sure which object class to select, please consult your directory admin.'));?> -

-

- - - - - -

-

- t('Available groups'));?> -

- -
- -
- -

- t('Selected groups'));?> -

-

-

- -

- -

- -

- t('The filter specifies which LDAP users shall have access to the %s instance.', [$theme->getName()]));?> -

-

-

-

-

-

- - -

- -
-
diff --git a/apps/user_ldap/templates/part.wizardcontrols.php b/apps/user_ldap/templates/part.wizardcontrols.php deleted file mode 100644 index 2c6b1f16d675a..0000000000000 --- a/apps/user_ldap/templates/part.wizardcontrols.php +++ /dev/null @@ -1,25 +0,0 @@ - -
- - - - - - - t('Help'));?> - -
diff --git a/apps/user_ldap/tests/Settings/AdminTest.php b/apps/user_ldap/tests/Settings/AdminTest.php index 8107849146183..51abba3091b55 100644 --- a/apps/user_ldap/tests/Settings/AdminTest.php +++ b/apps/user_ldap/tests/Settings/AdminTest.php @@ -41,14 +41,6 @@ protected function setUp(): void { } public function testGetForm(): void { - $prefixes = ['s01']; - $hosts = ['s01' => '']; - - $wControls = $this->templateManager->getTemplate('user_ldap', 'part.wizardcontrols'); - $wControls = $wControls->fetchPage(); - $sControls = $this->templateManager->getTemplate('user_ldap', 'part.settingcontrols'); - $sControls = $sControls->fetchPage(); - $parameters = []; // assign default values From 6815e1604262272bb222d45d3d1bc9ef67c2bd58 Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Fri, 21 Nov 2025 15:37:47 +0100 Subject: [PATCH 2/5] refactor(user_ldap): Migrate to Vue3 Signed-off-by: Louis Chmn --- apps/user_ldap/lib/AppInfo/Application.php | 4 +++- apps/user_ldap/lib/Settings/Admin.php | 5 ++++- apps/user_ldap/src/main.ts | 20 ------------------- apps/user_ldap/src/settings-admin.ts | 11 ++++++++++ apps/user_ldap/templates/settings.php | 1 - build/frontend-legacy/webpack.modules.cjs | 3 --- .../apps/user_ldap | 0 build/frontend/vite.config.mts | 3 +++ 8 files changed, 21 insertions(+), 26 deletions(-) delete mode 100644 apps/user_ldap/src/main.ts create mode 100644 apps/user_ldap/src/settings-admin.ts rename build/{frontend-legacy => frontend}/apps/user_ldap (100%) diff --git a/apps/user_ldap/lib/AppInfo/Application.php b/apps/user_ldap/lib/AppInfo/Application.php index 70b7920f7ab17..25ca6f3a7baea 100644 --- a/apps/user_ldap/lib/AppInfo/Application.php +++ b/apps/user_ldap/lib/AppInfo/Application.php @@ -45,8 +45,10 @@ use Psr\Log\LoggerInterface; class Application extends App implements IBootstrap { + public const APP_ID = 'user_ldap'; + public function __construct() { - parent::__construct('user_ldap'); + parent::__construct(self::APP_ID); $container = $this->getContainer(); /** diff --git a/apps/user_ldap/lib/Settings/Admin.php b/apps/user_ldap/lib/Settings/Admin.php index a3144fd9c57fe..e1410441292ed 100644 --- a/apps/user_ldap/lib/Settings/Admin.php +++ b/apps/user_ldap/lib/Settings/Admin.php @@ -6,6 +6,7 @@ */ namespace OCA\User_LDAP\Settings; +use OCA\User_LDAP\AppInfo\Application; use OCA\User_LDAP\Configuration; use OCA\User_LDAP\Helper; use OCP\AppFramework\Http\TemplateResponse; @@ -59,7 +60,9 @@ public function getForm(): TemplateResponse { $this->initialState->provideInitialState('ldapConfigs', $ldapConfigs); $this->initialState->provideInitialState('ldapModuleInstalled', function_exists('ldap_connect')); - return new TemplateResponse('user_ldap', 'settings', $parameters); + \OCP\Util::addStyle(Application::APP_ID, 'settings-admin'); + \OCP\Util::addScript(Application::APP_ID, 'settings-admin'); + return new TemplateResponse(Application::APP_ID, 'settings', $parameters); } public function getSection(): string { diff --git a/apps/user_ldap/src/main.ts b/apps/user_ldap/src/main.ts deleted file mode 100644 index a65f030d69d86..0000000000000 --- a/apps/user_ldap/src/main.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { getCSPNonce } from '@nextcloud/auth' -import { PiniaVuePlugin } from 'pinia' -/** - * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -import Vue from 'vue' -import LDAPSettingsApp from './LDAPSettingsApp.vue' -import { pinia } from './store/index.ts' - -__webpack_nonce__ = getCSPNonce() - -// Init Pinia store -Vue.use(PiniaVuePlugin) - -const LDAPSettingsAppVue = Vue.extend(LDAPSettingsApp) -new LDAPSettingsAppVue({ - name: 'LDAPSettingsApp', - pinia, -}).$mount('#content-ldap-settings') diff --git a/apps/user_ldap/src/settings-admin.ts b/apps/user_ldap/src/settings-admin.ts new file mode 100644 index 0000000000000..f7c25e2344a1f --- /dev/null +++ b/apps/user_ldap/src/settings-admin.ts @@ -0,0 +1,11 @@ +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import { createApp } from 'vue' +import LDAPSettingsApp from './LDAPSettingsApp.vue' +import { pinia } from './store/index.ts' + +const app = createApp(LDAPSettingsApp) +app.use(pinia) +app.mount('#content-ldap-settings') diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 3b0b966a096d8..b08bd8041c265 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -6,7 +6,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -\OCP\Util::addScript('user_ldap', 'main', 'core'); ?>
diff --git a/build/frontend-legacy/webpack.modules.cjs b/build/frontend-legacy/webpack.modules.cjs index 027f22967b916..78792048d8835 100644 --- a/build/frontend-legacy/webpack.modules.cjs +++ b/build/frontend-legacy/webpack.modules.cjs @@ -100,9 +100,6 @@ module.exports = { updatenotification: path.join(__dirname, 'apps/updatenotification/src', 'updatenotification.js'), 'update-notification-legacy': path.join(__dirname, 'apps/updatenotification/src', 'update-notification-legacy.ts'), }, - user_ldap: { - main: path.join(__dirname, 'apps/user_ldap/src', 'main.js'), - }, user_status: { menu: path.join(__dirname, 'apps/user_status/src', 'menu.js'), }, diff --git a/build/frontend-legacy/apps/user_ldap b/build/frontend/apps/user_ldap similarity index 100% rename from build/frontend-legacy/apps/user_ldap rename to build/frontend/apps/user_ldap diff --git a/build/frontend/vite.config.mts b/build/frontend/vite.config.mts index e5b04daef8a47..00730d67c415a 100644 --- a/build/frontend/vite.config.mts +++ b/build/frontend/vite.config.mts @@ -24,6 +24,9 @@ const modules = { twofactor_backupcodes: { 'settings-personal': resolve(import.meta.dirname, 'apps/twofactor_backupcodes/src', 'settings-personal.ts'), }, + user_ldap: { + 'settings-admin': resolve(import.meta.dirname, 'apps/user_ldap/src', 'settings-admin.js'), + }, } // convert modules to modules entries prefied with the app id From cc39ae3e2b3ad27989f09fb795e655efc189c703 Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Fri, 21 Nov 2025 16:19:16 +0100 Subject: [PATCH 3/5] fix(user_ldap): Use v-model for settings inputs Signed-off-by: Louis Chmn --- .../components/SettingsTabs/AdvancedTab.vue | 88 +++++++++---------- .../src/components/SettingsTabs/ExpertTab.vue | 7 +- .../src/components/SettingsTabs/GroupsTab.vue | 6 +- .../src/components/SettingsTabs/LoginTab.vue | 6 +- .../src/components/SettingsTabs/ServerTab.vue | 17 ++-- .../src/components/SettingsTabs/UsersTab.vue | 6 +- apps/user_ldap/src/views/Settings.vue | 2 +- 7 files changed, 65 insertions(+), 67 deletions(-) diff --git a/apps/user_ldap/src/components/SettingsTabs/AdvancedTab.vue b/apps/user_ldap/src/components/SettingsTabs/AdvancedTab.vue index 5adb0181dbb0d..9bbe2ee7aa395 100644 --- a/apps/user_ldap/src/components/SettingsTabs/AdvancedTab.vue +++ b/apps/user_ldap/src/components/SettingsTabs/AdvancedTab.vue @@ -9,29 +9,29 @@ + @change="(event) => ldapConfigProxy.ldapBackupHost = event.target.value" /> + :label="t('user_ldap', 'Backup (Replica) Port')" + @change="(event) => ldapConfigProxy.ldapBackupPort = event.target.value" /> + @update:model-value="ldapConfigProxy.ldapOverrideMainServer = $event ? '1' : '0'"> {{ t('user_ldap', 'Disable Main Server') }} + @update:model-value="ldapConfigProxy.turnOffCertCheck = $event ? '1' : '0'"> {{ t('user_ldap', 'Turn off SSL certificate validation.') }} @@ -40,7 +40,7 @@ :label="t('user_ldap', 'Cache Time-To-Live')" :value="ldapConfigProxy.ldapCacheTTL" :helper-text="t('user_ldap', 'in seconds. A change empties the cache.')" - @change.native="(event) => ldapConfigProxy.ldapCacheTTL = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapCacheTTL = event.target.value" />
@@ -51,31 +51,31 @@ :value="ldapConfigProxy.ldapUserDisplayName" :label="t('user_ldap', 'User Display Name Field')" :helper-text="t('user_ldap', 'The LDAP attribute to use to generate the user\'s display name.')" - @change.native="(event) => ldapConfigProxy.ldapUserDisplayName = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapUserDisplayName = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapUserDisplayName2 = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapBaseUsers = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributesForUserSearch = event.target.value" /> + @update:model-value="ldapConfigProxy.markRemnantsAsDisabled = $event ? '1' : '0'"> {{ t('user_ldap', 'Disable users missing from LDAP') }} @@ -84,19 +84,19 @@ :value="ldapConfigProxy.ldapGroupDisplayName" :label="t('user_ldap', 'Group Display Name Field')" :title="t('user_ldap', 'The LDAP attribute to use to generate the groups\'s display name.')" - @change.native="(event) => ldapConfigProxy.ldapGroupDisplayName = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapGroupDisplayName = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapBaseGroups = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributesForGroupSearch = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapDynamicGroupMemberURL = event.target.value" /> + @update:model-value="ldapConfigProxy.ldapNestedGroups = $event ? '1' : '0'"> {{ t('user_ldap', 'Nested Groups') }} @@ -129,12 +129,12 @@ :label="t('user_ldap', 'Paging chunksize')" :value="ldapConfigProxy.ldapPagingSize" :helper-text="t('user_ldap', 'Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)')" - @change.native="(event) => ldapConfigProxy.ldapPagingSize = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapPagingSize = event.target.value" /> + @update:model-value="ldapConfigProxy.turnOnPasswordChange = $event ? '1' : '0'"> {{ t('user_ldap', 'Enable LDAP password changes per user') }} @@ -146,7 +146,7 @@ :label="t('user_ldap', 'Default password policy DN')" :value="ldapConfigProxy.ldapDefaultPPolicyDN" :helper-text="t('user_ldap', 'The DN of a default password policy that will be used for password expiry handling. Works only when LDAP password changes per user are enabled and is only supported by OpenLDAP. Leave empty to disable password expiry handling.')" - @change.native="(event) => ldapConfigProxy.ldapDefaultPPolicyDN = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapDefaultPPolicyDN = event.target.value" />
@@ -157,35 +157,35 @@ :value="ldapConfigProxy.ldapQuotaAttribute" :label="t('user_ldap', 'Quota Field')" :helper-text="t('user_ldap', 'Leave empty for user\'s default quota. Otherwise, specify an LDAP/AD attribute.')" - @change.native="(event) => ldapConfigProxy.ldapQuotaAttribute = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapQuotaAttribute = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapQuotaDefault = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapEmailAttribute = event.target.value" /> + @change="(event) => ldapConfigProxy.homeFolderNamingRule = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapExtStorageHomeAttribute = event.target.value" />
@@ -196,70 +196,70 @@ :label="t('user_ldap', 'Phone Field')" :value="ldapConfigProxy.ldapAttributePhone" :helper-text="t('user_ldap', 'User profile Phone will be set from the specified attribute')" - @change.native="(event) => ldapConfigProxy.ldapAttributePhone = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributePhone = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributeWebsite = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributeAddress = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributeTwitter = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributeFediverse = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributeOrganisation = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributeRole = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributeHeadline = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributeBiography = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapAttributeBirthDate = event.target.value" />
diff --git a/apps/user_ldap/src/components/SettingsTabs/ExpertTab.vue b/apps/user_ldap/src/components/SettingsTabs/ExpertTab.vue index 35962ae5c2874..79559115a30b5 100644 --- a/apps/user_ldap/src/components/SettingsTabs/ExpertTab.vue +++ b/apps/user_ldap/src/components/SettingsTabs/ExpertTab.vue @@ -14,8 +14,7 @@ autocomplete="off" :label="t('user_ldap', 'Internal Username Attribute:')" :value="ldapConfigProxy.ldapExpertUsernameAttr" - :label-outside="true" - @change.native="(event) => ldapConfigProxy.ldapExpertUsernameAttr = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapExpertUsernameAttr = event.target.value" />
@@ -28,12 +27,12 @@ autocomplete="off" :label="t('user_ldap', 'UUID Attribute for Users')" :value="ldapConfigProxy.ldapExpertUUIDUserAttr" - @change.native="(event) => ldapConfigProxy.ldapExpertUUIDUserAttr = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapExpertUUIDUserAttr = event.target.value" /> + @change="(event) => ldapConfigProxy.ldapExpertUUIDGroupAttr = event.target.value" />
diff --git a/apps/user_ldap/src/components/SettingsTabs/GroupsTab.vue b/apps/user_ldap/src/components/SettingsTabs/GroupsTab.vue index 00a045b7fc2a8..240cee9a8bc22 100644 --- a/apps/user_ldap/src/components/SettingsTabs/GroupsTab.vue +++ b/apps/user_ldap/src/components/SettingsTabs/GroupsTab.vue @@ -28,14 +28,14 @@
+ :model-value="ldapConfigProxy.ldapGroupFilterMode === '1'" + @update:model-value="toggleFilterMode"> {{ t('user_ldap', 'Edit LDAP Query') }}
diff --git a/apps/user_ldap/src/components/SettingsTabs/LoginTab.vue b/apps/user_ldap/src/components/SettingsTabs/LoginTab.vue index d734c726cb34c..1c7570fd237a6 100644 --- a/apps/user_ldap/src/components/SettingsTabs/LoginTab.vue +++ b/apps/user_ldap/src/components/SettingsTabs/LoginTab.vue @@ -21,16 +21,16 @@