]> source.dussan.org Git - nextcloud-server.git/commitdiff
WIP2
authorLouis Chemineau <louis@chmn.me>
Mon, 16 Sep 2024 19:59:42 +0000 (21:59 +0200)
committerLouis Chemineau <louis@chmn.me>
Thu, 3 Oct 2024 13:44:38 +0000 (15:44 +0200)
Signed-off-by: Louis Chemineau <louis@chmn.me>
apps/user_ldap/src/components/SettingsTabs/GroupsTab.vue
apps/user_ldap/src/components/SettingsTabs/LoginTab.vue
apps/user_ldap/src/components/SettingsTabs/ServerTab.vue
apps/user_ldap/src/components/SettingsTabs/UsersTab.vue
apps/user_ldap/src/components/WizardControls.vue [new file with mode: 0644]
apps/user_ldap/src/store/config.ts
apps/user_ldap/src/views/Settings.vue

index 5fda7bef03d73e0f7939f639d921a28e140d45d0..b154d4ec40400a360a5ba508b39048bb5b3bd9a8 100644 (file)
@@ -3,46 +3,43 @@
  - SPDX-License-Identifier: AGPL-3.0-or-later
  -->
 <template>
-       <fieldset id="ldapWizard4">
-               <div>
-                       <p>
-                               {{ t('user_ldap', 'Groups meeting these criteria are available in %s:', {themeName: theme.getName()}) }}
-                       </p>
-                       <p>
-                               <label for="ldap_groupfilter_objectclass">
-                                       {{ t('user_ldap', 'Only these object classes:') }}
-                               </label>
+       <fieldset class="ldap-wizard__groups">
+               {{ t('user_ldap', 'Groups meeting these criteria are available in {instanceName}:', {instanceName}) }}
 
-                               <select id="ldap_groupfilter_objectclass"
-                                       multiple="multiple"
-                                       name="ldap_groupfilter_objectclass"
-                                       class="multiSelectPlugin" />
-                       </p>
-                       <p>
-                               <label for="ldap_groupfilter_groups">
-                                       {{ t('user_ldap', 'Only from these groups:') }}
-                               </label>
+               <div class="ldap-wizard__groups__line ldap-wizard__groups__filter-selection">
+                       <NcSelect v-model="ldapConfig.ldapGroupFilterObjectClass"
+                               class="ldap-wizard__groups__group-filter-groups__select"
+                               :options="['TODO']"
+                               :disable="allowUserFilterGroupsSelection"
+                               :input-label="t('user_ldap', 'Only these object classes:')"
+                               :multiple="true" />
 
-                               <input type="text" class="ldapManyGroupsSupport ldapManyGroupsSearch hidden" placeholder="t('user_ldap', 'Search groups')">
+                       <!-- <input type="text" class="ldapManyGroupsSupport ldapManyGroupsSearch hidden" placeholder="t('user_ldap', 'Search groups')"> -->
+                       <NcSelect v-model="ldapConfig.ldapGroupFilterObjectClass"
+                               class="ldap-wizard__groups__group-filter-groups__select"
+                               :options="['TODO']"
+                               :disable="allowUserFilterGroupsSelection"
+                               :input-label="t('user_ldap', 'Only from these groups:')"
+                               :multiple="true" />
+               </div>
 
-                               <select id="ldap_groupfilter_groups"
-                                       multiple="multiple"
-                                       name="ldap_groupfilter_groups"
-                                       class="multiSelectPlugin" />
-                       </p>
+               <!-- TODO -->
+               <div class="ldap-wizard__groups__line">
                        <p class="ldapManyGroupsSupport hidden">
-                               <label />
                                <select class="ldapGroupList ldapGroupListAvailable"
                                        multiple="multiple"
                                        aria-describedby="ldapGroupListAvailable_instructions"
                                        title="t('user_ldap', 'Available groups')" />
-                       </p><p id="ldapGroupListAvailable_instructions" class="hidden-visually">
+                       </p>
+                       <p id="ldapGroupListAvailable_instructions" class="hidden-visually">
                                {{ t('user_ldap', 'Available groups') }}
                        </p>
-                       <span class="buttonSpan">
-                               <NcButton class="ldapGroupListSelect" type="button">&gt;</NcButton><br>
-                               <NcButton class="ldapGroupListDeselect" type="button">&lt;</NcButton>
+
+                       <span>
+                               <NcButton class="ldapGroupListSelect">&gt;</NcButton><br>
+                               <NcButton class="ldapGroupListDeselect">&lt;</NcButton>
                        </span>
+
                        <select class="ldapGroupList ldapGroupListSelected"
                                multiple="multiple"
                                aria-describedby="ldapGroupListSelected_instructions"
                        <p id="ldapGroupListSelected_instructions" class="hidden-visually">
                                {{ t('user_ldap', 'Selected groups') }}
                        </p>
-                       <p>
-                               <label><a id="toggleRawGroupFilter" class="ldapToggle">↓ {{ t('user_ldap', 'Edit LDAP Query') }}</a></label>
-                       </p>
-                       <p id="ldapReadOnlyGroupFilterContainer" class="hidden ldapReadOnlyFilterContainer">
-                               <label>{{ t('user_ldap', 'LDAP Filter:') }}</label>
-                               <span class="ldapFilterReadOnlyElement ldapInputColElement" />
-                       </p>
-                       <p id="rawGroupFilterContainer" class="invisible">
-                               <textarea id="ldap_group_filter"
-                                       type="text"
-                                       name="ldap_group_filter"
-                                       placeholder="t('user_ldap', 'Edit LDAP Query')"
-                                       aria-describedby="rawGroupFilterContainer_instructions"
-                                       title="t('user_ldap', 'The filter specifies which LDAP groups shall have access to the {themeName} instance.', {themeName: theme.getName()}) }}" />
-                       </p><p id="rawGroupFilterContainer_instructions" class="hidden-visually">
-                               {{ t('user_ldap', 'The filter specifies which LDAP groups shall have access to the {themeName} instance.', {themeName: theme.getName()}) }}
-                       </p>
+               </div>
+
+               <div class="ldap-wizard__groups__line ldap-wizard__groups__groups-filter">
+                       <NcCheckboxRadioSwitch :checked.sync="editGroupsFilter">
+                               {{ t('user_name', 'Edit LDAP Query') }}
+                       </NcCheckboxRadioSwitch>
 
