using System;
using System.Collections.Generic;
using System.Data;
using System.Collections;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web.UI.WebControls;
using CMS.CMSHelper;
using CMS.DataEngine;
using CMS.EventLog;
using CMS.GlobalHelper;
using CMS.SettingsProvider;
using CMS.DocumentEngine;
using CMS.UIControls;
using CMS.WorkflowEngine;
using TreeNode = CMS.DocumentEngine.TreeNode;
public partial class CMSModules_Workflows_Workflow_Documents : CMSWorkflowPage
{
#region "Private variables & enumerations"
private CurrentUserInfo currentUser = null;
private string currentCulture = CultureHelper.DefaultUICulture;
private static readonly Hashtable mInfos = new Hashtable();
private const string GET_WORKFLOW_DOCS_WHERE = "DocumentWorkflowStepID IN (SELECT StepID FROM CMS_WorkflowStep WHERE StepWorkflowID = {0})";
private enum Action
{
SelectAction = 0,
PublishAndFinish = 1,
RemoveWorkflow = 2
}
protected enum What
{
SelectedDocuments = 0,
AllDocuments = 1
}
private What currentWhat = default(What);
#endregion
#region "Properties"
///
/// Current log context.
///
public LogContext CurrentLog
{
get
{
return EnsureLog();
}
}
///
/// Current Error.
///
private string CurrentError
{
get
{
return ValidationHelper.GetString(mInfos["WorkflowError_" + ctlAsync.ProcessGUID], string.Empty);
}
set
{
mInfos["WorkflowError_" + ctlAsync.ProcessGUID] = value;
}
}
///
/// Current Info.
///
private string CurrentInfo
{
get
{
return ValidationHelper.GetString(mInfos["WorkflowInfo_" + ctlAsync.ProcessGUID], string.Empty);
}
set
{
mInfos["WorkflowInfo_" + ctlAsync.ProcessGUID] = value;
}
}
///
/// Gets or sets the cancel string.
///
public string CanceledString
{
get
{
return ValidationHelper.GetString(mInfos["CanceledString_" + ctlAsync.ProcessGUID], string.Empty);
}
set
{
mInfos["CanceledString_" + ctlAsync.ProcessGUID] = value;
}
}
#endregion
#region "Page events"
protected void Page_Load(object sender, EventArgs e)
{
currentUser = CMSContext.CurrentUser;
currentCulture = CultureHelper.PreferredUICulture;
// Initialize events
ctlAsync.OnFinished += ctlAsync_OnFinished;
ctlAsync.OnError += ctlAsync_OnError;
ctlAsync.OnRequestLog += ctlAsync_OnRequestLog;
ctlAsync.OnCancel += ctlAsync_OnCancel;
if (!RequestHelper.IsCallback())
{
// Set visibility of panels
pnlContent.Visible = true;
pnlLog.Visible = false;
// Initialize unigrid
docElem.ZeroRowsText = GetString(filterDocuments.FilterIsSet ? "unigrid.filteredzerorowstext" : "workflowdocuments.nodata");
docElem.UniGrid.OnAfterDataReload += UniGrid_OnAfterDataReload;
docElem.UniGrid.OnBeforeDataReload += UniGrid_OnBeforeDataReload;
docElem.Tree = Tree;
// Create action script
StringBuilder actionScript = new StringBuilder();
actionScript.AppendLine("function PerformAction(selectionFunction, selectionField, dropId)");
actionScript.AppendLine("{");
actionScript.AppendLine(" var selectionFieldElem = document.getElementById(selectionField);");
actionScript.AppendLine(" var label = document.getElementById('" + lblValidation.ClientID + "');");
actionScript.AppendLine(" var items = selectionFieldElem.value;");
actionScript.AppendLine(" var whatDrp = document.getElementById('" + drpWhat.ClientID + "');");
actionScript.AppendLine(" var action = document.getElementById(dropId).value;");
actionScript.AppendLine(" if (action == '" + (int)Action.SelectAction + "')");
actionScript.AppendLine(" {");
actionScript.AppendLine(" label.innerHTML = '" + GetString("massaction.selectsomeaction") + "';");
actionScript.AppendLine(" return false;");
actionScript.AppendLine(" }");
actionScript.AppendLine(" if(!eval(selectionFunction) || whatDrp.value == '" + (int)What.AllDocuments + "')");
actionScript.AppendLine(" {");
actionScript.AppendLine(" var confirmed = false;");
actionScript.AppendLine(" var confMessage = '';");
actionScript.AppendLine(" switch(action)");
actionScript.AppendLine(" {");
actionScript.AppendLine(" case '" + (int)Action.PublishAndFinish + "':");
actionScript.AppendLine(" confMessage = '" + GetString("workflowdocuments.confrimpublish") + "';");
actionScript.AppendLine(" break;");
actionScript.AppendLine(" case '" + (int)Action.RemoveWorkflow + "':");
actionScript.AppendLine(" confMessage = '" + GetString("workflowdocuments.confirmremove") + "';");
actionScript.AppendLine(" break;");
actionScript.AppendLine(" }");
actionScript.AppendLine(" return confirm(confMessage);");
actionScript.AppendLine(" }");
actionScript.AppendLine(" else");
actionScript.AppendLine(" {");
actionScript.AppendLine(" label.innerHTML = '" + GetString("documents.selectdocuments") + "';");
actionScript.AppendLine(" return false;");
actionScript.AppendLine(" }");
actionScript.AppendLine("}");
ScriptHelper.RegisterClientScriptBlock(this, typeof(string), "actionScript", ScriptHelper.GetScript(actionScript.ToString()));
// Add action to button
btnOk.OnClientClick = "return PerformAction('" + docElem.UniGrid.GetCheckSelectionScript() + "','" + docElem.UniGrid.GetSelectionFieldClientID() + "','" + drpAction.ClientID + "');";
btnCancel.Text = GetString("general.cancel");
btnCancel.Attributes.Add("onclick", ctlAsync.GetCancelScript(true) + "return false;");
// Initialize dropdown list with actions
if (!RequestHelper.IsPostBack())
{
if (currentUser.IsGlobalAdministrator || currentUser.IsAuthorizedPerResource("CMS.Content", "manageworkflow"))
{
drpAction.Items.Add(new ListItem(ResHelper.GetString("general." + Action.SelectAction), Convert.ToInt32(Action.SelectAction).ToString()));
drpAction.Items.Add(new ListItem(ResHelper.GetString("workflowdocuments." + Action.PublishAndFinish), Convert.ToInt32(Action.PublishAndFinish).ToString()));
drpAction.Items.Add(new ListItem(ResHelper.GetString("workflowdocuments." + Action.RemoveWorkflow), Convert.ToInt32(Action.RemoveWorkflow).ToString()));
}
drpWhat.Items.Add(new ListItem(ResHelper.GetString("contentlisting." + What.SelectedDocuments), Convert.ToInt32(What.SelectedDocuments).ToString()));
drpWhat.Items.Add(new ListItem(ResHelper.GetString("contentlisting." + What.AllDocuments), Convert.ToInt32(What.AllDocuments).ToString()));
}
}
docElem.SiteName = filterDocuments.SelectedSite;
}
#endregion
#region "Other events"
protected void UniGrid_OnBeforeDataReload()
{
string where = string.Format(GET_WORKFLOW_DOCS_WHERE, WorkflowId);
where = SqlHelperClass.AddWhereCondition(where, filterDocuments.WhereCondition);
docElem.UniGrid.WhereCondition = SqlHelperClass.AddWhereCondition(docElem.UniGrid.WhereCondition, where);
}
protected void UniGrid_OnAfterDataReload()
{
plcFilter.Visible = docElem.UniGrid.DisplayExternalFilter(filterDocuments.FilterIsSet);
pnlFooter.Visible = !DataHelper.DataSourceIsEmpty(docElem.UniGrid.GridView.DataSource);
}
#endregion
#region "Button handling"
protected void btnOk_OnClick(object sender, EventArgs e)
{
pnlLog.Visible = true;
pnlContent.Visible = false;
CurrentError = string.Empty;
EnsureLog();
int actionValue = ValidationHelper.GetInteger(drpAction.SelectedValue, 0);
Action action = (Action)actionValue;
int whatValue = ValidationHelper.GetInteger(drpWhat.SelectedValue, 0);
currentWhat = (What)whatValue;
ctlAsync.Parameter = GetCurrentWhere();
switch (action)
{
case Action.PublishAndFinish:
// Publish and finish workflow
titleElemAsync.TitleText = GetString("content.publishingdocuments");
titleElemAsync.TitleImage = GetImageUrl("CMSModules/CMS_Workflows/publish.png");
ctlAsync.RunAsync(PublishAndFinish, WindowsIdentity.GetCurrent());
break;
case Action.RemoveWorkflow:
// Remove workflow
titleElemAsync.TitleText = GetString("workflowdocuments.removingwf");
titleElemAsync.TitleImage = GetImageUrl("CMSModules/CMS_Workflows/removeworkflow.png");
ctlAsync.RunAsync(RemoveWorkflow, WindowsIdentity.GetCurrent());
break;
}
}
#endregion
#region "Thread methods"
private void PublishAndFinish(object parameter)
{
TreeNode node = null;
Tree.AllowAsyncActions = false;
CanceledString = ResHelper.GetString("content.publishcanceled", currentCulture);
try
{
// Begin log
AddLog(ResHelper.GetString("content.preparingdocuments", currentCulture));
string where = parameter as string;
// Get the documents
DataSet documents = GetDocumentsToProcess(where);
if (!DataHelper.DataSourceIsEmpty(documents))
{
// Create instance of workflow manager class
WorkflowManager wm = WorkflowManager.GetInstance(Tree);
// Begin publishing
AddLog(ResHelper.GetString("content.publishingdocuments", currentCulture));
foreach (DataTable classTable in documents.Tables)
{
foreach (DataRow nodeRow in classTable.Rows)
{
// Get the current document
string className = ValidationHelper.GetString(nodeRow["ClassName"], string.Empty);
string aliasPath = ValidationHelper.GetString(nodeRow["NodeAliasPath"], string.Empty);
string docCulture = ValidationHelper.GetString(nodeRow["DocumentCulture"], string.Empty);
string siteName = ValidationHelper.GetString(nodeRow["SiteName"], string.Empty);
node = DocumentHelper.GetDocument(siteName, aliasPath, docCulture, false, className, null, null, -1, false, null, Tree);
// Publish document
if (Publish(node, wm))
{
return;
}
}
}
CurrentInfo = GetString("workflowdocuments.publishcomplete");
}
else
{
AddError(ResHelper.GetString("content.nothingtopublish", currentCulture));
}
}
catch (ThreadAbortException ex)
{
string state = ValidationHelper.GetString(ex.ExceptionState, string.Empty);
if (state == CMSThread.ABORT_REASON_STOP)
{
// When canceled
CurrentInfo = CanceledString;
}
else
{
int siteId = (node != null) ? node.NodeSiteID : CMSContext.CurrentSiteID;
// Log error
LogExceptionToEventLog("PUBLISHDOC", "content.publishfailed", ex, siteId);
}
}
catch (Exception ex)
{
int siteId = (node != null) ? node.NodeSiteID : CMSContext.CurrentSiteID;
// Log error
LogExceptionToEventLog("PUBLISHDOC", "content.publishfailed", ex, siteId);
}
}
private void RemoveWorkflow(object parameter)
{
VersionManager verMan = VersionManager.GetInstance(Tree);
TreeNode node = null;
// Custom logging
Tree.LogEvents = false;
Tree.AllowAsyncActions = false;
CanceledString = ResHelper.GetString("workflowdocuments.removingcanceled", currentCulture);
try
{
// Begin log
AddLog(ResHelper.GetString("content.preparingdocuments", currentCulture));
string where = parameter as string;
// Get the documents
DataSet documents = GetDocumentsToProcess(where);
if (!DataHelper.DataSourceIsEmpty(documents))
{
// Begin log
AddLog(ResHelper.GetString("workflowdocuments.removingwf", currentCulture));
foreach (DataTable classTable in documents.Tables)
{
foreach (DataRow nodeRow in classTable.Rows)
{
// Get the current document
string className = ValidationHelper.GetString(nodeRow["ClassName"], string.Empty);
string aliasPath = ValidationHelper.GetString(nodeRow["NodeAliasPath"], string.Empty);
string docCulture = ValidationHelper.GetString(nodeRow["DocumentCulture"], string.Empty);
string siteName = ValidationHelper.GetString(nodeRow["SiteName"], string.Empty);
// Get published version
node = Tree.SelectSingleNode(siteName, aliasPath, docCulture, false, className, false);
string encodedAliasPath = HTMLHelper.HTMLEncode(ValidationHelper.GetString(aliasPath, string.Empty) + " (" + node.GetValue("DocumentCulture") + ")");
// Destroy document history
verMan.DestroyDocumentHistory(node.DocumentID);
// Clear workflow
DocumentHelper.ClearWorkflowInformation(node);
node.Update();
// Add log record
AddLog(encodedAliasPath);
// Add record to eventlog
LogContext.LogEvent(EventLogProvider.EVENT_TYPE_INFORMATION, DateTime.Now, "Content", "REMOVEDOCWORKFLOW", currentUser.UserID,
currentUser.UserName, node.NodeID, node.GetDocumentName(),
HTTPHelper.UserHostAddress, string.Format(GetString("workflowdocuments.removeworkflowsuccess"), encodedAliasPath),
node.NodeSiteID, HTTPHelper.GetAbsoluteUri(), HTTPHelper.MachineName, HTTPHelper.GetUrlReferrer(), HTTPHelper.GetUserAgent());
}
}
CurrentInfo = GetString("workflowdocuments.removecomplete");
}
else
{
AddError(ResHelper.GetString("workflowdocuments.nodocumentstoclear", currentCulture));
}
}
catch (ThreadAbortException ex)
{
string state = ValidationHelper.GetString(ex.ExceptionState, string.Empty);
if (state == CMSThread.ABORT_REASON_STOP)
{
// When canceled
CurrentInfo = CanceledString;
}
else
{
int siteId = (node != null) ? node.NodeSiteID : CMSContext.CurrentSiteID;
// Log error
LogExceptionToEventLog("REMOVEDOCWORKFLOW", "workflowdocuments.removefailed", ex, siteId);
}
}
catch (Exception ex)
{
int siteId = (node != null) ? node.NodeSiteID : CMSContext.CurrentSiteID;
// Log error
LogExceptionToEventLog("REMOVEDOCWORKFLOW", "workflowdocuments.removefailed", ex, siteId);
}
}
#endregion
#region "Private methods"
///
/// Gets a set of documents to be processed.
///
/// Set of documents based on where condition and other settings
private DataSet GetDocumentsToProcess(string where)
{
string columns = SqlHelperClass.MergeColumns(TreeProvider.SELECTNODES_REQUIRED_COLUMNS, "NodeAliasPath, ClassName, DocumentCulture, SiteName");
return DocumentHelper.GetDocuments(TreeProvider.ALL_SITES, "/%", TreeProvider.ALL_CULTURES, false, TreeProvider.ALL_CLASSNAMES, where, "NodeAliasPath DESC", -1, false, -1, columns, Tree);
}
///
/// Gets current where condition.
///
/// Where condition determining documents to process
private string GetCurrentWhere()
{
List documentIds = docElem.UniGrid.SelectedItems;
// Prepare the where condition
string where = string.Empty;
if (currentWhat == What.SelectedDocuments)
{
// Get where for selected documents
where = "DocumentID IN (";
foreach (string id in documentIds)
{
string documentId = ValidationHelper.GetString(id, string.Empty);
where += SqlHelperClass.GetSafeQueryString(documentId, false) + ",";
}
where = where.TrimEnd(',') + ")";
}
else
{
if (!string.IsNullOrEmpty(filterDocuments.WhereCondition))
{
// Add filter condition
where = SqlHelperClass.AddWhereCondition(where, filterDocuments.WhereCondition);
}
// Select documents only under current workflow
where = SqlHelperClass.AddWhereCondition(where, string.Format(GET_WORKFLOW_DOCS_WHERE, WorkflowId));
}
return where;
}
///
/// When exception occurs, log it to event log.
///
/// Code of event
/// Message to log to asynchronous log
/// Exception to log
/// ID of site
private void LogExceptionToEventLog(string eventCode, string errorTitle, Exception ex, int siteId)
{
AddError(ResHelper.GetString(errorTitle, currentCulture) + ": " + ex.Message);
LogContext.LogEvent(EventLogProvider.EVENT_TYPE_ERROR, DateTime.Now, "Content", eventCode, currentUser.UserID,
currentUser.UserName, 0, null,
HTTPHelper.UserHostAddress, EventLogProvider.GetExceptionLogMessage(ex),
siteId, HTTPHelper.GetAbsoluteUri(), HTTPHelper.MachineName, HTTPHelper.GetUrlReferrer(), HTTPHelper.GetUserAgent());
}
///
/// Publishes document.
///
/// Node to publish
/// Workflow manager
/// Whether node is already published
private bool Publish(TreeNode node, WorkflowManager wm)
{
string pathCulture = HTMLHelper.HTMLEncode(node.NodeAliasPath + " (" + node.DocumentCulture + ")");
WorkflowStepInfo currentStep = wm.GetStepInfo(node);
bool alreadyPublished = (currentStep == null) || currentStep.StepIsPublished;
if (!alreadyPublished)
{
using (CMSActionContext ctx = new CMSActionContext() { LogEvents = false })
{
// Remove possible checkout
if (node.DocumentCheckedOutByUserID > 0)
{
TreeProvider.ClearCheckoutInformation(node);
node.Update();
}
}
// Publish document
currentStep = wm.PublishDocument(node, null);
}
// Document is already published, check if still under workflow
if (alreadyPublished && (currentStep != null) && currentStep.StepIsPublished)
{
WorkflowScopeInfo wsi = wm.GetNodeWorkflowScope(node);
if (wsi == null)
{
DocumentHelper.ClearWorkflowInformation(node);
VersionManager vm = VersionManager.GetInstance(node.TreeProvider);
vm.RemoveWorkflow(node);
}
}
// Document already published
if (alreadyPublished)
{
AddLog(string.Format(ResHelper.GetString("content.publishedalready"), pathCulture));
}
else if ((currentStep == null) || !currentStep.StepIsPublished)
{
AddError(string.Format(ResHelper.GetString("content.PublishWasApproved"), pathCulture));
return true;
}
else
{
// Add log record
AddLog(pathCulture);
}
return false;
}
private void HandlePossibleError()
{
if (!string.IsNullOrEmpty(CurrentError))
{
ShowError(CurrentError);
}
if (!string.IsNullOrEmpty(CurrentInfo))
{
ShowConfirmation(CurrentInfo);
}
// Clear selection
docElem.UniGrid.ResetSelection();
CurrentLog.Close();
}
///
/// Ensures the logging context.
///
protected LogContext EnsureLog()
{
LogContext log = LogContext.EnsureLog(ctlAsync.ProcessGUID);
log.Reversed = true;
log.LineSeparator = "
";
return log;
}
///
/// Adds the log information.
///
/// New log information
protected void AddLog(string newLog)
{
EnsureLog();
LogContext.AppendLine(newLog);
}
///
/// Adds the error to collection of errors.
///
/// Error message
protected void AddError(string error)
{
AddLog(error);
CurrentError = (error + "
" + CurrentError);
}
#endregion
#region "Handling async thread"
private void ctlAsync_OnCancel(object sender, EventArgs e)
{
CurrentInfo = CanceledString;
ltlScript.Text += ScriptHelper.GetScript("var __pendingCallbacks = new Array();");
HandlePossibleError();
}
private void ctlAsync_OnRequestLog(object sender, EventArgs e)
{
ctlAsync.Log = CurrentLog.Log;
}
private void ctlAsync_OnError(object sender, EventArgs e)
{
HandlePossibleError();
}
private void ctlAsync_OnFinished(object sender, EventArgs e)
{
HandlePossibleError();
}
#endregion
}