From d1b07ef26996b856fd01d61b93f5770ebde54d24 Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Sat, 10 Mar 2012 12:30:47 -0800 Subject: [PATCH 01/17] Fixed a typo in the error message. --- .../Code/Common/UsernameAssertion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Code/Common/UsernameAssertion.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Code/Common/UsernameAssertion.cs index 97489d5f..fab3a2a3 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Code/Common/UsernameAssertion.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Code/Common/UsernameAssertion.cs @@ -232,7 +232,7 @@ namespace WebsitePanel.EnterpriseServer ); if (user == null) - throw new Exception("Authentication token is invalid or borken"); + throw new Exception("Authentication token is invalid or broken"); SecurityContext.SetThreadPrincipal(user); } From 0ef2f7b8083e96dc31006aad0e1fd99ba6e2c8ce Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Thu, 26 Apr 2012 00:17:15 -0700 Subject: [PATCH 02/17] [workitem:225] Could not create FTP account in a vanilla fresh installation of FileZilla --- .../FileZilla.cs | 583 +++++++++--------- 1 file changed, 294 insertions(+), 289 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.FTP.FileZilla/FileZilla.cs b/WebsitePanel/Sources/WebsitePanel.Providers.FTP.FileZilla/FileZilla.cs index 80432655..3ab8e6dc 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.FTP.FileZilla/FileZilla.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.FTP.FileZilla/FileZilla.cs @@ -40,361 +40,366 @@ using WebsitePanel.Server.Utils; namespace WebsitePanel.Providers.FTP { - public class FileZilla : HostingServiceProviderBase, IFtpServer - { - #region Constants + public class FileZilla : HostingServiceProviderBase, IFtpServer + { + #region Constants - private const string FILEZILLA_REG = @"SOFTWARE\FileZilla Server"; - private const string FILEZILLA_REG_X64 = @"SOFTWARE\Wow6432Node\FileZilla Server"; - private const string FILEZILLA_SERVER_FILE = "FileZilla Server.xml"; + private const string FILEZILLA_REG = @"SOFTWARE\FileZilla Server"; + private const string FILEZILLA_REG_X64 = @"SOFTWARE\Wow6432Node\FileZilla Server"; + private const string FILEZILLA_SERVER_FILE = "FileZilla Server.xml"; - #endregion + #endregion - #region Properties - protected virtual string FileZillaFolder - { - get - { - RegistryKey fzKey = Registry.LocalMachine.OpenSubKey(FILEZILLA_REG) ?? - Registry.LocalMachine.OpenSubKey(FILEZILLA_REG_X64); + #region Properties + protected virtual string FileZillaFolder + { + get + { + RegistryKey fzKey = Registry.LocalMachine.OpenSubKey(FILEZILLA_REG) ?? + Registry.LocalMachine.OpenSubKey(FILEZILLA_REG_X64); if (fzKey == null) - throw new Exception("FileZilla registry key was not found: " + FILEZILLA_REG); + throw new Exception("FileZilla registry key was not found: " + FILEZILLA_REG); - return (string)fzKey.GetValue("Install_Dir"); - } - } - #endregion + return (string)fzKey.GetValue("Install_Dir"); + } + } + #endregion - #region Sites + #region Sites - public virtual void ChangeSiteState(string siteId, ServerState state) - { - // not implemented - } + public virtual void ChangeSiteState(string siteId, ServerState state) + { + // not implemented + } - public virtual string CreateSite(FtpSite site) - { - // not implemented - return null; - } + public virtual string CreateSite(FtpSite site) + { + // not implemented + return null; + } - public virtual void DeleteSite(string siteId) - { - // not implemented - } + public virtual void DeleteSite(string siteId) + { + // not implemented + } - public virtual FtpSite GetSite(string siteId) - { - // not implemented - return null; - } + public virtual FtpSite GetSite(string siteId) + { + // not implemented + return null; + } - public virtual FtpSite[] GetSites() - { - // not implemented - return null; - } + public virtual FtpSite[] GetSites() + { + // not implemented + return null; + } - public virtual bool SiteExists(string siteId) - { - // not implemented - return false; - } + public virtual bool SiteExists(string siteId) + { + // not implemented + return false; + } - public virtual ServerState GetSiteState(string siteId) - { - // not implemented - return ServerState.Started; - } + public virtual ServerState GetSiteState(string siteId) + { + // not implemented + return ServerState.Started; + } - public virtual void UpdateSite(FtpSite site) - { - // not implemented - } + public virtual void UpdateSite(FtpSite site) + { + // not implemented + } - #endregion + #endregion - #region Accounts + #region Accounts - public virtual bool AccountExists(string accountName) - { - XmlDocument doc = GetFileZillaConfig(); - XmlNode nodeUser = doc.SelectSingleNode("/FileZillaServer/Users/User[@Name='" + accountName + "']"); - return (nodeUser != null); - } + public virtual bool AccountExists(string accountName) + { + XmlDocument doc = GetFileZillaConfig(); + XmlNode nodeUser = doc.SelectSingleNode("/FileZillaServer/Users/User[@Name='" + accountName + "']"); + return (nodeUser != null); + } - public virtual FtpAccount GetAccount(string accountName) - { - XmlDocument doc = GetFileZillaConfig(); - XmlNode nodeUser = doc.SelectSingleNode("/FileZillaServer/Users/User[@Name='" + accountName + "']"); - if (nodeUser == null) - return null; + public virtual FtpAccount GetAccount(string accountName) + { + XmlDocument doc = GetFileZillaConfig(); + XmlNode nodeUser = doc.SelectSingleNode("/FileZillaServer/Users/User[@Name='" + accountName + "']"); + if (nodeUser == null) + return null; - return CreateAccountFromXmlNode(nodeUser, false); - } + return CreateAccountFromXmlNode(nodeUser, false); + } - public virtual FtpAccount[] GetAccounts() - { - XmlDocument doc = GetFileZillaConfig(); - XmlNodeList nodeUsers = doc.SelectNodes("/FileZillaServer/Users/User"); + public virtual FtpAccount[] GetAccounts() + { + XmlDocument doc = GetFileZillaConfig(); + XmlNodeList nodeUsers = doc.SelectNodes("/FileZillaServer/Users/User"); - List accounts = new List(); - foreach (XmlNode nodeUser in nodeUsers) - accounts.Add(CreateAccountFromXmlNode(nodeUser, true)); + List accounts = new List(); + foreach (XmlNode nodeUser in nodeUsers) + accounts.Add(CreateAccountFromXmlNode(nodeUser, true)); - return accounts.ToArray(); - } + return accounts.ToArray(); + } - public virtual void CreateAccount(FtpAccount account) - { + public virtual void CreateAccount(FtpAccount account) + { Log.WriteInfo("GetFileZillaConfig"); - XmlDocument doc = GetFileZillaConfig(); + XmlDocument doc = GetFileZillaConfig(); Log.WriteInfo("Find users nodes"); - // find users node - XmlNode nodeUsers = doc.SelectSingleNode("/FileZillaServer/Users"); - - XmlElement nodeUser = doc.CreateElement("User"); - if (nodeUsers != null) nodeUsers.AppendChild(nodeUser); + // find users node + XmlNode fzServerNode = doc.SelectSingleNode("/FileZillaServer"); + XmlNode fzAccountsNode = fzServerNode.SelectSingleNode("/Users"); + if (fzAccountsNode == null) + { + fzAccountsNode = doc.CreateElement("Users"); + fzServerNode.AppendChild(fzAccountsNode); + } - // set properties - nodeUser.SetAttribute("Name", account.Name); - SetOption(nodeUser, "Pass", MD5(account.Password)); - SetOption(nodeUser, "Group", ""); - SetOption(nodeUser, "Bypass server userlimit", "0"); - SetOption(nodeUser, "User Limit", "0"); - SetOption(nodeUser, "IP Limit", "0"); - SetOption(nodeUser, "Enabled", BoolToString(account.Enabled)); - SetOption(nodeUser, "Comments", ""); - SetOption(nodeUser, "ForceSsl", "0"); + XmlElement fzAccountNode = doc.CreateElement("User"); + fzAccountsNode.AppendChild(fzAccountNode); + // set properties + fzAccountNode.SetAttribute("Name", account.Name); + SetOption(fzAccountNode, "Pass", MD5(account.Password)); + SetOption(fzAccountNode, "Group", ""); + SetOption(fzAccountNode, "Bypass server userlimit", "0"); + SetOption(fzAccountNode, "User Limit", "0"); + SetOption(fzAccountNode, "IP Limit", "0"); + SetOption(fzAccountNode, "Enabled", BoolToString(account.Enabled)); + SetOption(fzAccountNode, "Comments", ""); + SetOption(fzAccountNode, "ForceSsl", "0"); - // IP filter - XmlElement nodeIPFilter = doc.CreateElement("IpFilter"); - nodeUser.AppendChild(nodeIPFilter); + // IP filter + XmlElement nodeIPFilter = doc.CreateElement("IpFilter"); + fzAccountNode.AppendChild(nodeIPFilter); - XmlElement nodeDisallowed = doc.CreateElement("Disallowed"); - nodeIPFilter.AppendChild(nodeDisallowed); + XmlElement nodeDisallowed = doc.CreateElement("Disallowed"); + nodeIPFilter.AppendChild(nodeDisallowed); - XmlElement nodeAllowed = doc.CreateElement("Allowed"); - nodeIPFilter.AppendChild(nodeAllowed); + XmlElement nodeAllowed = doc.CreateElement("Allowed"); + nodeIPFilter.AppendChild(nodeAllowed); - // folder - XmlElement nodePermissions = doc.CreateElement("Permissions"); - nodeUser.AppendChild(nodePermissions); + // folder + XmlElement nodePermissions = doc.CreateElement("Permissions"); + fzAccountNode.AppendChild(nodePermissions); - XmlElement nodePermission = doc.CreateElement("Permission"); - nodePermissions.AppendChild(nodePermission); + XmlElement nodePermission = doc.CreateElement("Permission"); + nodePermissions.AppendChild(nodePermission); - // folder settings - nodePermission.SetAttribute("Dir", account.Folder); - SetOption(nodePermission, "FileRead", BoolToString(account.CanRead)); - SetOption(nodePermission, "FileWrite", BoolToString(account.CanWrite)); - SetOption(nodePermission, "FileDelete", BoolToString(account.CanWrite)); - SetOption(nodePermission, "DirCreate", BoolToString(account.CanWrite)); - SetOption(nodePermission, "DirDelete", BoolToString(account.CanWrite)); - SetOption(nodePermission, "DirList", BoolToString(account.CanRead)); - SetOption(nodePermission, "DirSubdirs", BoolToString(account.CanRead)); - SetOption(nodePermission, "IsHome", "1"); - SetOption(nodePermission, "AutoCreate", "0"); + // folder settings + nodePermission.SetAttribute("Dir", account.Folder); + SetOption(nodePermission, "FileRead", BoolToString(account.CanRead)); + SetOption(nodePermission, "FileWrite", BoolToString(account.CanWrite)); + SetOption(nodePermission, "FileDelete", BoolToString(account.CanWrite)); + SetOption(nodePermission, "DirCreate", BoolToString(account.CanWrite)); + SetOption(nodePermission, "DirDelete", BoolToString(account.CanWrite)); + SetOption(nodePermission, "DirList", BoolToString(account.CanRead)); + SetOption(nodePermission, "DirSubdirs", BoolToString(account.CanRead)); + SetOption(nodePermission, "IsHome", "1"); + SetOption(nodePermission, "AutoCreate", "0"); - // speed limits - XmlElement nodeSpeedLimits = doc.CreateElement("SpeedLimits"); - nodeUser.AppendChild(nodeSpeedLimits); - nodeSpeedLimits.SetAttribute("DlType", "0"); - nodeSpeedLimits.SetAttribute("DlLimit", "10"); - nodeSpeedLimits.SetAttribute("ServerDlLimitBypass", "0"); - nodeSpeedLimits.SetAttribute("UlType", "0"); - nodeSpeedLimits.SetAttribute("UlLimit", "10"); - nodeSpeedLimits.SetAttribute("ServerUlLimitBypass", "0"); + // speed limits + XmlElement nodeSpeedLimits = doc.CreateElement("SpeedLimits"); + fzAccountNode.AppendChild(nodeSpeedLimits); + nodeSpeedLimits.SetAttribute("DlType", "0"); + nodeSpeedLimits.SetAttribute("DlLimit", "10"); + nodeSpeedLimits.SetAttribute("ServerDlLimitBypass", "0"); + nodeSpeedLimits.SetAttribute("UlType", "0"); + nodeSpeedLimits.SetAttribute("UlLimit", "10"); + nodeSpeedLimits.SetAttribute("ServerUlLimitBypass", "0"); - XmlElement nodeDownload = doc.CreateElement("Download"); - nodeSpeedLimits.AppendChild(nodeDownload); + XmlElement nodeDownload = doc.CreateElement("Download"); + nodeSpeedLimits.AppendChild(nodeDownload); - XmlElement nodeUpload = doc.CreateElement("Upload"); - nodeSpeedLimits.AppendChild(nodeUpload); + XmlElement nodeUpload = doc.CreateElement("Upload"); + nodeSpeedLimits.AppendChild(nodeUpload); - // save document - doc.Save(GetFileZillaConfigPath()); + // save document + doc.Save(GetFileZillaConfigPath()); - // reload config - ReloadFileZillaConfig(); - } + // reload config + ReloadFileZillaConfig(); + } - public virtual void UpdateAccount(FtpAccount account) - { - XmlDocument doc = GetFileZillaConfig(); - XmlNode nodeUser = doc.SelectSingleNode("/FileZillaServer/Users/User[@Name='" + account.Name + "']"); - if (nodeUser == null) - return; + public virtual void UpdateAccount(FtpAccount account) + { + XmlDocument doc = GetFileZillaConfig(); + XmlNode nodeUser = doc.SelectSingleNode("/FileZillaServer/Users/User[@Name='" + account.Name + "']"); + if (nodeUser == null) + return; - // update user - if(!String.IsNullOrEmpty(account.Password)) - SetOption(nodeUser, "Pass", MD5(account.Password)); - SetOption(nodeUser, "Enabled", BoolToString(account.Enabled)); + // update user + if(!String.IsNullOrEmpty(account.Password)) + SetOption(nodeUser, "Pass", MD5(account.Password)); + SetOption(nodeUser, "Enabled", BoolToString(account.Enabled)); - // update folder - XmlNode nodePermission = nodeUser.SelectSingleNode("Permissions/Permission"); - if (nodePermission != null) - { - ((XmlElement)nodePermission).SetAttribute("Dir", account.Folder); - SetOption(nodePermission, "FileRead", BoolToString(account.CanRead)); - SetOption(nodePermission, "FileWrite", BoolToString(account.CanWrite)); - SetOption(nodePermission, "FileDelete", BoolToString(account.CanWrite)); - SetOption(nodePermission, "DirCreate", BoolToString(account.CanWrite)); - SetOption(nodePermission, "DirDelete", BoolToString(account.CanWrite)); - SetOption(nodePermission, "DirList", BoolToString(account.CanRead)); - SetOption(nodePermission, "DirSubdirs", BoolToString(account.CanRead)); - } + // update folder + XmlNode nodePermission = nodeUser.SelectSingleNode("Permissions/Permission"); + if (nodePermission != null) + { + ((XmlElement)nodePermission).SetAttribute("Dir", account.Folder); + SetOption(nodePermission, "FileRead", BoolToString(account.CanRead)); + SetOption(nodePermission, "FileWrite", BoolToString(account.CanWrite)); + SetOption(nodePermission, "FileDelete", BoolToString(account.CanWrite)); + SetOption(nodePermission, "DirCreate", BoolToString(account.CanWrite)); + SetOption(nodePermission, "DirDelete", BoolToString(account.CanWrite)); + SetOption(nodePermission, "DirList", BoolToString(account.CanRead)); + SetOption(nodePermission, "DirSubdirs", BoolToString(account.CanRead)); + } - // save document - doc.Save(GetFileZillaConfigPath()); + // save document + doc.Save(GetFileZillaConfigPath()); - // reload config - ReloadFileZillaConfig(); - } + // reload config + ReloadFileZillaConfig(); + } - public virtual void DeleteAccount(string accountName) - { - XmlDocument doc = GetFileZillaConfig(); - XmlNode nodeUser = doc.SelectSingleNode("/FileZillaServer/Users/User[@Name='" + accountName + "']"); - if (nodeUser == null) - return; + public virtual void DeleteAccount(string accountName) + { + XmlDocument doc = GetFileZillaConfig(); + XmlNode nodeUser = doc.SelectSingleNode("/FileZillaServer/Users/User[@Name='" + accountName + "']"); + if (nodeUser == null) + return; - // delete account - nodeUser.ParentNode.RemoveChild(nodeUser); + // delete account + nodeUser.ParentNode.RemoveChild(nodeUser); - // save document - doc.Save(GetFileZillaConfigPath()); + // save document + doc.Save(GetFileZillaConfigPath()); - // reload config - ReloadFileZillaConfig(); - } + // reload config + ReloadFileZillaConfig(); + } - #endregion + #endregion - public override void ChangeServiceItemsState(ServiceProviderItem[] items, bool enabled) - { - foreach (ServiceProviderItem item in items) - { - if (item is FtpAccount) - { - try - { - // change FTP account state - FtpAccount account = GetAccount(item.Name); - account.Enabled = enabled; - UpdateAccount(account); - } - catch (Exception ex) - { - Log.WriteError(String.Format("Error switching '{0}' {1}", item.Name, item.GetType().Name), ex); - } - } - } - } + public override void ChangeServiceItemsState(ServiceProviderItem[] items, bool enabled) + { + foreach (ServiceProviderItem item in items) + { + if (item is FtpAccount) + { + try + { + // change FTP account state + FtpAccount account = GetAccount(item.Name); + account.Enabled = enabled; + UpdateAccount(account); + } + catch (Exception ex) + { + Log.WriteError(String.Format("Error switching '{0}' {1}", item.Name, item.GetType().Name), ex); + } + } + } + } - public override void DeleteServiceItems(ServiceProviderItem[] items) - { - foreach (ServiceProviderItem item in items) - { - if (item is FtpAccount) - { - try - { - // delete FTP account - DeleteAccount(item.Name); - } - catch (Exception ex) - { - Log.WriteError(String.Format("Error deleting '{0}' {1}", item.Name, item.GetType().Name), ex); - } - } - } - } + public override void DeleteServiceItems(ServiceProviderItem[] items) + { + foreach (ServiceProviderItem item in items) + { + if (item is FtpAccount) + { + try + { + // delete FTP account + DeleteAccount(item.Name); + } + catch (Exception ex) + { + Log.WriteError(String.Format("Error deleting '{0}' {1}", item.Name, item.GetType().Name), ex); + } + } + } + } - #region Private Helpers - private string BoolToString(bool val) - { - return val ? "1" : "0"; - } + #region Private Helpers + private string BoolToString(bool val) + { + return val ? "1" : "0"; + } - private void SetOption(XmlNode parentNode, string name, string val) - { - XmlNode option = parentNode.SelectSingleNode("Option[@Name='" + name + "']"); - if (option == null) - { - option = parentNode.OwnerDocument.CreateElement("Option"); - parentNode.AppendChild(option); - ((XmlElement)option).SetAttribute("Name", name); - } - option.InnerText = val; - } + private void SetOption(XmlNode parentNode, string name, string val) + { + XmlNode option = parentNode.SelectSingleNode("Option[@Name='" + name + "']"); + if (option == null) + { + option = parentNode.OwnerDocument.CreateElement("Option"); + parentNode.AppendChild(option); + ((XmlElement)option).SetAttribute("Name", name); + } + option.InnerText = val; + } - private FtpAccount CreateAccountFromXmlNode(XmlNode nodeUser, bool excludeDetails) - { - FtpAccount account = new FtpAccount(); - account.Name = nodeUser.Attributes["Name"].Value; + private FtpAccount CreateAccountFromXmlNode(XmlNode nodeUser, bool excludeDetails) + { + FtpAccount account = new FtpAccount(); + account.Name = nodeUser.Attributes["Name"].Value; - if (!excludeDetails) - { - account.Password = nodeUser.SelectSingleNode("Option[@Name='Pass']").InnerText; - account.Enabled = (nodeUser.SelectSingleNode("Option[@Name='Enabled']").InnerText == "1"); - XmlNode nodeFolder = nodeUser.SelectSingleNode("Permissions/Permission"); - if (nodeFolder != null) - { - account.Folder = nodeFolder.Attributes["Dir"].Value; - account.CanRead = (nodeFolder.SelectSingleNode("Option[@Name='FileRead']").InnerText == "1"); - account.CanWrite = (nodeFolder.SelectSingleNode("Option[@Name='FileWrite']").InnerText == "1"); - } - } + if (!excludeDetails) + { + account.Password = nodeUser.SelectSingleNode("Option[@Name='Pass']").InnerText; + account.Enabled = (nodeUser.SelectSingleNode("Option[@Name='Enabled']").InnerText == "1"); + XmlNode nodeFolder = nodeUser.SelectSingleNode("Permissions/Permission"); + if (nodeFolder != null) + { + account.Folder = nodeFolder.Attributes["Dir"].Value; + account.CanRead = (nodeFolder.SelectSingleNode("Option[@Name='FileRead']").InnerText == "1"); + account.CanWrite = (nodeFolder.SelectSingleNode("Option[@Name='FileWrite']").InnerText == "1"); + } + } - return account; - } + return account; + } - private XmlDocument GetFileZillaConfig() - { - string path = GetFileZillaConfigPath(); - if (!File.Exists(path)) - throw new Exception("FileZilla configuration file was not found: " + path); + private XmlDocument GetFileZillaConfig() + { + string path = GetFileZillaConfigPath(); + if (!File.Exists(path)) + throw new Exception("FileZilla configuration file was not found: " + path); - XmlDocument doc = new XmlDocument(); - doc.Load(path); - return doc; - } + XmlDocument doc = new XmlDocument(); + doc.Load(path); + return doc; + } - private string GetFileZillaConfigPath() - { - return Path.Combine(FileZillaFolder, FILEZILLA_SERVER_FILE); - } + private string GetFileZillaConfigPath() + { + return Path.Combine(FileZillaFolder, FILEZILLA_SERVER_FILE); + } - private string MD5(string str) - { - System.Text.UTF8Encoding ue = new System.Text.UTF8Encoding(); - byte[] bytes = ue.GetBytes(str); + private string MD5(string str) + { + System.Text.UTF8Encoding ue = new System.Text.UTF8Encoding(); + byte[] bytes = ue.GetBytes(str); - // encrypt bytes - System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); - byte[] hashBytes = md5.ComputeHash(bytes); + // encrypt bytes + System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); + byte[] hashBytes = md5.ComputeHash(bytes); - // Convert the encrypted bytes back to a string (base 16) - string hashString = ""; + // Convert the encrypted bytes back to a string (base 16) + string hashString = ""; - for (int i = 0; i < hashBytes.Length; i++) - hashString += Convert.ToString(hashBytes[i], 16).PadLeft(2, '0'); + for (int i = 0; i < hashBytes.Length; i++) + hashString += Convert.ToString(hashBytes[i], 16).PadLeft(2, '0'); - return hashString.PadLeft(32, '0'); - } + return hashString.PadLeft(32, '0'); + } - private void ReloadFileZillaConfig() - { - FileUtils.ExecuteSystemCommand( - Path.Combine(FileZillaFolder, "FileZilla Server.exe"), - "/reload-config"); - } - #endregion + private void ReloadFileZillaConfig() + { + FileUtils.ExecuteSystemCommand( + Path.Combine(FileZillaFolder, "FileZilla Server.exe"), + "/reload-config"); + } + #endregion public override bool IsInstalled() { @@ -423,5 +428,5 @@ namespace WebsitePanel.Providers.FTP } } - } + } } From b51da7e9559ea8e6f12c8060356d3b77674026b0 Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Thu, 26 Apr 2012 00:31:16 -0700 Subject: [PATCH 03/17] Added simple IoC implementation to make the class a bit testable --- .../WebsitePanel.Server.Utils/FileUtils.cs | 130 +++++++++++++----- 1 file changed, 94 insertions(+), 36 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.Server.Utils/FileUtils.cs b/WebsitePanel/Sources/WebsitePanel.Server.Utils/FileUtils.cs index 54bd5f60..01185cf4 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server.Utils/FileUtils.cs +++ b/WebsitePanel/Sources/WebsitePanel.Server.Utils/FileUtils.cs @@ -37,15 +37,101 @@ using System.Collections.Generic; using System.Reflection; using Ionic.Zip; using WebsitePanel.Providers.OS; +using System.Diagnostics.Contracts; namespace WebsitePanel.Providers.Utils { + /// + /// Defines a contract that a system command provider needs to implement. + /// + public interface ICommandLineProvider + { + /// + /// Executes the file specifed as if you were executing it via command-line interface. + /// + /// Path to the executable file (eq. .exe, .bat, .cmd and etc). + /// Arguments to pass to the executable file + /// Path to the output file if you want the output to be written somewhere. + /// Output of the command being executed. + string Execute(string filePath, string args, string outputFile); + } + + /// + /// Provides a default implementation of running system commands. + /// + public sealed class DefaultCommandLineProvider : ICommandLineProvider + { + /// + /// Creates a new process and executes the file specifed as if you were executing it via command-line interface. + /// + /// Path to the executable file (eq. .exe, .bat, .cmd and etc). + /// Arguments to pass to the executable file + /// Path to the output file if you want the output to be written somewhere. + /// Output of the command being executed. + public string Execute(string filePath, string args, string outputFile) + { + // launch system process + ProcessStartInfo startInfo = new ProcessStartInfo(filePath, args); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.RedirectStandardOutput = true; + startInfo.UseShellExecute = false; + startInfo.CreateNoWindow = true; + + // get working directory from executable path + startInfo.WorkingDirectory = Path.GetDirectoryName(filePath); + Process proc = Process.Start(startInfo); + + // analyze results + StreamReader reader = proc.StandardOutput; + string results = ""; + if (!String.IsNullOrEmpty(outputFile)) + { + // stream to writer + StreamWriter writer = new StreamWriter(outputFile); + int BUFFER_LENGTH = 2048; + int readBytes = 0; + char[] buffer = new char[BUFFER_LENGTH]; + while ((readBytes = reader.Read(buffer, 0, BUFFER_LENGTH)) > 0) + { + writer.Write(buffer, 0, readBytes); + } + writer.Close(); + } + else + { + // return as string + results = reader.ReadToEnd(); + } + reader.Close(); + + return results; + } + } + /// /// Summary description for FileUtils. /// public class FileUtils { + private static ICommandLineProvider CliProvider; + + static FileUtils() + { + SetDefaultCliProvider(new DefaultCommandLineProvider()); + } + + /// + /// Initializes command-line provider for the utility class. Yet this is not a perfect way to inverse control over CLI processing + /// but it does its job for the testing purposes. + /// + /// An instance of a command-line provider to initialize the utility with. + public static void SetDefaultCliProvider(ICommandLineProvider provider) + { + Contract.Requires(provider != null); + CliProvider = provider; + } + public static string EvaluateSystemVariables(string str) { if (String.IsNullOrEmpty(str)) @@ -616,45 +702,17 @@ namespace WebsitePanel.Providers.Utils return ExecuteSystemCommand(cmd, args, null); } + /// + /// Executes the file specifed as if you were executing it via command-line interface. + /// + /// Path to the executable file (eq. .exe, .bat, .cmd and etc). + /// Arguments to pass to the executable file + /// Path to the output file if you want the output to be written somewhere. + /// Output of the command being executed. public static string ExecuteSystemCommand(string cmd, string args, string outputFile) { // launch system process - ProcessStartInfo startInfo = new ProcessStartInfo(cmd, args); - startInfo.WindowStyle = ProcessWindowStyle.Hidden; - startInfo.RedirectStandardOutput = true; - startInfo.StandardOutputEncoding = Encoding.UTF8; - startInfo.UseShellExecute = false; - startInfo.CreateNoWindow = true; - - // get working directory from executable path - startInfo.WorkingDirectory = Path.GetDirectoryName(cmd); - Process proc = Process.Start(startInfo); - - - // analyze results - StreamReader reader = proc.StandardOutput; - string results = ""; - if (!String.IsNullOrEmpty(outputFile)) - { - // stream to writer - StreamWriter writer = new StreamWriter(outputFile); - int BUFFER_LENGTH = 2048; - int readBytes = 0; - char[] buffer = new char[BUFFER_LENGTH]; - while ((readBytes = reader.Read(buffer, 0, BUFFER_LENGTH)) > 0) - { - writer.Write(buffer, 0, readBytes); - } - writer.Close(); - } - else - { - // return as string - results = reader.ReadToEnd(); - } - reader.Close(); - - return results; + return CliProvider.Execute(cmd, args, outputFile); } public static void ExecuteCmdCommand(string command) From 596f40c18d100cb7a922df3b245509472d79fb05 Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Thu, 26 Apr 2012 00:34:53 -0700 Subject: [PATCH 04/17] Removed hardcoded paths to MsDeploy and SqlCmd tools like MsDeploy; Removed harcoded path referencing the previous WebsitePanel build location; --- WebsitePanel/build.xml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/WebsitePanel/build.xml b/WebsitePanel/build.xml index 5415e9b6..b7c9eba9 100644 --- a/WebsitePanel/build.xml +++ b/WebsitePanel/build.xml @@ -1,5 +1,5 @@  - + 1.2.1.0 1.2.1.6 @@ -15,12 +15,13 @@ $(TrunkFolder)\Build\$(BuildConfiguration) $(TrunkFolder)\Deploy\$(BuildConfiguration) - C:\Projects\WebsitePanel-1.2.0-distr\$(BuildConfiguration) + $(RootFolder)\..\prev\$(BuildConfiguration) $(TrunkFolder)\Tools\Diff.exe - "C:\Program Files\Microsoft SQL Server\100\Tools\Binn\sqlcmd.exe" -S (local)\SQLEXPRESS -E + "$(ProgramFiles)\Microsoft SQL Server\100\Tools\Binn\sqlcmd.exe" -S (local)\SQLEXPRESS -E - "C:\Program Files\IIS\Microsoft Web Deploy V2\msdeploy.exe" + "$(ProgramFiles)\IIS\Microsoft Web Deploy\msdeploy.exe" + "$(ProgramFiles)\IIS\Microsoft Web Deploy V2\msdeploy.exe" WebsitePanel_build server=(local)\SQLEXPRESS;database=$(DataBaseName);Integrated Security=true; @@ -42,9 +43,9 @@ - - @@ -316,8 +317,8 @@ $(DeployFolder)\Database - - + + From 187eb507d86e1128e3930a40fdd65380276ac7b8 Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Thu, 26 Apr 2012 01:53:34 -0700 Subject: [PATCH 05/17] Updated reference path to MySql.Data.dll to point to Lib\References --- .../WebsitePanel.Providers.Database.MySQL.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Database.MySQL/WebsitePanel.Providers.Database.MySQL.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Database.MySQL/WebsitePanel.Providers.Database.MySQL.csproj index 74fa14c7..5dda564e 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Database.MySQL/WebsitePanel.Providers.Database.MySQL.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Database.MySQL/WebsitePanel.Providers.Database.MySQL.csproj @@ -57,7 +57,7 @@ False - ..\..\..\..\..\Program Files (x86)\MySQL\Connector NET 6.3.7\Assemblies\v2.0\MySql.Data.dll + ..\..\Lib\References\MySQL\MySql.Data.dll From c46c660dc9832da7cf66289434bf4a041f53538f Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Thu, 26 Apr 2012 02:21:18 -0700 Subject: [PATCH 06/17] Switched to use 64-bit version of MsBuild --- WebsitePanel/deploy-debug.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebsitePanel/deploy-debug.bat b/WebsitePanel/deploy-debug.bat index 49fb2b46..3088a1ef 100644 --- a/WebsitePanel/deploy-debug.bat +++ b/WebsitePanel/deploy-debug.bat @@ -1 +1 @@ -%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe build.xml /target:Deploy /property:BuildConfiguration=Debug /v:n /fileLogger +%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe build.xml /target:Deploy /property:BuildConfiguration=Debug /v:n /fileLogger From 629039fb626a04f7d3ef0b4be88a273b032a669e Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Thu, 26 Apr 2012 02:22:39 -0700 Subject: [PATCH 07/17] Removed .NET 4.0 traces (eq. Code Contracts) as it breaks the build --- WebsitePanel/Sources/WebsitePanel.Server.Utils/FileUtils.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.Server.Utils/FileUtils.cs b/WebsitePanel/Sources/WebsitePanel.Server.Utils/FileUtils.cs index 01185cf4..c12ceea8 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server.Utils/FileUtils.cs +++ b/WebsitePanel/Sources/WebsitePanel.Server.Utils/FileUtils.cs @@ -37,8 +37,6 @@ using System.Collections.Generic; using System.Reflection; using Ionic.Zip; using WebsitePanel.Providers.OS; -using System.Diagnostics.Contracts; - namespace WebsitePanel.Providers.Utils { @@ -128,7 +126,7 @@ namespace WebsitePanel.Providers.Utils /// An instance of a command-line provider to initialize the utility with. public static void SetDefaultCliProvider(ICommandLineProvider provider) { - Contract.Requires(provider != null); + Debug.Assert(provider != null, "Command line provider is null"); CliProvider = provider; } From af8e0ed935a8f1951eb8be090d46e4d681dd3117 Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Thu, 26 Apr 2012 02:24:00 -0700 Subject: [PATCH 08/17] Updated year in the copyright text to 2012 --- WebsitePanel/Sources/VersionInfo.cs | 2 +- WebsitePanel/Sources/VersionInfo.vb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WebsitePanel/Sources/VersionInfo.cs b/WebsitePanel/Sources/VersionInfo.cs index 9efe6796..76d765dc 100644 --- a/WebsitePanel/Sources/VersionInfo.cs +++ b/WebsitePanel/Sources/VersionInfo.cs @@ -14,7 +14,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: AssemblyCompany("Outercurve Foundation")] -[assembly: AssemblyCopyright("Copyright © 2011 Outercurve Foundation.")] +[assembly: AssemblyCopyright("Copyright © 2012 Outercurve Foundation.")] [assembly: AssemblyVersion("1.2.1.0")] [assembly: AssemblyFileVersion("1.2.1.6")] [assembly: AssemblyInformationalVersion("1.2.1")] diff --git a/WebsitePanel/Sources/VersionInfo.vb b/WebsitePanel/Sources/VersionInfo.vb index 9acd5803..5ca71dce 100644 --- a/WebsitePanel/Sources/VersionInfo.vb +++ b/WebsitePanel/Sources/VersionInfo.vb @@ -16,7 +16,7 @@ Imports System.Reflection Imports System.Runtime.CompilerServices Imports System.Runtime.InteropServices From 6ff01b5adf58b4fa631a36b92df537c915fb5539 Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Sun, 6 May 2012 14:28:25 -0700 Subject: [PATCH 09/17] - Fixed loader issue for SilentInstaller; - Updated file version to 1.2.1.1; --- WebsitePanel.Installer/Sources/VersionInfo.cs | 2 +- .../WebsitePanel.Installer.Core/Loader.cs | 4 ++-- .../WebsitePanel.Installer/Updater.exe | Bin 198144 -> 199168 bytes .../WebsitePanel.SilentInstaller/Program.cs | 18 +++++++++++------- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/WebsitePanel.Installer/Sources/VersionInfo.cs b/WebsitePanel.Installer/Sources/VersionInfo.cs index f939546f..9434d902 100644 --- a/WebsitePanel.Installer/Sources/VersionInfo.cs +++ b/WebsitePanel.Installer/Sources/VersionInfo.cs @@ -37,5 +37,5 @@ using System.Reflection; [assembly: AssemblyCompany("Outercurve Foundation")] [assembly: AssemblyCopyright("Copyright © 2012 Outercurve Foundation.")] [assembly: AssemblyVersion("1.2.1.0")] -[assembly: AssemblyFileVersion("1.2.1.0")] +[assembly: AssemblyFileVersion("1.2.1.1")] [assembly: AssemblyInformationalVersion("1.2.1")] \ No newline at end of file diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Loader.cs b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Loader.cs index 39fe1861..54ef5d79 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Loader.cs +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer.Core/Loader.cs @@ -80,7 +80,7 @@ namespace WebsitePanel.Installer.Core private WebClient fileLoader; - public BitlyLoader(string remoteFile) + internal BitlyLoader(string remoteFile) : base(remoteFile) { InitFileLoader(); @@ -175,7 +175,7 @@ namespace WebsitePanel.Installer.Core public event EventHandler> ProgressChanged; public event EventHandler OperationCompleted; - public Loader(string remoteFile) + internal Loader(string remoteFile) { this.remoteFile = remoteFile; } diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Updater.exe b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Updater.exe index 9105eeef3bb49c3a4d2c266c1f99c21cb48f39cf..201af23a28368ad906da0ac3b67b8033ae1ccd72 100644 GIT binary patch delta 42950 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 R!2LPvqg3&gQrkW3e*vfoo2&o; delta 41947 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 z zBjxdHg!s8_#znTMlquK!GF?ya|2;$mA*QEq6#kNg_zer)yqtS3Hr0yE#t z4j5&Tp8h^VI5JLNVvgZQjChABPv{jH=kJJRM~w3NN!?{xdZefPio{;tF5^A!sq5!) U`Mz%a`yuNMk@8p4lGKR*1#~I@r~m)} diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.SilentInstaller/Program.cs b/WebsitePanel.Installer/Sources/WebsitePanel.SilentInstaller/Program.cs index 76fbefd8..621b0c16 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.SilentInstaller/Program.cs +++ b/WebsitePanel.Installer/Sources/WebsitePanel.SilentInstaller/Program.cs @@ -337,10 +337,13 @@ namespace WebsitePanel.SilentInstaller string installerPath = Utils.GetDbString(row["InstallerPath"]); string installerType = Utils.GetDbString(row["InstallerType"]); + // Get appropriate loader to download the app distributive + var loader = LoaderFactory.CreateFileLoader(fileName); + // Mimic synchronous download process for the console app + AutoResetEvent autoEvent = new AutoResetEvent(false); + try { - // download installer - var loader = new Loader(fileName); // loader.OperationCompleted += new EventHandler((object sender, EventArgs e) => { @@ -374,25 +377,26 @@ namespace WebsitePanel.SilentInstaller Log.WriteEnd("Installer finished"); // Remove temporary directory FileUtils.DeleteTempDirectory(); + // We are done + autoEvent.Set(); }); - + // TODO: Add cleanup for events. loader.OperationFailed += new EventHandler>(loader_OperationFailed); loader.ProgressChanged += new EventHandler>(loader_ProgressChanged); loader.StatusChanged += new EventHandler>(loader_StatusChanged); // loader.LoadAppDistributive(); + // Wait until the download is complete + autoEvent.WaitOne(); } catch (Exception ex) { Log.WriteError("Installer error", ex); - //AppContext.AppForm.ShowError(ex); } finally { - //this.componentSettingsXml = null; - //this.componentCode = null; + autoEvent.Set(); } - } static void loader_StatusChanged(object sender, LoaderEventArgs e) From d7adf241873a07174385691de8778ac4ebf26cb0 Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Thu, 7 Jun 2012 10:42:14 -0700 Subject: [PATCH 10/17] #111: System.InvalidCastException is thrown when accessing a VPS via Remote Desktop Connection feature --- ...lizationServerControllerForPrivateCloud.cs | 2 +- .../Code/Helpers/VirtualMachinesHelper.cs | 60 +++++++++++++++---- .../VPSForPC/RemoteDesktop/Connect.aspx.cs | 7 ++- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Code/VirtualizationForPrivateCloud/VirtualizationServerControllerForPrivateCloud.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Code/VirtualizationForPrivateCloud/VirtualizationServerControllerForPrivateCloud.cs index 913e3eef..0742287c 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Code/VirtualizationForPrivateCloud/VirtualizationServerControllerForPrivateCloud.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Code/VirtualizationForPrivateCloud/VirtualizationServerControllerForPrivateCloud.cs @@ -2700,7 +2700,7 @@ namespace WebsitePanel.EnterpriseServer public static NetworkAdapterDetails GetExternalNetworkAdapterDetails(int itemId) { // load service item - VirtualMachine vm = (VirtualMachine)PackageController.GetPackageItem(itemId); + VMInfo vm = (VMInfo)PackageController.GetPackageItem(itemId); if (vm == null) return null; diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Code/Helpers/VirtualMachinesHelper.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Code/Helpers/VirtualMachinesHelper.cs index 594c8785..0be00d79 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Code/Helpers/VirtualMachinesHelper.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/Code/Helpers/VirtualMachinesHelper.cs @@ -29,9 +29,54 @@ using WebsitePanel.EnterpriseServer; using WebsitePanel.Providers.Virtualization; using System.Web; +using System; namespace WebsitePanel.Portal { + // TODO: Move this extension to a separate file later. + public static class VirtualMachinesExtensions + { + #region Privates with specific purposes (eq. caching, usability, performance and etc) + /// + /// This method supports the Portal internal infrastructure and is not intended to be used directly from your code. Gets a cached copy of virtual machine object of the specified type or retrieves it for the first time and then caches it. + /// + /// Type of virtual machine to be retrieved (possible types are VirtualMachine|VMInfo) + /// Virtual machine item id + /// Function to retrieve the virtual machine data from Enterprise Server + /// An instance of the specified virtual machine + internal static T GetCachedVirtualMachine(object cacheIdentityKey, Func getVmFunc) + { + // TODO: Make this method private when all dependents will be consolidated in the extension. + string cacheKey = "CachedVirtualMachine_" + cacheIdentityKey; + if (HttpContext.Current.Items[cacheKey] != null) + return (T)HttpContext.Current.Items[cacheKey]; + + // load virtual machine + T virtualMachine = getVmFunc(); + + // place to cache + if (virtualMachine != null) + HttpContext.Current.Items[cacheKey] = virtualMachine; + + return virtualMachine; + } + #endregion + + #region Extension methods + /// + /// Gets a cached copy of virtual machine object of the specified type or retrieves it for the first time and then caches it. + /// + /// + /// Virtual machine id + /// An instance of the virtual machine speficied + public static VMInfo GetCachedVirtualMachine(this esVirtualizationServerForPrivateCloud client, int itemId) + { + return GetCachedVirtualMachine( + itemId, () => ES.Services.VPSPC.GetVirtualMachineItem(itemId)); + } + #endregion + } + public class VirtualMachinesHelper { public static bool IsVirtualMachineManagementAllowed(int packageId) @@ -57,20 +102,11 @@ namespace WebsitePanel.Portal return manageAllowed; } + // TODO: Move this method to the corresponding extension later. public static VirtualMachine GetCachedVirtualMachine(int itemId) { - string key = "CachedVirtualMachine" + itemId; - if (HttpContext.Current.Items[key] != null) - return (VirtualMachine)HttpContext.Current.Items[key]; - - // load virtual machine - VirtualMachine vm = ES.Services.VPS.GetVirtualMachineItem(itemId); - - // place to cache - if (vm != null) - HttpContext.Current.Items[key] = vm; - - return vm; + return VirtualMachinesExtensions.GetCachedVirtualMachine( + itemId, () => ES.Services.VPS.GetVirtualMachineItem(itemId)); } #region Virtual Machines diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPSForPC/RemoteDesktop/Connect.aspx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPSForPC/RemoteDesktop/Connect.aspx.cs index 5805a371..c4faa3a9 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPSForPC/RemoteDesktop/Connect.aspx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/VPSForPC/RemoteDesktop/Connect.aspx.cs @@ -40,13 +40,14 @@ namespace WebsitePanel.Portal.VPSForPC.RemoteDesktop resolution.Text = Request["Resolution"]; // load server info - VirtualMachine vm = VirtualMachinesHelper.GetCachedVirtualMachine(PanelRequest.ItemID); + VMInfo vm = ES.Services.VPSPC.GetCachedVirtualMachine(PanelRequest.ItemID); litServerName.Text = vm.Name + " - "; username.Text = "Administrator"; - password.Text = vm.AdministratorPassword; + // TODO: Review VMInfo class fields and underlying data for correctness + password.Text = vm.AdminPassword; // load external network parameters - NetworkAdapterDetails nic = ES.Services.VPS.GetExternalNetworkAdapterDetails(PanelRequest.ItemID); + NetworkAdapterDetails nic = ES.Services.VPSPC.GetExternalNetworkAdapterDetails(PanelRequest.ItemID); if (nic.IPAddresses.Length > 0) { NetworkAdapterIPAddress ip = nic.IPAddresses[0]; From c1350cf404545d4e2f1c2d72abc8e3395d07cc45 Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Wed, 1 Aug 2012 07:39:13 -0700 Subject: [PATCH 11/17] Corrected default configuration for installer --- .../Sources/WebsitePanel.Installer/App.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/App.config b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/App.config index 18e7f2ee..60bc4b39 100644 --- a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/App.config +++ b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/App.config @@ -9,7 +9,7 @@ - + From 0d8f6c61e851e14195e2ca0c60346456351f446f Mon Sep 17 00:00:00 2001 From: ptsurbeleu Date: Wed, 1 Aug 2012 09:20:11 -0700 Subject: [PATCH 12/17] Recovering from erroneous merge --- WebsitePanel.Installer/Sources/VersionInfo.cs | 6 +-- WebsitePanel/Sources/VersionInfo.cs | 2 +- WebsitePanel/Sources/VersionInfo.vb | 2 +- WebsitePanel/build.xml | 50 +++++++++++++++---- WebsitePanel/deploy-debug.bat | 2 +- 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/WebsitePanel.Installer/Sources/VersionInfo.cs b/WebsitePanel.Installer/Sources/VersionInfo.cs index 9434d902..f829f4b5 100644 --- a/WebsitePanel.Installer/Sources/VersionInfo.cs +++ b/WebsitePanel.Installer/Sources/VersionInfo.cs @@ -36,6 +36,6 @@ using System.Reflection; // [assembly: AssemblyCompany("Outercurve Foundation")] [assembly: AssemblyCopyright("Copyright © 2012 Outercurve Foundation.")] -[assembly: AssemblyVersion("1.2.1.0")] -[assembly: AssemblyFileVersion("1.2.1.1")] -[assembly: AssemblyInformationalVersion("1.2.1")] \ No newline at end of file +[assembly: AssemblyVersion("1.2.2.0")] +[assembly: AssemblyFileVersion("1.2.2.1")] +[assembly: AssemblyInformationalVersion("1.2.2")] \ No newline at end of file diff --git a/WebsitePanel/Sources/VersionInfo.cs b/WebsitePanel/Sources/VersionInfo.cs index 692b7f6f..e876f147 100644 --- a/WebsitePanel/Sources/VersionInfo.cs +++ b/WebsitePanel/Sources/VersionInfo.cs @@ -16,7 +16,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyCompany("Outercurve Foundation")] [assembly: AssemblyCopyright("Copyright © 2012 Outercurve Foundation.")] [assembly: AssemblyVersion("1.2.2.0")] -[assembly: AssemblyFileVersion("1.2.2.0")] +[assembly: AssemblyFileVersion("1.2.2.1")] [assembly: AssemblyInformationalVersion("1.2.2")] diff --git a/WebsitePanel/Sources/VersionInfo.vb b/WebsitePanel/Sources/VersionInfo.vb index 356d6c92..a457662c 100644 --- a/WebsitePanel/Sources/VersionInfo.vb +++ b/WebsitePanel/Sources/VersionInfo.vb @@ -18,6 +18,6 @@ Imports System.Runtime.InteropServices diff --git a/WebsitePanel/build.xml b/WebsitePanel/build.xml index dde929c4..f2805da8 100644 --- a/WebsitePanel/build.xml +++ b/WebsitePanel/build.xml @@ -2,7 +2,7 @@ 1.2.2.0 - 1.2.2.0 + 1.2.2.1 1.2.2 2012-08-01 Release @@ -33,6 +33,8 @@ $(TrunkFolder)\Sources\Tools\WebsitePanel.Import.Enterprise\bin\$(BuildConfiguration) $(TrunkFolder)\Sources\Tools\WebsitePanel.AWStats.Viewer +$(TrunkFolder)\Sources\Tools\WSPTransportAgent + $(BuildFolder)\Server $(BuildFolder)\EnterpriseServer $(BuildFolder)\Portal @@ -40,13 +42,13 @@ $(BuildFolder)\Import.Enterprise $(BuildFolder)\AWStats.Viewer +$(BuildFolder)\WSPTransportAgent + - - - + + + @@ -57,7 +59,8 @@ - + + @@ -67,6 +70,7 @@ + @@ -76,6 +80,7 @@ + @@ -185,7 +190,31 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + $(DeployFolder)\Install $(InstallFolder)\Server @@ -198,6 +227,8 @@ $(ToolsFolder)\Import.CsvBulk $(ToolsFolder)\Import.Enterprise $(ToolsFolder)\AWStats.Viewer + $(ToolsFolder)\WSPTransportAgent + @@ -429,6 +460,7 @@ - + + diff --git a/WebsitePanel/deploy-debug.bat b/WebsitePanel/deploy-debug.bat index 3088a1ef..35fd6db8 100644 --- a/WebsitePanel/deploy-debug.bat +++ b/WebsitePanel/deploy-debug.bat @@ -1 +1 @@ -%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe build.xml /target:Deploy /property:BuildConfiguration=Debug /v:n /fileLogger +%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe build.xml /target:Deploy /property:BuildConfiguration=Debug /v:n /fileLogger /m From 31fb739b1838e31f665408b37bb356e1cb042544 Mon Sep 17 00:00:00 2001 From: robvde Date: Wed, 1 Aug 2012 21:38:51 +0400 Subject: [PATCH 13/17] Added ability to apply exchange and lync plan templates to all tenants. Plan will only be added is the planName does not exist yet for this tenant. Added ability to match exchange plan (based on size and mapi) to mail enabled users (room and equipment) and add the mailbox plan to the object for all users who has no plan assigned yet configured generate_es_proxies.bat added Allowed admins to delete reseller space --- .../ExchangeServerProxy.cs | 76 +++++++- .../esExchangeServer.asmx.cs | 7 + .../WebsitePanel_SharedResources.ascx.resx | 18 ++ ...ttingsExchangeMailboxPlansPolicy.ascx.resx | 6 + .../SettingsExchangeMailboxPlansPolicy.ascx | 17 +- ...SettingsExchangeMailboxPlansPolicy.ascx.cs | 183 ++++++++++++++++++ ...xchangeMailboxPlansPolicy.ascx.designer.cs | 66 ++++--- .../SettingsLyncUserPlansPolicy.ascx | 9 +- .../SettingsLyncUserPlansPolicy.ascx.cs | 69 +++++++ ...ttingsLyncUserPlansPolicy.ascx.designer.cs | 58 +++--- .../WebsitePanel/SpaceDetails.ascx.cs | 2 +- WebsitePanel/Sources/generate_es_proxies.bat | 98 ++++++++++ 12 files changed, 545 insertions(+), 64 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Client/ExchangeServerProxy.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Client/ExchangeServerProxy.cs index 341ae893..b074daa5 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Client/ExchangeServerProxy.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Client/ExchangeServerProxy.cs @@ -26,7 +26,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - //------------------------------------------------------------------------------ // // This code was generated by a tool. @@ -114,6 +113,8 @@ namespace WebsitePanel.EnterpriseServer { private System.Threading.SendOrPostCallback DisableMailboxOperationCompleted; + private System.Threading.SendOrPostCallback GetMailboxAdvancedSettingsOperationCompleted; + private System.Threading.SendOrPostCallback GetMailboxGeneralSettingsOperationCompleted; private System.Threading.SendOrPostCallback SetMailboxGeneralSettingsOperationCompleted; @@ -304,6 +305,9 @@ namespace WebsitePanel.EnterpriseServer { /// public event DisableMailboxCompletedEventHandler DisableMailboxCompleted; + /// + public event GetMailboxAdvancedSettingsCompletedEventHandler GetMailboxAdvancedSettingsCompleted; + /// public event GetMailboxGeneralSettingsCompletedEventHandler GetMailboxGeneralSettingsCompleted; @@ -1775,6 +1779,50 @@ namespace WebsitePanel.EnterpriseServer { } } + /// + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://smbsaas/websitepanel/enterpriseserver/GetMailboxAdvancedSettings", RequestNamespace="http://smbsaas/websitepanel/enterpriseserver", ResponseNamespace="http://smbsaas/websitepanel/enterpriseserver", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + public ExchangeMailbox GetMailboxAdvancedSettings(int itemId, int accountId) { + object[] results = this.Invoke("GetMailboxAdvancedSettings", new object[] { + itemId, + accountId}); + return ((ExchangeMailbox)(results[0])); + } + + /// + public System.IAsyncResult BeginGetMailboxAdvancedSettings(int itemId, int accountId, System.AsyncCallback callback, object asyncState) { + return this.BeginInvoke("GetMailboxAdvancedSettings", new object[] { + itemId, + accountId}, callback, asyncState); + } + + /// + public ExchangeMailbox EndGetMailboxAdvancedSettings(System.IAsyncResult asyncResult) { + object[] results = this.EndInvoke(asyncResult); + return ((ExchangeMailbox)(results[0])); + } + + /// + public void GetMailboxAdvancedSettingsAsync(int itemId, int accountId) { + this.GetMailboxAdvancedSettingsAsync(itemId, accountId, null); + } + + /// + public void GetMailboxAdvancedSettingsAsync(int itemId, int accountId, object userState) { + if ((this.GetMailboxAdvancedSettingsOperationCompleted == null)) { + this.GetMailboxAdvancedSettingsOperationCompleted = new System.Threading.SendOrPostCallback(this.OnGetMailboxAdvancedSettingsOperationCompleted); + } + this.InvokeAsync("GetMailboxAdvancedSettings", new object[] { + itemId, + accountId}, this.GetMailboxAdvancedSettingsOperationCompleted, userState); + } + + private void OnGetMailboxAdvancedSettingsOperationCompleted(object arg) { + if ((this.GetMailboxAdvancedSettingsCompleted != null)) { + System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); + this.GetMailboxAdvancedSettingsCompleted(this, new GetMailboxAdvancedSettingsCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); + } + } + /// [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://smbsaas/websitepanel/enterpriseserver/GetMailboxGeneralSettings", RequestNamespace="http://smbsaas/websitepanel/enterpriseserver", ResponseNamespace="http://smbsaas/websitepanel/enterpriseserver", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public ExchangeMailbox GetMailboxGeneralSettings(int itemId, int accountId) { @@ -5196,6 +5244,32 @@ namespace WebsitePanel.EnterpriseServer { } } + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] + public delegate void GetMailboxAdvancedSettingsCompletedEventHandler(object sender, GetMailboxAdvancedSettingsCompletedEventArgs e); + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + public partial class GetMailboxAdvancedSettingsCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { + + private object[] results; + + internal GetMailboxAdvancedSettingsCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : + base(exception, cancelled, userState) { + this.results = results; + } + + /// + public ExchangeMailbox Result { + get { + this.RaiseExceptionIfNecessary(); + return ((ExchangeMailbox)(this.results[0])); + } + } + } + /// [System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")] public delegate void GetMailboxGeneralSettingsCompletedEventHandler(object sender, GetMailboxGeneralSettingsCompletedEventArgs e); diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/esExchangeServer.asmx.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/esExchangeServer.asmx.cs index d3da1430..0dc14964 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/esExchangeServer.asmx.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/esExchangeServer.asmx.cs @@ -236,12 +236,19 @@ namespace WebsitePanel.EnterpriseServer } + [WebMethod] + public ExchangeMailbox GetMailboxAdvancedSettings(int itemId, int accountId) + { + return ExchangeServerController.GetMailboxAdvancedSettings(itemId, accountId); + } + [WebMethod] public ExchangeMailbox GetMailboxGeneralSettings(int itemId, int accountId) { return ExchangeServerController.GetMailboxGeneralSettings(itemId, accountId); } + [WebMethod] public int SetMailboxGeneralSettings(int itemId, int accountId, bool hideAddressBook, bool disabled) { diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/App_GlobalResources/WebsitePanel_SharedResources.ascx.resx b/WebsitePanel/Sources/WebsitePanel.WebPortal/App_GlobalResources/WebsitePanel_SharedResources.ascx.resx index ee430246..0f583fb1 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/App_GlobalResources/WebsitePanel_SharedResources.ascx.resx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/App_GlobalResources/WebsitePanel_SharedResources.ascx.resx @@ -5164,4 +5164,22 @@ Enable Lync User Plans Editing + + Failed to apply plans template + + + Succesfully applied plans template + + + Failed to match plans + + + Succesfully plans matched and applied + + + Failed to apply plans template + + + Succesfully applied plans template + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/App_LocalResources/SettingsExchangeMailboxPlansPolicy.ascx.resx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/App_LocalResources/SettingsExchangeMailboxPlansPolicy.ascx.resx index 055532df..84e8e718 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/App_LocalResources/SettingsExchangeMailboxPlansPolicy.ascx.resx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/App_LocalResources/SettingsExchangeMailboxPlansPolicy.ascx.resx @@ -213,4 +213,10 @@ * + + Add Mailbox Plans Template to All Tenants + + + Match Mailbox Plan to User + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx index 9df47609..d1150ca3 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx @@ -2,8 +2,11 @@ <%@ Register Src="ExchangeServer/UserControls/SizeBox.ascx" TagName="SizeBox" TagPrefix="wsp" %> <%@ Register Src="ExchangeServer/UserControls/DaysBox.ascx" TagName="DaysBox" TagPrefix="wsp" %> <%@ Register Src="UserControls/CollapsiblePanel.ascx" TagName="CollapsiblePanel" TagPrefix="wsp" %> +<%@ Register Src="UserControls/SimpleMessageBox.ascx" TagName="SimpleMessageBox" TagPrefix="wsp" %> +<%@ Register Src="UserControls/EnableAsyncTasksSupport.ascx" TagName="EnableAsyncTasksSupport" TagPrefix="wsp" %> - + + @@ -184,5 +187,15 @@ Text="Add New Mailboxplan" CssClass="Button1" OnClick="btnAddMailboxPlan_Click" /> +
+ +
- +
+ +
+ \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx.cs index c17886c6..7169e55a 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx.cs @@ -238,5 +238,188 @@ namespace WebsitePanel.Portal settings[UserSettings.DEFAULT_MAILBOXPLANS] = writer.ToString(); } + + protected void btnAddMailboxPlanToOrganizations_Click(object sender, EventArgs e) + { + AddMailboxPlanToOrganizations("ServerAdmin"); + } + + private void AddMailboxPlanToOrganizations(string serverAdmin) + { + UserInfo ServerAdminInfo = ES.Services.Users.GetUserByUsername(serverAdmin); + + if (ServerAdminInfo == null) return; + + UserInfo[] UsersInfo = ES.Services.Users.GetUsers(ServerAdminInfo.UserId, true); + + try + { + foreach (UserInfo ui in UsersInfo) + { + PackageInfo[] Packages = ES.Services.Packages.GetPackages(ui.UserId); + + if ((Packages != null) & (Packages.GetLength(0) > 0)) + { + foreach (PackageInfo Package in Packages) + { + Providers.HostedSolution.Organization[] orgs = null; + + orgs = ES.Services.ExchangeServer.GetExchangeOrganizations(Package.PackageId, false); + + if ((orgs != null) & (orgs.GetLength(0) > 0)) + { + foreach (Organization org in orgs) + { + if (!string.IsNullOrEmpty(org.GlobalAddressList)) + { + ExchangeMailboxPlan[] plans = ES.Services.ExchangeServer.GetExchangeMailboxPlans(org.Id); + + foreach (ExchangeMailboxPlan p in list) + { + if (!PlanExists(p, plans)) ES.Services.ExchangeServer.AddExchangeMailboxPlan(org.Id, p); + } + } + } + } + } + } + } + messageBox.ShowSuccessMessage("EXCHANGE_APPLYPLANTEMPLATE"); + } + catch (Exception ex) + { + messageBox.ShowErrorMessage("EXCHANGE_APPLYPLANTEMPLATE", ex); + } + } + + private bool PlanExists(ExchangeMailboxPlan plan, ExchangeMailboxPlan[] plans) + { + bool result = false; + + foreach (ExchangeMailboxPlan p in plans) + { + if (p.MailboxPlan.ToLower() == plan.MailboxPlan.ToLower()) + { + result = true; + break; + } + } + return result; + } + + protected void btnMatchMailboxPlanToUser_Click(object sender, EventArgs e) + { + MatchMailboxPlanToUser("serverAdmin"); + } + + private void MatchMailboxPlanToUser(string serverAdmin) + { + UserInfo ServerAdminInfo = ES.Services.Users.GetUserByUsername(serverAdmin); + + if (ServerAdminInfo == null) return; + + UserInfo[] UsersInfo = ES.Services.Users.GetUsers(ServerAdminInfo.UserId, true); + + try + { + foreach (UserInfo ui in UsersInfo) + { + PackageInfo[] Packages = ES.Services.Packages.GetPackages(ui.UserId); + + if ((Packages != null) & (Packages.GetLength(0) > 0)) + { + foreach (PackageInfo Package in Packages) + { + Providers.HostedSolution.Organization[] orgs = null; + + orgs = ES.Services.ExchangeServer.GetExchangeOrganizations(Package.PackageId, false); + + if ((orgs != null) & (orgs.GetLength(0) > 0)) + { + foreach (Organization org in orgs) + { + if (!string.IsNullOrEmpty(org.GlobalAddressList)) + { + ExchangeMailboxPlan[] plans = ES.Services.ExchangeServer.GetExchangeMailboxPlans(org.Id); + + ExchangeAccount[] mailboxes = ES.Services.ExchangeServer.GetAccounts(org.Id, ExchangeAccountType.Mailbox); + + ExchangeAccount[] rooms = ES.Services.ExchangeServer.GetAccounts(org.Id, ExchangeAccountType.Room); + + ExchangeAccount[] equipment = ES.Services.ExchangeServer.GetAccounts(org.Id, ExchangeAccountType.Equipment); + + MatchExchangeAccountToPlan(org.Id, mailboxes, plans); + MatchExchangeAccountToPlan(org.Id, rooms, plans); + MatchExchangeAccountToPlan(org.Id, equipment, plans); + } + } + } + } + } + } + messageBox.ShowSuccessMessage("EXCHANGE_MATCHPLANS"); + } + catch (Exception ex) + { + messageBox.ShowErrorMessage("EXCHANGE_MATCHPLANS", ex); + } + } + + private void MatchExchangeAccountToPlan(int itemId, ExchangeAccount[] mailboxes, ExchangeMailboxPlan[] plans) + { + + foreach (ExchangeAccount a in mailboxes) + { + if (string.IsNullOrEmpty(a.MailboxPlan)) + { + ExchangeMailbox mailbox = ES.Services.ExchangeServer.GetMailboxAdvancedSettings(itemId, a.AccountId); + + if (mailbox != null) + { + List pl = new List(); + //sort a list of similar MAPI + foreach (ExchangeMailboxPlan p in plans) + { + if (p.EnableMAPI == mailbox.EnableMAPI) + pl.Add(p); + } + + //remove plans smaller than mailbox size + ExchangeMailboxPlan p3 = null; + foreach (ExchangeMailboxPlan p2 in pl) + { + if (p2.MailboxSizeMB >= (mailbox.ProhibitSendReceiveKB / 1024)) + { + if (p3 == null) + p3 = p2; + else + if ((p2.MailboxSizeMB) <= p3.MailboxSizeMB) + p3 = p2; + } + } + + // no matching plan, just match on size + if (p3 == null) + { + foreach (ExchangeMailboxPlan p in plans) + { + if (p.MailboxSizeMB >= (mailbox.ProhibitSendReceiveKB / 1024)) + { + if (p3 == null) + p3 = p; + else + if ((p.MailboxSizeMB) <= p3.MailboxSizeMB) + p3 = p; + } + } + } + + if (p3 != null) + ES.Services.ExchangeServer.SetExchangeMailboxPlan(itemId, a.AccountId, p3.MailboxPlanId); + } + } + } + } + } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx.designer.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx.designer.cs index 949d2653..be8483e7 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx.designer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsExchangeMailboxPlansPolicy.ascx.designer.cs @@ -1,32 +1,4 @@ -// Copyright (c) 2012, Outercurve Foundation. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// - Neither the name of the Outercurve Foundation nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // @@ -40,6 +12,24 @@ namespace WebsitePanel.Portal { public partial class SettingsExchangeMailboxPlansPolicy { + /// + /// asyncTasks control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::WebsitePanel.Portal.EnableAsyncTasksSupport asyncTasks; + + /// + /// messageBox control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::WebsitePanel.Portal.UserControls.SimpleMessageBox messageBox; + /// /// gvMailboxPlans control. /// @@ -381,5 +371,23 @@ namespace WebsitePanel.Portal { /// To modify move field declaration from designer file to code-behind file. ///
protected global::System.Web.UI.WebControls.Button btnAddMailboxPlan; + + /// + /// btnAddMailboxPlanToOrganizations control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnAddMailboxPlanToOrganizations; + + /// + /// btnMatchMailboxPlanToUser control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnMatchMailboxPlanToUser; } } diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx index 26602358..3ac10731 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx @@ -1,7 +1,10 @@ <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SettingsLyncUserPlansPolicy.ascx.cs" Inherits="WebsitePanel.Portal.SettingsLyncUserPlansPolicy" %> <%@ Register Src="UserControls/CollapsiblePanel.ascx" TagName="CollapsiblePanel" TagPrefix="wsp" %> +<%@ Register Src="UserControls/SimpleMessageBox.ascx" TagName="SimpleMessageBox" TagPrefix="wsp" %> +<%@ Register Src="UserControls/EnableAsyncTasksSupport.ascx" TagName="EnableAsyncTasksSupport" TagPrefix="wsp" %> - + + @@ -133,5 +136,9 @@ Text="Add New plan" CssClass="Button1" OnClick="btnAddPlan_Click" /> +
+ +
diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx.cs index 1ff83f72..a68f007b 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx.cs @@ -256,6 +256,75 @@ namespace WebsitePanel.Portal settings[UserSettings.DEFAULT_LYNCUSERPLANS] = writer.ToString(); } + protected void btnAddPlanToOrganizations_Click(object sender, EventArgs e) + { + AddPlanToOrganizations("ServerAdmin"); + } + + + private void AddPlanToOrganizations(string serverAdmin) + { + UserInfo ServerAdminInfo = ES.Services.Users.GetUserByUsername(serverAdmin); + + if (ServerAdminInfo == null) return; + + UserInfo[] UsersInfo = ES.Services.Users.GetUsers(ServerAdminInfo.UserId, true); + + try + { + foreach (UserInfo ui in UsersInfo) + { + PackageInfo[] Packages = ES.Services.Packages.GetPackages(ui.UserId); + + if ((Packages != null) & (Packages.GetLength(0) > 0)) + { + foreach (PackageInfo Package in Packages) + { + Providers.HostedSolution.Organization[] orgs = null; + + orgs = ES.Services.ExchangeServer.GetExchangeOrganizations(Package.PackageId, false); + + if ((orgs != null) & (orgs.GetLength(0) > 0)) + { + foreach (Organization org in orgs) + { + if (!string.IsNullOrEmpty(org.LyncTenantId)) + { + LyncUserPlan[] plans = ES.Services.Lync.GetLyncUserPlans(org.Id); + + foreach (LyncUserPlan p in list) + { + if (!PlanExists(p, plans)) ES.Services.Lync.AddLyncUserPlan(org.Id, p); + } + } + } + } + } + } + } + messageBox.ShowSuccessMessage("LYNC_APPLYPLANTEMPLATE"); + } + catch (Exception ex) + { + messageBox.ShowErrorMessage("LYNC_APPLYPLANTEMPLATE", ex); + } + } + + private bool PlanExists(LyncUserPlan plan, LyncUserPlan[] plans) + { + bool result = false; + + foreach (LyncUserPlan p in plans) + { + if (p.LyncUserPlanName.ToLower() == plan.LyncUserPlanName.ToLower()) + { + result = true; + break; + } + } + return result; + } + } diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx.designer.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx.designer.cs index 887af2a4..813d06c8 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx.designer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SettingsLyncUserPlansPolicy.ascx.designer.cs @@ -1,33 +1,4 @@ -// Copyright (c) 2012, Outercurve Foundation. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, this -// list of conditions and the following disclaimer. -// -// - Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// - Neither the name of the Outercurve Foundation nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // @@ -41,6 +12,24 @@ namespace WebsitePanel.Portal { public partial class SettingsLyncUserPlansPolicy { + /// + /// asyncTasks control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::WebsitePanel.Portal.EnableAsyncTasksSupport asyncTasks; + + /// + /// messageBox control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::WebsitePanel.Portal.UserControls.SimpleMessageBox messageBox; + /// /// gvPlans control. /// @@ -229,5 +218,14 @@ namespace WebsitePanel.Portal { /// To modify move field declaration from designer file to code-behind file. ///
protected global::System.Web.UI.WebControls.Button btnAddPlan; + + /// + /// btnAddPlanToOrganizations control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnAddPlanToOrganizations; } } diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SpaceDetails.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SpaceDetails.ascx.cs index 35bcfb94..b4ea5185 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SpaceDetails.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SpaceDetails.ascx.cs @@ -88,7 +88,7 @@ namespace WebsitePanel.Portal lnkEditSpaceDetails.Visible = (PanelSecurity.PackageId > 1 && !ownSpace); lnkDelete.NavigateUrl = EditUrl(PortalUtils.SPACE_ID_PARAM, PanelSecurity.PackageId.ToString(), "delete"); - if (PanelSecurity.LoggedUser.Role != UserRole.Reseller) + if (!((PanelSecurity.LoggedUser.Role == UserRole.Reseller) | (PanelSecurity.LoggedUser.Role == UserRole.Administrator))) lnkDelete.Visible = false; else lnkDelete.Visible = ((PanelSecurity.SelectedUserId != PanelSecurity.EffectiveUserId) && (PanelSecurity.PackageId > 1)); diff --git a/WebsitePanel/Sources/generate_es_proxies.bat b/WebsitePanel/Sources/generate_es_proxies.bat index d190a83a..b6a921a1 100644 --- a/WebsitePanel/Sources/generate_es_proxies.bat +++ b/WebsitePanel/Sources/generate_es_proxies.bat @@ -2,5 +2,103 @@ SET WSDL="C:\Program Files (x86)\Microsoft WSE\v3.0\Tools\WseWsdl3.exe" SET WSE_CLEAN=..\Tools\WseClean.exe SET SERVER_URL=http://localhost:9005 +REM %WSDL% %SERVER_URL%/esApplicationsInstaller.asmx /out:.\WebsitePanel.EnterpriseServer.Client\ApplicationsInstallerProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\ApplicationsInstallerProxy.cs + +REM %WSDL% %SERVER_URL%/esAuditLog.asmx /out:.\WebsitePanel.EnterpriseServer.Client\AuditLogProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\AuditLogProxy.cs + +REM %WSDL% %SERVER_URL%/esAuthentication.asmx /out:.\WebsitePanel.EnterpriseServer.Client\AuthenticationProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\AuthenticationProxy.cs + +REM %WSDL% %SERVER_URL%/esBackup.asmx /out:.\WebsitePanel.EnterpriseServer.Client\BackupProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\BackupProxy.cs + +REM %WSDL% %SERVER_URL%/esBlackBerry.asmx /out:.\WebsitePanel.EnterpriseServer.Client\BlackBerryProxy.cs /namespace:WebsitePanel.EnterpriseServer.HostedSolution /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\BlackBerryProxy.cs + +REM %WSDL% %SERVER_URL%/esComments.asmx /out:.\WebsitePanel.EnterpriseServer.Client\CommentsProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\CommentsProxy.cs + +REM %WSDL% %SERVER_URL%/esCRM.asmx /out:.\WebsitePanel.EnterpriseServer.Client\CRMProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\CRMProxy.cs + +REM %WSDL% %SERVER_URL%/esDatabaseServers.asmx /out:.\WebsitePanel.EnterpriseServer.Client\DatabaseServersProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\DatabaseServersProxy.cs + +REM %WSDL% %SERVER_URL%/ecServiceHandler.asmx /out:.\WebsitePanel.EnterpriseServer.Client\ecServiceHandlerProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\ecServiceHandlerProxy.cs + +REM %WSDL% %SERVER_URL%/ecStorefront.asmx /out:.\WebsitePanel.EnterpriseServer.Client\ecStorefrontProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\ecStorefrontProxy.cs + +REM %WSDL% %SERVER_URL%/ecStorehouse.asmx /out:.\WebsitePanel.EnterpriseServer.Client\ecStorehouseProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\ecStorehouseProxy.cs + %WSDL% %SERVER_URL%/esExchangeServer.asmx /out:.\WebsitePanel.EnterpriseServer.Client\ExchangeServerProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\ExchangeServerProxy.cs + +REM %WSDL% %SERVER_URL%/esExchangeHostedEdition.asmx /out:.\WebsitePanel.EnterpriseServer.Client\ExchangeHostedEditionProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\ExchangeHostedEditionProxy.cs + +REM %WSDL% %SERVER_URL%/esFiles.asmx /out:.\WebsitePanel.EnterpriseServer.Client\FilesProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\FilesProxy.cs + +REM %WSDL% %SERVER_URL%/esHostedSharePointServers.asmx /out:.\WebsitePanel.EnterpriseServer.Client\HostedSharePointServersProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\HostedSharePointServersProxy.cs + +REM %WSDL% %SERVER_URL%/esImport.asmx /out:.\WebsitePanel.EnterpriseServer.Client\ImportProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\ImportProxy.cs + +REM %WSDL% %SERVER_URL%/esOCS.asmx /out:.\WebsitePanel.EnterpriseServer.Client\OCSProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\OCSProxy.cs + +REM %WSDL% %SERVER_URL%/esOperatingSystems.asmx /out:.\WebsitePanel.EnterpriseServer.Client\OperatingSystemsProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\OperatingSystemsProxy.cs + +REM %WSDL% %SERVER_URL%/esOrganizations.asmx /out:.\WebsitePanel.EnterpriseServer.Client\OrganizationProxy.cs /namespace:WebsitePanel.EnterpriseServer.HostedSolution /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\OrganizationProxy.cs + +REM %WSDL% %SERVER_URL%/esPackages.asmx /out:.\WebsitePanel.EnterpriseServer.Client\PackagesProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\PackagesProxy.cs + +REM %WSDL% %SERVER_URL%/esScheduler.asmx /out:.\WebsitePanel.EnterpriseServer.Client\SchedulerProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\SchedulerProxy.cs + +REM %WSDL% %SERVER_URL%/esServers.asmx /out:.\WebsitePanel.EnterpriseServer.Client\ServersProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\ServersProxy.cs + +REM %WSDL% %SERVER_URL%/esSharePointServers.asmx /out:.\WebsitePanel.EnterpriseServer.Client\SharePointServersProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\SharePointServersProxy.cs + +REM %WSDL% %SERVER_URL%/esSystem.asmx /out:.\WebsitePanel.EnterpriseServer.Client\SystemProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\SystemProxy.cs + +REM %WSDL% %SERVER_URL%/esTasks.asmx /out:.\WebsitePanel.EnterpriseServer.Client\TasksProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\TasksProxy.cs + +REM %WSDL% %SERVER_URL%/esUsers.asmx /out:.\WebsitePanel.EnterpriseServer.Client\UsersProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\UsersProxy.cs + +REM %WSDL% %SERVER_URL%/esVirtualizationServer.asmx /out:.\WebsitePanel.EnterpriseServer.Client\VirtualizationServerProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\VirtualizationServerProxy.cs + +REM %WSDL% %SERVER_URL%/esWebApplicationGallery.asmx /out:.\WebsitePanel.EnterpriseServer.Client\WebApplicationGalleryProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\WebApplicationGalleryProxy.cs + +REM %WSDL% %SERVER_URL%/esWebServers.asmx /out:.\WebsitePanel.EnterpriseServer.Client\WebServersProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\WebServersProxy.cs + +REM %WSDL% %SERVER_URL%/esVirtualizationServerForPrivateCloud.asmx /out:.\WebsitePanel.EnterpriseServer.Client\VirtualizationServerProxyForPrivateCloud.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\VirtualizationServerProxyForPrivateCloud.cs + +REM %WSDL% %SERVER_URL%/esLync.asmx /out:.\WebsitePanel.EnterpriseServer.Client\LyncProxy.cs /namespace:WebsitePanel.EnterpriseServer /type:webClient +REM %WSE_CLEAN% .\WebsitePanel.EnterpriseServer.Client\LyncProxy.cs + + + + + + + + From 02c3967d27318f549c187d83aba54a50b3e3341d Mon Sep 17 00:00:00 2001 From: robvde Date: Wed, 1 Aug 2012 22:07:58 +0400 Subject: [PATCH 14/17] Webhosting: If WebPolicy is configured for dedicated application pool (checked) this setting cannot be change by tenants and greyed out as such. --- .../WebsitePanel/WebSitesHomeFolderControl.ascx.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesHomeFolderControl.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesHomeFolderControl.ascx.cs index bdbceabd..0fc58962 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesHomeFolderControl.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesHomeFolderControl.ascx.cs @@ -108,7 +108,11 @@ namespace WebsitePanel.Portal chkDedicatedPool.Visible = PackagesHelper.CheckGroupQuotaEnabled(packageId, ResourceGroups.Web, Quotas.WEB_APPPOOLS); pnlDefaultDocuments.Visible = PackagesHelper.CheckGroupQuotaEnabled(packageId, ResourceGroups.Web, Quotas.WEB_DEFAULTDOCS); - + UserSettings settings = ES.Services.Users.GetUserSettings(PanelSecurity.SelectedUserId, "WebPolicy"); + chkDedicatedPool.Checked = Utils.ParseBool(settings["EnableDedicatedPool"], false); + + chkDedicatedPool.Enabled = !chkDedicatedPool.Checked; + } public void SaveWebItem(WebVirtualDirectory item) From a6c14d8c4b2fce5b90f7d1b994deae35fd32dbdc Mon Sep 17 00:00:00 2001 From: robvde Date: Wed, 1 Aug 2012 22:09:32 +0400 Subject: [PATCH 15/17] resubmit previous one --- .../WebsitePanel/WebSitesHomeFolderControl.ascx.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesHomeFolderControl.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesHomeFolderControl.ascx.cs index 0fc58962..1eaf38bb 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesHomeFolderControl.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesHomeFolderControl.ascx.cs @@ -109,9 +109,10 @@ namespace WebsitePanel.Portal pnlDefaultDocuments.Visible = PackagesHelper.CheckGroupQuotaEnabled(packageId, ResourceGroups.Web, Quotas.WEB_DEFAULTDOCS); UserSettings settings = ES.Services.Users.GetUserSettings(PanelSecurity.SelectedUserId, "WebPolicy"); - chkDedicatedPool.Checked = Utils.ParseBool(settings["EnableDedicatedPool"], false); + if (Utils.ParseBool(settings["EnableDedicatedPool"], false) == true) + chkDedicatedPool.Checked = true; - chkDedicatedPool.Enabled = !chkDedicatedPool.Checked; + chkDedicatedPool.Enabled = !(Utils.ParseBool(settings["EnableDedicatedPool"], false)); } From 635a2bf6c397e5489939c5695df077f022da45bb Mon Sep 17 00:00:00 2001 From: robvde Date: Wed, 1 Aug 2012 23:29:47 +0400 Subject: [PATCH 16/17] Fixed: SSL Tab only visible for sites with dedicated IP Address --- .../WebsitePanel/WebSitesEditSite.ascx.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs index 871a41af..fb39a45f 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesEditSite.ascx.cs @@ -66,7 +66,6 @@ namespace WebsitePanel.Portal new Tab { Id = "mime", ResourceKey = "Tab.MIMETypes", Quota = Quotas.WEB_MIME, ViewId = "tabMimes" }, new Tab { Id = "coldfusion", ResourceKey = "Tab.ColdFusion", Quota = Quotas.WEB_COLDFUSION, ViewId = "tabCF" }, new Tab { Id = "webman", ResourceKey = "Tab.WebManagement", Quota = Quotas.WEB_REMOTEMANAGEMENT, ViewId = "tabWebManagement" }, - new Tab { Id = "SSL", ResourceKey = "Tab.SSL", Quota = Quotas.WEB_SSL, ViewId = "SSL" }, }; private int PackageId @@ -166,6 +165,7 @@ namespace WebsitePanel.Portal if (!String.IsNullOrEmpty(site.SiteIPAddress)) litIPAddress.Text = String.Format("({0})", site.SiteIPAddress); + litFrontPageUnavailable.Visible = false; tblSharePoint.Visible = site.SharePointInstalled; @@ -234,7 +234,17 @@ namespace WebsitePanel.Portal webSitesMimeTypesControl.BindWebItem(site); webSitesCustomHeadersControl.BindWebItem(site); webSitesCustomErrorsControl.BindWebItem(site); - WebsitesSSLControl.BindWebItem(site); + if (site.SiteIPAddress != null) + { + TabsList.Add(new Tab { Id = "SSL", ResourceKey = "Tab.SSL", Quota = Quotas.WEB_SSL, ViewId = "SSL" }); + TabsList.ForEach((x) => + { + x.Name = GetLocalizedString(x.ResourceKey); + x.ResourceGroup = x.ResourceGroup ?? ResourceGroups.Web; + }); + + WebsitesSSLControl.BindWebItem(site); + } BindVirtualDirectories(); From d5492fa3010066d7256971537e099d76c5815075 Mon Sep 17 00:00:00 2001 From: omara Date: Wed, 1 Aug 2012 18:28:56 -0400 Subject: [PATCH 17/17] Commit Various changes --- .../WebsitePanel.Installer/Updater.exe | Bin 198144 -> 198144 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Updater.exe b/WebsitePanel.Installer/Sources/WebsitePanel.Installer/Updater.exe index 7b7387583130c9b860fd372974059a2b95863b66..26627f7cb779719a976b0933b552eeac9e84481e 100644 GIT binary patch delta 45 zcmZqZ;c4jMnb5)P@m8X-yS1BfYd6!u0)dW0%rZCaKe$ZPG&=fU-`8_{Od->fDFCfN B6CnTq delta 45 zcmV+|0Mh?}j0}K`43LNe^Q9Gmi-n5;wTlA5a1esgOVuX3fI_@)%t7