-                       <p />
-                       <div class="ldapWizardInfo invisible">
-&nbsp;
+                       <div v-if="!editGroupsFilter">
+                               <label>{{ t('user_name', 'LDAP Filter:') }}</label>
+                               <span>{{ ldapConfig.ldapGroupsListFilter }}</span>
                        </div>
-                       <p class="ldap_count">
-                               <NcButton class="ldapGetEntryCount ldapGetGroupCount" name="ldapGetEntryCount" type="button">
-                                       {{ t('user_ldap', 'Verify settings and count the groups') }}
-                               </NcButton>
-                               <span id="ldap_group_count" />
-                       </p>
+                       <div v-else>
+                               <NcTextArea :value.sync="ldapConfig.ldapGroupListFilter"
+                                       :placeholder="t('user_name', 'Edit LDAP Query')"
+                                       :helper-text="t('user_name', 'The filter specifies which LDAP groups shall have access to the {instanceName} instance.', {instanceName})" />
+                       </div>
+               </div>
+
+               <div class="ldap-wizard__groups__line ldap-wizard__groups__groups-count-check">
+                       <NcButton @click="getGroupsCount">
+                               {{ t('user_ldap', 'Verify settings and count the groups') }}
+                       </NcButton>
 
-                       <!-- TODO: What is this -->
-                       {{ wizardControls }}
+                       <span v-if="groupsCount !== undefined">{{ t('user_ldap', "Groups count: {groupsCount}", { groupsCount }) }}</span>
                </div>
        </fieldset>
 </template>
 
 <script lang="ts" setup>
-import { defineProps, PropType } from 'vue'
+import { defineProps, computed, ref } from 'vue'
 
 import { t } from '@nextcloud/l10n'
-import { NcButton } from '@nextcloud/vue'
+import { NcButton, NcTextArea, NcCheckboxRadioSwitch, NcSelect } from '@nextcloud/vue'
 
-import { LDAPConfig } from '../../services/ldapConfigService';
+import { useLDAPConfigStore } from '../../store/config'
 
