آموزش اضافه کردن تاریخ شمسی به Outlook در اکسچنج سرور 2016

یکی از دغدغه های کاربران نرم افزارهای مختلف ویندوزی و بسته Microsoft Office طی یک دهه اخیر دسترسی به تقویم و تاریخ هجری شمسی بوده است و این دغدغه و مشکل عدم پشتیبانی از تقویم کماکان ادامه دارد. یکی از نرم افزارهایی که بطور شخصی و یا در سازمان ها مورد استفاده واقع می شود و با این مسئله دست به گریبان است نرم افزار outlook و در محیط های دارای سرور Exchange واسط تحت وب آن یعنی owa می باشد.Outlook دارای تقویم برای ثبت قرارهای کاری ، قرارهای ملاقات و رخداد هاست و فاقد تقویم شمسی است. تلاش وافر اینجانب برای استفاده از روش هایی که برای افزودن تقویم فارسی به آن برای نسخه Microsoft Outlook 2016 بیان شده است به نتیجه موفقیت آمیزی ختم نگردید.

دوره های شبکه، برنامه نویسی، مجازی سازی، امنیت، نفوذ و ... با برترین های ایران

یک روش ابتکاری برای دیدن تقویم شمسی در Outlook افزودن event هایی است که متن آن متناظر با تاریخ شمسی روز میلادی است که آن رخداد برروی آن قرارمیگیرد.اگر شما از Outlook برای بازکردن صندوق پستی شخصی یا سازمانی خود استفاده میکنید می توانید با افزودن فایل های با فرمت .ics که حاوی event هایی با ویژگی فوق الذکر برای تمامی روزهای سال به calendar خود به این هدف نایل شوید. سپس در بخش calendar از Microsoft Outlook بر روی My calendar کلیک راست کنید و از بخشی که در عکس زیر نشان داده شده این فایل را به تقویم خود اضافه نمایید.

راهکاری برای افزودن تقویم هجری شمسی به تقویم outlook در Exchange 2016

در نهایت تقویم outlook شما به شکل زیر درخواهد آمد:

راهکاری برای افزودن تقویم هجری شمسی به تقویم outlook در Exchange 2016

اگر در محیط خود سرور Exchange دارید و با تعداد بالایی از کاربران سروکار دارید انجام عملیات فوق به صورت دستی نه امکان پذیر است و نه منطقی زیرا یا باید به تک تک user های دامنه خود لاگین نمایید و این عمل را انجام دهید و یا از تک تک آن ها بخواهید عملیات فوق را انجام دهند.

شرکت گستره نگار برای owa نسخه 2010 سرور اکسچنج یک بسته نرم افزاری ارائه نمود که امکان مشاهده تاریخ شمسی را (به طور کامل با نام ماه ها و روزها)در محیط owa فراهم میکرد اما متاسفانه برای نسخه های بعدی اکسچنج این بسته نرم افزاری را ارائه نکرد همچنین این بسته فقط مختص owa بوده و در outlook تغییری اعمال نمی کند.شکل زیر فارسی ساز تقویم پرنیان را نشان می دهد:

راهکاری برای افزودن تقویم هجری شمسی به تقویم outlook در Exchange 2016

اگر بتوانیم که عملیات افزودن event برای نمایش تاریخ فارسی برروی هر روز را به گونه ای خودکار سازی کنیم که بتوانیم از آن برای تعداد زیادی کاربر استفاده کنیم میتوانیم از روش فوق الذکر برای محیط های بزرگ با تعداد زیاد کاربر هم استفاده کنیم.

با استفاده از اسکریپتی که در این بخش معرفی می شود می توانید یک فایل .csv که حاوی تاریخ های شمسی و میلادی متناظر آن (از سال 1395 تا شهریور 1399 ) به calendar تمامی کاربران اکسچنج افزود.در نهایت شما با استفاده از این اسکریپت powershell میتوانید تقویم تمام کاربران خود را به شکل زیر در آورید:

