# Аргументы командной строки
Param(
	[Parameter(Mandatory=$True)]
	[string]$WebUrl, # URL-адрес веб-узла, в котором существует целевой список
	[Parameter(Mandatory=$True)]
	[string]$OU # Путь к корневой организационной единице
)

$AddressBook = "Адресная книга"
$CompanyStructure = "Структура компании"
$MSG_LINE = "--------------------------------------------------------------------"

function StartProcess
{
	# Загружаем ActiveDirectory модуль для PowerShell
	Write-Host $MSG_LINE
	Write-Host "Загружаем модуль PowerShell для работы с Active Directory..." 
	Import-Module ActiveDirectory;
	Write-Host "Модуль PowerShell для работы с Active Directory загружен" -ForeGroundColor Green
	Write-Host $MSG_LINE
	
	
	# Загружаем SharePoint плагин для PowerShell
	Write-Host "Загружаем модуль PowerShell для работы с SharePoint..." 
	Add-PSSnapin Microsoft.SharePoint.PowerShell
	Write-Host "Модуль PowerShell для работы с SharePoint загружен" -ForeGroundColor Green
	Write-Host $MSG_LINE
	
	Write-Host "Получение объекта Web по адресу $WebUrl"
	$web = Get-SPWeb $WebUrl -ErrorAction Stop
	Write-Host "Объект Web получен" -ForeGroundColor Green
	Write-Host $MSG_LINE

	$AddressBookList = $web.Lists[$AddressBook]
	$CompanyStructureList = $web.Lists[$CompanyStructure]
	
	if($AddressBookList -eq $null)
	{
		Write-Host "Список $AddressBook не найден" -ForeGroundColor Red
	}
	else
	{
		if($CompanyStructureList -eq $null)
		{
			Write-Host "Список $CompanyStructure не найден" -ForeGroundColor Red
		}
		else
		{
			Write-Host "Списки $AddressBook и $CompanyStructure найдены. Начинаем наполнение списков данными из Active Directory" -ForeGroundColor Green

			Write-Host "Получаем список всех контактов адресной книги"
			$AllContactItems = $AddressBookList.GetItems();
			Write-Host "Все контакты получены" -ForeGroundColor Green
			Write-Host ("Всего контактов {0}" -f $AllContactItems.Count) -ForeGroundColor Green
			
			Write-Host "Получаем список всех подразделений"
			$AllDepartmentItems = $CompanyStructureList.GetItems();
			Write-Host "Все подразделения получены" -ForeGroundColor Green
			Write-Host ("Всего подразделений {0}" -f $AllDepartmentItems.Count) -ForeGroundColor Green
			
			# Получаем список всех объектов AD внутри указанной директории
			Write-Host $MSG_LINE
			#Write-Host "Получаем список всех объектов AD внутри указанной директории $OU"
			#$objs = Get-ADObject -SearchBase $OU -SearchScope SubTree -Filter * -Properties * -ErrorAction Stop
			
			Write-Host "Получаем корневую организационную единицу"
			$rootOU = Get-ADOrganizationalUnit -Identity $OU -ErrorAction Stop
			Write-Host "Корневая организационная единица получена" -ForeGroundColor Green
			
			$users =  Get-ADUser -SearchBase $OU -SearchScope SubTree -Filter * -Properties Name,Surname,DisplayName,EmailAddress,Company,Title,telephoneNumber,HomePhone,MobilePhone,Fax,StreetAddress,City,State,PostalCode,Country,Office,thumbnailPhoto,DistinguishedName -ResultSetSize 999999999 -ResultPageSize 999999999 -ErrorAction Stop
			#$departments = Get-ADOrganizationalUnit -SearchBase $OU -SearchScope SubTree -Filter * -Properties * -ResultSetSize 999999999 -ResultPageSize 999999999 -ErrorAction Stop
			$departments = Get-ADObject -SearchBase $OU -SearchScope SubTree -Filter * -Properties * -ErrorAction Stop | Where-Object { $_.ObjectClass -ne "user" }
			Write-Host "Cписок объектов получен" -ForeGroundColor Green
			
			$addressBookUsersBySID = @{ };
			#Создание словаря для поиска уже добавленного в список пользователя
			foreach($addressBookUser in $AllContactItems)
			{
                $userlookupValue = New-Object Microsoft.SharePoint.SPFieldUserValue($web, $addressBookUser["Пользователь"]);
                $nameId = $userlookupValue.User.UserId.NameId;
                
                if($nameId -eq $null)
                {
                    Write-Host $("В адресной книге найден элемент с незаполненным полем Пользователь с id = " + $addressBookUser.ID + " " + $addressBookUser.Title) -ForeGroundColor DarkYellow
                    continue
                }
                Try
                {
		            $addressBookUsersBySID.Add($nameId.ToUpper(), $addressBookUser);
                }
                Catch [System.ArgumentException]
                {
                    Write-Host $("Произошла ошибка при просмотре адресной книги " + $error[0] + " ИД элемента " + $addressBookUser.ID) -ForeGroundColor Red
                }
			}
			
			$departmentTitleDict = @{ };
			$departmentTitleToIDDict = @{ };
			
			#Создание словаря для поиска уже добавленного в список подразделения
			foreach($exitingDepartment in $AllDepartmentItems)
			{
				Write-Host ("Найдено подразделение " + $exitingDepartment.Title) -ForeGroundColor Cyan
				$departmentTitleToIDDict.Add($exitingDepartment.Title, $exitingDepartment.ID);
			}
			
			$usersAccountDict = @{ };
			$usersAccountNameToIDDict = @{ };
			
			$rootOUName = $rootOU.Name;
			#$rootDepartmentID = CreateDepartment $CompanyStructureList $rootOUName;
			
			# Сначала создаем все подразделения
			foreach($department in $departments)
			{
				if($departmentTitleToIDDict.ContainsKey($department.Name))
                {
					$newDepartmentID = $departmentTitleToIDDict[$department.Name];
				}
				else
				{
					Write-Host ("Добавление в список подразделения " + $department.Name) -ForeGroundColor Cyan
					$newDepartmentID = CreateDepartment $CompanyStructureList ($department.Name)
					$departmentTitleToIDDict.Add($department.Name, $newDepartmentID);
				}
				if($department.Name -eq $rootOUName)
				{
					$rootDepartmentID = $newDepartmentID
				}
			}
			
			Write-Host "Все подразделения добавлены" -ForeGroundColor Green
			
			# Сначала создаем всех пользователей
			foreach($user in $users)
			{
				#Write-Host ("Добавление в список контактов учетной записи " + $user.DisplayName + " " + $user.SamAccountName) -ForeGroundColor Yellow
				$udn = $user.DistinguishedName
				$cdn  = $udn.Substring($udn.IndexOf(",") + 1);
				$parentDepartment = $departments | Where-Object { $_.DistinguishedName -eq $cdn }
				
				# Write-Host "Родительское подразделение $parentDepartment"
				
				if(-not $departmentTitleDict.ContainsKey($parentDepartment.Name))
				{
					$departmentTitleDict.Add($parentDepartment.Name, @($user));
				}
				else
				{
					$departmentTitleDict[$parentDepartment.Name] += $user;
				}
				
				$usersAccountDict.Add($user.DistinguishedName, $user);
				$newAccountID = EnsureUserID $web $AddressBookList $user $addressBookUsersBySID $departmentTitleToIDDict $parentDepartment.Name;
				$usersAccountNameToIDDict.Add($user.DistinguishedName, $newAccountID);
			}
			
			Write-Host "Подразделения и контакты добавлены и обновлены" 
			Write-Host $MSG_LINE
			Write-Host "Заполняем иерархию подразделений"
				
			# Заполняем иерархию подразделений
			foreach($department in $departments)
			{
				$childDepartmentName = $department.Name;
				
				if($department.Name -ne $rootOU.Name)
				{
					$ddn = $department.DistinguishedName;
					$cdn  = $ddn.Substring($ddn.IndexOf(",") + 1);
					$parentDepartment = $departments | Where-Object { $_.DistinguishedName -eq $cdn }
					$parentDepartmentName = $parentDepartment.Name;
				}
				else
				{
					$parentDepartment = $null;
					$parentDepartmentName = $null;
				}
				
				if(-not [string]::IsNullOrEmpty($childDepartmentName) -and $departmentTitleToIDDict.ContainsKey($childDepartmentName))
				{
					$childDepartmentItem = $CompanyStructureList.GetItemById($departmentTitleToIDDict[$childDepartmentName]);
					$childDepartmentItem["Численность"] = $departmentTitleDict[$childDepartmentName].Length;
					if(-not [string]::IsNullOrEmpty($parentDepartmentName) -and $departmentTitleToIDDict.ContainsKey($parentDepartmentName))
					{
						Write-Host "$parentDepartmentName <- $childDepartmentName" -ForeGroundColor Magenta;
						$childDepartmentItem["Родительское подразделение"] = $departmentTitleToIDDict[$parentDepartmentName];
					}
					$childDepartmentItem.Update();
				}
			}
			
			Write-Host "Иерархия подразделений создана" -ForeGroundColor Green
			Write-Host $MSG_LINE
			Write-Host "Заполняем руководителей подразделений"
			
			# Заполняем руководителей
			
			$managers = $users | Where-Object { $_.Title -match "начальник" -or $_.Title -match "руководитель" -or $_.Title -match "глава" };
			
			foreach($manager in $managers)
			{
				$udn = $manager.DistinguishedName
				$cdn  = $udn.Substring($udn.IndexOf(",") + 1);
				$parentDepartment = $departments | Where-Object { $_.DistinguishedName -eq $cdn }
				
				$departmentItem = $CompanyStructureList.GetItemById($departmentTitleToIDDict[$parentDepartment.Name]);
				
				$managerName = $manager.Name;
				$parentDepartmentName = $parentDepartment.Name;
				if($usersAccountNameToIDDict.ContainsKey($manager.DistinguishedName))
				{
					$departmentItem["Фамилия руководителя"] = $usersAccountNameToIDDict[$manager.DistinguishedName];
				}
				Write-Host "$parentDepartmentName <- $managerName" -ForeGroundColor Yellow;
				$departmentItem.Update();
			}
			
			Write-Host "Руководители подразделений проставлены" -ForeGroundColor Green
			Write-Host $MSG_LINE
			Write-Host "Заполняем фотографии пользователей"
			
			# Заполняем фотографии
			
			foreach($user in $users)
			{
				if($user.thumbnailPhoto -ne $null)
				{
					Write-Host ("Заполнение фотографии учетной записи " + $user.DisplayName + " " + $user.SamAccountName) -ForeGroundColor Yellow
					[byte[]]$bytes = [byte[]]$user.thumbnailPhoto;                   
					[string]$userDisplayName = $user.SamAccountName;
					
					if(-not [string]::IsNullOrEmpty($userDisplayName))
					{
						$userDisplayName = $user.DisplayName;
					}
					
					$item = $AddressBookList.GetItemById($usersAccountNameToIDDict[$user.DistinguishedName]);
					$urlFieldValue = New-Object Microsoft.SharePoint.SPFieldUrlValue;
					$urlFieldValue.Description = $userDisplayName;
					
					Try
					{
						$item.Attachments.DeleteNow("$userDisplayName.jpg");
					}
					Catch [System.Exception]{}
					
                    $userDisplayName = $userDisplayName.Replace(".","");
					$urlFieldValue.Url = "$WebUrl/" + $item.Attachments.AddNow($userDisplayName+".jpg", $bytes);
                    Write-Host $userDisplayName
					$hyperlinkfield = $AddressBookList.Fields["Фотография контакта"] -as [Microsoft.SharePoint.SPFieldUrl];         
					$hyperlinkfield.ValidateParseAndSetValue($item, $urlFieldValue.ToString()); #SharePoint 2013 only
					$item.Update();
				}
			}
			
			Write-Host "Фотографии пользователей заполнены" -ForeGroundColor Green
			Write-Host $MSG_LINE
		}
	}

	$web.Dispose()
}

