using System; using System.Data; using System.Collections; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using CMS.CMSHelper; using CMS.GlobalHelper; using CMS.OnlineMarketing; using CMS.PortalControls; using CMS.PortalEngine; using CMS.SettingsProvider; using CMS.DocumentEngine; using CMS.WorkflowEngine; using TreeNode = CMS.DocumentEngine.TreeNode; using CMS.ExtendedControls; public partial class CMSModules_OnlineMarketing_Controls_Content_CombinationPanel : CMSAbstractPortalUserControl, ICallbackEventHandler { #region "Variables" private const string MVTCOMBINATION_ID = "MVTCombinationId"; private const string ITEM_ENABLED_CLASS = "DropDownItemEnabled"; private const string ITEM_DISABLED_CLASS = "DropDownItemDisabled"; private string callbackValue = string.Empty; private string cookieTestName = string.Empty; private ViewModeEnum viewMode = ViewModeEnum.Unknown; private CurrentUserInfo currentUser = null; /// /// Indicates whether processing should be stopped. /// private bool stopProcessing = false; #endregion #region "Methods" /// /// Init event handler. /// protected override void OnInit(EventArgs e) { currentUser = CMSContext.CurrentUser; // Set the selected combination (from cookie by default) MVTestInfo mvTestInfo = MVTestInfoProvider.GetRunningTest(CMSContext.CurrentAliasPath, CMSContext.CurrentSiteID, CMSContext.CurrentDocumentCulture.CultureCode); // Get the cookie name if (mvTestInfo != null) { // Get a cookie name for the mvt test cookieTestName = CookieName.GetMVTCookieName(mvTestInfo.MVTestName); } else { // Get a template cookie name (used just in CMSDesk when no test is running) cookieTestName = CookieName.GetNoMVTCookieName(CMSContext.CurrentDocument.GetUsedPageTemplateId()); } // Move cookies expiration to next 30 days HttpCookie cookieMVTTest = CookieHelper.GetExistingCookie(cookieTestName); if (cookieMVTTest != null) { CookieHelper.SetValue(cookieMVTTest.Name, cookieMVTTest.Value, cookieMVTTest.Path, DateTime.Now.AddDays(30), false); } base.OnInit(e); viewMode = CMSContext.ViewMode; // Check permissions if ((currentUser == null) || (!currentUser.IsAuthorizedPerResource("CMS.Design", "Design") && ((viewMode == ViewModeEnum.Design) || (viewMode == ViewModeEnum.DesignDisabled))) || (!currentUser.IsAuthorizedPerResource("CMS.MVTest", "Read"))) { stopProcessing = true; } } /// /// Handles the PreRender event of the Page control. /// protected void Page_PreRender(object sender, EventArgs e) { if (stopProcessing) { Visible = false; return; } // Show the warning panel when there is a running MVT test if ((viewMode != ViewModeEnum.Preview) && (ModuleCommands.OnlineMarketingContainsRunningMVTest(CMSContext.CurrentAliasPath, CMSContext.CurrentSiteID, CMSContext.CurrentPageInfo.DocumentCulture))) { plcRunningTestWarning.Visible = true; } // Setup the Update progress panel loading.ProgressHTML = "
" + CMSUpdateProgress.GetDefaultContent(Page) + "
"; loading.DisplayAfter = 200; if (RequestHelper.IsPostBack()) { // Reload the combination panel because one of the combination could have been removed ReloadData(true); } // Set the OnChange attribute => Save the variant slider configuration into a cookie and raise a postback combinationSelector.DropDownSelect.Attributes.Add("onchange", "SaveCombinationPanelSelection(); " + Page.ClientScript.GetPostBackEventReference(this, "combinationchanged") + "; return false;"); MVTCombinationInfo ci = null; if (CMSContext.CurrentDocument != null) { int templateId = CMSContext.CurrentDocument.GetUsedPageTemplateId(); // Get the combination name from cookie string combinationName = CookieHelper.GetValue(cookieTestName); if (string.IsNullOrEmpty(combinationName)) { // CombinationName is not defined, use the default combination for the page template ci = MVTCombinationInfoProvider.GetDefaultCombinationInfo(templateId); } else { // Keep current instance node TreeNode tn = CMSContext.CurrentDocument; // Use the defined combination ci = MVTCombinationInfoProvider.GetMVTCombinationInfo(tn.NodeAliasPath, combinationName, CMSContext.CurrentSiteName, tn.DocumentCulture); if (ci == null) { // Combination not found (can happen after deleting a variant), use the default combination for the page template ci = MVTCombinationInfoProvider.GetDefaultCombinationInfo(templateId); } } } // Show the combination panel only if there are any combinations for the document pnlMvtCombination.Enabled = combinationSelector.HasData; if (ci != null) { int combinationId = ci.MVTCombinationID; // Setup the combination panel values combinationSelector.DropDownSelect.SelectedValue = ci.MVTCombinationName.ToString(); chkEnabled.Checked = ci.MVTCombinationEnabled; txtCustomName.Text = ResHelper.LocalizeString(ci.MVTCombinationCustomName, currentUser.PreferredUICultureCode); // Create javascript variables of the combination panel. Used when changing combination by the variation slider/arrows StringBuilder combinationJSList = new StringBuilder(); combinationJSList.Append("var mvtCPselector = document.getElementById('" + combinationSelector.DropDownSelect.ClientID + "');"); combinationJSList.Append("var mvtCPenabled = document.getElementById('" + chkEnabled.ClientID + "');"); combinationJSList.Append("var mvtCPcustomName = document.getElementById('" + txtCustomName.ClientID + "');"); combinationJSList.Append("var mvtCPcurrentCombinationName = document.getElementById('" + hdnCurrentCombination.ClientID + "');"); // Move the update progress panel to the body element. This will fix the absolute/relative positioning issue. combinationJSList.Append("function InitUpdateProgress() { $j(document.body).prepend($j('#", loading.ClientID, "')); }"); // Generate the JS configuration array for the Edit and Design view modes only (modes where the variant sliders can be used) if ((viewMode == ViewModeEnum.Edit) || (viewMode == ViewModeEnum.EditDisabled) || (viewMode == ViewModeEnum.Design) || (viewMode == ViewModeEnum.DesignDisabled)) { // Get variants for the selected combination DataSet dsSelectedCombinationVariants = MVTVariantInfoProvider.GetMVTVariants(ci.MVTCombinationPageTemplateID, combinationId); #region "Generate javascript arrays used for changing the selected combination according to the selected variants" // List of compulsory combination variants (used for the combination JS array only). // For example: // Selected combination contains WidgetVariantID3 + ZoneVariantID5 and the user is in the Edit mode. // Therefore compulsory combination variants will be only containing the zone variant ID 5-> that means: only combinations with this compulsory variant will be proceed // This ensures a correct behavior of the combination panel when changing the variant sliders. combinationJSList.Append("var compulsoryCombinationVariants = ["); int compulsoryCombinationVariantsCounter = 0; // Fill the array 'compulsoryCombinationVariants' if (!DataHelper.DataSourceIsEmpty(dsSelectedCombinationVariants)) { // Edit mode if ((viewMode == ViewModeEnum.Edit) || (viewMode == ViewModeEnum.EditDisabled)) { foreach (DataRow row in dsSelectedCombinationVariants.Tables[0].Rows) { // Process web part and zones only if (ValidationHelper.GetInteger(row["MVTVariantDocumentID"], 0) == 0) { if (compulsoryCombinationVariantsCounter > 0) { combinationJSList.Append(","); } // Add the web part/zone to the JS array combinationJSList.Append(ValidationHelper.GetInteger(row["MVTVariantID"], 0)); compulsoryCombinationVariantsCounter++; } } } // Design mode else if ((viewMode == ViewModeEnum.Design) || (viewMode == ViewModeEnum.DesignDisabled)) { foreach (DataRow row in dsSelectedCombinationVariants.Tables[0].Rows) { // Process widgets only if (ValidationHelper.GetInteger(row["MVTVariantDocumentID"], 0) > 0) { if (compulsoryCombinationVariantsCounter > 0) { combinationJSList.Append(","); } // Add the widget to the JS array combinationJSList.Append(ValidationHelper.GetInteger(row["MVTVariantID"], 0)); compulsoryCombinationVariantsCounter++; } } } } combinationJSList.Append("];"); // combinationsArray - array containing configuration of each combination displayed in the combination panel. // This array is used after the user changes a variant slider and the new combination (selected by the combination panel) is to be calculated. combinationJSList.Append("var combinationsArray = ["); int combinationCount = 0; foreach (ListItem item in combinationSelector.DropDownSelect.Items) { // Get the combination object MVTCombinationInfo cObj = MVTCombinationInfoProvider.GetMVTCombinationInfo(combinationSelector.PageTemplateID, item.Value); if (cObj != null) { DataSet cVariants = MVTVariantInfoProvider.GetMVTVariants(cObj.MVTCombinationPageTemplateID, cObj.MVTCombinationID); if (combinationCount > 0) { combinationJSList.Append(","); } combinationJSList.Append("['"); combinationJSList.Append(cObj.MVTCombinationName); combinationJSList.Append("',"); combinationJSList.Append(cObj.MVTCombinationEnabled.ToString().ToLowerCSafe()); combinationJSList.Append(","); combinationJSList.Append(ScriptHelper.GetString(ResHelper.LocalizeString(cObj.MVTCombinationCustomName, currentUser.PreferredUICultureCode))); // Generate the unique variant IDs code (format: 155_158_180) - must be ordered by variantID combinationJSList.Append(",'"); if (!DataHelper.DataSourceIsEmpty(cVariants)) { int variantCount = 0; foreach (DataRow row in cVariants.Tables[0].Rows) { if (variantCount > 0) { combinationJSList.Append("_"); } combinationJSList.Append(ValidationHelper.GetString(row["MVTVariantID"], "0")); variantCount++; } } combinationJSList.Append("']"); } combinationCount++; } combinationJSList.Append("];"); #endregion // Temporary variables Guid instanceGuid = Guid.Empty; string variantGuid = string.Empty; string itemIdentifier = string.Empty; int itemVariantId = 0; bool itemIsZoneVariant = false; // Choose the correct variant from all rendered variants for a current web part (used in Content->Design/Edit page) if (!DataHelper.DataSourceIsEmpty(dsSelectedCombinationVariants)) { combinationJSList.Append("function SetCombinationVariants() {"); bool variantWasSet = false; // Process all the variants of the selected combination foreach (DataRow row in dsSelectedCombinationVariants.Tables[0].Rows) { itemIsZoneVariant = string.IsNullOrEmpty(ValidationHelper.GetString(row["MVTVariantInstanceGUID"], string.Empty)); itemVariantId = ValidationHelper.GetInteger(row["MVTVariantID"], 0); if (itemIsZoneVariant) { // Zone itemIdentifier = "Variant_Zone_" + HTMLHelper.HTMLEncode(ValidationHelper.GetString(row["MVTVariantZoneID"], string.Empty)); } else { // Web part/widget itemIdentifier = "Variant_WP_" + ValidationHelper.GetGuid(row["MVTVariantInstanceGUID"], Guid.Empty).ToString("N"); } // Set the appropriate variant combinationJSList.Append("SetVariant('" + itemIdentifier + "', " + itemVariantId + ");"); variantWasSet = true; } if (variantWasSet) { // Refresh the combination panel if any variant was set manually combinationJSList.Append("UpdateCombinationPanel();"); } combinationJSList.Append("}"); } } // Save the current combination id in javascript combinationJSList.Append("mvtCPcurrentCombinationName.value = '" + ci.MVTCombinationName + "';"); // Register the JS arrays and current variants ScriptHelper.RegisterStartupScript(this, typeof(string), "combinationJSList", ScriptHelper.GetScript(combinationJSList.ToString())); } // Display the "set as result" button when there any MVT variants in the page if ((currentUser != null) && (currentUser.IsAuthorizedPerResource("CMS.Design", "Design"))) { plcUseCombination.Visible = combinationSelector.DropDownSelect.Items.Count > 1; } } /// /// Creates child controls. /// protected override void CreateChildControls() { base.CreateChildControls(); if (!stopProcessing) { SetupControl(); } } /// /// Reloads the data. /// /// If set to true, reload the control even if the control has been already reloaded protected void ReloadData(bool forceLoad) { combinationSelector.ReloadData(forceLoad); } /// /// Sets the current combination as default /// protected void btnUseCombination_Click(object sender, EventArgs e) { if ((CMSContext.CurrentPageInfo != null)) { // Keep current page template instance PageInfo pi = CMSContext.CurrentPageInfo; // Set the selected combination (from cookie by default) MVTestInfo mvTestInfo = MVTestInfoProvider.GetRunningTest(CMSContext.CurrentAliasPath, CMSContext.CurrentSiteID, CMSContext.CurrentDocumentCulture.CultureCode); // Get combination from cookies string combinationName = CookieHelper.GetValue(cookieTestName); // Get the page template id int templateId = GetPageTemplateId(pi); // Get combination info MVTCombinationInfo currentCombination = MVTCombinationInfoProvider.GetMVTCombinationInfo(templateId, combinationName); // Get default combination info MVTCombinationInfo defaultCombination = MVTCombinationInfoProvider.GetDefaultCombinationInfo(templateId); // Check whether default and current combination exists if ((currentCombination != null) && (defaultCombination != null)) { // Do not save default combination if (currentCombination.MVTCombinationID != defaultCombination.MVTCombinationID) { // Copy the combination custom name defaultCombination.MVTCombinationCustomName = currentCombination.MVTCombinationCustomName; defaultCombination.Update(); // Tree provider instance TreeProvider tp = new TreeProvider(currentUser); // Documents - use the Preview view mode to ensure that only chosen variant will be rendered (Design mode renders all web part/zone variants and does not combine the instance with the variants) PageTemplateInstance instance = MVTestInfoProvider.CombineWithMVT(pi, pi.UsedPageTemplateInfo.TemplateInstance, currentCombination.MVTCombinationID, ViewModeEnum.Preview); CMSPortalManager.SaveTemplateChanges(pi, instance, WidgetZoneTypeEnum.None, ViewModeEnum.Design, tp); // Widgets - use the Preview view mode to ensure that only chosen variant will be rendered (Edit mode renders all widget variants and does not combine the instance with the variants) instance = MVTestInfoProvider.CombineWithMVT(pi, pi.DocumentTemplateInstance, currentCombination.MVTCombinationID, ViewModeEnum.Preview); CMSPortalManager.SaveTemplateChanges(pi, instance, WidgetZoneTypeEnum.Editor, ViewModeEnum.Edit, tp); } // Remove all variants InfoDataSet variants = MVTVariantInfoProvider.GetMVTVariants("MVTVariantPageTemplateID = " + templateId, null); if (!DataHelper.DataSourceIsEmpty(variants)) { foreach (MVTVariantInfo info in variants) { MVTVariantInfoProvider.DeleteMVTVariantInfo(info); } } // Clear cached template info pi.UsedPageTemplateInfo.Update(); // Redirect to the same page to update the current view URLHelper.Redirect(URLHelper.CurrentURL); } } } #endregion #region "Private methods" /// /// Setups this control. /// private void SetupControl() { // Check permissions if ((currentUser == null) || (!currentUser.IsAuthorizedPerResource("CMS.MVTest", "Manage"))) { plcEditCombination.Visible = false; } // Set callback events btnChange.OnClientClick = Page.ClientScript.GetCallbackEventReference(this, "MVTSetCustomName()", "MVTReceiveServerData", null) + ";return false;"; chkEnabled.Attributes.Add("onclick", Page.ClientScript.GetCallbackEventReference(this, "MVTSetEnabled()", "MVTReceiveServerData", null) + ";return true;"); btnUseCombination.OnClientClick = "if (!confirm('" + ResHelper.GetString("mvt.usecombinationconfirm") + "')) { return false; }"; btnUseCombination.Click += new EventHandler(btnUseCombination_Click); if ((CMSContext.CurrentPageInfo != null) && (CMSContext.CurrentPageInfo.UsedPageTemplateInfo != null)) { // Get the current document ID int documentId = 0; if (CMSContext.CurrentDocument != null) { documentId = CMSContext.CurrentDocument.DocumentID; } // Display the combinations only for the current document combinationSelector.DocumentID = documentId; combinationSelector.PageTemplateID = CMSContext.CurrentPageInfo.UsedPageTemplateInfo.PageTemplateId; } // Setup the localized strings string prefferedUICode = currentUser.PreferredUICultureCode; lblCombination.Text = ResHelper.GetString("mvtcombination.name", prefferedUICode); chkEnabled.Text = ResHelper.GetString("general.enabled", prefferedUICode); lblCustomName.Text = ResHelper.GetString("mvtcombination.customName", prefferedUICode); btnChange.Text = ResHelper.GetString("general.change", prefferedUICode); lblSaved.Text = ResHelper.GetString("mvtvariant.customnamesaved", prefferedUICode); btnUseCombination.Text = ResHelper.GetString("mvt.usecombination", prefferedUICode); lblWarning.Text = ResHelper.GetString("mvtest.runningtestwarning", prefferedUICode); // Hide label "Saved" lblSaved.Style.Add("display", "none"); // Setup the combination uniselector combinationSelector.UniSelector.EnabledColumnName = "MVTCombinationEnabled"; combinationSelector.UniSelector.OrderBy = "MVTCombinationName"; combinationSelector.DropDownSelect.CssClass = "MVTCombinationDDL"; combinationSelector.UniSelector.AllowEmpty = false; // Ensure a full postback for the uniselector (this is set manually due to the Update panel in the uniselector) ScriptManager scr = ScriptManager.GetCurrent(Page); scr.RegisterPostBackControl(combinationSelector.DropDownSelect); // Register page javascript ScriptHelper.RegisterClientScriptBlock(Page, typeof(string), "MVTCombinationPanel", ScriptHelper.GetScript(@" function MVTSetCustomName() { var ddlCombinations = document.getElementById('" + combinationSelector.DropDownSelect.ClientID + @"'); var combinationId = ddlCombinations.options[ddlCombinations.selectedIndex].value; var txtCustomName = document.getElementById('" + txtCustomName.ClientID + @"'); return combinationId + ';cname;' + txtCustomName.value; } function MVTSetEnabled() { var ddlCombinations = document.getElementById('" + combinationSelector.DropDownSelect.ClientID + @"'); var combinationId = ddlCombinations.options[ddlCombinations.selectedIndex].value; var chkEnabled = document.getElementById('" + chkEnabled.ClientID + @"'); return combinationId + ';enabled;' + chkEnabled.checked; } function MVTReceiveServerData(value) { var ddlCombinations = document.getElementById('" + combinationSelector.DropDownSelect.ClientID + @"'); var listitem = ddlCombinations.options[ddlCombinations.selectedIndex]; if (value.length > 0) { /* Custom name changed */ listitem.text = value; /* Update the combinationsArray array */ combinationsArray[ddlCombinations.selectedIndex][2] = document.getElementById('" + txtCustomName.ClientID + @"').value; document.getElementById('" + lblSaved.ClientID + @"').style.display = 'block'; setTimeout(""document.getElementById('" + lblSaved.ClientID + @"').style.display = 'none'"", 4000); } else { /* Enabled changed */ var chkEnabled = document.getElementById('" + chkEnabled.ClientID + @"'); if (listitem != null) { if (chkEnabled.checked) { listitem.className = '" + ITEM_ENABLED_CLASS + @"'; } else { listitem.className = '" + ITEM_DISABLED_CLASS + @"'; } } /* Update the combinationsArray array */ combinationsArray[ddlCombinations.selectedIndex][1] = chkEnabled.checked; } } function SaveCombinationPanelSelection() { var ddlCombinations = document.getElementById('" + combinationSelector.DropDownSelect.ClientID + @"'); var listitem = ddlCombinations.options[ddlCombinations.selectedIndex]; var combinationName = listitem.value; if (combinationName.length > 0) { $j.cookie('" + cookieTestName + @"', combinationName, { expires: 7, path: '/' }); } else { $j.cookie('" + cookieTestName + @"', null); } } ")); ReloadData(false); } /// /// Gets the page template id of the given page info (or template id of the parent page info if inherited). /// /// The PageInfo private int GetPageTemplateId(PageInfo pi) { int templateId = 0; if (pi != null) { if (!pi.NodeInheritPageTemplate) { templateId = pi.GetUsedPageTemplateId(); } else if (pi.UsedPageTemplateInfo != null) { templateId = pi.UsedPageTemplateInfo.PageTemplateId; } } return templateId; } #endregion #region "Callback handling" /// /// Callback event handler. /// /// Callback argument public void RaiseCallbackEvent(string argument) { // Check permissions if ((currentUser == null) || (!currentUser.IsAuthorizedPerResource("CMS.MVTest", "Manage")) || stopProcessing) { return; } // Get arguments if (!string.IsNullOrEmpty(argument)) { string[] args = argument.Split(new char[] { ';' }, 3); if (args.Length == 3) { string combinationName = ValidationHelper.GetString(args[0], string.Empty); string action = args[1].ToLowerCSafe(); string newValue = args[2]; // Get the combination info MVTCombinationInfo mvtcInfo = MVTCombinationInfoProvider.GetMVTCombinationInfo(combinationSelector.PageTemplateID, combinationName); if (mvtcInfo != null) { switch (action) { case "cname": // Custom name changed mvtcInfo.MVTCombinationCustomName = newValue; if (string.IsNullOrEmpty(newValue)) { newValue = mvtcInfo.MVTCombinationName; } // return the new value (when newValue=="", then return combination code name) callbackValue = newValue; break; case "enabled": // combination Enabled changed mvtcInfo.MVTCombinationEnabledOriginal = mvtcInfo.MVTCombinationEnabled; mvtcInfo.MVTCombinationEnabled = ValidationHelper.GetBoolean(newValue, true); callbackValue = string.Empty; break; default: return; } MVTCombinationInfoProvider.SetMVTCombinationInfo(mvtcInfo); // Synchronize widget variants if enabling combination if ((mvtcInfo.MVTCombinationDocumentID > 0) || (!mvtcInfo.MVTCombinationEnabledOriginal && mvtcInfo.MVTCombinationEnabled )) { // Log synchronization TreeProvider tree = new TreeProvider(currentUser); TreeNode node = tree.SelectSingleDocument(mvtcInfo.MVTCombinationDocumentID); DocumentSynchronizationHelper.LogDocumentChange(node, TaskTypeEnum.UpdateDocument, tree); } } } } } /// /// Callback result retrieving handler. /// public string GetCallbackResult() { // Return combination custom name or combination code name return callbackValue; } #endregion }