راهکاری برای افزودن تقویم هجری شمسی به تقویم outlook در Exchange 2016

پایه و اساس این اسکریپت فایل Import-CalendarCSV است.

برای اجرای اسکریپت مراحل زیر را انجام دهید:

1-نصب ews managed api

فایل آن را از اینجا دانلود و بر روی سرور Exchange 2016 خود نصب نمایید.

2-دادن حق impersonation به کاربری که قصد اجرای این اسکریپت را برای دیگر کاربران دارد

شما میتوانید این اسکریپت را با استفاده از پارامتر -username و -password برای یک کاربر اجرا کنید و یا از حق impersonation را به یک کاربر بدهید سپس آن کاربر میتواند اسکریپت را برای تمامی mailbox های کاربران اجرا کند.دقت کنید که دسترسی impersonation یک دسترسی بسیار قدرتمند است و کاربری که آن را داشته باشد می تواند تمام عملیات مجاز هر کاربر بر روی mailbox خود را او نیز انجام دهد.برای تفویض این دسترسی به کاربر MyUser دستور زیر را در Exchange Management Shell اجرا نمایید:

 New-ManagementRoleAssignment –Name:impersonation1 –Role:ApplicationImpersonation –User:MyUser

3-اجرای اسکریپت زیر برای نادیده گرفتن تنظیمات SSL

برای اینکه در اجرای اسکریپت اصلی به خطا برنخورید نیاز است که ابتدا اسکریپت زیر را اجرا کنید.روش راحت تر برای اجرای اسکریپت copy-paste کردن اسکریپت در یک فایل متنی و ذخیره آن با فرمت .ps1 و سپس اجرای آن می باشد.

$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
$Compiler=$Provider.CreateCompiler()
$Params=New-Object System.CodeDom.Compiler.CompilerParameters
$Params.GenerateExecutable=$False
$Params.GenerateInMemory=$True
$Params.IncludeDebugInformation=$False
$Params.ReferencedAssemblies.Add("System.DLL") | Out-Null

$TASource=@'
  namespace Local.ToolkitExtensions.Net.CertificatePolicy{
    public class TrustAll : System.Net.ICertificatePolicy {
      public TrustAll() {
      }
      public bool CheckValidationResult(System.Net.ServicePoint sp,
        System.Security.Cryptography.X509Certificates.X509Certificate cert,
        System.Net.WebRequest req, int problem) {
        return true;
      }
    }
  }
'@
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
$TAAssembly=$TAResults.CompiledAssembly

## We now create an instance of the TrustAll and attach it to the ServicePointManager
$TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll

4-اجرای اسکریپت Import-CalendarCSV.ps1 برای یک کاربر:

بدین منظور اسکریپت را که در انتهای این آموزش ارائه شده است در یک فایل متنی کپی کنید و با فرمت .ps1 ذخیره نمایید سپس دستور زیر را در powershell در مسیر فایل ایجاد شده برای کاربر testuser که قصد افزودن تقویم هجری به تقویم او را دارید اجرا کنید.پارامتر -csvname مشخص کننده مسیر فایل csv است که تاریخ های شمسی و میلادی متناظر را داراست. همچنین نام FQDN سرور Exchange ی که EWS Api را در آن نصب کرده اید با ServerFQDN جایگزین نمایید.

.\Import-CalendarCSV.ps1 -EmailAddress TestUser@MyDomain.ir -CSVFileName "d:\1\dates.csv" -Domain MyDomain.ir -Impersonate $true -EwsUrl "https://ServerFQDN/ews/exchange.asmx"

5-اجرای اسکریپت برای تعدادی زیادی از کاربران

کافی است با استفاده از یک حلقه for دستور مرحله قبل را برای تمامی کاربران مدنظر خود اجرا کنیم.مثلا میتوانید با دستور زیر این دستور را برای کل mailbox های محیط خود اجرا نمایید:

$users=get-Mailbox
foreach ($user in $users)
{
دستور مرحله 4
}

و یا مثلا نام mailbox ها را از یک فایل بخوانید سپس دستور مرحله 4 را برای هر یک از آن ها اجرا کنید.

Import-CalendarCSV.ps1

param([string]$CSVFileName,[string]$EmailAddress,[string]$Username,[string]$Password,[string]$Domain,[bool]$Impersonate,[string]$EwsUrl,[string]$EWSManagedApiPath);
 
#
# Import-CalendarCSV.ps1
#
# By David Barrett, Microsoft Ltd. Use at your own risk.
#

Function ShowParams()
{
	Write-Host "Import-CalendarCSV -CSVFileName  -EmailAddress ";
	Write-Host "                   [-Username  -Password  [-Domain ]]";
	Write-Host "                   [-Impersonate ]";
	Write-Host "                   [-EwsUrl ]";
	Write-Host "                   [-EWSManagedApiPath ]";
	Write-Host "";
	Write-Host "Required:";
	Write-Host " -CSVFileName : Filename of the CSV file to import appointments for this user from.";
	Write-Host " -EmailAddress : Mailbox SMTP email address";
	Write-Host "";
	Write-Host "Optional:";
	Write-Host " -Username : Username for the account being used to connect to EWS (if not specified, current user is assumed)";
	Write-Host " -Password : Password for the specified user (required if username specified)";
	Write-Host " -Domain : If specified, used for authentication (not required even if username specified)";
	Write-Host " -Impersonate : Set to $true to use impersonation.";
	Write-Host " -EwsUrl : Forces a particular EWS URl (otherwise autodiscover is used, which is recommended)";
	Write-Host " -EWSManagedApiDLLFilePath : Full and path to the DLL for EWS Managed API (if not specified, default path for v1.1 is used)";
	Write-Host "";
}

$RequiredFields=@{
	"Subject" = "Subject";
	"StartDate" = "Start Date";
	#"StartTime" = "Start Time";
	"EndDate" = "End Date";
	#"EndTime" = "End Time"
}
 
# Check email address
 if (!$EmailAddress)
 {
	ShowParams;
    throw "Required parameter EmailAddress missing";
 }
 
# CSV File Checks
if (!$CSVFileName)
{
	ShowParams;
	throw "Required parameter CSVFileName missing";
}
if (!(Get-Item -Path $CSVFileName -ErrorAction SilentlyContinue))
{
	throw "Unable to open file: $CSVFileName";
}
 
# Import CSV File
try
{
	$CSVFile = Import-Csv -Path $CSVFileName;
}
catch { }
if (!$CSVFile)
{
	Write-Host "CSV header line not found, using predefined header: Subject;StartDate;StartTime;EndDate;EndTime";
	$CSVFile = Import-Csv -Path $CSVFileName -header Subject,StartDate,StartTime,EndDate,EndTime;
}

# Check file has required fields
foreach ($Key in $RequiredFields.Keys)
{
	if (!$CSVFile[0].$Key)
	{
		# Missing required field
		throw "Import file is missing required field: $Key";
	}
}
 
# Check EWS Managed API available
 if (!$EWSManagedApiPath)
 {
     $EWSManagedApiPath = "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
 }
 if (!(Get-Item -Path $EWSManagedApiPath -ErrorAction SilentlyContinue))
 {
     throw "EWS Managed API could not be found at $($EWSManagedApiPath).";
 }
 
# Load EWS Managed API
 [void][Reflection.Assembly]::LoadFile($EWSManagedApiPath);
 
# Create Service Object.  We only need Exchange 2007 schema for creating calendar items (this will work with Exchange>=12)
$tz=[System.TimeZoneInfo]::FindSystemTimeZoneById("Iran Standard Time")
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013,$tz)

