From 8642bf2401c6c9cbb278c904ca8c6ad29f63c219 Mon Sep 17 00:00:00 2001 From: vfedosevich Date: Thu, 4 Apr 2013 17:31:46 +0300 Subject: [PATCH] Added support for SharePoint 2013. --- .../HostedSharePointServer2013.cs | 299 +++++++ .../HostedSharePointServer2013Impl.cs | 825 ++++++++++++++++++ ...bsitePanel.Providers.HostedSolution.csproj | 2 + ...HostedSharePointEditSiteCollection.ascx.cs | 59 +- 4 files changed, 1174 insertions(+), 11 deletions(-) create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/HostedSharePointServer2013.cs create mode 100644 WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/HostedSharePointServer2013Impl.cs diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/HostedSharePointServer2013.cs b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/HostedSharePointServer2013.cs new file mode 100644 index 00000000..0d549014 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/HostedSharePointServer2013.cs @@ -0,0 +1,299 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Win32; +using WebsitePanel.Providers.SharePoint; +using WebsitePanel.Providers.Utils; +using WebsitePanel.Server.Utils; + +namespace WebsitePanel.Providers.HostedSolution +{ + /// + /// Provides hosted SharePoint server functionality implementation. + /// + public class HostedSharePointServer2013 : HostingServiceProviderBase, IHostedSharePointServer + { + #region Delegate + + private delegate TReturn SharePointAction(HostedSharePointServer2013Impl impl); + + #endregion + + #region Fields + + protected string LanguagePacksPath; + protected string Wss3Registry32Key; + protected string Wss3RegistryKey; + + #endregion + + #region Properties + + public string BackupTemporaryFolder + { + get { return ProviderSettings["BackupTemporaryFolder"]; } + } + + public Uri RootWebApplicationUri + { + get { return new Uri(ProviderSettings["RootWebApplicationUri"]); } + } + + #endregion + + #region Constructor + + public HostedSharePointServer2013() + { + Wss3RegistryKey = @"SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\15.0"; + Wss3Registry32Key = @"SOFTWARE\Wow6432Node\Microsoft\Shared Tools\Web Server Extensions\15.0"; + LanguagePacksPath = @"%commonprogramfiles%\microsoft shared\Web Server Extensions\15\HCCab\"; + } + + #endregion + + #region Methods + + /// Gets list of supported languages by this installation of SharePoint. + /// List of supported languages + public int[] GetSupportedLanguages() + { + var impl = new HostedSharePointServer2013Impl(); + return impl.GetSupportedLanguages(RootWebApplicationUri); + } + + /// Gets list of SharePoint collections within root web application. + /// List of SharePoint collections within root web application. + public SharePointSiteCollection[] GetSiteCollections() + { + return ExecuteSharePointAction(impl => impl.GetSiteCollections(RootWebApplicationUri)); + } + + /// Gets SharePoint collection within root web application with given name. + /// Url that uniquely identifies site collection to be loaded. + /// SharePoint collection within root web application with given name. + public SharePointSiteCollection GetSiteCollection(string url) + { + return ExecuteSharePointAction(impl => impl.GetSiteCollection(RootWebApplicationUri, url)); + } + + /// Creates site collection within predefined root web application. + /// Information about site coolection to be created. + public void CreateSiteCollection(SharePointSiteCollection siteCollection) + { + ExecuteSharePointAction(delegate(HostedSharePointServer2013Impl impl) + { + impl.CreateSiteCollection(RootWebApplicationUri, siteCollection); + return null; + }); + } + + /// Deletes site collection under given url. + /// The site collection to be deleted. + public void DeleteSiteCollection(SharePointSiteCollection siteCollection) + { + ExecuteSharePointAction(delegate(HostedSharePointServer2013Impl impl) + { + impl.DeleteSiteCollection(RootWebApplicationUri, siteCollection); + return null; + }); + } + + /// Backups site collection under give url. + /// Url that uniquely identifies site collection to be deleted. + /// Resulting backup file name. + /// A value which shows whether created backup must be archived. + /// Created backup full path. + public string BackupSiteCollection(string url, string filename, bool zip) + { + return ExecuteSharePointAction(impl => impl.BackupSiteCollection(RootWebApplicationUri, url, filename, zip, BackupTemporaryFolder)); + } + + /// Restores site collection under given url from backup. + /// Site collection to be restored. + /// Backup file name to restore from. + public void RestoreSiteCollection(SharePointSiteCollection siteCollection, string filename) + { + ExecuteSharePointAction(delegate(HostedSharePointServer2013Impl impl) + { + impl.RestoreSiteCollection(RootWebApplicationUri, siteCollection, filename); + return null; + }); + } + + /// Gets binary data chunk of specified size from specified offset. + /// Path to file to get bunary data chunk from. + /// Offset from which to start data reading. + /// Binary data chunk length. + /// Binary data chunk read from file. + public virtual byte[] GetTempFileBinaryChunk(string path, int offset, int length) + { + byte[] buffer = FileUtils.GetFileBinaryChunk(path, offset, length); + + if (buffer.Length < length) + { + FileUtils.DeleteFile(path); + } + + return buffer; + } + + /// Appends supplied binary data chunk to file. + /// Non existent file name to append to. + /// Full path to existent file to append to. + /// Binary data chunk to append to. + /// Path to file that was appended with chunk. + public virtual string AppendTempFileBinaryChunk(string fileName, string path, byte[] chunk) + { + if (path == null) + { + path = Path.Combine(Path.GetTempPath(), fileName); + if (FileUtils.FileExists(path)) + { + FileUtils.DeleteFile(path); + } + } + + FileUtils.AppendFileBinaryContent(path, chunk); + + return path; + } + + public void UpdateQuotas(string url, long maxStorage, long warningStorage) + { + ExecuteSharePointAction(delegate(HostedSharePointServer2013Impl impl) + { + impl.UpdateQuotas(RootWebApplicationUri, url, maxStorage, warningStorage); + return null; + }); + } + + public SharePointSiteDiskSpace[] CalculateSiteCollectionsDiskSpace(string[] urls) + { + return ExecuteSharePointAction(impl => impl.CalculateSiteCollectionDiskSpace(RootWebApplicationUri, urls)); + } + + public long GetSiteCollectionSize(string url) + { + return ExecuteSharePointAction(impl => impl.GetSiteCollectionSize(RootWebApplicationUri, url)); + } + + public void SetPeoplePickerOu(string site, string ou) + { + ExecuteSharePointAction(delegate(HostedSharePointServer2013Impl impl) + { + impl.SetPeoplePickerOu(site, ou); + return null; + }); + } + + + public override bool IsInstalled() + { + return IsSharePointInstalled(); + } + + /// Deletes service items that represent SharePoint site collection. + /// Items to be deleted. + public override void DeleteServiceItems(ServiceProviderItem[] items) + { + foreach (ServiceProviderItem item in items) + { + var sharePointSiteCollection = item as SharePointSiteCollection; + + if (sharePointSiteCollection != null) + { + try + { + DeleteSiteCollection(sharePointSiteCollection); + } + catch (Exception ex) + { + Log.WriteError(String.Format("Error deleting '{0}' {1}", item.Name, item.GetType().Name), ex); + } + } + } + } + + /// Calculates diskspace used by supplied service items. + /// Service items to get diskspace usage for. + /// Calculated disk space usage statistics. + public override ServiceProviderItemDiskSpace[] GetServiceItemsDiskSpace(ServiceProviderItem[] items) + { + var itemsDiskspace = new List(); + + foreach (ServiceProviderItem item in items) + { + if (item is SharePointSiteCollection) + { + try + { + Log.WriteStart(String.Format("Calculating '{0}' site logs size", item.Name)); + + SharePointSiteCollection site = GetSiteCollection(item.Name); + var diskspace = new ServiceProviderItemDiskSpace {ItemId = item.Id, DiskSpace = site.Diskspace}; + itemsDiskspace.Add(diskspace); + + Log.WriteEnd(String.Format("Calculating '{0}' site logs size", item.Name)); + } + catch (Exception ex) + { + Log.WriteError(ex); + } + } + } + + return itemsDiskspace.ToArray(); + } + + /// Checks whether SharePoint 2013 is installed. + /// true - if it is installed; false - otherwise. + private bool IsSharePointInstalled() + { + RegistryKey spKey = Registry.LocalMachine.OpenSubKey(Wss3RegistryKey); + RegistryKey spKey32 = Registry.LocalMachine.OpenSubKey(Wss3Registry32Key); + + if (spKey == null && spKey32 == null) + { + return false; + } + + var spVal = (string) spKey.GetValue("SharePoint"); + + return (String.Compare(spVal, "installed", true) == 0); + } + + /// Executes supplied action within separate application domain. + /// Action to be executed. + /// Any object that results from action execution or null if nothing is supposed to be returned. + /// Is thrown in case supplied action is null. + private static TReturn ExecuteSharePointAction(SharePointAction action) + { + if (action == null) + { + throw new ArgumentNullException("action"); + } + + AppDomain domain = null; + + try + { + Type type = typeof (HostedSharePointServer2013Impl); + var info = new AppDomainSetup {ApplicationBase = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory), PrivateBinPath = "bin; bin/debug"}; + domain = AppDomain.CreateDomain("WSS30", null, info); + var impl = (HostedSharePointServer2013Impl) domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName); + + return action(impl); + } + finally + { + if (domain != null) + { + AppDomain.Unload(domain); + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/HostedSharePointServer2013Impl.cs b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/HostedSharePointServer2013Impl.cs new file mode 100644 index 00000000..cc040480 --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/HostedSharePointServer2013Impl.cs @@ -0,0 +1,825 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Security.Principal; +using Microsoft.SharePoint; +using Microsoft.SharePoint.Administration; +using WebsitePanel.Providers.SharePoint; +using WebsitePanel.Providers.Utils; + +namespace WebsitePanel.Providers.HostedSolution +{ + public class HostedSharePointServer2013Impl : MarshalByRefObject + { + #region Fields + + private static RunspaceConfiguration runspaceConfiguration; + + #endregion + + #region Properties + + private string SharepointSnapInName + { + get { return "Microsoft.SharePoint.Powershell"; } + } + + #endregion + + #region Methods + + /// Gets list of SharePoint collections within root web application. + /// The root web application Uri. + /// List of SharePoint collections within root web application. + public SharePointSiteCollection[] GetSiteCollections(Uri rootWebApplicationUri) + { + return GetSPSiteCollections(rootWebApplicationUri).Select(pair => NewSiteCollection(pair.Value)).ToArray(); + } + + /// Gets list of supported languages by this installation of SharePoint. + /// The root web application Uri. + /// List of supported languages + public int[] GetSupportedLanguages(Uri rootWebApplicationUri) + { + var languages = new List(); + + try + { + WindowsImpersonationContext wic = WindowsIdentity.GetCurrent().Impersonate(); + + try + { + languages.AddRange(from SPLanguage lang in SPRegionalSettings.GlobalInstalledLanguages select lang.LCID); + } + finally + { + wic.Undo(); + } + } + catch (Exception ex) + { + throw new InvalidOperationException("Failed to create site collection.", ex); + } + + return languages.ToArray(); + } + + /// Gets site collection size in bytes. + /// The root web application uri. + /// The site collection url. + /// Size in bytes. + public long GetSiteCollectionSize(Uri rootWebApplicationUri, string url) + { + Dictionary sizes = GetSitesCollectionSize(rootWebApplicationUri, new[] {url}); + + if (sizes.Count() == 1) + { + return sizes.First().Value; + } + + throw new ApplicationException(string.Format("SiteCollection {0} does not exist", url)); + } + + /// Gets sites disk space. + /// The root web application uri. + /// The sites urls. + /// The disk space. + public SharePointSiteDiskSpace[] CalculateSiteCollectionDiskSpace(Uri rootWebApplicationUri, string[] urls) + { + return GetSitesCollectionSize(rootWebApplicationUri, urls).Select(pair => new SharePointSiteDiskSpace {Url = pair.Key, DiskSpace = (long) Math.Round(pair.Value/1024.0/1024.0)}).ToArray(); + } + + /// Calculates size of the required seti collections. + /// The root web application uri. + /// The sites urls. + /// Calculated sizes. + private Dictionary GetSitesCollectionSize(Uri rootWebApplicationUri, IEnumerable urls) + { + Runspace runspace = null; + var result = new Dictionary(); + + try + { + runspace = OpenRunspace(); + + foreach (string url in urls) + { + string siteCollectionUrl = String.Format("{0}:{1}", url, rootWebApplicationUri.Port); + var scripts = new List {string.Format("$site=Get-SPSite -Identity \"{0}\"", siteCollectionUrl), "$site.RecalculateStorageUsed()", "$site.Usage.Storage"}; + Collection scriptResult = ExecuteShellCommand(runspace, scripts); + + if (scriptResult != null && scriptResult.Any()) + { + result.Add(url, Convert.ToInt64(scriptResult.First().BaseObject)); + } + } + } + finally + { + CloseRunspace(runspace); + } + + return result; + } + + /// Sets people picker OU. + /// The site. + /// OU. + public void SetPeoplePickerOu(string site, string ou) + { + HostedSolutionLog.LogStart("SetPeoplePickerOu"); + HostedSolutionLog.LogInfo(" Site: {0}", site); + HostedSolutionLog.LogInfo(" OU: {0}", ou); + + Runspace runspace = null; + + try + { + runspace = OpenRunspace(); + var cmd = new Command("Set-SPSite"); + cmd.Parameters.Add("Identity", site); + cmd.Parameters.Add("UserAccountDirectoryPath", ou); + ExecuteShellCommand(runspace, cmd); + } + finally + { + CloseRunspace(runspace); + } + + HostedSolutionLog.LogEnd("SetPeoplePickerOu"); + } + + /// Gets SharePoint collection within root web application with given name. + /// Root web application uri. + /// Url that uniquely identifies site collection to be loaded. + /// SharePoint collection within root web application with given name. + public SharePointSiteCollection GetSiteCollection(Uri rootWebApplicationUri, string url) + { + return NewSiteCollection(GetSPSiteCollection(rootWebApplicationUri, url)); + } + + /// Deletes quota. + /// The quota name. + private static void DeleteQuotaTemplate(string name) + { + SPFarm farm = SPFarm.Local; + + var webService = farm.Services.GetValue(""); + SPQuotaTemplateCollection quotaColl = webService.QuotaTemplates; + quotaColl.Delete(name); + } + + /// Updates site collection quota. + /// The root uri. + /// The site collection url. + /// The max storage. + /// The warning storage value. + public void UpdateQuotas(Uri root, string url, long maxStorage, long warningStorage) + { + if (maxStorage != -1) + { + maxStorage = maxStorage*1024*1024; + } + else + { + maxStorage = 0; + } + + if (warningStorage != -1 && maxStorage != -1) + { + warningStorage = Math.Min(warningStorage, maxStorage)*1024*1024; + } + else + { + warningStorage = 0; + } + + Runspace runspace = null; + + try + { + runspace = OpenRunspace(); + GrantAccess(runspace, root); + string siteCollectionUrl = String.Format("{0}:{1}", url, root.Port); + var command = new Command("Set-SPSite"); + command.Parameters.Add("Identity", siteCollectionUrl); + command.Parameters.Add("MaxSize", maxStorage); + command.Parameters.Add("WarningSize", warningStorage); + ExecuteShellCommand(runspace, command); + } + finally + { + CloseRunspace(runspace); + } + } + + /// Grants acces to current user. + /// The runspace. + /// The root web application uri. + private void GrantAccess(Runspace runspace, Uri rootWebApplicationUri) + { + ExecuteShellCommand(runspace, new List {string.Format("$webApp=Get-SPWebApplication {0}", rootWebApplicationUri.AbsoluteUri), string.Format("$webApp.GrantAccessToProcessIdentity(\"{0}\")", WindowsIdentity.GetCurrent().Name)}); + } + + /// Deletes site collection. + /// The runspace. + /// The site collection url. + /// True - if active directory accounts should be deleted. + private void DeleteSiteCollection(Runspace runspace, string url, bool deleteADAccounts) + { + var command = new Command("Remove-SPSite"); + command.Parameters.Add("Identity", url); + command.Parameters.Add("DeleteADAccounts", deleteADAccounts); + ExecuteShellCommand(runspace, command); + } + + /// Creates site collection within predefined root web application. + /// Root web application uri. + /// Information about site coolection to be created. + /// Is thrown in case requested operation fails for any reason. + public void CreateSiteCollection(Uri rootWebApplicationUri, SharePointSiteCollection siteCollection) + { + HostedSolutionLog.LogStart("CreateSiteCollection"); + WindowsImpersonationContext wic = null; + Runspace runspace = null; + + try + { + wic = WindowsIdentity.GetCurrent().Impersonate(); + runspace = OpenRunspace(); + CreateCollection(runspace, rootWebApplicationUri, siteCollection); + } + finally + { + CloseRunspace(runspace); + HostedSolutionLog.LogEnd("CreateSiteCollection"); + + if (wic != null) + { + wic.Undo(); + } + } + } + + /// Creates site collection within predefined root web application. + /// The runspace. + /// Root web application uri. + /// Information about site coolection to be created. + /// Is thrown in case requested operation fails for any reason. + private void CreateCollection(Runspace runspace, Uri rootWebApplicationUri, SharePointSiteCollection siteCollection) + { + string siteCollectionUrl = String.Format("{0}:{1}", siteCollection.Url, rootWebApplicationUri.Port); + HostedSolutionLog.DebugInfo("siteCollectionUrl: {0}", siteCollectionUrl); + + try + { + SPWebApplication rootWebApplication = SPWebApplication.Lookup(rootWebApplicationUri); + rootWebApplication.Sites.Add(siteCollectionUrl, siteCollection.Title, siteCollection.Description, (uint) siteCollection.LocaleId, String.Empty, siteCollection.OwnerLogin, siteCollection.OwnerName, siteCollection.OwnerEmail, null, null, null, true); + rootWebApplication.Update(); + } + catch (Exception) + { + DeleteSiteCollection(runspace, siteCollectionUrl, true); + throw; + } + + try + { + GrantAccess(runspace, rootWebApplicationUri); + var command = new Command("Set-SPSite"); + command.Parameters.Add("Identity", siteCollectionUrl); + + if (siteCollection.MaxSiteStorage != -1) + { + command.Parameters.Add("MaxSize", siteCollection.MaxSiteStorage*1024*1024); + } + + if (siteCollection.WarningStorage != -1 && siteCollection.MaxSiteStorage != -1) + { + command.Parameters.Add("WarningSize", Math.Min(siteCollection.WarningStorage, siteCollection.MaxSiteStorage)*1024*1024); + } + + ExecuteShellCommand(runspace, command); + } + catch (Exception) + { + DeleteQuotaTemplate(siteCollection.Title); + DeleteSiteCollection(runspace, siteCollectionUrl, true); + throw; + } + + AddHostsRecord(siteCollection); + } + + /// Deletes site collection under given url. + /// Root web application uri. + /// The site collection to be deleted. + /// Is thrown in case requested operation fails for any reason. + public void DeleteSiteCollection(Uri rootWebApplicationUri, SharePointSiteCollection siteCollection) + { + HostedSolutionLog.LogStart("DeleteSiteCollection"); + Runspace runspace = null; + + try + { + string siteCollectionUrl = String.Format("{0}:{1}", siteCollection.Url, rootWebApplicationUri.Port); + HostedSolutionLog.DebugInfo("siteCollectionUrl: {0}", siteCollectionUrl); + runspace = OpenRunspace(); + DeleteSiteCollection(runspace, siteCollectionUrl, false); + RemoveHostsRecord(siteCollection); + } + catch (Exception ex) + { + throw new InvalidOperationException("Failed to delete site collection.", ex); + } + finally + { + CloseRunspace(runspace); + HostedSolutionLog.LogEnd("DeleteSiteCollection"); + } + } + + /// Backups site collection under give url. + /// Root web application uri. + /// Url that uniquely identifies site collection to be deleted. + /// Resulting backup file name. + /// A value which shows whether created backup must be archived. + /// Custom temp path for backup + /// Full path to created backup. + /// Is thrown in case requested operation fails for any reason. + public string BackupSiteCollection(Uri rootWebApplicationUri, string url, string filename, bool zip, string tempPath) + { + try + { + string siteCollectionUrl = String.Format("{0}:{1}", url, rootWebApplicationUri.Port); + HostedSolutionLog.LogStart("BackupSiteCollection"); + HostedSolutionLog.DebugInfo("siteCollectionUrl: {0}", siteCollectionUrl); + + if (String.IsNullOrEmpty(tempPath)) + { + tempPath = Path.GetTempPath(); + } + + string backupFileName = Path.Combine(tempPath, (zip ? StringUtils.CleanIdentifier(siteCollectionUrl) + ".bsh" : StringUtils.CleanIdentifier(filename))); + HostedSolutionLog.DebugInfo("backupFilePath: {0}", backupFileName); + Runspace runspace = null; + + try + { + runspace = OpenRunspace(); + var command = new Command("Backup-SPSite"); + command.Parameters.Add("Identity", siteCollectionUrl); + command.Parameters.Add("Path", backupFileName); + ExecuteShellCommand(runspace, command); + + if (zip) + { + string zipFile = Path.Combine(tempPath, filename); + string zipRoot = Path.GetDirectoryName(backupFileName); + + FileUtils.ZipFiles(zipFile, zipRoot, new[] {Path.GetFileName(backupFileName)}); + FileUtils.DeleteFile(backupFileName); + + backupFileName = zipFile; + } + + return backupFileName; + } + finally + { + CloseRunspace(runspace); + HostedSolutionLog.LogEnd("BackupSiteCollection"); + } + } + catch (Exception ex) + { + throw new InvalidOperationException("Failed to backup site collection.", ex); + } + } + + /// Restores site collection under given url from backup. + /// Root web application uri. + /// Site collection to be restored. + /// Backup file name to restore from. + /// Is thrown in case requested operation fails for any reason. + public void RestoreSiteCollection(Uri rootWebApplicationUri, SharePointSiteCollection siteCollection, string filename) + { + string url = siteCollection.Url; + + try + { + string siteCollectionUrl = String.Format("{0}:{1}", url, rootWebApplicationUri.Port); + HostedSolutionLog.LogStart("RestoreSiteCollection"); + HostedSolutionLog.DebugInfo("siteCollectionUrl: {0}", siteCollectionUrl); + + HostedSolutionLog.DebugInfo("backupFilePath: {0}", filename); + Runspace runspace = null; + + try + { + string tempPath = Path.GetTempPath(); + string expandedFile = filename; + + if (Path.GetExtension(filename).ToLower() == ".zip") + { + expandedFile = FileUtils.UnzipFiles(filename, tempPath)[0]; + + // Delete zip archive. + FileUtils.DeleteFile(filename); + } + + runspace = OpenRunspace(); + DeleteSiteCollection(runspace, siteCollectionUrl, false); + var command = new Command("Restore-SPSite"); + command.Parameters.Add("Identity", siteCollectionUrl); + command.Parameters.Add("Path", filename); + ExecuteShellCommand(runspace, command); + + command = new Command("Set-SPSite"); + command.Parameters.Add("Identity", siteCollectionUrl); + command.Parameters.Add("OwnerAlias", siteCollection.OwnerLogin); + ExecuteShellCommand(runspace, command); + + command = new Command("Set-SPUser"); + command.Parameters.Add("Identity", siteCollection.OwnerLogin); + command.Parameters.Add("Email", siteCollection.OwnerEmail); + command.Parameters.Add("DisplayName", siteCollection.Name); + ExecuteShellCommand(runspace, command); + + FileUtils.DeleteFile(expandedFile); + } + finally + { + CloseRunspace(runspace); + HostedSolutionLog.LogEnd("RestoreSiteCollection"); + } + } + catch (Exception ex) + { + throw new InvalidOperationException("Failed to restore site collection.", ex); + } + } + + /// Creates new site collection with information from administration object. + /// Administration object. + private static SharePointSiteCollection NewSiteCollection(SPSite site) + { + var siteUri = new Uri(site.Url); + string url = (siteUri.Port > 0) ? site.Url.Replace(String.Format(":{0}", siteUri.Port), String.Empty) : site.Url; + + return new SharePointSiteCollection {Url = url, OwnerLogin = site.Owner.LoginName, OwnerName = site.Owner.Name, OwnerEmail = site.Owner.Email, LocaleId = site.RootWeb.Locale.LCID, Title = site.RootWeb.Title, Description = site.RootWeb.Description, Bandwidth = site.Usage.Bandwidth, Diskspace = site.Usage.Storage, MaxSiteStorage = site.Quota.StorageMaximumLevel, WarningStorage = site.Quota.StorageWarningLevel}; + } + + /// Gets SharePoint sites collection. + /// The root web application uri. + /// The SharePoint sites. + private Dictionary GetSPSiteCollections(Uri rootWebApplicationUri) + { + Runspace runspace = null; + var collections = new Dictionary(); + + try + { + runspace = OpenRunspace(); + var cmd = new Command("Get-SPSite"); + cmd.Parameters.Add("WebApplication", rootWebApplicationUri.AbsoluteUri); + Collection result = ExecuteShellCommand(runspace, cmd); + + if (result != null) + { + foreach (PSObject psObject in result) + { + var spSite = psObject.BaseObject as SPSite; + + if (spSite != null) + { + collections.Add(spSite.Url, spSite); + } + } + } + } + finally + { + CloseRunspace(runspace); + } + + return collections; + } + + /// Gets SharePoint site collection. + /// The root web application uri. + /// The required site url. + /// The SharePoint sites. + private SPSite GetSPSiteCollection(Uri rootWebApplicationUri, string url) + { + Runspace runspace = null; + + try + { + string siteCollectionUrl = String.Format("{0}:{1}", url, rootWebApplicationUri.Port); + runspace = OpenRunspace(); + var cmd = new Command("Get-SPSite"); + cmd.Parameters.Add("Identity", siteCollectionUrl); + Collection result = ExecuteShellCommand(runspace, cmd); + + if (result != null && result.Count() == 1) + { + var spSite = result.First().BaseObject as SPSite; + + if (spSite == null) + { + throw new ApplicationException(string.Format("SiteCollection {0} does not exist", url)); + } + + return result.First().BaseObject as SPSite; + } + else + { + throw new ApplicationException(string.Format("SiteCollection {0} does not exist", url)); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError(ex); + throw; + } + finally + { + CloseRunspace(runspace); + } + } + + /// Opens PowerShell runspace. + /// The runspace. + private Runspace OpenRunspace() + { + HostedSolutionLog.LogStart("OpenRunspace"); + + if (runspaceConfiguration == null) + { + runspaceConfiguration = RunspaceConfiguration.Create(); + PSSnapInException exception; + runspaceConfiguration.AddPSSnapIn(SharepointSnapInName, out exception); + HostedSolutionLog.LogInfo("Sharepoint snapin loaded"); + + if (exception != null) + { + HostedSolutionLog.LogWarning("SnapIn error", exception); + } + } + + Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration); + runspace.Open(); + runspace.SessionStateProxy.SetVariable("ConfirmPreference", "none"); + HostedSolutionLog.LogEnd("OpenRunspace"); + + return runspace; + } + + /// Closes runspace. + /// The runspace. + private void CloseRunspace(Runspace runspace) + { + try + { + if (runspace != null && runspace.RunspaceStateInfo.State == RunspaceState.Opened) + { + runspace.Close(); + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError("Runspace error", ex); + } + } + + /// Executes shell command. + /// The runspace. + /// The command to be executed. + /// PSobjecs collection. + private Collection ExecuteShellCommand(Runspace runspace, object cmd) + { + object[] errors; + var command = cmd as Command; + + if (command != null) + { + return ExecuteShellCommand(runspace, command, out errors); + } + + return ExecuteShellCommand(runspace, cmd as List, out errors); + } + + /// Executes shell command. + /// The runspace. + /// The command to be executed. + /// The errors. + /// PSobjecs collection. + private Collection ExecuteShellCommand(Runspace runspace, Command cmd, out object[] errors) + { + HostedSolutionLog.LogStart("ExecuteShellCommand"); + var errorList = new List(); + + HostedSolutionLog.DebugCommand(cmd); + Collection results; + + using (Pipeline pipeLine = runspace.CreatePipeline()) + { + pipeLine.Commands.Add(cmd); + results = pipeLine.Invoke(); + + if (pipeLine.Error != null && pipeLine.Error.Count > 0) + { + foreach (object item in pipeLine.Error.ReadToEnd()) + { + errorList.Add(item); + string errorMessage = string.Format("Invoke error: {0}", item); + HostedSolutionLog.LogWarning(errorMessage); + } + } + } + + errors = errorList.ToArray(); + HostedSolutionLog.LogEnd("ExecuteShellCommand"); + + return results; + } + + /// Executes shell command. + /// The runspace. + /// The scripts to be executed. + /// The errors. + /// PSobjecs collection. + private Collection ExecuteShellCommand(Runspace runspace, List scripts, out object[] errors) + { + HostedSolutionLog.LogStart("ExecuteShellCommand"); + var errorList = new List(); + Collection results; + + using (Pipeline pipeLine = runspace.CreatePipeline()) + { + foreach (string script in scripts) + { + pipeLine.Commands.AddScript(script); + } + + results = pipeLine.Invoke(); + + if (pipeLine.Error != null && pipeLine.Error.Count > 0) + { + foreach (object item in pipeLine.Error.ReadToEnd()) + { + errorList.Add(item); + string errorMessage = string.Format("Invoke error: {0}", item); + HostedSolutionLog.LogWarning(errorMessage); + + throw new ArgumentException(scripts.First()); + } + } + } + + errors = errorList.ToArray(); + HostedSolutionLog.LogEnd("ExecuteShellCommand"); + + return results; + } + + /// Adds record to hosts file. + /// The site collection object. + public void AddHostsRecord(SharePointSiteCollection siteCollection) + { + try + { + if (siteCollection.RootWebApplicationInteralIpAddress != string.Empty) + { + string dirPath = FileUtils.EvaluateSystemVariables(@"%windir%\system32\drivers\etc"); + string path = dirPath + "\\hosts"; + + if (FileUtils.FileExists(path)) + { + string content = FileUtils.GetFileTextContent(path); + content = content.Replace("\r\n", "\n").Replace("\n\r", "\n"); + string[] contentArr = content.Split(new[] {'\n'}); + bool bRecordExist = false; + + foreach (string s in contentArr) + { + if (s != string.Empty) + { + string hostName = string.Empty; + + if (s[0] != '#') + { + bool bSeperator = false; + + foreach (char c in s) + { + if ((c != ' ') & (c != '\t')) + { + if (bSeperator) + { + hostName += c; + } + } + else + { + bSeperator = true; + } + } + + if (hostName.ToLower() == siteCollection.RootWebApplicationFQDN.ToLower()) + { + bRecordExist = true; + break; + } + } + } + } + + if (!bRecordExist) + { + string outPut = contentArr.Where(o => o != string.Empty).Aggregate(string.Empty, (current, o) => current + (o + "\r\n")); + outPut += siteCollection.RootWebApplicationInteralIpAddress + '\t' + siteCollection.RootWebApplicationFQDN + "\r\n"; + FileUtils.UpdateFileTextContent(path, outPut); + } + } + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError(ex); + } + } + + /// Removes record from hosts file. + /// The site collection object. + private void RemoveHostsRecord(SharePointSiteCollection siteCollection) + { + try + { + if (siteCollection.RootWebApplicationInteralIpAddress != string.Empty) + { + string dirPath = FileUtils.EvaluateSystemVariables(@"%windir%\system32\drivers\etc"); + string path = dirPath + "\\hosts"; + + if (FileUtils.FileExists(path)) + { + string content = FileUtils.GetFileTextContent(path); + content = content.Replace("\r\n", "\n").Replace("\n\r", "\n"); + string[] contentArr = content.Split(new[] {'\n'}); + string outPut = string.Empty; + + foreach (string s in contentArr) + { + if (s != string.Empty) + { + string hostName = string.Empty; + + if (s[0] != '#') + { + bool bSeperator = false; + + foreach (char c in s) + { + if ((c != ' ') & (c != '\t')) + { + if (bSeperator) + { + hostName += c; + } + } + else + { + bSeperator = true; + } + } + + if (hostName.ToLower() != siteCollection.RootWebApplicationFQDN.ToLower()) + { + outPut += s + "\r\n"; + } + } + else + { + outPut += s + "\r\n"; + } + } + } + + FileUtils.UpdateFileTextContent(path, outPut); + } + } + } + catch (Exception ex) + { + HostedSolutionLog.LogError(ex); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/WebsitePanel.Providers.HostedSolution.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/WebsitePanel.Providers.HostedSolution.csproj index 6325906e..05169241 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/WebsitePanel.Providers.HostedSolution.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.HostedSolution/WebsitePanel.Providers.HostedSolution.csproj @@ -144,6 +144,8 @@ + + diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/HostedSharePointEditSiteCollection.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/HostedSharePointEditSiteCollection.ascx.cs index bc712364..8f1a77d4 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/HostedSharePointEditSiteCollection.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/HostedSharePointEditSiteCollection.ascx.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Web.UI.WebControls; using WebsitePanel.EnterpriseServer; using WebsitePanel.Providers.DNS; @@ -148,20 +149,11 @@ namespace WebsitePanel.Portal } Organization org = ES.Services.Organizations.GetOrganization(OrganizationId); + if (org != null) { - maxStorage.ParentQuotaValue = org.MaxSharePointStorage; - maxStorage.QuotaValue = org.MaxSharePointStorage; - - editMaxStorage.ParentQuotaValue = org.MaxSharePointStorage; - - - - warningStorage.ParentQuotaValue = org.WarningSharePointStorage; - warningStorage.QuotaValue = org.WarningSharePointStorage; - editWarningStorage.ParentQuotaValue = org.WarningSharePointStorage; + SetStorageQuotas(org, item); } - } //OrganizationDomainName[] domains = ES.Services.Organizations.GetOrganizationDomains(PanelRequest.ItemID); @@ -192,6 +184,51 @@ namespace WebsitePanel.Portal } } + /// Checks and sets disk quotas values. + /// The organization. + /// The site collection. + private void SetStorageQuotas(Organization organization, SharePointSiteCollection collection) + { + var quotaValue = organization.MaxSharePointStorage; + + if (quotaValue != -1) + { + var spaceResrved = GetReservedDiskStorageSpace(); + + if (spaceResrved > quotaValue) + { + quotaValue = 0; + } + else + { + quotaValue -= spaceResrved; + } + + if (collection != null) + { + quotaValue += (int)collection.MaxSiteStorage; + } + } + + maxStorage.ParentQuotaValue = quotaValue; + maxStorage.QuotaValue = quotaValue; + editMaxStorage.ParentQuotaValue = quotaValue; + warningStorage.ParentQuotaValue = quotaValue; + warningStorage.QuotaValue = quotaValue; + editWarningStorage.ParentQuotaValue = quotaValue; + + btnUpdate.Enabled = quotaValue != 0; + } + + /// Gets disk space reserved by existing site collections. + /// Reserved disk space vallue. + private int GetReservedDiskStorageSpace() + { + var existingCollections = ES.Services.HostedSharePointServers.GetSiteCollections(PanelSecurity.PackageId, false); + + return (int)existingCollections.Sum(siteCollection => siteCollection.MaxSiteStorage); + } + private void SaveItem() { if (!Page.IsValid)