-const { ldapConfig } = defineProps({
-       ldapConfig: {
-               type: Object as PropType<LDAPConfig>,
+const ldapConfigStore = useLDAPConfigStore()
+
+const { ldapConfigId } = defineProps({
+       ldapConfigId: {
+               type: String,
                required: true,
        },
 })
 
-const wizardControls = ''
-const theme = {
-       getName() {
-               return 'TODO'
-       },
+const ldapConfig = computed(() => ldapConfigStore.ldapConfigs[ldapConfigId])
+
+const instanceName = 'TODO'
+
+const groupsCount = ref<number>(-1)
+const editGroupsFilter = ref(false)
+const allowUserFilterGroupsSelection = ref(false)
+
+/**
+ *
+ */
+async function getGroupsCount() {
+       groupsCount.value++ // TODO: Implement
 }
 </script>
+<style lang="scss" scoped>
+.ldap-wizard__groups {
+       display: flex;
+       flex-direction: column;
+       gap: 16px;
+
+       &__line {
+               display: flex;
+               align-items: start;
+       }
+
+       &__filter-selection {
+               flex-direction: column;
+       }
+
+       &__groups-filter {
+               display: flex;
+               flex-direction: column;
+       }
+
+       &__groups-count-check {
+               display: flex;
+               align-items: center;
+               gap: 16px;
+       }
+}
+</style>
index bae7f6e779868cc85e0a43241fd9533ec12b13eb..b81f94228d7424f96a9560205891f2ebf3b9cbdc 100644 (file)
  - SPDX-License-Identifier: AGPL-3.0-or-later
  -->
 <template>
-       <fieldset id="ldapWizard3">
-               <div>
-                       <p>
-                               {{ t('user_ldap', 'When logging in, %s will find the user based on the following attributes:', { themeName: theme.getName() }) }}
-                       </p>
-                       <p>
-                               <label for="ldap_loginfilter_username">
-                                       {{ t('user_ldap', 'LDAP/AD Username:') }}
-                               </label>
-
-                               <input id="ldap_loginfilter_username"
-                                       type="checkbox"
-                                       aria-describedby="ldap_loginfilter_username_instructions"
-                                       name="ldap_loginfilter_username"
-                                       value="1">
-                       </p>
-                       <p id="ldap_loginfilter_username_instructions" class="hidden-visually">
-                               {{ t('user_ldap', 'Allows login against the LDAP/AD username, which is either "uid" or "sAMAccountName" and will be detected.') }}
-                       </p>
-                       <p>
-                               <label for="ldap_loginfilter_email">
-                                       {{ t('user_ldap', 'LDAP/AD Email Address:') }}
-                               </label>
-
-                               <input id="ldap_loginfilter_email"
-                                       type="checkbox"
-                                       aria-describedby="ldap_loginfilter_email_instructions"
-                                       name="ldap_loginfilter_email"
-                                       value="1">
-                       </p><p id="ldap_loginfilter_email_instructions" class="hidden-visually">
-                               {{ t('user_ldap', 'Allows login against an email attribute. "mail" and "mailPrimaryAddress" allowed.') }}
-                       </p>
-                       <p>
-                               <label for="ldap_loginfilter_attributes">
-                                       {{ t('user_ldap', 'Other Attributes:') }}
-                               </label>
-
-                               <select id="ldap_loginfilter_attributes"
-                                       multiple="multiple"
-                                       name="ldap_loginfilter_attributes"
-                                       class="multiSelectPlugin" />
-                       </p>
-                       <p>
-                               <label><a id="toggleRawLoginFilter" class="ldapToggle">↓ {{ t('user_ldap', 'Edit LDAP Query') }}</a></label>
-                       </p>
-                       <p id="ldapReadOnlyLoginFilterContainer" class="hidden ldapReadOnlyFilterContainer">
-                               <label>{{ t('user_ldap', 'LDAP Filter:') }}</label>
-                               <span class="ldapFilterReadOnlyElement ldapInputColElement" />
-                       </p>
-                       <p id="rawLoginFilterContainer" class="invisible">
-                               <textarea id="ldap_login_filter"
-                                       type="text"
-                                       name="ldap_login_filter"
-                                       class="ldapFilterInputElement"
-                                       :placeholder="t('user_ldap', 'Edit LDAP Query')"
-                                       aria-describedby="ldap_login_filter_instructions" />
-                       </p><p id="ldap_login_filter_instructions" class="hidden-visually">
-                               {{ t('user_ldap', 'Defines the filter to apply, when login is attempted. \"%%uid\" replaces the username in the login action. Example: \"uid=%%uid\"') }}
-                       </p>
-                       <p /><div class="ldapWizardInfo invisible">
-&nbsp;
+       <fieldset class="ldap-wizard__login">
+               {{ t('user_ldap', 'When logging in, {instanceName} will find the user based on the following attributes:', { instanceName }) }}
+
+               <div class="ldap-wizard__login__line ldap-wizard__login__login-attributes">
+                       <NcCheckboxRadioSwitch :checked.sync="ldapConfig.ldapUsername"
+                               :aria-label="t('user_ldap', 'Allows login against the LDAP/AD username, which is either `uid` or `sAMAccountName` and will be detected.')">
+                               {{ t('user_ldap', 'LDAP/AD Username') }}
+                       </NcCheckboxRadioSwitch>
+
+                       <NcCheckboxRadioSwitch :checked.sync="ldapConfig.ldapEmail"
+                               :aria-label="t('user_ldap', 'Allows login against an email attribute. `mail` and `mailPrimaryAddress` allowed.')">
+                               {{ t('user_ldap', 'LDAP/AD Email Address') }}
+                       </NcCheckboxRadioSwitch>
+
+                       <NcSelect v-model="ldapConfig.ldapLoginFilterAttributes"
+                               :options="['TODO']"
+                               :input-label="t('user_ldap', 'Other Attributes:')"
+                               :multiple="true" />
+               </div>
+
+               <div class="ldap-wizard__login__line ldap-wizard__login__user-login-filter">
+                       <NcCheckboxRadioSwitch :checked.sync="editUserLoginFilter">
+                               {{ t('user_name', 'Edit LDAP Query') }}
+                       </NcCheckboxRadioSwitch>
+
+                       <div v-if="!editUserLoginFilter">
+                               <label>{{ t('user_name', 'LDAP Filter:') }}</label>
+                               <span>{{ ldapConfig.ldapUserLoginFilter }}</span>
+                       </div>
+                       <div v-else>
+                               <NcTextArea :value.sync="ldapConfig.ldapUserLoginFilter"
+                                       :placeholder="t('user_name', 'Edit LDAP Query')"
+                                       :helper-text="t('user_name', 'Defines the filter to apply, when login is attempted. `%%uid` replaces the username in the login action. Example: `uid=%%uid`')" />
                        </div>
-                       <p class="ldap_verify">
-                               <input id="ldap_test_loginname"
-                                       type="text"
-                                       name="ldap_test_loginname"
-                                       :placeholder="t('user_ldap', 'Test Loginname')"
-                                       class="ldapVerifyInput"
-                                       aria-describedby="ldap_test_loginname_instructions">
-                       </p><p id="ldap_test_loginname_instructions" class="hidden-visually">
-                               {{ t('user_ldap', 'Attempts to receive a DN for the given loginname and the current login filter') }}
-                       </p>
-                       <NcButton class="ldapVerifyLoginName"
-                               name="ldapTestLoginSettings"
-                               type="button"
-                               disabled="disabled">
+               </div>
+
+               <div class="ldap-wizard__login__line">
+                       <NcTextField :value.sync="testUsername"
+                               :helper-text="t('user_ldap', 'Attempts to receive a DN for the given loginname and the current login filter')"
+                               :placeholder="t('user_ldap', 'Test Loginname')"
+                               autocomplete="off" />
+
+                       <NcButton :disabled="enableVerifyButton"
+                               @click="verifyLoginName">
                                {{ t('user_ldap', 'Verify settings') }}
                        </NcButton>
-
-                       <!-- TODO: What is this -->
-                       {{ wizardControls }}
                </div>
        </fieldset>
 </template>
 
 <script lang="ts" setup>
-import { defineProps, PropType } from 'vue'
+import { defineProps, computed, ref } from 'vue'
 
 import { t } from '@nextcloud/l10n'
-import { NcButton } from '@nextcloud/vue'
+import { NcButton, NcTextField, NcTextArea, NcCheckboxRadioSwitch, NcSelect } from '@nextcloud/vue'
+
+import { useLDAPConfigStore } from '../../store/config'
 
-import { LDAPConfig } from '../../services/ldapConfigService';
+const ldapConfigStore = useLDAPConfigStore()
 
-const { ldapConfig } = defineProps({
-       ldapConfig: {
-               type: Object as PropType<LDAPConfig>,
+const { ldapConfigId } = defineProps({
+       ldapConfigId: {
+               type: String,
                required: true,
        },
 })
 
-const wizardControls = ''
-const theme = {
-       getName() {
-               return 'TODO'
-       },
+const ldapConfig = computed(() => ldapConfigStore.ldapConfigs[ldapConfigId])
+
+const instanceName = 'TODO'
+const testUsername = ref('TODO')
+const enableVerifyButton = ref(false)
+const editUserLoginFilter = ref(false)
+
+/**
+ *
+ */
+async function verifyLoginName() {
+       // TODO: Implement
 }
 </script>
+<style lang="scss" scoped>
+.ldap-wizard__login {
+       display: flex;
+       flex-direction: column;
+       gap: 16px;
+
+       button {
+               flex-shrink: 0;
+       }
+
+       &__line {
+               display: flex;
+               align-items: start;
+       }
+
+       &__login-attributes {
+               display: flex;
+               flex-direction: column;
+       }
+
+       &__user-login-filter {
+               display: flex;
+               flex-direction: column;
+       }
+}
+</style>
index 83ab423b8211fcb1644ec1e781130f7e566ea429..1ae2964874fd3047c7ee27f5bfad21ab31fc97fe 100644 (file)
  - SPDX-License-Identifier: AGPL-3.0-or-later
  -->
 <template>
-       <fieldset id="ldapWizard1">
-               <NcButton id="ldap_action_copy_configuration"
-                       type="button"
-                       name="ldap_action_copy_configuration"
-                       aria-describedby="ldap_action_copy_configuration_instructions"
-                       class="ldapIconCopy icon-default-style"
-                       :title="t('user_ldap', 'Copy current configuration into new directory binding')">
-                       <template #icon>
-                               <ContentCopy :size="20" />
-                       </template>
-               </NcButton>
-               <p id="ldap_action_copy_configuration_instructions" class="hidden-visually">
-                       {{ t('user_ldap', 'Copy current configuration into new directory binding') }}
-               </p>
-               <NcButton id="ldap_action_delete_configuration"
-                       type="button"
-                       aria-describedby="ldap_action_delete_configuration_instructions"
-                       name="ldap_action_delete_configuration"
-                       class="icon-delete icon-default-style"
-                       :title="t('user_ldap', 'Delete the current configuration')">
-                       <template #icon>
-                               <Delete :size="20" />
-                       </template>
-               </NcButton>
-               <p id="ldap_action_delete_configuration_instructions" class="hidden-visually">
-                       {{ t('user_ldap', 'Delete the current configuration') }}
-               </p>
-
-               <div class="hostPortCombinator">
-                       <div class="tablerow">
-                               <div class="tablecell">
-                                       <div class="table">
-                                               <NcTextField id="ldap_host"
-                                                       class="host"
-                                                       :value.sync="ldapConfig.ldapHost"
-                                                       :helper-text="t('user_ldap', 'You can omit the protocol, unless you require SSL. If so, start with ldaps://')"
-                                                       :placeholder="t('user_ldap', 'Host')"
-                                                       autocomplete="off" />
-                                               <span class="hostPortCombinatorSpan">
-                                                       <input id="ldap_port"
-                                                               type="number"
-                                                               name="ldap_port"
-                                                               :placeholder="t('user_ldap', 'Port')">
-                                                       <NcButton class="ldapDetectPort" name="ldapDetectPort" @click="detectPort">
-                                                               {{ t('user_ldap', 'Detect Port') }}
-                                                       </NcButton>
-                                               </span>
-                                       </div>
-                               </div>
-                       </div>
-                       <div class="tablerow">
-&nbsp;
-                       </div>
-                       <div class="tablerow">
-                               <NcTextField id="ldap_dn"
-                                       class="tablecell"
-                                       :value.sync="ldapConfig.ldapAgentName"
-                                       :helper-text="t('user_ldap', '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.')"
-                                       :placeholder="t('user_ldap', 'User DN')"
-                                       autocomplete="off" />
-                       </div>
+       <fieldset class="ldap-wizard__server">
+               <div class="ldap-wizard__server__line">
+                       <NcButton :aria-label="t('user_ldap', 'Copy current configuration into new directory binding')"
+                               @click="() => ldapConfigStore.copy(ldapConfigId)">
+                               <template #icon>
+                                       <ContentCopy :size="20" />
+                               </template>
+                       </NcButton>
+                       <NcButton :aria-label="t('user_ldap', 'Delete the current configuration')"
+                               @click="() => ldapConfigStore.remove(ldapConfigId)">
+                               <template #icon>
+                                       <Delete :size="20" />
+                               </template>
+                       </NcButton>
+               </div>
 
-                       <div class="tablerow">
-                               <NcTextField id="ldap_agent_password"
-                                       type="password"
-                                       class="tablecell"
-                                       :helper-text="t('user_ldap', 'For anonymous access, leave DN and Password empty.')"
-                                       :value.sync="ldapConfig.ldapAgentPassword"
-                                       :placeholder="t('user_ldap', 'Password')"
+               <div class="ldap-wizard__server__line">
+                       <NcTextField :value.sync="ldapConfig.ldapHost"
+                               :helper-text="t('user_ldap', 'You can omit the protocol, unless you require SSL. If so, start with ldaps://')"
+                               :placeholder="t('user_ldap', 'Host')"
+                               autocomplete="off" />
+                       <div class="ldap-wizard__server__host__port">
+                               <NcTextField :value.sync="ldapConfig.ldapPort"
+                                       :placeholder="t('user_ldap', 'Port')"
+                                       type="number"
                                        autocomplete="off" />
-
-                               <NcButton class="ldapSaveAgentCredentials" name="ldapSaveAgentCredentials" @click="ldapConfigStore.create(ldapConfig)">
-                                       {{ t('user_ldap', 'Save Credentials') }}
+                               <NcButton @click="detectPort">
+                                       {{ t('user_ldap', 'Detect Port') }}
                                </NcButton>
                        </div>
+               </div>
 
-                       <div id="ldap_base" class="tablecell">
-                               <NcTextArea label="Text area"
-                                       :placeholder="t('user_ldap', 'One Base DN per line')"
-                                       helper-text="t('user_ldap', 'You can specify Base DN for users and groups in the Advanced tab')" />
+               <div class="ldap-wizard__server__line">
+                       <NcTextField :value.sync="ldapConfig.ldapAgentName"
+                               :helper-text="t('user_ldap', '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.')"
+                               :placeholder="t('user_ldap', 'User DN')"
+                               autocomplete="off" />
+               </div>
 
-                               <NcButton class="ldapDetectBase" name="ldapDetectBase" type="button">
-                                       {{ t('user_ldap', 'Detect Base DN') }}
-                               </NcButton>
-                               <NcButton class="ldapTestBase" name="ldapTestBase" type="button">
-                                       {{ t('user_ldap', 'Test Base DN') }}
-                               </NcButton>
-                       </div>
+               <div class="ldap-wizard__server__line">
+                       <NcTextField type="password"
+                               :helper-text="t('user_ldap', 'For anonymous access, leave DN and Password empty.')"
+                               :value.sync="ldapConfig.ldapAgentPassword"
+                               :placeholder="t('user_ldap', 'Password')"
+                               autocomplete="off" />
 
-                       <div class="tablerow left">
-                               <input id="ldap_experienced_admin"
-                                       type="checkbox"
-                                       value="1"
-                                       name="ldap_experienced_admin"
-                                       class="tablecell"
-                                       aria-describedby="ldap_experienced_admin_instructions"
-                                       :title="t('user_ldap', 'Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge.')">
-                               <p id="ldap_experienced_admin_instructions" class="hidden-visually">
-                                       {{ t('user_ldap', 'Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge.') }}
-                               </p>
-                               <label for="ldap_experienced_admin" class="tablecell">
-                                       {{ t('user_ldap', 'Manually enter LDAP filters (recommended for large directories)') }}
-                               </label>
-                       </div>
+                       <NcButton @click="ldapConfigStore.create(ldapConfig)">
+                               {{ t('user_ldap', 'Save Credentials') }}
+                       </NcButton>
                </div>
 
-               <!-- TODO: What is this -->
-               {{ wizardControls }}
+               <div class="ldap-wizard__server__line">
+                       <NcTextArea :label="t('user_ldap', 'Base DN')"
+                               :value.sync="ldapConfig.ldapBase"
+                               :placeholder="t('user_ldap', 'One Base DN per line')"
+                               :helper-text="t('user_ldap', 'You can specify Base DN for users and groups in the Advanced tab')" />
+
+                       <NcButton @click="detectBaseDN">
+                               {{ t('user_ldap', 'Detect Base DN') }}
+                       </NcButton>
+                       <NcButton @click="testBaseDN">
+                               {{ t('user_ldap', 'Test Base DN') }}
+                       </NcButton>
+               </div>
+
+               <div class="ldap-wizard__server__line">
+                       <NcCheckboxRadioSwitch :checked.sync="advancedAdmin"
+                               :aria-label="t('user_ldap', 'Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge.')">
+                               {{ t('user_ldap', 'Manually enter LDAP filters (recommended for large directories)') }}
+                       </NcCheckboxRadioSwitch>
+               </div>
        </fieldset>
 </template>
 
 <script lang="ts" setup>
-import { defineProps, PropType } from 'vue'
+import { defineProps, computed, ref } from 'vue'
 
 import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
 import Delete from 'vue-material-design-icons/Delete.vue'
 
 import { t } from '@nextcloud/l10n'
-import { NcButton } from '@nextcloud/vue'
+import { NcButton, NcTextField, NcTextArea, NcCheckboxRadioSwitch } from '@nextcloud/vue'
 
-import { LDAPConfig } from '../../models'
 import { useLDAPConfigStore } from '../../store/config'
 
 const ldapConfigStore = useLDAPConfigStore()
 
-const { ldapConfig } = defineProps({
-       ldapConfig: {
-               type: Object as PropType<LDAPConfig>,
+const { ldapConfigId } = defineProps({
+       ldapConfigId: {
+               type: String,
                required: true,
        },
 })
 
-const wizardControls = ''
+const ldapConfig = computed(() => ldapConfigStore.ldapConfigs[ldapConfigId])
+
+// TODO: use this
+const advancedAdmin = ref(false)
+
+/**
+ *
+ */
+async function detectPort() {
+       // TODO
+}
+
+/**
+ *
+ */
+async function detectBaseDN() {
+       // TODO
+}
+
+/**
+ *
+ */
+async function testBaseDN() {
+       // TODO
+}
 </script>
+<style lang="scss" scoped>
+.ldap-wizard__server {
+       display: flex;
+       flex-direction: column;
+       gap: 16px;
+
+       button {
+               flex-shrink: 0;
+       }
+
+       &__line {
+               display: flex;
+               align-items: start;
+               gap: 16px;
+       }
+
+       &__host__port {
+               display: flex;
+               align-items: center;
+               flex-shrink: 0;
+               gap: 16px;
+       }
+}
+</style>
index f181cea2790ae51618d393a41ec021b27d74e60b..3c4137b40cfe70a3c9ee811f276c658f84345e97 100644 (file)
  - SPDX-License-Identifier: AGPL-3.0-or-later
  -->
 <template>
-       <fieldset id="ldapWizard2">
-               <div>
-                       <p>
-                               {{ t('user_name', 'Listing and searching for users is constrained by these criteria:') }}
-                       </p>
-                       <p>
-                               <label for="ldap_userfilter_objectclass">
-                                       {{ t('user_name', 'Only these object classes:') }}
-                               </label>
+       <fieldset class="ldap-wizard__users">
+               {{ t('user_name', 'Listing and searching for users is constrained by these criteria:') }}
 
-                               <select id="ldap_userfilter_objectclass"
-                                       multiple="multiple"
-                                       name="ldap_userfilter_objectclass"
-                                       class="multiSelectPlugin" />
-                       </p>
-                       <p>
-                               <label />
-                               <span class="ldapInputColElement">{{ t('user_name', '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.') }}</span>
-                       </p>
-                       <p>
-                               <label for="ldap_userfilter_groups">
-                                       {{ t('user_name', 'Only from these groups:') }}
-                               </label>
+               <div class="ldap-wizard__users__line ldap-wizard__users__user-filter-object-class">
+                       <NcSelect v-model="ldapConfig.ldapUserFilterObjectclass"
+                               class="ldap-wizard__users__user-filter-object-class__select"
+                               :disable="editUserFilter"
+                               :options="['TODO']"
+                               :input-label="t('user_name', 'Only these object classes:')"
+                               :multiple="true" />
+                       {{ t('user_name', '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.') }}</span>
+               </div>
 
-                               <input type="text" class="ldapManyGroupsSupport ldapManyGroupsSearch hidden" placeholder="t('user_name', 'Search groups')">
+               <div class="ldap-wizard__users__line ldap-wizard__users__user-filter-groups">
+                       <div>
+                               {{ t('user_name', 'Only from these groups:') }}
+                       </div>
 
-                               <select id="ldap_userfilter_groups"
-                                       multiple="multiple"
-                                       name="ldap_userfilter_groups"
-                                       class="multiSelectPlugin" />
-                       </p>
+                       <!-- <input type="text" class="ldapManyGroupsSupport ldapManyGroupsSearch hidden" > -->
+                       <!-- <NcTextField :disable="editUserFilter"
+                               :value.sync="ldapConfig.ldapUserFilterGroups"
+                               :placeholder="t('user_name', 'Search groups')"
+                               autocomplete="off" /> -->
+
+                       <NcSelect v-model="ldapConfig.ldapUserFilterGroups"
+                               class="ldap-wizard__users__user-filter-groups__select"
+                               :options="['TODO']"
+                               :disable="allowUserFilterGroupsSelection"
+                               :input-label="t('user_name', 'Only these object classes:')"
+                               :multiple="true" />
+               </div>
+
+               <!-- TODO -->
+               <div class="ldap-wizard__users__line">
                        <p class="ldapManyGroupsSupport hidden">
-                               <label />
                                <select class="ldapGroupList ldapGroupListAvailable"
                                        multiple="multiple"
                                        aria-describedby="ldapGroupListAvailable_instructions"
                                        :title="t('user_name', 'Available groups')" />
-                       </p><p id="ldapGroupListAvailable_instructions" class="hidden-visually">
+                       </p>
+                       <p id="ldapGroupListAvailable_instructions" class="hidden-visually">
                                {{ t('user_name', 'Available groups') }}
                        </p>
-                       <span class="buttonSpan">
-                               <button class="ldapGroupListSelect" type="button">&gt;</button><br>
-                               <button class="ldapGroupListDeselect" type="button">&lt;</button>
+
+                       <span>
+                               <NcButton class="ldapGroupListSelect">&gt;</NcButton>
+                               <NcButton class="ldapGroupListDeselect">&lt;</NcButton>
                        </span>
+
                        <select class="ldapGroupList ldapGroupListSelected"
                                multiple="multiple"
                                aria-describedby="ldapGroupListSelected_instructions"
                                :title="t('user_name', 'Selected groups')" />
+
                        <p id="ldapGroupListSelected_instructions" class="hidden-visually">
                                {{ t('user_name', 'Selected groups') }}
                        </p>
-                       <p>
-                               <label><a id="toggleRawUserFilter" class="ldapToggle">↓ {{ t('user_name', 'Edit LDAP Query') }}</a></label>
-                       </p>
-                       <p id="ldapReadOnlyUserFilterContainer" class="hidden ldapReadOnlyFilterContainer">
+               </div>
+
+               <div class="ldap-wizard__users__line ldap-wizard__users__user-filter">
+                       <NcCheckboxRadioSwitch :checked.sync="editUserFilter">
+                               {{ t('user_name', 'Edit LDAP Query') }}
+                       </NcCheckboxRadioSwitch>
+
+                       <div v-if="!editUserFilter">
                                <label>{{ t('user_name', 'LDAP Filter:') }}</label>
-                               <span class="ldapFilterReadOnlyElement ldapInputColElement" />
-                       </p>
-                       <p id="rawUserFilterContainer">
-                               <textarea id="ldap_userlist_filter"
-                                       type="text"
-                                       name="ldap_userlist_filter"
-                                       class="ldapFilterInputElement"
-                                       placeholder="t('user_name', 'Edit LDAP Query')"
-                                       aria-describedby="ldap_userlist_filter_instructions"
-                                       :title="t('user_name', 'The filter specifies which LDAP users shall have access to the %s instance.', { themeName: theme.getName()})" />
-                       </p>
-                       <p id="ldap_userlist_filter_instructions" class="hidden-visually">
-                               {{ t('user_name', 'The filter specifies which LDAP users shall have access to the %s instance.', { themeName: theme.getName()}) }}
-                       </p>
-                       <div class="ldapWizardInfo invisible">
-&nbsp;
+                               <span>{{ ldapConfig.ldapUserListFilter }}</span>
                        </div>
-                       <p class="ldap_count">
-                               <NcButton class="ldapGetEntryCount ldapGetUserCount" name="ldapGetEntryCount" type="button">
-                                       {{ t('user_name', 'Verify settings and count users') }}
-                               </NcButton>
-                               <span id="ldap_user_count" />
-                       </p>
-                       {{ wizardControls }}
+                       <div v-else>
+                               <NcTextArea :value.sync="ldapConfig.ldapUserListFilter"
+                                       :placeholder="t('user_name', 'Edit LDAP Query')"
+                                       :helper-text="t('user_name', 'The filter specifies which LDAP users shall have access to the {instanceName} instance.', { instanceName })" />
+                       </div>
+               </div>
+
+               <div class="ldap-wizard__users__line ldap-wizard__users__user-count-check">
+                       <NcButton @click="getUserCount">
+                               {{ t('user_name', 'Verify settings and count users') }}
+                       </NcButton>
+
+                       <span v-if="userCount !== undefined">{{ t('user_ldap', "User count: {userCount}", { userCount }) }}</span>
                </div>
        </fieldset>
 </template>
 
 <script lang="ts" setup>
-import { defineProps, PropType } from 'vue'
+import { defineProps, computed, ref } from 'vue'
 
 import { t } from '@nextcloud/l10n'
-import { NcButton } from '@nextcloud/vue'
+import { NcButton, NcTextArea, NcCheckboxRadioSwitch, NcSelect } from '@nextcloud/vue'
+
+import { useLDAPConfigStore } from '../../store/config'
 
-import type { LDAPConfig } from '../../services/ldapConfigService'
+const ldapConfigStore = useLDAPConfigStore()
 
-const { ldapConfig } = defineProps({
-       ldapConfig: {
-               type: Object as PropType<LDAPConfig>,
+const { ldapConfigId } = defineProps({
+       ldapConfigId: {
+               type: String,
                required: true,
        },
 })
 
-const wizardControls = ''
-const theme = {
-       getName() {
-               return 'TODO'
-       },
+const ldapConfig = computed(() => ldapConfigStore.ldapConfigs[ldapConfigId])
+
+const userCount = ref<number>(-1)
+
+const editUserFilter = ref(false)
+const allowUserFilterGroupsSelection = ref(true) // TODO
+const instanceName = 'TODO'
+
+/**
+ *
+ */
+async function getUserCount() {
+       userCount.value++ // TODO: Implement
 }
 </script>
+<style lang="scss" scoped>
+.ldap-wizard__users {
+       display: flex;
+       flex-direction: column;
+       gap: 16px;
+
+       &__line {
+               display: flex;
+               align-items: start;
+       }
+
+       &__user-filter-object-class {
+               display: flex;
+               gap: 16px;
+
+               &__select {
+                       min-width: 50%;
+                       flex-grow: 1;
+               }
+       }
+
+       &__user-filter-groups {
+               display: flex;
+               gap: 16px;
+
+               &__select {
+                       min-width: 50%;
+                       flex-grow: 1;
+               }
+       }
+
+       &__user-filter {
+               display: flex;
+               flex-direction: column;
+       }
+
+       &__user-count-check {
+               display: flex;
+               align-items: center;
+               gap: 16px;
+       }
+}
+</style>
diff --git a/apps/user_ldap/src/components/WizardControls.vue b/apps/user_ldap/src/components/WizardControls.vue
new file mode 100644 (file)
index 0000000..e89fcb0
--- /dev/null
@@ -0,0 +1,28 @@
+<!--
+ - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+ -->
+<template>
+       <div>
+               TODO: Wizard Controls: {{ ldapConfig.ldapHost }}
+       </div>
+</template>
+
+<script lang="ts" setup>
+import { defineProps, computed } from 'vue'
+
+import { useLDAPConfigStore } from '../store/config'
+
+const ldapConfigStore = useLDAPConfigStore()
+
+const { ldapConfigId } = defineProps({
+       ldapConfigId: {
+               type: String,
+               required: true,
+       },
+})
+
+const ldapConfig = computed(() => ldapConfigStore.ldapConfigs[ldapConfigId])
+</script>
+<style lang="scss" scoped>
+</style>
index 1b48dd8f5eeac77175435168b9bd329d9b81f750..6ada1184072f46e6898f62d583230e2e26b7499d 100644 (file)
@@ -22,12 +22,17 @@ export function useLDAPConfigStore(...args) {
                }),
 
                actions: {
-                       async create(config: LDAPConfig) {
-                               const { configId, config: newConfig } = await createConfig(config)
-                               Vue.set(this.ldapConfigs, configId, newConfig)
+                       async create() {
+                               const { configId, config } = await createConfig({ ...this.defaultLdapConfig })
+                               Vue.set(this.ldapConfigs, configId, config)
                        },
 
-                       async delete(configId: string) {
+                       async copy(fromConfigId: string) {
+                               const { configId, config } = await createConfig({ ...this.ldapConfigs[fromConfigId] })
+                               Vue.set(this.ldapConfigs, configId, config)
+                       },
+
+                       async remove(configId: string) {
                                const result = await deleteConfig(configId)
                                if (result === true) {
                                        Vue.delete(this.ldapConfigs, configId)
@@ -38,6 +43,18 @@ export function useLDAPConfigStore(...args) {
                                config = await updateConfig(configId, config)
                                Vue.set(this.ldapConfigs, configId, config)
                        },
+
+                       async detectPort() {
+                                // TODO
+                       },
+
+                       async detectBaseDN() {
+                                // TODO
+                       },
+
+                       async testBaseDN() {
+                                // TODO
+                       },
                },
        })
 
index 615e8e6f59210d25b794b11d853922a3157eab4e..9ebd268455b64549804ea027540aae036dc0aef5 100644 (file)
@@ -3,12 +3,12 @@
  - SPDX-License-Identifier: AGPL-3.0-or-later
  -->
 <template>
-       <form id="ldap" class="section">
+       <form class="ldap-wizard">
                <h2>{{ t('user_ldap', 'LDAP/AD integration') }}</h2>
 
-               <div class="ldap__config-selection">
+               <div class="ldap-wizard__config-selection">
                        <NcSelect v-model="selectedConfigId"
-                               :options="Object.values(ldapConfigStore.ldapConfigs)"
+                               :options="selectOptions"
                                :input-label="t('user_ldap', 'Select LDAP Config')" />
                        <NcButton :label="t('user_ldap','Create New Config')" @click="ldapConfigStore.create">
                                <template #icon>
                        <NcLoadingIcon v-if="false" />
                </div>
 
-               <div v-if="selectedConfig !== undefined" id="ldapSettings">
-                       <div class="ldap__tab-selection">
-                               <NcCheckboxRadioSwitch v-for="(tabId, tabLabel) in tabs"
-                                       :key="tabId"
-                                       :button-variant="true"
-                                       :checked.sync="selectedTab"
-                                       :value="tabId"
-                                       name="ldap_selected_tab"
-                                       type="radio"
-                                       button-variant-grouped="horizontal">
-                                       {{ tabLabel }}
-                               </NcCheckboxRadioSwitch>
+               <div v-if="selectedConfigId !== undefined" class="ldap-wizard__tab-container">
+                       <div class="ldap-wizard__tab-selection-container">
+                               <div class="ldap-wizard__tab-selection">
+                                       <NcCheckboxRadioSwitch v-for="(tabLabel, tabId) in leftTabs"
+                                               :key="tabId"
+                                               :button-variant="true"
+                                               :checked.sync="selectedTab"
+                                               :value="tabId"
+                                               type="radio"
+                                               button-variant-grouped="horizontal">
+                                               {{ tabLabel }}
+                                       </NcCheckboxRadioSwitch>
+                               </div>
+                               <div class="ldap-wizard__tab-selection">
+                                       <NcCheckboxRadioSwitch v-for="(tabLabel, tabId) in rightTabs"
+                                               :key="tabId"
+                                               :button-variant="true"
+                                               :checked.sync="selectedTab"
+                                               :value="tabId"
+                                               type="radio"
+                                               button-variant-grouped="horizontal">
+                                               {{ tabLabel }}
+                                       </NcCheckboxRadioSwitch>
+                               </div>
                        </div>
 
-                       <p v-if="!ldapModuleInstalled" class="ldapwarning">
+                       <!-- TODO: change ldapwarning -->
+                       <div v-if="!ldapModuleInstalled" class="ldapwarning">
                                {{ t('user_ldap', '<b>Warning:</b> The PHP LDAP module is not installed, the backend will not work. Please ask your system administrator to install it.') }}
-                       </p>
-
-                       <ServerTab v-if="selectedTab === 'server'" :ldap-config="selectedConfig" />
-                       <!-- <UsersTab v-else-if="selectedTab === 'users'" :ldap-config="selectedConfig" />
-                       <LoginTab v-else-if="selectedTab === 'login'" :ldap-config="selectedConfig" />
-                       <GroupsTab v-else-if="selectedTab === 'groups'" :ldap-config="selectedConfig" />
-                       <ExpertTab v-else-if="selectedTab === 'expert'" :ldap-config="selectedConfig" />
-                       <AdvancedTab v-else-if="selectedTab === 'advanced'" :ldap-config="selectedConfig" /> -->
+                       </div>
+
+                       <ServerTab v-if="selectedTab === 'server'" :ldap-config-id="selectedConfigId" />
+                       <UsersTab v-else-if="selectedTab === 'users'" :ldap-config-id="selectedConfigId" />
+                       <LoginTab v-else-if="selectedTab === 'login'" :ldap-config-id="selectedConfigId" />
+                       <GroupsTab v-else-if="selectedTab === 'groups'" :ldap-config-id="selectedConfigId" />
+                       <ExpertTab v-else-if="selectedTab === 'expert'" :ldap-config-id="selectedConfigId" />
+                       <AdvancedTab v-else-if="selectedTab === 'advanced'" :ldap-config-id="selectedConfigId" />
+
+                       <WizardControls class="ldap-wizard__controls" :ldap-config-id="selectedConfigId" />
                </div>
        </form>
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue'
+import { ref } from 'vue'
+
 import Plus from 'vue-material-design-icons/Plus.vue'
 
 import { t } from '@nextcloud/l10n'
 import { NcCheckboxRadioSwitch, NcLoadingIcon, NcSelect, NcButton } from '@nextcloud/vue'
 
 import ServerTab from '../components/SettingsTabs/ServerTab.vue'
-// import UsersTab from '../components/SettingsTabs/UsersTab.vue'
-// import LoginTab from '../components/SettingsTabs/LoginTab.vue'
-// import GroupsTab from '../components/SettingsTabs/GroupsTab.vue'
-// import ExpertTab from '../components/SettingsTabs/ExpertTab.vue'
-// import AdvancedTab from '../components/SettingsTabs/AdvancedTab.vue'
+import UsersTab from '../components/SettingsTabs/UsersTab.vue'
+import LoginTab from '../components/SettingsTabs/LoginTab.vue'
+import GroupsTab from '../components/SettingsTabs/GroupsTab.vue'
+import ExpertTab from '../components/SettingsTabs/ExpertTab.vue'
+import AdvancedTab from '../components/SettingsTabs/AdvancedTab.vue'
+import WizardControls from '../components/WizardControls.vue'
 import { useLDAPConfigStore } from '../store/config'
 
-const selectedTab = 'server'
-const selectedConfigId = null
-// TODO: Setup from initial state
-const ldapModuleInstalled = true
-
-const ldapConfigStore = useLDAPConfigStore()
-
-const selectedConfig = computed(() => ldapConfigStore.ldapConfigs[selectedConfigId ?? 0] ?? { ...ldapConfigStore.defaultLdapConfig })
-
-const tabs = {
+const leftTabs = {
        server: t('user_ldap', 'Server'),
        users: t('user_ldap', 'Users'),
        login: t('user_ldap', 'Login Attributes'),
        groups: t('user_ldap', 'Groups'),
+}
+
+const rightTabs = {
        expert: t('user_ldap', 'Expert'),
        advanced: t('user_ldap', 'Advanced'),
 }
+
+const ldapConfigStore = useLDAPConfigStore()
+
+const selectedTab = ref('server')
+// eslint-disable-next-line prefer-const
+const selectedConfigId = ref(Object.keys(ldapConfigStore.ldapConfigs)[0] ?? undefined)
+// TODO: Setup from initial state
+const ldapModuleInstalled = true
+
+const selectOptions = Object.entries(ldapConfigStore.ldapConfigs).map(([configId, config]) => ({
+       id: configId,
+       label: `${configId}: ${config.ldapHost}`,
+}))
+
 </script>
 <style lang="scss" scoped>
-.ldap__config-selection {
-       display: flex;
-       align-items: center;
-       gap: 16px;
-}
+.ldap-wizard {
+       padding: 16px;
+       max-width: 1000px;
+
+       &__config-selection {
+               display: flex;
+               align-items: bottom;
+               margin-bottom: 8px;
+               gap: 16px;
+       }
+
+       &__tab-selection-container {
+               display: flex;
+               justify-content: space-between;
+       }
+
+       &__tab-selection {
+               display: flex;
+               margin-left: -16px;
+               margin-bottom: 16px;
+
+               &:last-of-type {
+                       margin-right: -16px;
+               }
+       }
+
+       &__tab-container {
+               border: 1px solid gray;
+               border-radius: var(--border-radius);
+               padding: 0 16px;
+       }
 
-.ldap__tab-selection {
-       display: flex;
+       &__controls {
+               margin-top: 16px;
+       }
 }
 </style>