HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/MBoogaard/oosting-horseriding.com/wwwroot/App_Code/CMS/UpgradeProcedure.txt
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Script.Serialization;
using System.Xml;
using System.Data.SqlClient;

using CMS.Base;
using CMS.CMSImportExport;
using CMS.Core;
using CMS.DataEngine;
using CMS.DocumentEngine;
using CMS.EventLog;
using CMS.FormEngine;
using CMS.Globalization;
using CMS.Helpers;
using CMS.IO;
using CMS.MacroEngine;
using CMS.Membership;
using CMS.Modules;
using CMS.PortalEngine;
using CMS.Search;
using CMS.Synchronization;
using CMS.URLRewritingEngine;
using CMS.WebAnalytics;
using CMS.WorkflowEngine;
using CMS.Taxonomy;
using System.Xml.Serialization;


#region "Code to bind to the ApplicationEvents using CMSModuleLoader"

/// <summary>
/// Upgrade loader
/// </summary>
[UpgradeLoader]
public partial class CMSModuleLoader
{
    /// <summary>
    /// Module registration
    /// </summary>
    private class UpgradeLoader : CMSLoaderAttribute
    {
        /// <summary>
        /// Initializes the module
        /// </summary>
        public override void PreInit()
        {
            ApplicationEvents.UpdateData.Execute += Update;
        }


        /// <summary>
        /// Updates the application data to a newer version if necessary
        /// </summary>
        private void Update(object sender, EventArgs eventArgs)
        {
            UpgradeProcedure.Update();
        }
    }
}

#endregion

/// <summary>
/// Class carrying the code to perform the upgrade procedure.
/// </summary>
public static class UpgradeProcedure
{
    #region "Variables"

    private const string FORM_DEFINITION_TABLE = "Temp_FormDefinition";
    private const string FORM_DEFINITION_NAME_COLUMN = "ObjectName";
    private const string FORM_DEFINITION_VALUE_COLUMN = "FormDefinition";

    // Path to the upgrade package
    private static string mUpgradePackagePath;
    private static string mWebsitePath;
    private static string mEventLogSource;

    #endregion


    #region "Properties"

    /// <summary>
    /// Gets the source text for event log records generated by upgrade actions
    /// </summary>
    private static string EventLogSource
    {
        get
        {
            return mEventLogSource ?? (mEventLogSource = string.Format("Upgrade to {0}", CMSVersion.MainVersion));
        }
    }

    #endregion


    #region "Main update method"

