using System; using System.Collections; using System.Security.Principal; using System.Text; using System.Web.UI; using System.Web.UI.WebControls; using CMS.ExtendedControls; using CMS.GlobalHelper; using CMS.SettingsProvider; using CMS.UIControls; public partial class CMSAdminControls_AsyncControl : CMSUserControl, ICallbackEventHandler, IPostBackEventHandler { #region "Private variables & constants" /// /// Table of the worker processes. /// private static readonly Hashtable mWorkers = new Hashtable(); private AsyncWorker mWorker = null; private string mCallbackResult = null; private bool mPostbackOnError = true; private string mLog = null; // Constants private const string RESULT_FINISHED = "finished"; private const string RESULT_RUNNING = "running"; private const string RESULT_ERROR = "error"; private const string RESULT_STOPPED = "stopped"; private const string RESULT_THREADLOST = "threadlost"; #endregion #region "Properties" /// /// Gets the current log. /// public string Log { get { if ((mLog == null) && (OnRequestLog != null)) { OnRequestLog(this, new EventArgs()); } return mLog; } set { mLog = value; } } /// /// Process GUID. /// public Guid ProcessGUID { get { if (ViewState["ProcessGUID"] == null) { ViewState["ProcessGUID"] = Guid.NewGuid(); } return ValidationHelper.GetGuid(ViewState["ProcessGUID"], Guid.NewGuid()); } set { ViewState["ProcessGUID"] = value; } } /// /// Asynchronous worker. /// public AsyncWorker Worker { get { if (mWorker == null) { string key = "AsyncWorker_" + ProcessGUID; mWorker = (AsyncWorker)mWorkers[key]; if (mWorker == null) { mWorker = new AsyncWorker { // Ensure process guid ProcessGUID = ProcessGUID }; mWorkers[key] = mWorker; } } return mWorker; } } /// /// True if the postback should occure after error. /// public bool PostbackOnError { get { return mPostbackOnError; } set { mPostbackOnError = value; } } /// /// Process parameter. /// public new object Parameter { get { return Worker.Parameter; } set { Worker.Parameter = value; } } /// /// Indicates if the logging is ascendant. /// public bool AscendantLog { get; set; } /// /// Indicates if the control should use the string from resource file. /// public bool UseFileStrings { get; set; } /// /// Gets the worker status. /// public AsyncWorkerStatusEnum Status { get { return Worker.Status; } } /// /// Maximum log length. (0 = unlimited) /// public int MaxLogLines { get; set; } /// /// Message panel where log is being displayed. /// public Panel LogPanel { get { return pnlAsync; } } /// /// Returns unique name of the instance, i.e. ServerName for multi-instance environments or MachineName in other cases. /// private string InstanceName { get { string serverName = WebSyncHelperClass.ServerName; return !String.IsNullOrEmpty(serverName) ? serverName : SqlHelperClass.MachineName; } } #endregion #region "Page events" protected override void OnLoad(EventArgs e) { base.OnLoad(e); // Register full postbacks btnCancel.Attributes.Add("style", "display: none;"); // Register full postbacks ControlsHelper.RegisterPostbackControl(this); ControlsHelper.RegisterPostbackControl(btnCancel); } protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); RenderScripts(false); } #endregion #region "Methods" /// /// Returns localized string. /// /// String to localize public override string GetString(string stringName) { if (UseFileStrings) { return ResHelper.GetFileString(stringName); } else { return base.GetString(stringName); } } /// /// Returns true if the worker for current control exists. /// protected bool WorkerExists() { if (ProcessGUID != Guid.Empty) { string key = "AsyncWorker_" + ProcessGUID; return (mWorkers[key] != null); } return false; } /// /// Runs the asynchronous process without any thread. /// public void RunAsync() { RenderScripts(false); } /// /// Runs the asynchronous action. /// /// Action to run /// Windows identity (windows user) public void RunAsync(AsyncAction action, WindowsIdentity wi) { RenderScripts(true); Worker.Stop(); Worker.Reset(); Worker.RunAsync(action, wi); } /// /// Stops the worker. /// public void Stop() { Worker.Stop(); } /// /// Registers scripts necessary. /// /// Determines whether to register scrips in any case protected void RenderScripts(bool force) { if (!RequestHelper.IsCallback() || force) { if (((Worker.Status == AsyncWorkerStatusEnum.Running) || (Worker.Status == AsyncWorkerStatusEnum.WaitForFinish)) || force) { string machineName = InstanceName; const int TIMEOUT = 200; // Prepare the scripts StringBuilder script = new StringBuilder(); // Initialize variables script.Append( @" var logText_", ClientID, @" = ''; var callBackParam_", ClientID, @" = ''; var machineName_", ClientID, " = '", machineName.ToLowerCSafe(), @"'; var asyncProcessFinished_", ClientID, @" = false; var timeout_", ClientID, @" = null; var asyncBusy = false;"); // Register function that repeatedly gets content script.Append(@" function AC_GetAsyncStatus_", ClientID, @"() { if (!asyncBusy) { asyncBusy = true; setTimeout('asyncBusy = false;', 2000); callBackParam_", ClientID, " = logText_", ClientID, ".length + '|' + machineName_", ClientID, @"; ", Page.ClientScript.GetCallbackEventReference(this, "callBackParam_" + ClientID, "AC_ReceiveAsyncStatus_" + ClientID, "logText_" + ClientID + ".length", false), @"; } if (asyncProcessFinished_", ClientID, @") { CancelAction_", ClientID, @"(false); return; } else { timeout = setTimeout(function() { AC_GetAsyncStatus_", ClientID, "(); }, ", TIMEOUT, @"); } }"); // Register closing script script.Append(@" function AC_SetClose_", ClientID, @"() { var cancelElem = document.getElementById('", btnCancel.ClientID, @"'); if (cancelElem != null) { cancelElem.value = '", GetString("General.Close"), @"'; } }"); // Register function that parses callback script.Append(@" function AC_ReceiveAsyncStatus_", ClientID, @"(rvalue, context) { asyncBusy = false; if (asyncProcessFinished_", ClientID, @") { return; } values = rvalue.split('|'); code = values[0]; var i = 1; var resultValue = ''; for (i = 1; i logText_", ClientID, @".length) { logText_", ClientID, @" = elem.innerHTML = messageText; } }"); } else { script.Append(@" elem.innerHTML = text; }"); } // Set timeout for getting content script.Append(@" timeout_", ClientID, " = setTimeout(function() {AC_GetAsyncStatus_", ClientID, "();}, ", TIMEOUT, ");"); ScriptHelper.RegisterStartupScript(this, typeof(string), "asyncScript" + ClientID, ScriptHelper.GetScript(script.ToString())); } // Register cancel script StringBuilder cancelScript = new StringBuilder(); cancelScript.Append( @"function CancelAction_", ClientID, @"(withPostback) { asyncProcessFinished_", ClientID, @" = true; if (withPostback) { ", Page.ClientScript.GetPostBackEventReference(btnCancel, null), @"; } else { var t = timeout_", ClientID, @"; if((t != 'undefined') && (t != null)) { clearTimeout(timeout_", ClientID, @"); } } }"); ScriptHelper.RegisterStartupScript(this, typeof(string), "cancelScript" + ClientID, ScriptHelper.GetScript(cancelScript.ToString())); } } public string GetCancelScript(bool withPostback) { return "CancelAction_" + ClientID + "(" + withPostback.ToString().ToLowerCSafe() + ");"; } #endregion #region "Callback handling" /// /// Raises the callback event. /// /// Event argument public void RaiseCallbackEvent(string eventArgument) { string[] args = eventArgument.Split('|'); int requestedLength = ValidationHelper.GetInteger(args[0], 0); mCallbackResult = string.Empty; if (InstanceName.EqualsCSafe(args[1], StringComparison.InvariantCultureIgnoreCase)) { if (WorkerExists()) { switch (Worker.Status) { case AsyncWorkerStatusEnum.Finished: case AsyncWorkerStatusEnum.WaitForFinish: // Allow worker to finish mCallbackResult = RESULT_FINISHED; break; case AsyncWorkerStatusEnum.Running: mCallbackResult = RESULT_RUNNING; break; case AsyncWorkerStatusEnum.Error: // Allow worker to finish mCallbackResult = RESULT_ERROR; break; case AsyncWorkerStatusEnum.Stopped: mCallbackResult = RESULT_STOPPED; break; } // Log only when worker is running if ((Worker.Status == AsyncWorkerStatusEnum.Running) && (!String.IsNullOrEmpty(Log))) { string log = Log; int logLength = log.Length; int trimStart = 0; int trimLength = 0; if (MaxLogLines > 0) { // Get position of the specified occurrence of new line tag int index = log.NthIndexOf("
", MaxLogLines, !AscendantLog); bool indexExists = (index > -1); if (AscendantLog) { // Select max. number of lines from the end of the log trimStart = indexExists ? index : 0; trimLength = indexExists ? (logLength - index) : logLength; } else { // Select max. number of lines from the begining of the log trimStart = 0; trimLength = indexExists ? index : logLength; } } else { // Correct the length in case of invalid value if (requestedLength > logLength) { requestedLength = logLength; } // Send only the part that is not present on the client machine trimStart = AscendantLog ? requestedLength : 0; trimLength = (logLength - requestedLength); } // Get the message within the specified bounds log = log.Substring(trimStart, trimLength); // Send the message to client mCallbackResult += "|" + log; } } else { mCallbackResult = RESULT_THREADLOST + "|" + GetString("AsyncControl.ThreadLost"); } } else { mCallbackResult = RESULT_RUNNING + "|"; } } /// /// Returns the result of a callback. /// public string GetCallbackResult() { return mCallbackResult; } #endregion #region "Events" /// /// Finished event handler. /// public event EventHandler OnFinished; /// /// Error event handler. /// public new event EventHandler OnError; /// /// Cancel event handler. /// public event EventHandler OnCancel; /// /// Error event handler. /// public event EventHandler OnRequestLog; /// /// Raises the finished event. /// /// Sender /// Event args public void RaiseFinished(object sender, EventArgs e) { if (OnFinished != null) { OnFinished(this, e); } } /// /// Raises the Error event. /// /// Sender /// Event args public void RaiseError(object sender, EventArgs e) { if (OnError != null) { OnError(this, e); } } /// /// Raises the Cancel event. /// /// Sender /// Event args public void RaiseCancel(object sender, EventArgs e) { if (OnCancel != null) { OnCancel(this, e); } } protected void btnCancel_Click(object sender, EventArgs e) { if (WorkerExists()) { if (Worker.Status == AsyncWorkerStatusEnum.Running) { // Stop worker Worker.Stop(); } } RaiseCancel(this, e); } #endregion #region "IPostbackEventHandler" /// /// Processes the postback event /// /// Event argument public void RaisePostBackEvent(string eventArgument) { var e = new EventArgs(); switch (eventArgument) { case "finished": RaiseFinished(this, e); break; case "error": RaiseError(this, e); break; } } #endregion }