# Set credentials if specified, or use logged on user.
 if ($Username -and $Password)
 {
     if ($Domain)
     {
         $service.Credentials = New-Object  Microsoft.Exchange.WebServices.Data.WebCredentials($Username,$Password,$Domain);
     } else {
         $service.Credentials = New-Object  Microsoft.Exchange.WebServices.Data.WebCredentials($Username,$Password);
     }
     
} else {
     $service.UseDefaultCredentials = $true;
 }
 

# Set EWS URL if specified, or use autodiscover if no URL specified.
if ($EwsUrl)
{
	$service.URL = New-Object Uri($EwsUrl);
}
else
{
	try
	{
		Write-Host "Performing autodiscover for $EmailAddress";
		$service.AutodiscoverUrl($EmailAddress);
	}
	catch
	{
		throw;
	}
}
 
# Bind to the calendar folder
 
if ($Impersonate)
{
	$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
}
<#try {
    $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$EmailAddress)
	$CalendarFolder = [Microsoft.Exchange.WebServices.Data.CalendarFolder]::Bind($service,$folderid);

} catch {
	throw;
}#>
$folderid=$null
$folderview=[Microsoft.Exchange.WebServices.Data.FolderView]::new(20)
$CalendarFolders=$service.FindFolders([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$folderview)
foreach($calfolder in $CalendarFolders)
{
    if($calfolder.DisplayName -eq "هـجـری شـمـسـی")
    {
        $folderid=$calfolder.id
        $CalendarFolder = [Microsoft.Exchange.WebServices.Data.CalendarFolder]::Bind($service,$folderid);
    }
}
if($folderid -eq $null)
{
    try{
        $CalendarFolder =[Microsoft.Exchange.WebServices.Data.calendarfolder]::new($service)
        $CalendarFolder.DisplayName="هـجـری شـمـسـی"
        $CalendarFolder.Save([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar)
        $folderid=$CalendarFolder.Id
       }
    catch
    {
        Write-Host "could not create calednar folder"
    }
}
# Parse the CSV file and add the appointments
foreach ($CalendarItem in $CSVFile)
{ 
	# Create the appointment and set the fields
	$NoError=$true;
	try
	{
		$Appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment($service);
		$Appointment.Subject=$CalendarItem."Subject";
		$StartDate=[DateTime]($CalendarItem."StartDate");
		$Appointment.Start=$StartDate;
        #$Appointment.StartTimeZone=$Appointment.EndTimeZone= [System.TimeZoneInfo]::FindSystemTimeZoneById("Iran Standard Time");
		$EndDate=[DateTime]($CalendarItem."EndDate");
		$Appointment.End=$EndDate;
        $Appointment.IsReminderSet =$false;
        $Appointment.LegacyFreeBusyStatus= [Microsoft.Exchange.WebServices.Data.LegacyFreeBusyStatus]::Free
        #$Appointment.IsAllDayEvent = $true;
        
        #Start-Sleep -s 5
        #$Appointment.Organizer=$null;
	}
	catch
	{
		# If we fail to set any of the required fields, we will not write the appointment
		$NoError=$false;
	}
	
	# Check for any other fields
	foreach ($Field in ($CalendarItem | Get-Member -MemberType Properties))
	{
		if (!($RequiredFields.Keys -contains $Field.Name))
		{
			# This is a custom (optional) field, so try to map it
			try
			{
				$Appointment.$($Field.Name)=$CalendarItem.$($Field.Name);
			}
			catch
			{
				# Failed to write this field
				Write-Host "Failed to set custom field $($Field.Name)" -ForegroundColor yellow;
			}
		}
	}

	if ($NoError)
	{
		# Save the appointment
		$Appointment.Save($CalendarFolder.id, [Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToNone);
		Write-Host "Created $($CalendarItem."Subject")" -ForegroundColor green;
	}
	else
	{
		# Failed to set a required field
		Write-Host "Failed to create appointment: $($CalendarItem."Subject")" -ForegroundColor red;
	}
}

نظرات