    /// <summary>
    /// Runs the update procedure.
    /// </summary>
    public static void Update()
    {
        if (DatabaseHelper.IsDatabaseAvailable && SystemContext.IsCMSRunningAsMainApplication)
        {
            try
            {
                string version = SettingsKeyInfoProvider.GetValue("CMSDataVersion");
                switch (version.ToLowerCSafe())
                {
                    case "8.2":
                        using (var context = new CMSActionContext())
                        {
                            context.LogLicenseWarnings = false;

                            UpgradeApplication(Upgrade82To90, "9.0", "Upgrade_82_90.zip");
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                EventLogProvider.LogException(EventLogSource, "UPGRADE", ex);
            }
        }
    }

    #endregion


    #region "General purpose - all versions methods"

    private static void UpgradeApplication(Func<bool> versionSpecificMethod, string newVersion, string packageName)
    {
        // Increase the timeout for upgrade request due to expensive operations like macro signing and conversion (needed for large DBs)
        HttpContext.Current.Server.ScriptTimeout = 14400;

        EventLogProvider.LogInformation(EventLogSource, "START");

        // Set the path to the upgrade package (this has to be done here, not in the Import method, because it's an async procedure without HttpContext)
        mUpgradePackagePath = HttpContext.Current.Server.MapPath("~/CMSSiteUtils/Import/" + packageName);
        mWebsitePath = HttpContext.Current.Server.MapPath("~/");

        var dtm = new TableManager(null);
        using (var context = new CMSActionContext())
        {
            context.DisableLogging();
            context.CreateVersion = false;
            context.LogIntegration = false;

            if (dtm.TableExists(FORM_DEFINITION_TABLE))
            {
                UpdateClasses();
                UpdateAlternativeForms();
                DropTempDefinitionTable(dtm);
            }
        }

        // Update all views
        dtm.RefreshDocumentViews();
        RefreshCustomViews(dtm);

        // Set data version
        SettingsKeyInfoProvider.SetGlobalValue("CMSDataVersion", newVersion);
        SettingsKeyInfoProvider.SetGlobalValue("CMSDBVersion", newVersion);

        // Clear hashtables
        ModuleManager.ClearHashtables();

        // Clear the cache
        CacheHelper.ClearCache(null, true);

        // Drop the routes
        CMSDocumentRouteHelper.DropAllRoutes();

        // Call version specific operations
        if (versionSpecificMethod != null)
        {
            using (var context = new CMSActionContext())
            {
                context.DisableLogging();
                context.CreateVersion = false;
                context.LogIntegration = false;

                versionSpecificMethod.Invoke();
            }
        }

        // Import upgrade package with webparts, widgets...
        UpgradeImportPackage();

        // Run hotfix SQL scripts
        RunHotfixSQL();

        RefreshMacroSignatures();

        EventLogProvider.LogInformation(EventLogSource, "FINISH");
    }


    /// <summary>
    /// Refreshes all custom views.
    /// </summary>
    private static void RefreshCustomViews(TableManager tm)
    {
        tm.RefreshView("View_CMS_User");
        tm.RefreshView("View_Community_Member");

        tm.RefreshView("View_NewsletterSubscriberUserRole_Joined");

        tm.RefreshView("View_Community_Group");

        tm.RefreshView("View_Community_Friend_Friends");
        tm.RefreshView("View_Community_Friend_RequestedFriends");

        tm.RefreshView("View_OM_Contact_Activity");
        tm.RefreshView("View_OM_Contact_Joined");
        tm.RefreshView("View_OM_ContactGroupMember_ContactJoined");

        tm.RefreshView("View_OM_Account_Joined");
        tm.RefreshView("View_OM_Account_MembershipJoined");
        tm.RefreshView("View_OM_ContactGroupMember_AccountJoined");
    }


    /// <summary>
    /// Update form definitions of classes (especially system tables).
    /// </summary>
    private static void UpdateClasses()
    {
        DataSet classes = GetFormDefinitions();
        if (!DataHelper.DataSourceIsEmpty(classes))
        {
            foreach (DataRow row in classes.Tables[0].Rows)
            {
                string objectName = DataHelper.GetStringValue(row, FORM_DEFINITION_NAME_COLUMN);
                string newDefinition = DataHelper.GetStringValue(row, FORM_DEFINITION_VALUE_COLUMN);

                if (!string.IsNullOrEmpty(objectName) && !string.IsNullOrEmpty(newDefinition))
                {
                    var dataClass = DataClassInfoProvider.GetDataClassInfo(objectName);
                    if (dataClass != null)
                    {
                        var newVersionFi = new FormInfo(newDefinition);
                        var oldVersionFi = new FormInfo(dataClass.ClassFormDefinition);

                        // Get removed system fields
                        var removedfields = GetRemovedSystemFields(oldVersionFi, newVersionFi);
                        if (removedfields != null)
                        {
                            // Remove the system fields from class's alt.forms
                            foreach (var field in removedfields)
                            {
                                FormHelper.RemoveFieldFromAlternativeForms(dataClass, field, 0);
                            }
                        }

                        // Copy custom fields only for system tables
                        if (dataClass.ClassShowAsSystemTable)
                        {
                            CopyCustomFields(oldVersionFi, newVersionFi, false, true);
                        }

                        // Save the modified form definition
                        dataClass.ClassFormDefinition = newVersionFi.GetXmlDefinition();

                        // Update the scheme
                        dataClass.ClassXmlSchema = new TableManager(dataClass.ClassConnectionString).GetXmlSchema(dataClass.ClassTableName);

                        // Update search settings
                        dataClass.ClassSearchSettings = SearchHelper.CleanSearchSettings(dataClass);

                        // Save the new definition
                        dataClass.Update();
                    }
                }
            }
        }
    }


    /// <summary>
    /// Updates an existing alternative forms form definitions. Appends existing custom fields to new version definitions.
    /// </summary>
    private static void UpdateAlternativeForms()
    {
        DataSet classes = GetFormDefinitions(true);
        if (!DataHelper.DataSourceIsEmpty(classes))
        {
            foreach (DataRow row in classes.Tables[0].Rows)
            {
                string objectName = DataHelper.GetStringValue(row, FORM_DEFINITION_NAME_COLUMN);
                string newDefinition = DataHelper.GetStringValue(row, FORM_DEFINITION_VALUE_COLUMN);

                if (!string.IsNullOrEmpty(objectName) && !string.IsNullOrEmpty(newDefinition))
                {
                    var altForm = AlternativeFormInfoProvider.GetAlternativeFormInfo(objectName);
                    if (altForm != null)
                    {
                        var mainDci = DataClassInfoProvider.GetDataClassInfo(altForm.FormClassID);
                        var classFormDefinition = mainDci.ClassFormDefinition;

                        if (altForm.FormCoupledClassID > 0)
                        {
                            // If coupled class is defined combine form definitions
                            var coupledDci = DataClassInfoProvider.GetDataClassInfo(altForm.FormCoupledClassID);
                            if (coupledDci != null)
                            {
                                classFormDefinition = FormHelper.MergeFormDefinitions(classFormDefinition, coupledDci.ClassFormDefinition);
                            }
                        }

                        var oldVersionDefinition = FormHelper.MergeFormDefinitions(classFormDefinition, altForm.FormDefinition);
                        var newVersionDefinition = FormHelper.MergeFormDefinitions(classFormDefinition, newDefinition);

                        var newVersionFi = new FormInfo(newVersionDefinition);
                        var oldVersionFi = new FormInfo(oldVersionDefinition);

                        CopyCustomFields(oldVersionFi, newVersionFi, true, mainDci.ClassShowAsSystemTable);

                        // Save the modified form definition
                        altForm.FormDefinition = FormHelper.GetFormDefinitionDifference(classFormDefinition, newVersionFi.GetXmlDefinition(), true);
                        altForm.Update();
                    }
                }
            }
        }
    }


    /// <summary>
    /// Copies custom fields from old version of form definition to the new form definition.
    /// </summary>
    /// <param name="oldVersionFi">Old version form definition</param>
    /// <param name="newVersionFi">New version form definition</param>
    /// <param name="overwrite">Indicates whether existing fields should be overwritten. Alternative form fields need to be overwritten due to the combination with upgraded class form.</param>
    /// <param name="isSystemTable">Indicates whether the class is customizable.</param>
    private static void CopyCustomFields(FormInfo oldVersionFi, FormInfo newVersionFi, bool overwrite, bool isSystemTable)
    {
        // Remove all system fields from old definition to get only custom fields
        // Remove all dummy fields from old definition for non-customizable classes (Since v9 is condition f => f.System sufficient because dummy fields created in development mode are system)
        // * Dummy field in alternative forms are not marked as system in version < v9 and should be updated. Without this condition the old version of dummy field was used instead of new one.
        // * Dummy field is not removed from customizable classes due to possible customization.
        oldVersionFi.RemoveFields(f => f.System || (f.IsDummyField && !isSystemTable));

        // Combine forms so that custom fields from old definition are appended to the new definition
        newVersionFi.CombineWithForm(oldVersionFi, new CombineWithFormSettings
        {
            IncludeCategories = false,
            RemoveEmptyCategories = true,
            OverwriteExisting = overwrite
        });
    }


    /// <summary>
    /// Returns list with names of system fields which were removed to the new version.
    /// </summary>
    /// <param name="oldDefinition">Old form definition</param>
    /// <param name="newDefinition">New form definition</param>
    private static IEnumerable<string> GetRemovedSystemFields(FormInfo oldDefinition, FormInfo newDefinition)
    {
        if ((oldDefinition != null) && (newDefinition != null))
        {
            var oldSystemFields = oldDefinition.ItemsList.OfType<FormFieldInfo>().Where(f => f.System).Select(f => f.Name);
            var newSystemFields = newDefinition.ItemsList.OfType<FormFieldInfo>().Where(f => f.System).Select(f => f.Name);

            // Get difference of the sets
            return oldSystemFields.Except(newSystemFields);
        }

        return null;
    }


    /// <summary>
    /// Returns dataset with class names (or alt.form full names) and form definitions which should be used for the upgrade.
    /// </summary>
    /// <param name="getAltForms">Indicates if alt.form definitions should be returned</param>
    private static DataSet GetFormDefinitions(bool getAltForms = false)
    {
        string queryText = String.Format("SELECT [{0}], [{1}] FROM [{2}] WHERE {3}", FORM_DEFINITION_NAME_COLUMN, FORM_DEFINITION_VALUE_COLUMN, FORM_DEFINITION_TABLE, getAltForms ? "IsAltForm = 1" : "IsAltForm = 0 OR IsAltForm IS NULL");

        DataSet ds = null;
        try
        {
            ds = ConnectionHelper.ExecuteQuery(queryText, null, QueryTypeEnum.SQLQuery);
        }
        catch (Exception ex)
        {
            EventLogProvider.LogException(EventLogSource, "GETFORMDEFINITION", ex);
        }

        return ds;
    }


    /// <summary>
    /// Drops temporary table with classes' and alt.forms' form definitions.
    /// </summary>
    /// <param name="dtm">Table manager</param>
    private static void DropTempDefinitionTable(TableManager dtm)
    {
        try
        {
            dtm.DropTable(FORM_DEFINITION_TABLE);
        }
        catch (Exception ex)
        {
            EventLogProvider.LogException(EventLogSource, "DROPTEMPTABLE", ex);
        }
    }


    /// <summary>
    /// Procedures which automatically imports the upgrade export package with all WebParts, Widgets, Reports and TimeZones.
    /// </summary>
    private static void UpgradeImportPackage()
    {
        // Import
        try
        {
            RequestStockHelper.Remove("CurrentDomain", true);

            var importSettings = new SiteImportSettings(MembershipContext.AuthenticatedUser)
            {
                DefaultProcessObjectType = ProcessObjectEnum.All,
                SourceFilePath = mUpgradePackagePath,
                WebsitePath = mWebsitePath
            };

            using (var context = new CMSActionContext())
            {
                context.DisableLogging();
                context.CreateVersion = false;
                context.LogIntegration = false;

                ImportProvider.ImportObjectsData(importSettings);

                // Regenerate time zones
                TimeZoneInfoProvider.GenerateTimeZoneRules();

                // Delete the files for separable modules which are not install and therefore not needed
                DeleteWebPartsOfUninstalledModules();

                ImportMetaFiles(Path.Combine(mWebsitePath, "App_Data\\CMSTemp\\Upgrade"));
            }
        }
        catch (Exception ex)
        {
            EventLogProvider.LogException(EventLogSource, "IMPORT", ex);
        }
    }


    /// <summary>
    /// Refreshes macro signatures in all object which can contain macros.
    /// </summary>
    private static void RefreshMacroSignatures()
    {
        // Get object types
        var objectTypes = new List<string> {
                TransformationInfo.OBJECT_TYPE,
                UIElementInfo.OBJECT_TYPE,
                FormUserControlInfo.OBJECT_TYPE,
                SettingsKeyInfo.OBJECT_TYPE,
                AlternativeFormInfo.OBJECT_TYPE,
                DataClassInfo.OBJECT_TYPE, // Process all data classes just through general object type to avoid duplicities
                PageTemplateInfo.OBJECT_TYPE,
                LayoutInfo.OBJECT_TYPE,
                CssStylesheetInfo.OBJECT_TYPE,
                WorkflowActionInfo.OBJECT_TYPE,
            };

        var admin = UserInfoProvider.AdministratorUserName;

        foreach (string type in objectTypes)
        {
            try
            {
                using (var context = new CMSActionContext())
                {
                    context.DisableLogging();
                    context.CreateVersion = false;
                    context.LogIntegration = false;

                    var infos = new InfoObjectCollection(type);
                    foreach (var info in infos)
                    {
                        MacroSecurityProcessor.RefreshSecurityParameters(info, admin, true);
                    }
                }
            }
            catch (Exception ex)
            {
                EventLogProvider.LogException(EventLogSource, "REFRESHMACROSIGNATURES", ex, 0, "Type: " + type);
            }
        }
    }


    /// <summary>
    /// Deletes the files for separable modules which are not install and therefore not needed.
    /// </summary>
    private static void DeleteWebPartsOfUninstalledModules()
    {
        var webPartsPath = mWebsitePath + "CMSWebParts\\";
        var files = new List<string>();

        var separableModules = new List<string>
        {
            ModuleName.BIZFORM, 
            ModuleName.BLOGS,
            ModuleName.COMMUNITY,
            ModuleName.ECOMMERCE,
            ModuleName.EVENTMANAGER,
            ModuleName.FORUMS,
            ModuleName.MEDIALIBRARY,
            ModuleName.MESSAGEBOARD,
            ModuleName.MESSAGING,
            ModuleName.NEWSLETTER,
            ModuleName.NOTIFICATIONS,
            ModuleName.ONLINEMARKETING,
            ModuleName.POLLS,
            ModuleName.REPORTING,
            ModuleName.STRANDSRECOMMENDER,
            ModuleName.CHAT,
        };

        foreach (var separableModule in separableModules)
        {
            // Add files from this folder to the list of files to delete if the module is not installed
            if (!ModuleEntryManager.IsModuleLoaded(separableModule))
            {
                var folderName = GetWebPartFolderName(separableModule);
                files.AddRange(GetAllFiles(webPartsPath + folderName));
            }
        }

        // Remove web parts for separated modules
        foreach (String file in files)
        {
            try
            {
                File.Delete(file);
            }
            catch (Exception ex)
            {
                EventLogProvider.LogException(EventLogSource, "DELETEWEBPARTS", ex, 0, "File: " + file);
            }
        }
    }


    /// <summary>
    /// Returns list of all files in given folder (recursively, from all subdirectories as well).
    /// </summary>
    /// <param name="folder">Folder to search in</param>
    private static List<String> GetAllFiles(String folder)
    {
        var files = new List<string>();

        if (Directory.Exists(folder))
        {
            files.AddRange(Directory.GetFiles(folder));

            var dirs = Directory.GetDirectories(folder);

            foreach (string dir in dirs)
            {
                files.AddRange(GetAllFiles(dir));
            }
        }

        return files;
    }


    /// <summary>
    /// For given module returns it's folder name within CMSWebParts folder.
    /// </summary>
    /// <param name="moduleName">Name of the module</param>
    /// <returns></returns>
    private static string GetWebPartFolderName(string moduleName)
    {
        // Handle exceptions
        switch (moduleName)
        {
            case ModuleName.BIZFORM:
                return "BizForms";

            case ModuleName.BLOGS:
                return "Blogs";

            case ModuleName.NEWSLETTER:
                return "Newsletters";
        }

        // By default, trim "CMS." prefix from module name which will give us folder name withing CMSWebParts directory
        return moduleName.Substring(4);
    }


    /// <summary>
    /// Imports default metafiles which were changed in the new version.
    /// </summary>
    /// <param name="upgradeFolder">Folder where the generated metafiles.xml file is</param>
    private static void ImportMetaFiles(string upgradeFolder)
    {
        try
        {
            // To get the file use Phobos - Generate files button, Metafile settings.
            // Choose only those object types which had metafiles in previous version and these metafiles changed to the new version.
            String xmlPath = Path.Combine(upgradeFolder, "metafiles.xml");
            if (File.Exists(xmlPath))
            {
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(xmlPath);

                XmlNode metaFilesNode = xDoc.SelectSingleNode("MetaFiles");
                if (metaFilesNode == null)
                {
                    return;
                }

                String filesDirectory = Path.Combine(upgradeFolder, "Metafiles");

                using (new CMSActionContext { LogEvents = false })
                {
                    foreach (XmlNode metaFile in metaFilesNode)
                    {
                        // Load metafiles information from XML
                        if (metaFile.Attributes == null)
                        {
                            continue;
                        }

                        String objType = metaFile.Attributes["ObjectType"].Value;
                        String groupName = metaFile.Attributes["GroupName"].Value;
                        String codeName = metaFile.Attributes["CodeName"].Value;
                        String fileName = metaFile.Attributes["FileName"].Value;
                        String extension = metaFile.Attributes["Extension"].Value;
                        String fileGUID = metaFile.Attributes["FileGUID"].Value;
                        String title = (metaFile.Attributes["Title"] != null) ? metaFile.Attributes["Title"].Value : null;
                        String description = (metaFile.Attributes["Description"] != null) ? metaFile.Attributes["Description"].Value : null;

                        // Try to find correspondent info object
                        BaseInfo infoObject = BaseAbstractInfoProvider.GetInfoByName(objType, codeName);
                        if (infoObject == null)
                        {
                            continue;
                        }

                        int infoObjectId = infoObject.Generalized.ObjectID;

                        // Check if metafile exists
                        InfoDataSet<MetaFileInfo> metaFilesSet = MetaFileInfoProvider.GetMetaFilesWithoutBinary(infoObjectId, objType, groupName, "MetaFileGUID = '" + fileGUID + "'", null);
                        if (!DataHelper.DataSourceIsEmpty(metaFilesSet))
                        {
                            continue;
                        }

                        // Create new metafile if does not exists
                        String mfFileName = String.Format("{0}.{1}", fileGUID, extension.TrimStart('.'));
                        MetaFileInfo mfInfo = new MetaFileInfo(Path.Combine(filesDirectory, mfFileName), infoObjectId, objType, groupName);
                        mfInfo.MetaFileGUID = ValidationHelper.GetGuid(fileGUID, Guid.NewGuid());

                        // Set correct properties
                        mfInfo.MetaFileName = fileName;
                        if (title != null)
                        {
                            mfInfo.MetaFileTitle = title;
                        }
                        if (description != null)
                        {
                            mfInfo.MetaFileDescription = description;
                        }

                        // Save new meta file
                        MetaFileInfoProvider.SetMetaFileInfo(mfInfo);
                    }

                    // Remove existing files after successful finish
                    String[] files = Directory.GetFiles(upgradeFolder);
                    foreach (String file in files)
                    {
                        File.Delete(file);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            EventLogProvider.LogException(EventLogSource, "IMPORTMETAFILES", ex);
        }
    }

    #endregion


    #region "Update from 8.2 to 9.0"

    /// <summary>
    /// Handles all the specific operations for upgrade from 8.2 to 9.0.
    /// </summary>
    private static bool Upgrade82To90()
    {
        UpgradeSocialMedia82To90();
        UpgradeBlogClassesDefinition82To90();
        RemoveWireFramePages82To90();
        DeleteWireFramePageType82To90();
        RemovedWireFrameWebPartsAndCategories82To90();
        ExtractNewsletterUnsubscriptionsFromActivities();
        MoveCleanOldShoppingCartSettingFromWebConfigToSettings82To90();
        ProcessClickedLinkAndOpenedEmailGeneratedViaSubscriberLink();
        UpdateTagInfoCodeNames();
        UpgradeUserDashboardApplications();

        return true;
    }
    
    #endregion


    #region "Private methods"

    /// <summary>
    /// Upgrades Blog page types and removes fields for Trackbacks feature.
    /// </summary>
    private static void UpgradeBlogClassesDefinition82To90()
    {
        try
        {
            var pageTypeBlog = DataClassInfoProvider.GetDataClassInfo("cms.blog");
            if (pageTypeBlog != null)
            {
                DocumentTypeHelper.RemoveColumnsFromPageType(pageTypeBlog, "BlogEnableTrackbacks");
            }

            var pageTypeBlogPost = DataClassInfoProvider.GetDataClassInfo("cms.blogpost");
            if (pageTypeBlogPost != null)
            {
                DocumentTypeHelper.RemoveColumnsFromPageType(pageTypeBlogPost, "BlogPostPingedUrls", "BlogPostNotPingedUrls");
            }
        }
        catch (Exception e)
        {
            EventLogProvider.LogException(EventLogSource, "UPGRADEBLOGPAGETYPES", e);
        }
    }


    /// <summary>
    /// Upgrades social media module
    /// </summary>
    private static void UpgradeSocialMedia82To90()
    {
        // Remove LinkedInRecommend button along with all dependencies.
        RemoveWebParts(new List<string> { "5079A5D7-0361-4F89-8E61-F44AD66CEA11" });
    }


    /// <summary>
    /// Removes list of the web parts
    /// </summary>
    /// <param name="webpartsList">List of web parts to remove</param>
    private static void RemoveWebParts(IEnumerable<string> webpartsList)
    {
        foreach (var guid in webpartsList)
        {
            var webPart = WebPartInfoProvider.GetWebPartInfoByGUID(new Guid(guid));
            if (webPart != null)
            {
                WebPartInfoProvider.DeleteWebPartInfo(webPart);
            }
        }
    }


    /// <summary>
    /// Removes pages that uses wireframe page type.
    /// </summary>
    private static void RemoveWireFramePages82To90()
    {
        const string WIREFRAME_CLASS_NAME = "cms.wireframe";
        
        // First, check whether wireframe data class exists and if it doesn't then skip removing wireframe pages.
        if (DataClassInfoProvider.GetDataClassInfo(WIREFRAME_CLASS_NAME) == null)
        {
            return;
        }

        // Select wireframe pages
        TreeProvider tp = new TreeProvider();
        var wireframePages = tp.SelectNodes(WIREFRAME_CLASS_NAME)
            .All()
            .Path("/", PathTypeEnum.Children)
            .OrderByDescending("NodeAliasPath");

        foreach (TreeNode node in wireframePages)
        {
            DeleteDocumentSettings dds = new DeleteDocumentSettings
            {
                DeleteAllCultures = true,
                DestroyHistory = true,
                Node = node,
                Tree = tp,
            };

            try
            {
                // Delete wireframe page and all its depending objects
                DocumentHelper.DeleteDocument(dds);
            }
            catch (Exception ex)
            {
                EventLogProvider.LogException(EventLogSource, "DELETEWIREFRAMEPAGE", ex, node.NodeSiteID);
            }
        }

        // Delete pages in recycle bin which were deleted earlier
        var q = VersionHistoryInfoProvider.GetVersionHistories().WhereEquals("VersionClassID", DataClassInfoProvider.GetClasses().WhereEquals("ClassName", WIREFRAME_CLASS_NAME).AsSingleColumn().AsValue());
        VersionManager vm = VersionManager.GetInstance(tp);

        q.ForEachObject(vhi =>
        {
            try
            {
                vm.DestroyDocumentVersion(vhi.VersionHistoryID);
            }
            catch (Exception ex)
            {
                EventLogProvider.LogException(EventLogSource, "DELETEWIREFRAMEPAGEHISTORY", ex);
            }
        });
    }


    /// <summary>
    /// Delete 'Wireframe' page type.
    /// </summary>
    private static void DeleteWireFramePageType82To90()
    {
        DeletePageType("cms.wireframe");
    }


    /// <summary>
    /// Removes all wireframe web parts.
    /// </summary>
    private static void RemovedWireFrameWebPartsAndCategories82To90()
    {
        var webpartsList = new List<string>
        {
            "25E923B3-8D93-47C6-BE75-BC9C14DCE3F9", // Paragraph of text
            "4E83FFC6-C7CA-43AA-BF44-2878AB740CA8", // Dialog
            "FE1E3231-4137-42DE-A558-4A79B51AC6E7", // Breadcrumbs
            "260BEE0B-99EF-4094-BD14-B459852ACA4F", // Button
            "C49E608B-ED51-4B3B-A3AD-4C45B6DEEF30", // Menu bar
            "06C3E7F9-3DAF-4CDB-8B07-67DB4CC920CB", // Image
            "BB2E8F79-D2C4-4218-BDEA-9BDF3AFA41DD", // Note
            "5BC544B6-FCAD-4AE5-96E7-119353C80474", // Label
            "1024AC18-34DD-4418-A431-79C9562AD622", // Checkbox
            "16263FC7-7007-4D23-B94F-29A59EFC22A9", // Radio button
            "1F727857-E042-4210-8A94-22035D98F173", // Dropdown list
            "3F9A3BA9-F885-467A-881D-E1B54277F2BB", // Checkbox list
            "79CD1AC7-F598-4C13-9350-81E3E44C2AC2", // Radio button list
            "B9D36520-3C25-4D7A-B5AB-D45478CA4C3C", // Text field / Textbox
            "FAA59B7C-5E93-4CE1-85A0-4E4D03178B67", // Text area
            "2CCE9045-DC25-46A2-9936-FAD2C63429A1", // Color picker
            "D1E569E1-5D1C-4435-ADD9-2DDDE030D722", // Grid
            "9C79EEE8-8EE9-4D38-894D-B6A1ADFFFB13", // Field set / Group
            "B310F903-F1C2-49A6-B3C6-C472A75A1D0E", // Rectangle
            "B03EEA82-81AB-4005-B19D-08F6FD42E81D", // Link bar
            "DAB5055F-4794-4114-A202-6731F3F3ACAD", // Tag cloud
            "05D0BCC0-7D46-43EF-ADC6-E4CCE1E9E9EE", // List
            "DADE4EFB-D7C7-450D-8F84-C9AB9E265B29", // Icon
            "76DD091C-F2BB-4EAE-B1CD-1F4EF8630110", // Multiline button
            "CBC3A3A5-EDAD-4C43-9A4A-B5EE76E11DB9", // Note marker
            "0AD8C3E1-39C5-4964-91D1-49FC4F1FAF35", // Horizontal slider
            "71E69432-D82A-4E08-B5D2-CD46F9404FA5", // Tabs
            "4A3A604A-8CBD-4A6E-91AC-06076AB852B4", // Wireframe area
            "E3AAFF1C-E4CB-40EE-87B6-350411CE25F6", // Next page level
            "CE9A73CC-C0AB-4CF3-9533-3530A71A3EF0", // Accordion
            "68D1067A-0066-4C93-9B8A-A7B00A345F2D", // WYSIWYG Editor
            "86BC31F6-CEDA-4D31-8CA5-D27604812198", // Multiline text
            "8D798009-B4C2-4EBC-B84A-52392E0D98BA", // Button / Tab bar
            "4D89268D-546D-4AAF-906E-C7E8967B5C96", // iPhone
            "3101271E-BEBA-4A09-9D91-A39F1C313878", // iPhone keyboard
            "98A14FD4-CAFF-487A-A3A7-DF19BC8C5699", // iPad
            "AB643ACA-3590-4EE2-AC1F-28FC70E5E6EB", // Calendar
            "08FF3141-112D-472F-8164-C160CC5B861B", // Bar chart
            "F9FF7049-0EA0-40D7-9C4C-78F77C6BCC8B", // Column chart
            "3D8EFD1C-4B96-4969-98A8-FC06DB88C81C", // Pie chart
            "5AC875A5-6B74-4276-A355-84DF943FD169", // Area chart
            "33BC09AC-1F2F-40A6-A8B2-5214A5C51A48", // Line chart
            "754FC5C2-446A-4FE1-8790-75351B41F103", // Image carousel
            "AEE34C85-993B-40CC-91A4-AB4DBACBED79", // Web camera
            "A77C87A2-E988-496D-A6C4-E885446B6524", // Link
            "FB403D35-55B7-4D2E-B053-1F19EE94DD79", // Title / header
            "C1FE8B58-5078-4C4E-9686-B05713D44C4C", // Subtitle / Subheader
            "B99A612B-25E7-439D-B547-9D10C72FD77B", // Date/time picker
            "687DBDB7-120B-4DA2-8F15-CDFA28E7E508", // Search box
            "3CCC0450-2281-4544-AF9A-4262944D5A0C", // Numeric up/down
            "7C75EE16-3E1A-4F0D-A97E-F0E248CAE7D5", // Horizontal rule
            "ACFA10B7-F22D-4F76-8409-3659A5E81187", // Vertical rule
            "EB24D055-E8BE-4361-8C88-23643A10BF08", // Horizontal splitter
            "5A9A7F09-66A6-4158-B95E-166896CBAB2B", // Vertical splitter
            "51DB35EA-2F04-40C5-862B-EA24E91CACC2", // WYSIWYG Toolbar
            "AD447470-296D-4FBA-83B0-952430906065", // Icon with label
            "81E1D41C-7A77-4497-BCB1-8FB6A9447D19", // Vertical slider
            "BEF0643D-9728-4A83-ABCA-24FC9145793E", // Progress bar
            "0EA8F655-350D-4A7D-BD77-2AD9B342E42D", // Horizontal scrollbar
            "4E79F21B-4960-4A2D-BBC3-521A6FD00AC1", // Vertical scrollbar
            "4FF11A04-1932-43C5-B9FB-DFD8D567063B", // Tree pane
        };

        RemoveWebParts(webpartsList);

        DeleteWireFrameWebPartCategories82To90();
    }


    /// <summary>
    /// Deletes a given page type completely.
    /// </summary>
    /// <param name="className">Page type name</param>
    private static void DeletePageType(string className)
    {
        DataClassInfo dci = DataClassInfoProvider.GetDataClassInfo(className);

        if (dci == null)
        {
            return;
        }

        try
        {
            // Delete data class itself
            dci.Destroy();

            // Destroy its history
            ObjectVersionManager.DestroyObjectHistory("cms.documenttype", dci.ClassID);
        }
        catch (Exception ex)
        {
            EventLogProvider.LogException(EventLogSource, "DELETEPAGETYPE", ex, 0, String.Format("Couldn't delete page type '{0}'.", dci.ClassName));
        }
    }


    public static void ExtractNewsletterUnsubscriptionsFromActivities()
    {
        if (!ModuleEntryManager.IsModuleLoaded(ModuleName.NEWSLETTER))
        {
            return;
        }

        var activitiesQuery = new ObjectQuery(PredefinedObjectType.ACTIVITY)
            .Source(s => s.Join("OM_Contact", "ActivityActiveContactID", "ContactID"))
            .WhereEquals("ActivityType", PredefinedActivityType.NEWSLETTER_UNSUBSCRIBING)
            .WhereNotEmpty("ContactEmail")
            .Columns("ContactEmail", "ActivitySiteID", "ActivityCreated", "ActivityItemID")
            .Distinct();

        var activities = activitiesQuery.Select(row => new
        {
            SiteID = DataHelper.GetIntValue(row, "ActivitySiteID"),
            ContactEmail = DataHelper.GetStringValue(row, "ContactEmail"),
            ActivityCreated = DataHelper.GetDateTimeValue(row, "ActivityCreated"),
            NewsletterID = DataHelper.GetIntValue(row, "ActivityItemID"),
        });

        var existingUnsubscriptionsByNewsletterID = new ObjectQuery(PredefinedObjectType.NEWSLETTERUNSUBSCRIPTION)
            .WhereNotNull("UnsubscriptionNewsletterID")
            .Select(row => new
            {
                Email = DataHelper.GetStringValue(row, "UnsubscriptionEmail"),
                NewsletterID = DataHelper.GetIntValue(row, "UnsubscriptionNewsletterID"),
            })
            .GroupBy(unsubscription => unsubscription.NewsletterID)
            .ToDictionary(group => group.Key, group => group.Select(u => u.Email).ToHashSet(StringComparer.InvariantCultureIgnoreCase));

        foreach (var activity in activities)
        {
            if (existingUnsubscriptionsByNewsletterID.ContainsKey(activity.NewsletterID) && existingUnsubscriptionsByNewsletterID[activity.NewsletterID].Contains(activity.ContactEmail))
            {
                continue;
            }

            if (!ValidationHelper.IsEmail(activity.ContactEmail))
            {
                continue;
            }

            var unsubscriptionInfo = ModuleManager.GetObject(PredefinedObjectType.NEWSLETTERUNSUBSCRIPTION);

            unsubscriptionInfo.SetValue("UnsubscriptionEmail", activity.ContactEmail.ToLowerInvariant());
            unsubscriptionInfo.SetValue("UnsubscriptionNewsletterID", activity.NewsletterID);
            unsubscriptionInfo.SetValue("UnsubscriptionSiteID", activity.SiteID);
            unsubscriptionInfo.SetValue("UnsubscriptionCreated", activity.ActivityCreated);
            unsubscriptionInfo.SetValue("UnsubscriptionGUID", Guid.NewGuid());

            unsubscriptionInfo.Insert();

            if (existingUnsubscriptionsByNewsletterID.ContainsKey(activity.NewsletterID))
            {
                existingUnsubscriptionsByNewsletterID[activity.NewsletterID].Add(activity.ContactEmail);
            }
            else
            {
                existingUnsubscriptionsByNewsletterID[activity.NewsletterID] = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { activity.ContactEmail };
            }
        }
    }


    /// <summary>
    /// Deletes wireframe web part categories. Must be run after web parts were deleted.
    /// </summary>
    private static void DeleteWireFrameWebPartCategories82To90()
    {
        // Get all empty (= no web part contained) categories
        var q = WebPartCategoryInfoProvider.GetCategories()
            .WhereStartsWith("CategoryPath", "Wireframes/")
            .WhereEquals("CategoryWebPartChildCount", 0)
            .OrderBy(OrderDirection.Descending, "CategoryLevel")
            .Columns("CategoryID", "CategoryDisplayName", "CategoryParentID", "CategoryPath");

        q.ForEachObject(wpci =>
        {
            try
            {
                wpci.Delete();
            }
            catch (Exception ex)
            {
                EventLogProvider.LogException(EventLogSource, "DELETEWIREFRAMEWEBPARTCATEGORY", ex, 0, String.Format("Couldn't delete web part category '{0}' because it is not empty.", wpci.CategoryDisplayName));
            }
        });
    }


    /// <summary>
    /// Moves setting value from web.config to the database table CMS_SettingsKey.
    /// After the SQL upgrade only one global setting exists, so distribution to particular sites is not
    /// necessary. If the value is not set in web.config file, default value is used (30 days).
    /// </summary>
    private static void MoveCleanOldShoppingCartSettingFromWebConfigToSettings82To90()
    {
        var settingValue = ValidationHelper.GetInteger(SettingsHelper.AppSettings["CMSShoppingCartExpirationPeriod"], 30);
        SettingsKeyInfoProvider.SetValue("CMSShoppingCartExpirationPeriod", 0, settingValue);
    }


    /// <summary>
    /// Post-processing of the clicked link/opened email records generated via subscriber link. Ensure valid emails from subscriber table.
    /// </summary>
    private static void ProcessClickedLinkAndOpenedEmailGeneratedViaSubscriberLink()
    {
        bool isSeparated = !String.IsNullOrEmpty(DatabaseSeparationHelper.ConnStringSeparate);

        // If the database is separated, create temporary table with subscribers' ID and email
        if (isSeparated)
        {
            // Get the subscribers with email from the default database
            var subscribersQuery = new ObjectQuery("newsletter.subscriber")
            .Columns("SubscriberID", "SubscriberEmail")
            .WhereNull("SubscriberType").Or()
            .WhereEquals("SubscriberType", "om.contact");

            // Get the subscribers of user type from the default database
            var subscribersFromUsersQuery = new ObjectQuery("newsletter.subscriber")
            .Source(s => s.LeftJoin<UserInfo>("SubscriberRelatedID", "UserID"))
            .Columns(new QueryColumn("SubscriberID"), new QueryColumn("Email").As("SubscriberEmail"))
            .WhereEquals("SubscriberType", "cms.user")
            .WhereNotNull("Email");

            DataSet subscribers = subscribersQuery.Union(subscribersFromUsersQuery);

            const string createTempTableScript =
@"
CREATE TABLE [Temp_Newsletter_Subscriber]
(
    [SubscriberEmail] [nvarchar] (400),
    [SubscriberID] [int] NOT NULL
) ON [PRIMARY];
";
            // Create temporary table with subscribers on the separated db
            using (new CMSConnectionScope(DatabaseSeparationHelper.OM_CONNECTION_STRING, false, true))
            {
                ConnectionHelper.ExecuteQuery(createTempTableScript, null, QueryTypeEnum.SQLQuery);
            }

            // Fill the table with subscribers' data
            using (var bulkCopy = new SqlBulkCopy(DatabaseSeparationHelper.ConnStringSeparate, SqlBulkCopyOptions.KeepIdentity))
            {
                bulkCopy.ColumnMappings.Add("SubscriberEmail", "SubscriberEmail");
                bulkCopy.ColumnMappings.Add("SubscriberID", "SubscriberID");
                bulkCopy.BulkCopyTimeout = 6000;
                bulkCopy.DestinationTableName = "Temp_Newsletter_Subscriber";
                bulkCopy.WriteToServer(subscribers.Tables[0]);
            }

            const string updateClicksScript =
@"
UPDATE Newsletter_ClickedLink
SET ClickedLinkEmail = SubscriberEmail
FROM Newsletter_ClickedLink
    LEFT JOIN Temp_Newsletter_Subscriber 
	ON REPLACE(ClickedLinkEmail,'subscriberid_','') = Temp_Newsletter_Subscriber.SubscriberID
WHERE ClickedLinkEmail NOT LIKE '%@%' AND SubscriberEmail IS NOT NULL;
";
            const string updateOpensScript =
@"
UPDATE Newsletter_OpenedEmail
SET OpenedEmailEmail = SubscriberEmail
FROM Newsletter_OpenedEmail
    LEFT JOIN Temp_Newsletter_Subscriber
	ON REPLACE(OpenedEmailEmail,'subscriberid_','') = SubscriberID
WHERE OpenedEmailEmail NOT LIKE '%@%' AND SubscriberEmail IS NOT NULL;
";
            var existingIssueIDs = new ObjectQuery("newsletter.issue")
                .Column("IssueID")
                .GetListResult<int>();

            string deleteOpensWithNonExistingIssues =
string.Format(@"
DELETE FROM [Newsletter_OpenedEmail]
WHERE [OpenedEmailIssueID] NOT IN ({0})
", string.Join(",", existingIssueIDs));

            
            // Update the data in ClickedLink/OpenedEmail table using OM connection string (or default)
            using (new CMSConnectionScope(DatabaseSeparationHelper.OM_CONNECTION_STRING, false, true))
            {
                ConnectionHelper.ExecuteQuery(updateClicksScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(updateOpensScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(deleteOpensWithNonExistingIssues, null, QueryTypeEnum.SQLQuery);
            }

            const string dropTempTableScript =
@"
DROP TABLE Temp_Newsletter_Subscriber;
";
            const string deleteUnresolvedClicksScript =
@"
DELETE FROM Newsletter_ClickedLink WHERE ClickedLinkEmail LIKE 'subscriberid_%' OR ClickedLinkEmail = '';
";
            const string deleteUnresolvedOpensScript =
@"
DELETE FROM Newsletter_OpenedEmail WHERE OpenedEmailEmail LIKE 'subscriberid_%' OR OpenedEmailEmail = '';
";
            const string deleteDuplicatesScript =
@"
WITH NOE AS(
   SELECT OpenedEmailID, OpenedEmailEmail, OpenedEmailIssueID, OpenedEmailTime,
       RN = ROW_NUMBER() OVER (PARTITION BY OpenedEmailEmail, OpenedEmailIssueID ORDER BY OpenedEmailTime ASC)
   FROM Newsletter_OpenedEmail
)
DELETE FROM NOE 
WHERE RN > 1;";

            // Delete the temporary table on the separated database and ClickedLinks/OpenedEmails with unresolved emails
            using (new CMSConnectionScope(DatabaseSeparationHelper.OM_CONNECTION_STRING, false, false))
            {
                ConnectionHelper.ExecuteQuery(dropTempTableScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(deleteUnresolvedClicksScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(deleteUnresolvedOpensScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(deleteDuplicatesScript, null, QueryTypeEnum.SQLQuery);
            }

            // Update Issues with opened email count
            var openedEmailsSummary = new ObjectQuery("newsletter.openedemail")
                .Columns(new QueryColumn("OpenedEmailIssueID"), new AggregatedColumn(AggregationType.Count, "OpenedEmailIssueID").As("Count"))
                .GroupBy("OpenedEmailIssueID");

            const string createOpenedEmailsSummaryTempTableScript =
@"
CREATE TABLE [Temp_Opened_Summary]
(
    [OpenedEmailIssueID] [int],
    [Count] [int]
) ON [PRIMARY];
";
            ConnectionHelper.ExecuteQuery(createOpenedEmailsSummaryTempTableScript, null, QueryTypeEnum.SQLQuery);

            using (var bulkCopy = new SqlBulkCopy(ConnectionHelper.ConnectionString, SqlBulkCopyOptions.KeepIdentity))
            {
                bulkCopy.ColumnMappings.Add("OpenedEmailIssueID", "OpenedEmailIssueID");
                bulkCopy.ColumnMappings.Add("Count", "Count");
                bulkCopy.BulkCopyTimeout = 6000;
                bulkCopy.DestinationTableName = "Temp_Opened_Summary";
                bulkCopy.WriteToServer(openedEmailsSummary.Tables[0]);
            }

            const string updateIssueOpenedEmailsScript =
@"
UPDATE Newsletter_NewsletterIssue
SET IssueOpenedEmails = (
	SELECT Count FROM Temp_Opened_Summary
	WHERE OpenedEmailIssueID = IssueID);
";
            ConnectionHelper.ExecuteQuery(updateIssueOpenedEmailsScript, null, QueryTypeEnum.SQLQuery);

            const string dropOpenedEmailsSummaryTempTableScript =
@"
DROP TABLE Temp_Opened_Summary;
";
            ConnectionHelper.ExecuteQuery(dropOpenedEmailsSummaryTempTableScript, null, QueryTypeEnum.SQLQuery);
        }
        else
        {
            const string updateClicksScript =
@"
UPDATE Newsletter_ClickedLink
SET ClickedLinkEmail = ISNULL(SubscriberEmail, Email)
FROM Newsletter_ClickedLink
    LEFT JOIN Newsletter_Subscriber
	ON REPLACE(ClickedLinkEmail,'subscriberid_','') = Newsletter_Subscriber.SubscriberID
	LEFT JOIN CMS_User 
	ON (UserID = SubscriberRelatedID AND SubscriberType = 'cms.user')
WHERE ClickedLinkEmail NOT LIKE '%@%' AND (SubscriberEmail IS NOT NULL OR Email IS NOT NULL);
";
            const string deleteUnresolvedClicksScript =
@"
DELETE FROM Newsletter_ClickedLink WHERE ClickedLinkEmail LIKE 'subscriberid_%' OR ClickedLinkEmail = '';
";
            const string updateOpensScript =
@"
UPDATE Newsletter_OpenedEmail
SET OpenedEmailEmail = SubscriberEmail
FROM Newsletter_OpenedEmail
    LEFT JOIN Newsletter_Subscriber
	ON REPLACE(OpenedEmailEmail,'subscriberid_','') = SubscriberID
WHERE OpenedEmailEmail NOT LIKE '%@%' AND SubscriberEmail IS NOT NULL;
";
            const string deleteUnresolvedOpensScript =
@"
DELETE FROM Newsletter_OpenedEmail WHERE OpenedEmailEmail LIKE 'subscriberid_%' OR OpenedEmailEmail = '';
";
            const string deleteDuplicatesScript =
@"
WITH NOE AS(
   SELECT OpenedEmailID, OpenedEmailEmail, OpenedEmailIssueID, OpenedEmailTime,
       RN = ROW_NUMBER() OVER (PARTITION BY OpenedEmailEmail, OpenedEmailIssueID ORDER BY OpenedEmailTime ASC)
   FROM Newsletter_OpenedEmail
)
DELETE FROM NOE 
WHERE RN > 1;
";
            const string updateIssueOpenedEmailsScript =
@"
UPDATE Newsletter_NewsletterIssue
SET IssueOpenedEmails = (
	SELECT COUNT(OpenedEmailIssueID) FROM Newsletter_OpenedEmail
	WHERE OpenedEmailIssueID = IssueID
	GROUP BY OpenedEmailIssueID);
";

            // Update the data in ClickedLink/OpenEmail and delete unresolved emails
            using (new CMSConnectionScope())
            {
                ConnectionHelper.ExecuteQuery(updateClicksScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(deleteUnresolvedClicksScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(updateOpensScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(deleteUnresolvedOpensScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(deleteDuplicatesScript, null, QueryTypeEnum.SQLQuery);
                ConnectionHelper.ExecuteQuery(updateIssueOpenedEmailsScript, null, QueryTypeEnum.SQLQuery);
            }
        }
    }


    /// <summary>
    /// TagInfo has new code name column, after upgrade we want that the code name will be similiar to the tag display name.
    /// </summary>
    private static void UpdateTagInfoCodeNames()
    {
        foreach (var tag in TagInfoProvider.GetTags().TypedResult)
        {
            var codeName = ValidationHelper.GetCodeName(tag.TagName);
            tag.TagCodeName = codeName;
            tag.Generalized.EnsureUniqueCodeName();
            TagInfoProvider.SetTagInfo(tag);
        }
    }


    /// <summary>
    /// Upgrades user dashboard applications to the new format.
    /// </summary>
    private static void UpgradeUserDashboardApplications()
    {
        var userSettings = UserSettingsInfoProvider.GetUserSettings().WhereNotEmpty("UserDashboardApplications").ToList();
        var serializer = new JavaScriptSerializer();

        foreach (var userSetting in userSettings)
        {
            var userApplications = serializer.Deserialize<List<Guid>>(userSetting.UserDashboardApplications);
            var newFormatUserDashboardApplications = serializer.Serialize(userApplications.Select(app => new { applicationGuid = app }));

            userSetting.UserDashboardApplications = newFormatUserDashboardApplications;
            UserSettingsInfoProvider.SetUserSettingsInfo(userSetting);
        }
    }


    /// <summary>
    /// Runs hotfix sql scripts
    /// </summary>
    private static void RunHotfixSQL()
    {
        string scriptsPath = SqlInstallationHelper.GetSQLInstallPath();

        try
        {
            string pathToHotfixXML = Path.Combine(scriptsPath, "Hotfix\\Hotfix.xml");
            bool dbSeparated = SqlInstallationHelper.DatabaseIsSeparated();

            // Load hotfix sql scripts configuration
            SQLSettings settings;
            using (FileStream stream = FileStream.New(pathToHotfixXML, FileMode.Open))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(SQLSettings));
                settings = (SQLSettings)serializer.Deserialize(stream.SystemStream);
            }

            // Process and run all loaded scripts
            settings.Scripts = settings.Scripts.OrderBy(s => s.SQLLaunchOrder).ToList();
            foreach (SQLScript sqlScript in settings.Scripts)
            {
                if (!dbSeparated)
                {
                    sqlScript.ConnectionStringName = ConnectionHelper.DEFAULT_CONNECTIONSTRING_NAME;
                }
                sqlScript.ConnectionString = ConnectionHelper.GetConnectionString(sqlScript.ConnectionStringName, false);

                sqlScript.SQLFilePath = Path.Combine(scriptsPath, "Hotfix\\" + sqlScript.SQLFileName);

                RunSQLScript(sqlScript);
            }
        }
        catch (Exception ex)
        {
            EventLogProvider.LogException("Upgrade - Applying hotfix sql scripts", "Upgrade", ex);
        }
        finally
        {
            // Dispose the zip storage provider to release memory and the file
            if (ZipStorageProvider.IsZipFolderPath(scriptsPath))
            {
                ZipStorageProvider.Dispose(scriptsPath);
            }
        }
    }


    /// <summary>
    /// Runs given hotfix script using the specified connection string.
    /// </summary>
    /// <param name="sqlScript">Sql script to run</param>
    private static void RunSQLScript(SQLScript sqlScript)
    {
        using (IDataConnection conn = ConnectionHelper.GetConnection(sqlScript.ConnectionString))
        {
            conn.Open();
            string query = File.ReadAllText(sqlScript.SQLFilePath);

            string[] sqlParts = query.Split(new [] { "GO" + Environment.NewLine, "GO\n" }, StringSplitOptions.RemoveEmptyEntries);

            foreach (string sql in sqlParts)
            {
                try
                {
                    // Run part of SQL script
                    conn.ExecuteNonQuery(sql, null, QueryTypeEnum.SQLQuery, false);
                }
                catch (Exception ex)
                {
                    EventLogProvider.LogException("Upgrade - Applying hotfix sql scripts", "Upgrade", ex);
                }
            }
        }
    }

    #endregion
}