function CreateDepartment($CompanyStructureList, $Name)
{
	$rootDepartment = $CompanyStructureList.AddItem();
	$rootDepartment["Title"] = $Name;
	$rootDepartment.Update();
	
	return $rootDepartment.ID;
}

function EnsureUserID($web, $AddressBookList, $user, $addressBookUsersBySID, $departmentTitleToIDDict, $parentDepartmentName)
{
	if($addressBookUsersBySID.ContainsKey($user.SID.ToString().ToUpper()))
	{	
        Write-Host ("Обновление контакта учетной записи " + $user.DisplayName + " " + $user.SamAccountName) -ForeGroundColor Magenta
        $newContact = $addressBookUsersBySID[$user.SID.ToString().ToUpper()];
    }
	else
    {
        Write-Host ("Добавление в список контактов учетной записи " + $user.DisplayName + " " + $user.SamAccountName) -ForeGroundColor Yellow
		$newContact = $AddressBookList.AddItem();
    }
	
	$newContact["Фамилия"] 								= $user.Surname;
	$newContact["Полное имя"] 							= $user.DisplayName;
	if(-not [string]::IsNullOrEmpty($user.DisplayName))
	{
		$nameParts = $user.DisplayName -split '\s+';
		if($nameParts.Length -ge 2)
		{
			$newContact["Имя"] 							= $nameParts[1];
		}
		else
		{
			$newContact["Имя"] 							= $nameParts[0];
		}
	}
	
	$newContact["Адрес электронной почты"] 				= $user.EmailAddress;
	$newContact["Организация"] 							= $user.Company;
	$newContact["Должность"] 							= $user.Title;
	$newContact["Рабочий телефон"] 						= $user.telephoneNumber;
	$newContact["Домашний телефон"] 					= $user.HomePhone;
	$newContact["Мобильный телефон"] 					= $user.MobilePhone;
	$newContact["Номер факса"] 							= $user.Fax;
	$newContact["Адрес"] 								= $user.StreetAddress;
	$newContact["Город"] 								= $user.City;
	$newContact["Республика, область или край"] 		= $user.State;
	$newContact["Индекс"] 								= $user.PostalCode;
	$newContact["Страна или регион"]					= $user.Country;
	# $newContact["Веб-страница"] 						= $user.HomePage;
	$newContact["Примечания"] 							= "";
	$newContact["Расположение"] 						= $user.Office;
	if([string]::IsNullOrEmpty($user.Surname))
	{
		$newContact["Пол"] 								= "мужской";
	}
	else 
	{
		if($user.Surname.EndsWith("ва"))
		{
			$newContact["Пол"]							= "женский";
		}
		else
		{
			$newContact["Пол"]							= "мужской";
		}
	}
	
	$newContact["Отображать на странице подразделения"] 	= $true;
	$newContact["Пользователь"] 						= GetUserIDByAccountName $web $user;
	
	if(-not [string]::IsNullOrEmpty($parentDepartmentName))
	{
		$newContact["Подразделение"] 					= $departmentTitleToIDDict[$parentDepartmentName];
	}
	$newContact.Update();
	
	return $newContact.ID;
}

function GetUserIDByAccountName($web, $user)
{
	$spuser = $null;
	Try
	{
		$spuser = $web.EnsureUser($user.SamAccountName);
	}
	Catch [System.Exception]
	{
		$username = $user.SamAccountName;
	}
	if($spuser -eq $null)
	{
		Write-Host "На портале не найден пользователь $username, существующий в Active Directory"
		return $null;
	}
	else
	{
		return $spuser.ID;
	}
}

StartProcess