o365-2.0.3/modules/o365_sso_user/src/SsoUserSync.php
modules/o365_sso_user/src/SsoUserSync.php
<?php
namespace Drupal\o365_sso_user;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\file\FileRepositoryInterface;
use Drupal\o365\GraphService;
use Drupal\o365_profile\O365ProfileGetDataService;
use Drupal\user\UserInterface;
use Mimey\MimeTypes;
/**
* User sync service that syncs data from Graph API to entities.
*/
class SsoUserSync {
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The modules config.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/**
* The o365 Graph Service.
*
* @var \Drupal\o365\GraphService
*/
protected $graphService;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The profile data service.
*
* @var \Drupal\o365_profile\O365ProfileGetDataService
*/
protected $profileGetDataService;
/**
* The file repository.
*
* @var \Drupal\file\FileRepositoryInterface
*/
protected $fileRepository;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
private FileSystemInterface $fileSystem;
/**
* Constructs a SsoUserSync object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\o365\GraphService $graphService
* The Graph API service.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
* @param \Drupal\o365_profile\O365ProfileGetDataService $o365ProfileGetDataService
* The profile data service.
* @param \Drupal\file\FileRepositoryInterface $fileRepository
* The drupal file repository.
* @param \Drupal\Core\File\FileSystemInterface $fileSystem
* The drupal file system.
*/
public function __construct(ConfigFactoryInterface $config_factory, GraphService $graphService, EntityTypeManagerInterface $entityTypeManager, O365ProfileGetDataService $o365ProfileGetDataService, FileRepositoryInterface $fileRepository, FileSystemInterface $fileSystem) {
$this->configFactory = $config_factory;
$this->graphService = $graphService;
$this->entityTypeManager = $entityTypeManager;
$this->profileGetDataService = $o365ProfileGetDataService;
$this->fileRepository = $fileRepository;
$this->fileSystem = $fileSystem;
$this->config = $config_factory->get('o365_sso_user.settings');
}
/**
* Sync the user data with data from the graph API.
*/
public function syncUserData($user): void {
// Only do this when we want to use graph data.
if (!empty($this->config->get('use_graph_data'))) {
// Get user data.
$userData = $this->profileGetDataService->getProfileData(NULL, '648x648');
if ($userData) {
$entityAltered = FALSE;
if ($this->config->get('use_profile_module')) {
$this->syncUserProfileData($user, $userData);
}
else {
$this->synUserUserData($user, $userData, $entityAltered);
}
// Save the user when it has changed.
if ($entityAltered) {
$user->save();
}
}
}
}
/**
* Sync a standard drupal user.
*
* @param \Drupal\user\UserInterface $user
* The user object.
* @param array $userData
* The user data from the Graph API.
* @param bool $entityAltered
* If the entity is altered or not. Used later on.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
private function synUserUserData(UserInterface $user, array $userData, &$entityAltered): void {
$this->mapDataToFields($user, $userData['userData'], $entityAltered);
// Sync the user picture.
$this->syncUserPicture($user, $entityAltered, $userData);
}
/**
* Sync a Drupal user using the profile module.
*
* @param \Drupal\user\UserInterface $user
* The user object.
* @param array $userData
* The user data from the Graph API.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
private function syncUserProfileData(UserInterface $user, array $userData): void {
// First get the profile for this user.
/** @var \Drupal\profile\ProfileStorage $storage */
$storage = $this->entityTypeManager->getStorage('profile');
$profileBundle = ($this->config->get('profile_bundle')) ?: 'profile';
$profiles = $storage->loadMultipleByUser($user, $profileBundle);
if (!empty($profiles)) {
$entityAltered = FALSE;
/** @var \Drupal\profile\Entity\Profile $profile */
$profile = reset($profiles);
$this->mapDataToFields($profile, $userData['userData'], $entityAltered);
// Sync the user picture.
$this->syncUserPicture($profile, $entityAltered, $userData);
if ($entityAltered) {
$profile->save();
}
}
}
/**
* Sync the user picture if needed and wanted.
*
* @param \Drupal\user\UserInterface|\Drupal\profile\Entity\Profile $entity
* The user or profile entity.
* @param bool $entityAltered
* If the entity is altered or not. Used later on.
* @param array $userData
* The user data array containing the image.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
private function syncUserPicture(&$entity, &$entityAltered, array $userData): void {
if (!empty($this->config->get('sync_profile_image'))) {
$entityField = $this->config->get('profile_image_mapping');
if ($entity->hasField($entityField)) {
if ($userData && array_key_exists('imageBase64', $userData)) {
/** @var \Drupal\file\Entity\File $origImage */
$origImage = $entity->get($entityField)->entity;
$origFileBase64 = 'empty';
$newFileBase64 = $userData['imageBase64'];
if ($origImage) {
$origFilePath = $this->fileSystem->realpath($origImage->getFileUri());
$origFileBase64 = base64_encode(file_get_contents($origFilePath));
}
// Only upload and save new profile pics.
if ($origFileBase64 !== $newFileBase64) {
$data = base64_decode($userData['imageBase64']);
// Set up the image file uri.
$fieldDefinition = $entity->getFieldDefinition($entityField);
if ($fieldDefinition) {
$fieldDefinitionSettings = $fieldDefinition->getSettings();
$uri = $fieldDefinitionSettings['uri_scheme'] . '://';
$mimes = new MimeTypes();
$imageName = $entity->id() . '.' . $mimes->getExtension($userData['imageMeta']['@odata.mediaContentType']);
$imageFile = $this->fileRepository->writeData($data, $uri . $imageName);
// Save the file to the entity .
if ($imageFile) {
$entity->set($entityField, $imageFile->id());
$entityAltered = TRUE;
}
}
}
}
}
}
}
/**
* Map the user data to field in a entity.
*
* @param \Drupal\user\UserInterface|\Drupal\profile\Entity\Profile $entity
* The user or profile entity.
* @param array $userData
* The user data.
* @param bool $entityAltered
* If the entity is altered or not. Used later on.
*/
private function mapDataToFields(&$entity, array $userData, &$entityAltered = FALSE): void {
$mapping = $this->getFieldMapping();
if (!empty($mapping)) {
// Loop through the mappings.
foreach ($mapping as $userDataField => $entityField) {
// Check if the fields exist in both the data and the user.
if (isset($userData[$userDataField]) && $entity->hasField($entityField)) {
$origValue = $entity->get($entityField)->getString();
if ($origValue != $userData[$userDataField]) {
$entity->set($entityField, $userData[$userDataField]);
$entityAltered = TRUE;
}
}
}
}
}
/**
* Get the field mappings from the settings.
*
* @return array
* The mapping as graph_api_field => drupal_field.
*/
private function getFieldMapping(): array {
$mapping = [];
$data = $this->config->get('graph_data_mapping');
$lines = preg_split('/\r\n|\r|\n/', $data);
if (!empty($lines) && !empty($lines[0])) {
foreach ($lines as $line) {
$field = explode('|', $line);
$mapping[trim($field[0])] = trim($field[1]);
}
}
return $mapping;
}
}
