entityTypeManager = $entity_type_manager; $this->configFactory = $config_factory; $this->logger = $logger_factory->get('ldap_courses_sync'); $this->ldapBridge = $ldap_bridge; // Check if entity type ldap_query_entity exists if ($entity_type_manager->hasDefinition('ldap_query_entity')) { $this->ldapQueryStorage = $entity_type_manager->getStorage('ldap_query_entity'); } else { $this->logger->warning('Entity type ldap_query_entity not found. Check if ldap_query module is properly installed.'); $this->ldapQueryStorage = NULL; } } /** * Synchronizes courses from LDAP to groups. */ public function syncCourses() { $this->logger->info('Starting courses synchronization using configured LDAP query.'); $config = $this->configFactory->get('ldap_courses_sync.settings'); $bundle_id = $this->getBundleId(); $this->logger->info('Configuração atual - Server: @server, Query: @query, Group Type: @bundle', [ '@server' => $config->get('ldap_server_id') ?: 'não configurado', '@query' => $config->get('ldap_query_id') ?: 'não configurado', '@bundle' => $bundle_id, ]); try { $courses = $this->fetchCoursesUsingQuery(); } catch (\Exception $e) { $this->logger->error('Erro durante fetchCoursesUsingQuery: @message', ['@message' => $e->getMessage()]); throw $e; } if (empty($courses)) { $this->logger->warning('Nenhum curso encontrado no LDAP.'); return; } $this->logger->info('Iniciando sincronização de @count cursos', ['@count' => count($courses)]); $entity_storage = $this->getEntityStorage(); $bundle_field = $this->getBundleField(); $existing_entities = $entity_storage->loadByProperties([$bundle_field => $bundle_id]); // Cria um mapa de códigos existentes $existing_codes = []; foreach ($existing_entities as $entity) { if ($entity->hasField('field_course_code') && !$entity->get('field_course_code')->isEmpty()) { $code = $entity->get('field_course_code')->value; $existing_codes[$code] = $entity; } } $created = 0; $updated = 0; foreach ($courses as $course_data) { $code = $course_data['code']; $name = $course_data['name']; $phone = $course_data['phone']; $mail = $course_data['mail']; // Campos extras (incluindo referências de usuário) $extra_fields = []; foreach ($course_data as $field => $value) { if (!in_array($field, ['code', 'name', 'phone', 'mail', '_ldap_members'])) { $extra_fields[$field] = $value; } } $this->logger->info('Processando curso - Código: @code, Nome: @name', [ '@code' => $code, '@name' => $name, ]); if (isset($existing_codes[$code])) { // Atualiza entidade existente $this->logger->info('Atualizando entidade existente com código: @code', ['@code' => $code]); $entity = $existing_codes[$code]; $name_field = $this->getNameField(); if ($name_field === 'label') { $entity->set('label', $name); } else { $entity->setName($name); } if ($entity->hasField('field_course_phone')) { $entity->set('field_course_phone', $phone); } if ($entity->hasField('field_course_mail')) { $entity->set('field_course_mail', $mail); } foreach ($extra_fields as $field => $value) { if ($entity->hasField($field)) { $entity->set($field, $value); $this->logger->debug('Campo @field atualizado com valor @value', [ '@field' => $field, '@value' => $value ?? '', ]); } else { $this->logger->warning('Campo @field não existe na entidade', [ '@field' => $field, ]); } } try { $entity->save(); $updated++; $this->logger->info('Entidade atualizada com sucesso: @code', ['@code' => $code]); } catch (\Exception $e) { $this->logger->error('Erro ao atualizar entidade @code: @error', [ '@code' => $code, '@error' => $e->getMessage(), ]); } } else { // Cria nova entidade $this->logger->info('Criando nova entidade com código: @code', ['@code' => $code]); try { $bundle_field = $this->getBundleField(); $name_field = $this->getNameField(); $entity_data = [ $bundle_field => $bundle_id, $name_field => $name, 'field_course_code' => $code, 'field_course_phone' => $phone, 'field_course_mail' => $mail, 'uid' => 1, ]; foreach ($extra_fields as $field => $value) { $entity_data[$field] = $value; $this->logger->debug('Campo @field configurado com valor @value', [ '@field' => $field, '@value' => $value ?? '', ]); } $entity = $entity_storage->create($entity_data); $entity->save(); $created++; $this->logger->info('Entidade criada com sucesso: @code (ID: @id)', [ '@code' => $code, '@id' => $entity->id(), ]); } catch (\Exception $e) { $this->logger->error('Erro ao criar entidade @code: @error', [ '@code' => $code, '@error' => $e->getMessage(), ]); } } } if ($created > 0 || $updated > 0) { $this->logger->info('Sincronização concluída com sucesso. Criados: @created, Atualizados: @updated', [ '@created' => $created, '@updated' => $updated, ]); } else { $this->logger->info('Nenhum curso criado ou atualizado.'); } // Sincroniza membros dos grupos via atributo LDAP $this->syncMembersFromLdapAttribute($courses); } /** * Busca entidade de curso pelo código. * * @param string $course_code * Código do curso. * * @return \Drupal\Core\Entity\EntityInterface|null * Entidade grupo ou NULL se não encontrado. */ public function getCourseByCode($course_code) { if (empty($course_code)) { return NULL; } $bundle_id = $this->getBundleId(); $bundle_field = $this->getBundleField(); $entity_storage = $this->getEntityStorage(); $entities = $entity_storage->loadByProperties([ $bundle_field => $bundle_id, 'field_course_code' => $course_code, ]); return !empty($entities) ? reset($entities) : NULL; } /** * Busca cursos usando a query LDAP configurada. * * @return array * Array de cursos com código e descrição. */ protected function fetchCoursesUsingQuery() { $courses = []; $this->logger->info('Iniciando fetchCoursesUsingQuery...'); if (!$this->ldapQueryStorage) { $this->logger->error('Storage de queries LDAP não está disponível. Verifique se o módulo ldap_query está instalado.'); return $courses; } $this->logger->info('Storage de queries LDAP está disponível.'); try { $config = $this->configFactory->get('ldap_courses_sync.settings'); $query_id = $config->get('ldap_query_id') ?: 'course_sync'; $this->logger->info('Tentando carregar query LDAP: @query_id', ['@query_id' => $query_id]); $query_entity = $this->ldapQueryStorage->load($query_id); if (!$query_entity) { $this->logger->error('Query LDAP "@query_id" não encontrada. Configure a query via /admin/config/local-modules/ldap-courses-sync', [ '@query_id' => $query_id, ]); return $courses; } $this->logger->info('Query LDAP carregada com sucesso: @label', ['@label' => $query_entity->label()]); if (!$query_entity->get('status')) { $this->logger->error('Query LDAP "@query_id" está desabilitada.', [ '@query_id' => $query_id, ]); return $courses; } $this->logger->info('Usando query LDAP configurada: @query_id (@label)', [ '@query_id' => $query_id, '@label' => $query_entity->label(), ]); $this->logger->info('Executando query LDAP...'); try { $server_id = $query_entity->getServerId(); $base_dns = $query_entity->getProcessedBaseDns(); $filter = $query_entity->getFilter(); $attributes = $query_entity->getProcessedAttributes(); $this->logger->info('Parâmetros da query - Server: @server, Base DN: @base_dn, Filter: @filter, Attributes: @attrs', [ '@server' => $server_id, '@base_dn' => !empty($base_dns) ? implode(', ', $base_dns) : 'vazio', '@filter' => $filter, '@attrs' => !empty($attributes) ? implode(', ', $attributes) : 'vazio', ]); $this->ldapBridge->setServerById($server_id); if (!$this->ldapBridge->bind()) { throw new \Exception('Falha ao conectar ao servidor LDAP'); } $this->logger->info('Conectado ao servidor LDAP com sucesso'); $all_results = []; foreach ($base_dns as $base_dn) { $this->logger->info('Executando busca em Base DN: @base_dn', ['@base_dn' => $base_dn]); try { $ldap = $this->ldapBridge->get(); $this->logger->info('Obtida instância LDAP do bridge'); // Garante que o atributo de membros está incluído na query $member_sync_on = $config->get('member_sync_enabled') ?? TRUE; if ($member_sync_on) { $member_attr = $config->get('member_attribute') ?: 'member'; if (!empty($attributes) && !in_array($member_attr, $attributes)) { $attributes[] = $member_attr; } } $attr_options = []; if (!empty($attributes)) { $attr_options = ['filter' => $attributes]; } $this->logger->info('Criando query Symfony LDAP...'); $query = $ldap->query($base_dn, $filter, $attr_options); $this->logger->info('Executando query Symfony LDAP...'); $base_results = $query->execute(); $this->logger->info('Query executada. Tipo de resultado: @type', ['@type' => gettype($base_results)]); if ($base_results instanceof \Traversable) { $base_results = iterator_to_array($base_results); } $this->logger->info('Resultados encontrados: @count', ['@count' => count($base_results)]); if (!empty($base_results) && is_array($base_results)) { $all_results = array_merge($all_results, $base_results); } } catch (\Exception $e) { $this->logger->error('Erro na busca LDAP para Base DN @base_dn: @message', [ '@base_dn' => $base_dn, '@message' => $e->getMessage(), ]); } } $results = $all_results; $this->logger->info('Query LDAP executada com sucesso. Total de resultados: @count', ['@count' => count($results)]); } catch (\Exception $e) { $this->logger->error('Erro ao executar query LDAP: @message', ['@message' => $e->getMessage()]); $this->logger->error('Stack trace: @trace', ['@trace' => $e->getTraceAsString()]); throw $e; } $this->logger->info('Verificando resultados da query...'); if (empty($results)) { $this->logger->warning('Query LDAP não retornou resultados.'); return $courses; } $this->logger->info('Query LDAP retornou @count resultados', ['@count' => count($results)]); $courses = $this->processLdapResults($results); $this->logger->info('Processados @count cursos via query LDAP', ['@count' => count($courses)]); } catch (\Exception $e) { $this->logger->error('Erro ao executar query LDAP: @message', ['@message' => $e->getMessage()]); } return $courses; } /** * Sincroniza membros dos grupos baseado no código de grupo dos usuários. */ public function syncGroupMembers() { $this->logger->info('Starting group membership synchronization.'); $config = $this->configFactory->get('ldap_courses_sync.settings'); $role_mapping_enabled = $config->get('role_mapping_enabled') ?? FALSE; $role_mappings = $config->get('role_mappings') ?? []; if (!$role_mapping_enabled) { $this->logger->info('Role mapping is disabled. Skipping group membership synchronization.'); return; } if (empty($role_mappings)) { $this->logger->warning('Role mapping is enabled but no mappings are configured. Skipping synchronization.'); return; } $user_storage = $this->entityTypeManager->getStorage('user'); $query = $user_storage->getQuery() ->condition('status', 1) ->condition('uid', 0, '>') ->accessCheck(FALSE); $uids = $query->execute(); if (empty($uids)) { $this->logger->warning('No active users found.'); return; } $users = $user_storage->loadMultiple($uids); $this->logger->info('Processing @count users for group membership', ['@count' => count($users)]); $group_storage = $this->entityTypeManager->getStorage('group'); $group_type_id = $this->getBundleId(); $groups = $group_storage->loadByProperties(['type' => $group_type_id]); $this->logger->info('Found @count groups', ['@count' => count($groups)]); $added = 0; $removed = 0; $role_updated = 0; $skipped = 0; $already_member = 0; $matched = 0; foreach ($users as $user) { try { $username = $user->getAccountName(); $expected_memberships = []; foreach ($groups as $group) { $group_role = $this->determineUserGroupRole($user, $group, $role_mapping_enabled, $role_mappings); if ($group_role !== NULL) { $expected_memberships[$group->id()] = $group_role; } } if (!empty($expected_memberships)) { $matched++; } foreach ($expected_memberships as $group_id => $expected_role) { $group = $groups[$group_id]; $membership = $group->getMember($user); if ($membership) { $current_roles = $membership->getRoles(); $current_role_ids = array_map(function ($role) { return $role->id(); }, $current_roles); if (!in_array($expected_role, $current_role_ids)) { try { foreach ($current_role_ids as $role_id) { if ($role_id !== 'member' && $role_id !== $expected_role) { $membership->removeRole($role_id); } } if ($expected_role !== 'member' && strpos($expected_role, '-member') === FALSE) { $membership->addRole($expected_role); } $membership->save(); $role_updated++; $this->logger->info('Updated role for user @username in group @group to @role', [ '@username' => $username, '@group' => $group->label(), '@role' => $expected_role, ]); } catch (\Exception $e) { $this->logger->error('Failed to update role for user @username in group @group: @error', [ '@username' => $username, '@group' => $group->label(), '@error' => $e->getMessage(), ]); } } else { $already_member++; } } else { try { $values = []; if ($expected_role !== 'member' && strpos($expected_role, '-member') === FALSE) { $values['group_roles'] = [$expected_role]; } $group->addMember($user, $values); $added++; $this->logger->info('Added user @username to group @group with role @role', [ '@username' => $username, '@group' => $group->label(), '@role' => $expected_role, ]); } catch (\Exception $e) { $this->logger->error('Failed to add user @username to group @group: @error', [ '@username' => $username, '@group' => $group->label(), '@error' => $e->getMessage(), ]); $skipped++; } } } foreach ($groups as $group) { if (!isset($expected_memberships[$group->id()])) { $membership = $group->getMember($user); if ($membership) { try { $group->removeMember($user); $removed++; $this->logger->info('Removed user @username from group @group', [ '@username' => $username, '@group' => $group->label(), ]); } catch (\Exception $e) { $this->logger->error('Failed to remove user @username from group @group: @error', [ '@username' => $username, '@group' => $group->label(), '@error' => $e->getMessage(), ]); } } } } } catch (\Exception $e) { $this->logger->error('Error processing user @uid: @error', [ '@uid' => $user->id(), '@error' => $e->getMessage(), ]); $skipped++; } } $this->logger->info('Group membership synchronization completed. Matched: @matched, Added: @added, Already member: @already, Removed: @removed, Role updated: @role_updated, Skipped: @skipped', [ '@matched' => $matched, '@added' => $added, '@already' => $already_member, '@removed' => $removed, '@role_updated' => $role_updated, '@skipped' => $skipped, ]); } /** * Sincroniza membros dos cursos usando o atributo member do LDAP. * * Para cada curso, lê a lista de DNs do atributo configurado * (padrão: 'member'), resolve cada DN para um usuário Drupal e gerencia * as associações de membros do grupo (adiciona e remove conforme necessário). * * @param array $courses * Array de dados dos cursos processados, com chave '_ldap_members'. */ public function syncMembersFromLdapAttribute(array $courses) { $config = $this->configFactory->get('ldap_courses_sync.settings'); if (!($config->get('member_sync_enabled') ?? TRUE)) { $this->logger->info('Member sync from LDAP attribute is disabled.'); return; } $this->logger->info('Starting member synchronization from LDAP attribute.'); $group_storage = $this->entityTypeManager->getStorage('group'); $user_storage = $this->entityTypeManager->getStorage('user'); $bundle_id = $this->getBundleId(); $bundle_field = $this->getBundleField(); $total_added = 0; $total_removed = 0; $total_not_found = 0; foreach ($courses as $course_data) { $code = $course_data['code']; $ldap_members = $course_data['_ldap_members'] ?? []; if (empty($ldap_members)) { $this->logger->debug('No LDAP members for course @code.', ['@code' => $code]); } // Localiza a entidade grupo correspondente $groups = $group_storage->loadByProperties([ $bundle_field => $bundle_id, 'field_course_code' => $code, ]); if (empty($groups)) { $this->logger->warning('Course with code @code not found for member sync.', ['@code' => $code]); continue; } $group = reset($groups); // Resolve DNs/UIDs LDAP para IDs de usuários Drupal $expected_uids = []; foreach ($ldap_members as $member_value) { $username = $member_value; // Se parece com um DN (contém '='), extrai o uid if (strpos($member_value, '=') !== FALSE) { if (preg_match('/uid=([^,]+)/i', $member_value, $matches)) { $username = $matches[1]; } else { $this->logger->debug('Could not extract uid from DN: @dn', ['@dn' => $member_value]); continue; } } $uid = $this->getUserIdByUsername($username); if ($uid) { $expected_uids[$uid] = TRUE; } else { $this->logger->debug('User @username (from @dn) not found in Drupal.', [ '@username' => $username, '@dn' => $member_value, ]); $total_not_found++; } } // Obtém membros atuais do grupo $current_memberships = $group->getMembers(); $current_uids = []; foreach ($current_memberships as $membership) { $member_entity = $membership->getUser(); if ($member_entity) { $current_uids[$member_entity->id()] = TRUE; } } // Adiciona novos membros foreach (array_keys($expected_uids) as $uid) { if (!isset($current_uids[$uid])) { $user = $user_storage->load($uid); if ($user) { try { $group->addMember($user); $total_added++; $this->logger->debug('Added user @uid to course @group.', [ '@uid' => $uid, '@group' => $group->label(), ]); // Append group to field_user_courses if not already there. if ($user->hasField('field_user_courses')) { $existing = $user->get('field_user_courses')->getValue(); $already_set = FALSE; foreach ($existing as $ref) { if ($ref['target_id'] == $group->id()) { $already_set = TRUE; break; } } if (!$already_set) { self::$syncing = TRUE; try { $user->get('field_user_courses')->appendItem(['target_id' => $group->id()]); $user->save(); } finally { self::$syncing = FALSE; } } } } catch (\Exception $e) { $this->logger->error('Failed to add user @uid to course @group: @error', [ '@uid' => $uid, '@group' => $group->label(), '@error' => $e->getMessage(), ]); } } } } // Remove membros que não estão mais no LDAP foreach (array_keys($current_uids) as $uid) { if (!isset($expected_uids[$uid])) { $user = $user_storage->load($uid); if ($user) { try { $group->removeMember($user); $total_removed++; $this->logger->debug('Removed user @uid from course @group.', [ '@uid' => $uid, '@group' => $group->label(), ]); // Remove group from field_user_courses if present. if ($user->hasField('field_user_courses')) { $existing = $user->get('field_user_courses')->getValue(); $updated = array_values(array_filter($existing, fn($ref) => $ref['target_id'] != $group->id())); if (count($updated) !== count($existing)) { self::$syncing = TRUE; try { $user->set('field_user_courses', $updated); $user->save(); } finally { self::$syncing = FALSE; } } } } catch (\Exception $e) { $this->logger->error('Failed to remove user @uid from course @group: @error', [ '@uid' => $uid, '@group' => $group->label(), '@error' => $e->getMessage(), ]); } } } } } $this->logger->info('Member sync completed. Added: @added, Removed: @removed, Not found in Drupal: @not_found', [ '@added' => $total_added, '@removed' => $total_removed, '@not_found' => $total_not_found, ]); } /** * Determina o papel (role) de um usuário em um grupo baseado nos mapeamentos. */ protected function determineUserGroupRole($user, $group, $role_mapping_enabled, $role_mappings) { if (!$role_mapping_enabled || empty($role_mappings)) { return NULL; } foreach ($role_mappings as $mapping) { $group_role = $mapping['group_role'] ?? ''; $source = $mapping['source'] ?? 'user_field'; $source_field = $mapping['source_field'] ?? ''; $values = $mapping['values'] ?? []; $group_field = $mapping['group_field'] ?? ''; if (empty($group_role) || empty($source_field)) { continue; } $user_value = NULL; if ($source === 'user_field') { if ($user->hasField($source_field) && !$user->get($source_field)->isEmpty()) { $field = $user->get($source_field); $field_type = $field->getFieldDefinition()->getType(); if (in_array($field_type, ['entity_reference', 'entity_reference_revisions'])) { $user_value = $field->target_id; } else { $user_value = $field->value; } } } elseif ($source === 'ldap_attribute') { $config = $this->configFactory->get('ldap_courses_sync.settings'); $ldap_user_data = $this->fetchUserLdapAttribute($user, $source_field, $config); if ($ldap_user_data !== NULL) { $user_value = $ldap_user_data; } } elseif ($source === 'group_field_match') { if ($user->hasField($source_field) && !$user->get($source_field)->isEmpty() && $group->hasField($group_field) && !$group->get($group_field)->isEmpty()) { $user_field = $user->get($source_field); $group_field_obj = $group->get($group_field); $user_field_type = $user_field->getFieldDefinition()->getType(); if (in_array($user_field_type, ['entity_reference', 'entity_reference_revisions'])) { $user_value = $user_field->target_id; } else { $user_value = $user_field->value; } $group_field_type = $group_field_obj->getFieldDefinition()->getType(); if (in_array($group_field_type, ['entity_reference', 'entity_reference_revisions'])) { $group_value = $group_field_obj->target_id; } else { $group_value = $group_field_obj->value; } if ($user_value !== NULL && $group_value !== NULL && strcasecmp(trim($user_value), trim($group_value)) === 0) { return $group_role; } } continue; } if ($user_value !== NULL && !empty($values)) { foreach ($values as $mapping_value) { if (strcasecmp(trim($user_value), trim($mapping_value)) === 0) { return $group_role; } } } } return NULL; } /** * Busca um atributo LDAP específico para um usuário. */ protected function fetchUserLdapAttribute($user, $attribute, $config) { try { $host = $config->get('ldap_host'); $port = $config->get('ldap_port'); $base_dn = $config->get('users_base_dn') ?? $config->get('ldap_base_dn'); $bind_dn = $config->get('ldap_bind_dn'); $bind_password = $config->get('ldap_bind_password'); $users_filter = $config->get('users_filter') ?? '(objectClass=person)'; if (empty($host) || empty($base_dn)) { return NULL; } $ldap_connection = ldap_connect($host, $port); if (!$ldap_connection) { return NULL; } ldap_set_option($ldap_connection, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($ldap_connection, LDAP_OPT_REFERRALS, 0); if (!empty($bind_dn) && !empty($bind_password)) { $bind = @ldap_bind($ldap_connection, $bind_dn, $bind_password); } else { $bind = @ldap_bind($ldap_connection); } if (!$bind) { ldap_close($ldap_connection); return NULL; } $username = $user->getAccountName(); $filter = "(&{$users_filter}(uid={$username}))"; $search = ldap_search($ldap_connection, $base_dn, $filter, [$attribute]); if (!$search) { ldap_close($ldap_connection); return NULL; } $entries = ldap_get_entries($ldap_connection, $search); ldap_close($ldap_connection); if (!empty($entries) && $entries['count'] > 0 && isset($entries[0][$attribute])) { return $entries[0][$attribute][0] ?? NULL; } return NULL; } catch (\Exception $e) { $this->logger->error('Error fetching LDAP attribute @attribute for user @username: @error', [ '@attribute' => $attribute, '@username' => $user->getAccountName(), '@error' => $e->getMessage(), ]); return NULL; } } /** * Processes LDAP results using configured attribute mappings. * * @param array $results * Array of LDAP results. * * @return array * Array of processed courses. */ protected function processLdapResults(array $results) { $courses = []; $config = $this->configFactory->get('ldap_courses_sync.settings'); $attribute_mappings = $config->get('attribute_mappings') ?: []; $code_mapping = NULL; foreach ($attribute_mappings as $mapping) { if ($mapping['field'] === 'field_course_code') { $code_mapping = $mapping; break; } } if (!$code_mapping) { $this->logger->error('No field_course_code mapping found. Cannot process results.'); return $courses; } $this->logger->debug('Processing @count LDAP results with @mappings mappings', [ '@count' => count($results), '@mappings' => count($attribute_mappings), ]); foreach ($results as $entry) { $course_data = []; foreach ($attribute_mappings as $mapping) { $field = $mapping['field']; $attribute = $mapping['attribute']; $mapping_type = $mapping['mapping_type'] ?? 'simple'; $value = NULL; if ($entry->hasAttribute($attribute)) { $attr_values = $entry->getAttribute($attribute); if (is_array($attr_values) && isset($attr_values[0])) { $value = $attr_values[0]; } elseif (!empty($attr_values)) { $value = $attr_values; } } elseif ($mapping_type === 'user_reference') { $this->logger->warning('Atributo LDAP "@attribute" não encontrado na entrada para o campo "@field". Verifique se o atributo está incluído na LDAP query.', [ '@attribute' => $attribute, '@field' => $field, ]); } if ($mapping_type === 'department_reference') { if (!empty($value)) { $dept_id = $this->getDepartmentIdByCode($value); $course_data[$field] = $dept_id; } else { $course_data[$field] = NULL; } } elseif ($mapping_type === 'user_reference') { if (!empty($value)) { $username = $value; // Se parece com DN (contém '='), tenta extrair o uid= ou o primeiro RDN. if (strpos($value, '=') !== FALSE) { if (preg_match('/uid=([^,]+)/i', $value, $matches)) { $username = $matches[1]; } else { // Extrai o valor do primeiro RDN (ex.: cn=João Silva,... → João Silva) preg_match('/^[^=]+=([^,]+)/i', $value, $matches); $username = $matches[1] ?? $value; $this->logger->warning('Campo "@field": DN sem uid= encontrado ("@dn"). Usando primeiro RDN "@username" como username.', [ '@field' => $field, '@dn' => $value, '@username' => $username, ]); } } $user_id = $this->getUserIdByUsername($username); if ($user_id) { $course_data[$field] = $user_id; } else { $this->logger->warning('Campo "@field": usuário "@username" (valor LDAP: "@value") não encontrado no Drupal. Campo ficará vazio.', [ '@field' => $field, '@username' => $username, '@value' => $value, ]); $course_data[$field] = NULL; } } else { $course_data[$field] = NULL; } } else { $course_data[$field] = $value; } } $code = $course_data['field_course_code'] ?? NULL; if ($code) { $result = [ 'code' => $code, 'name' => $course_data['label'] ?? $course_data['name'] ?? '', 'phone' => $course_data['field_course_phone'] ?? '', 'mail' => $course_data['field_course_mail'] ?? '', ]; // Captura lista de membros do atributo LDAP configurado $member_attribute = $config->get('member_attribute') ?: 'member'; if ($entry->hasAttribute($member_attribute)) { $result['_ldap_members'] = $entry->getAttribute($member_attribute) ?? []; } else { $result['_ldap_members'] = []; } $basic_course_fields = ['field_course_code', 'label', 'name', 'field_course_phone', 'field_course_mail']; foreach ($course_data as $key => $value) { if (!in_array($key, $basic_course_fields)) { $result[$key] = $value; } } $courses[] = $result; } } return $courses; } /** * Gets Drupal user ID by username. */ protected function getUserIdByUsername($username) { if (empty($username)) { return NULL; } $user_storage = $this->entityTypeManager->getStorage('user'); $users = $user_storage->loadByProperties(['name' => $username]); if (!empty($users)) { $user = reset($users); return $user->id(); } return NULL; } /** * Returns the Drupal group ID for a department with the given code. */ protected function getDepartmentIdByCode(string $code): ?int { if (empty($code)) { return NULL; } $groups = $this->entityTypeManager->getStorage('group') ->loadByProperties(['field_dept_code' => $code]); if (!empty($groups)) { return (int) reset($groups)->id(); } $this->logger->debug('Department with field_dept_code @code not found.', ['@code' => $code]); return NULL; } /** * Returns the group entity storage. */ protected function getEntityStorage() { return $this->entityTypeManager->getStorage('group'); } /** * Returns the group type ID from configuration. */ protected function getBundleId() { $config = $this->configFactory->get('ldap_courses_sync.settings'); return $config->get('group_type_id') ?: 'course'; } /** * Returns the name field for groups. */ protected function getNameField() { return 'label'; } /** * Returns the bundle field name for groups. */ protected function getBundleField() { return 'type'; } }