Beispiel #1
0
 /// <summary>
 /// Add the whole set of items
 /// </summary>
 /// <param name="items"></param>
 public KeyedLookup(IEnumerable <T> items, TaskStatusLogs statusLogger)
 {
     foreach (var thisItem in items)
     {
         AddItem(thisItem.Id, thisItem, statusLogger);
     }
 }
Beispiel #2
0
 /// <summary>
 /// Adds a keyed item to the dictionary
 /// </summary>
 /// <param name="key"></param>
 /// <param name="item"></param>
 /// <param name="statusLogger">If non-NULL; then trap and record errors,  If NULL the error will get thrown upward</param>
 public void AddItem(string key, T item, TaskStatusLogs statusLogger = null)
 {
     //There are cases where building the dictionary may fail, such as if the incoming data has
     //duplicate ID entries.  If we have a status logger, we want to log the error and then
     //continue onward
     try
     {
         _dictionary.Add(key, item);
     }
     catch (Exception exAddDictionaryItem)
     {
         //If we have an error logger, then log the error
         if (statusLogger != null)
         {
             string itemDescription = "null item";
             if (item != null)
             {
                 itemDescription = item.ToString();
             }
             statusLogger.AddError("Error building lookup dictionary. Item: " + itemDescription + ", " + exAddDictionaryItem.ToString());
         }
         else //Otherwise thrown the error upward
         {
             throw;
         }
     }
 }
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="pathTwbx">TWBX we are going to unpack</param>
 /// <param name="workingDirectory"></param>
 public TwbDataSourceEditor(string pathTwbInput, string pathTwbOutput, ITableauServerSiteInfo serverMapInfo, TaskStatusLogs statusLog)
 {
     _pathToTwbInput = pathTwbInput;
     _pathToTwbOutput = pathTwbOutput;
     _serverMapInfo = serverMapInfo;
     _statusLog = statusLog;
 }
Beispiel #4
0
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="jobName">Name to associate with this work</param>
    /// <param name="onlineUrls"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    /// <param name="taskOptions"></param>
    /// <param name="manualActions"></param>
    public TaskMaster(
        string jobName,
        TableauServerUrls onlineUrls,
        string userName,
        string password,
        TaskMasterOptions taskOptions,
        CustomerManualActionManager manualActions = null)
    {
        this.JobName = jobName;

        _manualActions = manualActions;
        if (_manualActions == null)
        {
            _manualActions = new CustomerManualActionManager();
        }
        //Get any export path
        _exportToLocalPath = taskOptions.GetOptionValue(TaskMasterOptions.OptionParameter_PathDownloadTo);
        _onlineUrls        = onlineUrls;
        _userName          = userName;
        _password          = password;

        //Store the status log at the class level where it is accessable
        _statusLog = new TaskStatusLogs();
        //Store the options
        _taskOptions = taskOptions;


        if (_taskOptions.IsOptionSet(TaskMasterOptions.Option_LogVerbose))
        {
            _statusLog.SetStatusLoggingLevel(int.MinValue);
        }
    }
Beispiel #5
0
        private void btnProvisionFromAzureAd_Click(object sender, EventArgs e)
        {
            var statusLogs = new TaskStatusLogs();

            statusLogs.AddStatus("Starting...");
            UpdateStatusText(statusLogs, true);

            string pathSecrets = txtPathToSecrets.Text;

            if (!File.Exists(pathSecrets))
            {
                MessageBox.Show("Secrets file does not exist at specified path (" + pathSecrets + ")");
                return;
            }


            string pathProvisionPlan = txtPathToAzureAdProvisioningConfig.Text;

            if (!File.Exists(pathProvisionPlan))
            {
                MessageBox.Show("Config file does not exist at specified path (" + pathProvisionPlan + ")");
                return;
            }

            string pathOutput =
                Path.Combine(
                    Path.GetDirectoryName(pathProvisionPlan),
                    "out");

            FileIOHelper.CreatePathIfNeeded(pathOutput);

            //Show the user a command line that they can use to run this same work
            GenerateProvisioningCommandLine(
                CommandLineParser.Command_ProvisionFromAzure,
                pathSecrets,
                pathProvisionPlan,
                pathOutput);

            //Run the work
            try
            {
                ProvisionFromAzureAd(
                    statusLogs,
                    pathSecrets,
                    txtPathToAzureAdProvisioningConfig.Text,
                    pathOutput);
            }
            catch (Exception exError)
            {
                MessageBox.Show("Error: " + exError.Message);
            }

            UpdateStatusText(statusLogs, true);

            //Open the file explorer to the output directory
            if (Directory.Exists(pathOutput))
            {
                System.Diagnostics.Process.Start(pathOutput);
            }
        }
Beispiel #6
0
        /// <summary>
        /// Provision the site based on the provisioning manifest in a file
        /// </summary>
        /// <param name="statusLogs">Store status logs here</param>
        /// <param name="pathSecrets">Where the log in secrets are</param>
        /// <param name="pathProvisioningManifest">Where the provisioning steps are</param>
        /// <param name="outputPath">Where output files go</param>
        private void ProvisionFromFileManifest(TaskStatusLogs statusLogs, string pathSecrets, string pathProvisioningManifest, string outputPath)
        {
            //Load the config from the files
            var secretsConfig = new ProvisionConfigSiteAccess(pathSecrets);

            //Load the user provisioning instructions
            var provisionUsersInfo = new ProvisionUserInstructions(
                pathProvisioningManifest);

            var provisionSite = new ProvisionSite(secretsConfig, provisionUsersInfo, this, statusLogs);

            provisionSite.Execute();

            //---------------------------------------------------------------------
            //Generate an output file
            //---------------------------------------------------------------------
            FileIOHelper.CreatePathIfNeeded(outputPath);

            var outputFilePath = Path.Combine(outputPath, "ProvisionSiteOutput.csv");

            provisionSite.CSVResultsReport.GenerateCSVFile(outputFilePath);

            statusLogs.AddStatusHeader("Done!");
            ((IShowLogs)this).NewLogResultsToShow(statusLogs);
        }
Beispiel #7
0
        /// <summary>
        /// Shows status text in the textboxes
        /// </summary>
        /// <param name="statusLog"></param>
        private void UpdateStatusText(TaskStatusLogs statusLog)
        {
            textBoxStatus.Text = statusLog.StatusText;
            ScrollToEndOfTextbox(textBoxStatus);

            textBoxErrors.Text = statusLog.ErrorText;
        }
    /// <summary>
    /// Load the DB credentials set from a file
    /// </summary>
    /// <param name="pathDBCredentials"></param>
    /// <returns></returns>
    internal static CredentialManager LoadFromFile(string pathDBCredentials, TaskStatusLogs statusLog)
    {
        if (statusLog == null)
        {
            statusLog = new TaskStatusLogs();
        }
        statusLog.AddStatus("Loading database credentials from " + pathDBCredentials);

        //Load the XML document and get the credentials
        var xDoc = new XmlDocument();

        xDoc.Load(pathDBCredentials);
        var nodesList = xDoc.SelectNodes("//credential");

        var credentialManager = new CredentialManager();

        foreach (XmlNode credentialNode in nodesList)
        {
            try
            {
                helper_parseCredentialNode(credentialManager, credentialNode);
            }
            catch (Exception ex)
            {
                statusLog.AddError("Error parsing credential, " + ex.Message + ", " + credentialNode.OuterXml);
            }
        }

        return(credentialManager);
    }
Beispiel #9
0
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="jobName">Name to associate with this work</param>
    /// <param name="onlineUrls"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    /// <param name="taskOptions"></param>
    /// <param name="manualActions"></param>
    public TaskMaster(
        string jobName,
        TableauServerUrls onlineUrls, 
        string userName, 
        string password,
        TaskMasterOptions taskOptions,
        CustomerManualActionManager manualActions = null)
    {
        this.JobName = jobName;

        _manualActions = manualActions;
        if(_manualActions == null)
        {
            _manualActions = new CustomerManualActionManager();
        }
        //Get any export path
        _exportToLocalPath = taskOptions.GetOptionValue(TaskMasterOptions.OptionParameter_PathDownloadTo);
        _onlineUrls = onlineUrls;
        _userName = userName;
        _password = password;

        //Store the status log at the class level where it is accessable
        _statusLog = new TaskStatusLogs();
        //Store the options
        _taskOptions = taskOptions;

        if(_taskOptions.IsOptionSet(TaskMasterOptions.Option_LogVerbose))
        {
            _statusLog.SetStatusLoggingLevel(int.MinValue);
        }
    }
Beispiel #10
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="pathTwbx">TWBX we are going to unpack</param>
 /// <param name="workingDirectory"></param>
 public TwbDataSourceEditor(string pathTwbInput, string pathTwbOutput, ITableauServerSiteInfo serverMapInfo, TaskStatusLogs statusLog)
 {
     _pathToTwbInput  = pathTwbInput;
     _pathToTwbOutput = pathTwbOutput;
     _serverMapInfo   = serverMapInfo;
     _statusLog       = statusLog;
 }
    /// <summary>
    /// Constructor.  Builds the data for the CSV file
    /// </summary>
    /// <param name="projects"></param>
    /// <param name="dataSources"></param>
    /// <param name="workbooks"></param>
    /// <param name="users"></param>
    /// <param name="groups"></param>
    public CustomerSiteInventory(
      IEnumerable<SiteProject> projects, 
      IEnumerable<SiteDatasource> dataSources,
      IEnumerable<SiteWorkbook> workbooks,
      IEnumerable<SiteUser> users,
      IEnumerable<SiteGroup> groups,
      TaskStatusLogs statusLogger)
  {
        //Somewhere to store status logs
        if (statusLogger == null)
        {
            statusLogger = new TaskStatusLogs();
        }
        this.StatusLog = statusLogger;

        //If we have a user-set, put it into a lookup class so we can quickly look up user names when we write out other data
        //that has user ids
        if(users != null)
        {
            _siteUserMapping = new KeyedLookup<SiteUser>(users);
        }

      AddProjectsData(projects);
      AddDatasourcesData(dataSources);
      AddWorkbooksData(workbooks);
      AddUsersData(users);
      AddGroupsData(groups);
  }
    /// <summary>
    /// Load the DB credentials set from a file
    /// </summary>
    /// <param name="pathDBCredentials"></param>
    /// <returns></returns>
    internal static CredentialManager LoadFromFile(string pathDBCredentials, TaskStatusLogs statusLog)
    {
        if(statusLog == null) 
        {
            statusLog = new TaskStatusLogs();
        }
        statusLog.AddStatus("Loading database credentials from " + pathDBCredentials);

        //Load the XML document and get the credentials
        var xDoc = new XmlDocument();
        xDoc.Load(pathDBCredentials);
        var nodesList =  xDoc.SelectNodes("//credential");

        var credentialManager = new CredentialManager();
        foreach (XmlNode credentialNode in nodesList)
        {
            try
            {
                helper_parseCredentialNode(credentialManager, credentialNode);
            }
            catch(Exception ex)
            {
                statusLog.AddError("Error parsing credential, " + ex.Message + ", " + credentialNode.OuterXml);
            }
        }

        return credentialManager;
    }
    /// <summary>
    /// Constructor.  Builds the data for the CSV file
    /// </summary>
    /// <param name="projects"></param>
    /// <param name="dataSources"></param>
    /// <param name="workbooks"></param>
    /// <param name="users"></param>
    /// <param name="groups"></param>
    public CustomerSiteInventory(
        IEnumerable <SiteProject> projects,
        IEnumerable <SiteDatasource> dataSources,
        IEnumerable <SiteWorkbook> workbooks,
        IEnumerable <SiteUser> users,
        IEnumerable <SiteGroup> groups,
        TaskStatusLogs statusLogger)
    {
        //Somewhere to store status logs
        if (statusLogger == null)
        {
            statusLogger = new TaskStatusLogs();
        }
        this.StatusLog = statusLogger;

        //If we have a user-set, put it into a lookup class so we can quickly look up user names when we write out other data
        //that has user ids
        if (users != null)
        {
            _siteUserMapping = new KeyedLookup <SiteUser>(users);
        }

        AddProjectsData(projects);
        AddDatasourcesData(dataSources);
        AddWorkbooksData(workbooks);
        AddUsersData(users);
        AddGroupsData(groups);
    }
Beispiel #14
0
    /// <summary>
    /// If we have Project Mapping information, generate a project based path for the download
    /// </summary>
    /// <param name="basePath"></param>
    /// <param name="projectList"></param>
    /// <param name="projectId"></param>
    /// <returns></returns>
    public static string EnsureProjectBasedPath(string basePath, IProjectsList projectList, IHasProjectId project, TaskStatusLogs statusLog)
    {
        //If we have no project list to do lookups in then just return the base path
        if (projectList == null) return basePath;

        //Look up the project name
        var projWithId = projectList.FindProjectWithId(project.ProjectId);
        if(projWithId == null)
        {
            statusLog.AddError("Project not found with id " + project.ProjectId);
            return basePath;
        }

        //Turn the project name into a directory name
        var safeDirectoryName = GenerateWindowsSafeFilename(projWithId.Name);

        var pathWithProject = Path.Combine(basePath, safeDirectoryName);
        //If needed, create the directory
        if(!Directory.Exists(pathWithProject))
        {
            Directory.CreateDirectory(pathWithProject);
        }

        return pathWithProject;
    }
Beispiel #15
0
    //readonly CsvDataGenerator _csvProvisionResults = null;

    /*/// <summary>
     * /// CSV for generated report
     * /// </summary>
     * public CsvDataGenerator CSVResultsReport
     * {
     *  get
     *  {
     *      return _csvProvisionResults;
     *  }
     * }
     */

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="config"></param>
    /// <param name="configSyncGroups"></param>
    /// <param name="showLogsHere"></param>
    /// <param name="statusLogs"></param>
    /// <param name="ignoreAllUsersGroup">(True recommended) Do not export the 'all users' group</param>
    public TableauProvisionDownload(
        ProvisionConfigSiteAccess config,
        IShowLogs showLogsHere,
        TaskStatusLogs statusLogs,
        bool ignoreAllUsersGroup = true)
    {
        _ignoreAllUsersGroupInExport = ignoreAllUsersGroup;
        _showLogsHere         = showLogsHere;
        _configTableauSecrets = config;

        if (statusLogs == null)
        {
            statusLogs = new TaskStatusLogs();
        }
        _statusLogs = statusLogs;

        //Either use one passed in, or create one

        /*if (csvDataGenerator == null)
         * {
         *  csvDataGenerator = new CsvDataGenerator();
         * }
         */
        //_csvProvisionResults = csvDataGenerator;
    }
Beispiel #16
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="pathTwbInput">TWB we are going to load and transform</param>
 /// <param name="pathTwbOutput">Output path for transformed CSV</param>
 /// <param name="oldDatasourceFilename">Old filename for the data source (case insensitive)</param>
 /// <param name="newCsvPath">Path to CSV file that we want the data source to point to</param>
 /// <param name="statusLog">Log status and errors here</param>
 public TwbReplaceCSVReference(string pathTwbInput, string pathTwbOutput, string oldDatasourceFilename, string newCsvPath, TaskStatusLogs statusLog)
 {
     _pathToTwbInput        = pathTwbInput;
     _pathToTwbOutput       = pathTwbOutput;
     _oldDatasourceFilename = oldDatasourceFilename;
     _datasourceNewCsvPath  = newCsvPath;
     _statusLog             = statusLog;
 }
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="pathTwbInput">TWB we are going to load and transform</param>
 /// <param name="pathTwbOutput">Output path for transformed CSV</param>
 /// <param name="dataSourceName">Name of data source inside path</param>
 /// <param name="newCsvPath">Path to CSV file that we want the data source to point to</param>
 /// <param name="statusLog">Log status and errors here</param>
 public TwbReplaceCSVReference(string pathTwbInput, string pathTwbOutput, string dataSourceName, string newCsvPath, TaskStatusLogs statusLog)
 {
     _pathToTwbInput = pathTwbInput;
     _pathToTwbOutput = pathTwbOutput;
     _datasourceName = dataSourceName;
     _datasourceNewCsvPath = newCsvPath;
     _statusLog = statusLog;
 }
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="onlineUrls"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    /// <param name="statusLog"></param>
    public TableauServerSignIn(TableauServerUrls onlineUrls, string userName, string password, TaskStatusLogs statusLog)
    {
        if (statusLog == null) { statusLog = new TaskStatusLogs(); }
        this.StatusLog = statusLog;

        _onlineUrls = onlineUrls;
        _userName = userName;
        _password = password;
        SiteUrlSegment = onlineUrls.SiteUrlSegement;
    }
    /// <summary>
    /// Synchronous call to test and make sure sign in works
    /// </summary>
    /// <param name="url"></param>
    /// <param name="userId"></param>
    /// <param name="userPassword"></param>
    /// <param name="statusLog"></param>
    public static void VerifySignInPossible(string url, string userId, string userPassword, TaskStatusLogs statusLog)
    {
        var urlManager = TableauServerUrls.FromContentUrl(url, TaskMasterOptions.RestApiReponsePageSizeDefault);
        var signIn = new TableauServerSignIn(urlManager, userId, userPassword, statusLog);
        bool success = signIn.ExecuteRequest();

        if(!success)
        {
            throw new Exception("Failed sign in");
        }
    }
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="config"></param>
    /// <param name="provisionInstructions"></param>
    /// <param name="showLogsHere"></param>
    /// <param name="statusLogs"></param>
    public ProvisionSite(ProvisionConfigSiteAccess config, ProvisionUserInstructions provisionInstructions, IShowLogs showLogsHere, TaskStatusLogs statusLogs)
    {
        _showLogsHere          = showLogsHere;
        _config                = config;
        _provisionInstructions = provisionInstructions;

        if (statusLogs == null)
        {
            statusLogs = new TaskStatusLogs();
        }
        _statusLogs = statusLogs;
    }
Beispiel #21
0
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="pathTwbx">TWBX we are going to unpack</param>
    /// <param name="workingDirectory"></param>
    public TwbxDataSourceEditor(string pathTwbx, string workingDirectory, ITableauServerSiteInfo serverInfo, TaskStatusLogs statusLog)
    {
        _pathToTwbx   = pathTwbx;
        _pathToWorkIn = workingDirectory;
        _statusLog    = statusLog;
        _serverInfo   = serverInfo;

        if (!File.Exists(_pathToTwbx))
        {
            throw new ArgumentException("Original file does not exist " + _pathToTwbx);
        }
    }
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="pathTwbx">TWBX we are going to unpack</param>
    /// <param name="workingDirectory"></param>
    public TwbxDataSourceEditor(string pathTwbx, string workingDirectory, ITableauServerSiteInfo serverInfo, TaskStatusLogs statusLog)
    {
        _pathToTwbx = pathTwbx;
        _pathToWorkIn = workingDirectory;
        _statusLog = statusLog;
        _serverInfo = serverInfo;

        if (!File.Exists(_pathToTwbx))
        {
            throw new ArgumentException("Original file does not exist " + _pathToTwbx);
        }
    }
Beispiel #23
0
        /// <summary>
        /// Shows status text in the textboxes
        /// </summary>
        /// <param name="statusLog"></param>
        private void UpdateStatusText(TaskStatusLogs statusLog, bool forceUIRefresh = false)
        {
            textBoxStatus.Text = statusLog.StatusText;
            ScrollToEndOfTextbox(textBoxStatus);

            textBoxErrors.Text = statusLog.ErrorText;

            if (forceUIRefresh)
            {
                textBoxStatus.Refresh();
                textBoxErrors.Refresh();
            }
        }
        /// <summary>
        /// Create a sign in manager for the given user
        /// </summary>
        /// <param name="url">Tableau site url</param>
        /// <param name="username">Tableau username</param>
        /// <param name="password">Tableau user's password</param>
        /// <param name="statusLog">Status log</param>
        public TableauServerSignIn(TableauServerUrls url, string username, string password, TaskStatusLogs statusLog)
        {
            if (statusLog == null)
            {
                statusLog = new TaskStatusLogs();
            }
            StatusLog = statusLog;

            _onlineUrls     = url;
            _userName       = username;
            _password       = password;
            _siteUrlSegment = url.SiteUrlSegement;
        }
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="onlineUrls"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    /// <param name="statusLog"></param>
    public TableauServerSignIn(TableauServerUrls onlineUrls, string userName, string password, TaskStatusLogs statusLog)
    {
        if (statusLog == null)
        {
            statusLog = new TaskStatusLogs();
        }
        this.StatusLog = statusLog;

        _onlineUrls    = onlineUrls;
        _userName      = userName;
        _password      = password;
        SiteUrlSegment = onlineUrls.SiteUrlSegement;
    }
Beispiel #26
0
        /// <summary>
        /// Sanity cehck for sign in
        /// </summary>
        /// <param name="siteUrl"></param>
        /// <param name="signInUser"></param>
        /// <param name="signInPassword"></param>
        /// <returns></returns>
        private bool ValidateSignInPossible(string siteUrl, bool useAccessToken, string signInUser, string signInPassword)
        {
            var testSignInStatusLog = new TaskStatusLogs();

            testSignInStatusLog.SetStatusLoggingLevel(int.MinValue);
            try
            {
                TableauServerSignIn.VerifySignInPossible(siteUrl, useAccessToken, signInUser, signInPassword, testSignInStatusLog);
            }
            catch
            {
                MessageBox.Show("Sign in to your Tableau Server failed. Please check URL and credentials");
                textBoxStatus.Text = testSignInStatusLog.StatusText;
                textBoxErrors.Text = testSignInStatusLog.ErrorText;
                return(false);
            }
            return(true);
        }
Beispiel #27
0
        /// <summary>
        /// Called to run us in commandn line mode
        /// </summary>
        /// <param name="commandLine"></param>
        internal void RunStartupCommandLine_Inner(TaskStatusLogs statusLogs)
        {
            statusLogs.AddStatusHeader("Processing command line");
            string pathProvisionPlan = AppSettings.CommandLine_PathProvisionPlan;
            string pathSecrets       = AppSettings.CommandLine_PathSecrets;
            string pathOutput        = AppSettings.CommandLine_PathOutput;

            //If an output directory was not specified, then output into an "out" subdirectory in the directory where the provision plan is
            if (string.IsNullOrWhiteSpace(pathOutput))
            {
                pathOutput = Path.Combine(
                    Path.GetDirectoryName(pathProvisionPlan),
                    "out");
            }

            //====================================================================================
            //Based on the command specified, run the specified task
            //====================================================================================
            switch (AppSettings.CommandLine_Command)
            {
            case CommandLineParser.Command_ProvisionFromAzure:
                //Update the paths in the UI so the user can see & re-run them if they want
                txtPathToSecrets.Text = pathSecrets;
                txtPathToAzureAdProvisioningConfig.Text = pathProvisionPlan;

                //Run the work...
                ProvisionFromAzureAd(statusLogs, pathSecrets, pathProvisionPlan, pathOutput);
                break;

            case CommandLineParser.Command_ProvisionFromFileManifest:
                //Update the paths in the UI so the user can see & re-run them if they want
                txtPathToSecrets.Text = pathSecrets;
                txtPathToFileProvisioningConfig.Text = pathProvisionPlan;

                //Run the work...
                ProvisionFromFileManifest(statusLogs, pathSecrets, pathProvisionPlan, pathOutput);
                break;

            default:
                statusLogs.AddError("1101-432: Unknown command: " + AppSettings.CommandLine_Command);
                break;
            }
        }
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="onlineUrls"></param>
    /// <param name="signInClientId">Email or Token name</param>
    /// <param name="signInSecret">Password or Secret Token</param>
    /// <param name="statusLog"></param>
    public TableauServerSignIn(
        TableauServerUrls onlineUrls,
        string signInClientId,
        string signInSecret,
        TaskStatusLogs statusLog,
        SignInMode signInMode = SignInMode.UserNameAndPassword)
    {
        if (statusLog == null)
        {
            statusLog = new TaskStatusLogs();
        }
        this.StatusLog = statusLog;

        _onlineUrls     = onlineUrls;
        _signInClientId = signInClientId;
        _signInSecret   = signInSecret;
        _signInMode     = signInMode;
        SiteUrlSegment  = onlineUrls.SiteUrlSegement;
    }
    /// <summary>
    /// Finds and changes a datasource reference inside a Workbook. Changes the CSV file the data source points to
    /// </summary>
    /// <param name="xmlDoc"></param>
    /// <param name="datasourceName"></param>
    /// <param name="pathToTargetCsv"></param>
    /// <param name="statusLog"></param>
    private bool RemapDatasourceCsvReference(XmlDocument xmlDoc, string datasourceName, string pathToTargetCsv, TaskStatusLogs statusLog)
    {
        int replaceItemCount = 0;
        string newCsvDirectory = Path.GetDirectoryName(_datasourceNewCsvPath);
        string newCsvFileName = Path.GetFileName(_datasourceNewCsvPath);
        string newDatasourceRelationName = Path.GetFileNameWithoutExtension(newCsvFileName) + "#csv";
        string newDatasourceRelationTable = "[" + newDatasourceRelationName + "]";
        string seekDatasourceCaption = _datasourceName;

        var xDataSources = xmlDoc.SelectNodes("workbook/datasources/datasource");
        if(xDataSources != null)
        {
            //Look through the data sources
            foreach (XmlNode xnodeDatasource in xDataSources)
            {
                //If the data source is matching the caption we are looking for
                if(XmlHelper.SafeParseXmlAttribute(xnodeDatasource, "caption", "") == seekDatasourceCaption)
                {
                    var xnodeConnection = xnodeDatasource.SelectSingleNode("connection");
                    //It should be 'textscan', it would be unexpected if it were not
                    if(XmlHelper.SafeParseXmlAttribute(xnodeConnection, "class", "") == "textscan")
                    {
                        //Point to the new directory/path
                        xnodeConnection.Attributes["directory"].Value = newCsvDirectory;
                        xnodeConnection.Attributes["filename"].Value = newCsvFileName;

                        //And it's got a Relation we need to update
                        var xNodeRelation = xnodeConnection.SelectSingleNode("relation");
                        xNodeRelation.Attributes["name"].Value = newDatasourceRelationName;
                        xNodeRelation.Attributes["table"].Value = newDatasourceRelationTable;

                        replaceItemCount++;
                    }
                    else
                    {
                        _statusLog.AddError("Data source remap error. Expected data source to be 'textscan'");
                    }
                }//end if
            }//end foreach
        }//end if

        return replaceItemCount > 0;        
    }
    /// <summary>
    /// Remaps global references in the workbook that refer to the site/server
    /// </summary>
    /// <param name="xmlDoc"></param>
    /// <param name="serverMapInfo"></param>
    /// <param name="statusLog"></param>
    private static void RemapWorkbookGlobalReferences(XmlDocument xmlDoc, ITableauServerSiteInfo serverMapInfo, TaskStatusLogs statusLog)
    {
        var xnodeWorkbook = xmlDoc.SelectSingleNode("//workbook");
        if(xnodeWorkbook == null)
        {
            statusLog.AddError("Workbook remapper, 'workbook' node not found");
            return;
        }

        //See if there is an an XML base node
        var attrXmlBase = xnodeWorkbook.Attributes["xml:base"];
        if(attrXmlBase != null)
        {
            attrXmlBase.Value = serverMapInfo.ServerNameWithProtocol;
        }

        //We may also have a repository node
        RemapSingleWorkbooksRepositoryNode(xmlDoc, xnodeWorkbook, serverMapInfo, true, statusLog);
    }
    //Update to: -<connection username="" server="preview-online.tableau.com" port="443" directory="/dataserver" dbname="At-Task60days" class="sqlproxy" channel="https">
    private static void RemapDataServerReferences(XmlDocument xmlDoc, ITableauServerSiteInfo serverMapInfo, TaskStatusLogs statusLog)
    {
        var xDataSources = xmlDoc.SelectNodes("workbook/datasources/datasource");
        foreach (XmlNode xnodeDatasource in xDataSources)
        {
            var xnodeConnection = xnodeDatasource.SelectSingleNode("connection");
            //Not all datasources have connection nodes (e.g. the parameters data source). If there is no connection, there is nothing to do
            if(xnodeConnection != null)
            { 
                string dbClass = xnodeConnection.Attributes["class"].Value;

                //If its 'sqlproxy' then its a data server connection
                if (dbClass == "sqlproxy")
                {
                    //Start remapping....
                    RemapSingleDataServerConnectionNode(xnodeDatasource, serverMapInfo, statusLog);
                    RemapSingleDataServerRepositoryNode(xmlDoc, xnodeDatasource, serverMapInfo, false, statusLog);
                }
            }
        }
    }
Beispiel #32
0
        /// <summary>
        /// Called to run a command line task
        /// </summary>
        internal void RunStartupCommandLine()
        {
            var statusLogs = new TaskStatusLogs();

            try
            {
                RunStartupCommandLine_Inner(statusLogs);
            }
            catch (Exception ex)
            {
                IwsDiagnostics.Assert(false, "1101-445: Command line error: " + ex.Message);
            }

            //If we are supposed to exit, then do so...
            if (AppSettings.CommandLine_ExitWhenDone)
            {
                ExitApplication();
                return;
            }

            UpdateStatusText(statusLogs);
        }
Beispiel #33
0
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="config"></param>
    /// <param name="configSyncGroups"></param>
    /// <param name="showLogsHere"></param>
    /// <param name="statusLogs"></param>
    /// <param name="csvDataGenerator"></param>
    public AzureDownload(
        AzureAdConfig config,
        ProvisionConfigExternalDirectorySync configSyncGroups,
        IShowLogs showLogsHere,
        TaskStatusLogs statusLogs,
        CsvDataGenerator csvDataGenerator)
    {
        _showLogsHere     = showLogsHere;
        _configAzure      = config;
        _configSyncGroups = configSyncGroups;

        if (statusLogs == null)
        {
            statusLogs = new TaskStatusLogs();
        }
        _statusLogs = statusLogs;

        //Either use one passed in, or create one
        if (csvDataGenerator == null)
        {
            csvDataGenerator = new CsvDataGenerator();
        }
        _csvProvisionResults = csvDataGenerator;
    }
    /// <summary>
    /// Remaps necesary attributes inside of the datasource->connection node to point to a new server
    /// </summary>
    /// <param name="xDSourceConnection"></param>
    /// <param name="serverMapInfo"></param>
    /// <param name="statusLog"></param>
    private static void RemapSingleDataServerConnectionNode(XmlNode xNodeDatasource, ITableauServerSiteInfo serverMapInfo, TaskStatusLogs statusLog)
    {
        //Get the XML sub mode we need
        var xNodeConnection = xNodeDatasource.SelectSingleNode("connection");
        if(xNodeConnection == null)
        {
            statusLog.AddError("Workbook remapper, no 'connection' node found");
            return;
        }
        //====================================================================================
        //PORT NUMBER
        //====================================================================================
        var attrPort = xNodeConnection.Attributes["port"];
        if(attrPort != null)
        {
            if(serverMapInfo.Protocol == ServerProtocol.http)
            {
                attrPort.Value = "80";
            }
            else if (serverMapInfo.Protocol == ServerProtocol.https)
            {
                attrPort.Value = "443";
            }
            else
            {
                statusLog.AddError("Workbook remapper, unknown protocol");
            }
        }
        else
        {
            statusLog.AddError("Workbook remapper, missing attribute 'port'");
        }

        //====================================================================================
        //Server name
        //====================================================================================
        var attrServer = xNodeConnection.Attributes["server"];
        if (attrServer != null)
        {
            attrServer.Value = serverMapInfo.ServerName;
        }
        else
        {
            statusLog.AddError("Workbook remapper, missing attribute 'server'");
        }

        //====================================================================================
        //Channel
        //====================================================================================
        var attrChannel = xNodeConnection.Attributes["channel"];
        if (attrChannel != null)
        {
            if (serverMapInfo.Protocol == ServerProtocol.http)
            {
                attrChannel.Value = "http";
            }
            else if (serverMapInfo.Protocol == ServerProtocol.https)
            {
                attrChannel.Value = "https";
            }
            else
            {
                statusLog.AddError("Workbook remapper, unknown protocol");
            }

        }
        else
        {
            statusLog.AddError("Workbook remapper, missing attribute 'channel'");
        }
    }
 /// <summary>
 /// Sanity cehck for sign in 
 /// </summary>
 /// <param name="siteUrl"></param>
 /// <param name="signInUser"></param>
 /// <param name="signInPassword"></param>
 /// <returns></returns>
 private bool ValidateSignInPossible(string siteUrl, string signInUser, string signInPassword)
 {
     var testSignInStatusLog = new TaskStatusLogs();
     testSignInStatusLog.SetStatusLoggingLevel(int.MinValue);
     try
     {
         TableauServerSignIn.VerifySignInPossible(siteUrl, signInUser, signInPassword, testSignInStatusLog);
     }
     catch
     {
         MessageBox.Show("Sign in to your Tableau Server failed. Please check URL and credentials");
         textBoxStatus.Text = testSignInStatusLog.StatusText;
         textBoxErrors.Text = testSignInStatusLog.ErrorText;
         return false;
     }
     return true;
 }
        /// <summary>
        /// Shows status text in the textboxes
        /// </summary>
        /// <param name="statusLog"></param>
        private void UpdateStatusText(TaskStatusLogs statusLog)
        {
            textBoxStatus.Text = statusLog.StatusText;
            ScrollToEndOfTextbox(textBoxStatus);

            textBoxErrors.Text = statusLog.ErrorText;
        }
Beispiel #37
0
    /// <summary>
    /// Attempt to log any detailed information we find about the failed web request
    /// </summary>
    /// <param name="webException"></param>
    /// <param name="onlineStatusLog"></param>
    private static void AttemptToLogWebException(WebException webException, string description, TaskStatusLogs onlineStatusLog)
    {
        if (onlineStatusLog == null)
        {
            return;                         //No logger? nothing to do
        }
        try
        {
            if (string.IsNullOrWhiteSpace(description))
            {
                description = "web request failed";
            }
            var response     = webException.Response;
            var responseText = GetWebResponseAsText(response);
            response.Close();
            if (responseText == null)
            {
                responseText = "";
            }

            onlineStatusLog.AddError(description + ": " + webException.Message + "\r\n" + responseText + "\r\n");
        }
        catch (Exception ex)
        {
            onlineStatusLog.AddError("Error in web request exception: " + ex.Message);
            return;
        }
    }
Beispiel #38
0
    /// <summary>
    /// Attempt to log any detailed information we find about the failed web request
    /// </summary>
    /// <param name="webException"></param>
    /// <param name="onlineStatusLog"></param>
    private static void AttemptToLogWebException(WebException webException, string description, TaskStatusLogs onlineStatusLog)
    {
        if (onlineStatusLog == null)
        {
            return;                         //No logger? nothing to do
        }
        try
        {
            if (string.IsNullOrWhiteSpace(description))
            {
                description = "web request failed";
            }
            string responseText = "";

            //NOTE: In some cases (e.g. time-out) the response may be NULL
            var response = webException.Response;
            if (response != null)
            {
                responseText = GetWebResponseAsText(response);
                response.Close();
            }

            //Cannonicalize a blank result...
            if (string.IsNullOrEmpty(responseText))
            {
                responseText = "";
            }

            onlineStatusLog.AddError(description + ": " + webException.Message + "\r\n" + responseText + "\r\n");
        }
        catch (Exception ex)
        {
            onlineStatusLog.AddError("811-830: Error in web request exception: " + ex.Message);
            return;
        }
    }
Beispiel #39
0
        /// <summary>
        /// Generate a maniefest file based on the current Online site
        /// </summary>
        /// <param name="statusLogs"></param>
        /// <param name="pathSecrets"></param>
        /// <param name="pathOutputFile"></param>
        /// <param name="ignoreAllUsersGroup">(recommend TRUE) If false, the manifest file will contain the "all users" group</param>
        private void GenerateManifestFromOnlineSite(
            TaskStatusLogs statusLogs,
            string pathSecrets,
            string pathOutputFile,
            bool ignoreAllUsersGroup)
        {
            var pathOutputs = Path.GetDirectoryName(pathOutputFile);

            ProvisionConfigSiteAccess secretsConfig;

            //===========================================================================================
            //Get the sign in information
            //===========================================================================================
            try
            {
                //Load the config from the files
                secretsConfig = new ProvisionConfigSiteAccess(pathSecrets);
            }
            catch (Exception exSignInConfig)
            {
                statusLogs.AddError("Error loading sign in config file");
                throw new Exception("1012-327: Error parsing sign in config, " + exSignInConfig.Message);
            }


            //===========================================================================================
            //Create a place for out output files
            //===========================================================================================
            FileIOHelper.CreatePathIfNeeded(pathOutputs);


            var provisionSettings = ProvisionConfigExternalDirectorySync.FromDefaults();

            //===========================================================================================
            //Download all the data we need from the Tableau Online site
            //===========================================================================================
            statusLogs.AddStatusHeader("Retrieving information from Tableau");
            UpdateStatusText(statusLogs);
            var tableauDownload = new TableauProvisionDownload(
                secretsConfig,
                this,
                statusLogs,
                ignoreAllUsersGroup);

            try
            {
                tableauDownload.Execute();
            }
            catch (Exception exTableauDownload)
            {
                statusLogs.AddError("Error retrieving data from Tableau");
                throw new Exception("813-0148: Error in Tableau Download, " + exTableauDownload.Message);
            }


            //===========================================================================================
            //Write the provisioning manifest out to a file
            //===========================================================================================
            statusLogs.AddStatusHeader("Writing out manifest file for Tableau provisioning");
            UpdateStatusText(statusLogs);
            var outputProvisioningRoles = tableauDownload.ProvisioningManifestResults;

            try
            {
                outputProvisioningRoles.GenerateProvisioningManifestFile(pathOutputFile, provisionSettings);
            }
            catch (Exception exWriteProvisioningManifest)
            {
                statusLogs.AddError("Error creating provisioning manifest");
                throw new Exception("1012-252: Error writing provisioning manifest, " + exWriteProvisioningManifest.Message);
            }
        }
    /// <summary>
    /// Attempt to log any detailed information we find about the failed web request
    /// </summary>
    /// <param name="webException"></param>
    /// <param name="onlineStatusLog"></param>
    private static void AttemptToLogWebException(WebException webException, string description, TaskStatusLogs onlineStatusLog)
    {
        if(onlineStatusLog == null) return; //No logger? nothing to do

        try
        {
            if(string.IsNullOrWhiteSpace(description))
            {
                description = "web request failed";
            }
            var response = webException.Response;
            var responseText = GetWebResponseAsText(response);
            response.Close();
            if(responseText == null) responseText = "";

            onlineStatusLog.AddError(description +  ": " + webException.Message + "\r\n" + responseText + "\r\n");
        }
        catch (Exception ex)
        {
            onlineStatusLog.AddError("Error in web request exception: " + ex.Message);
            return;
        }
    }
Beispiel #41
0
        /// <summary>
        /// Called to generate a provisioning manifest file from a Tableau site
        /// This is useful for creating a "backup" of the sites existing users/groups
        /// provisioning
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCreateBackupManifestFile_Click(object sender, EventArgs e)
        {
            var statusLogs = new TaskStatusLogs();

            statusLogs.AddStatus("Starting...");
            UpdateStatusText(statusLogs, true);
            bool ignoreAllUsersGroup = chkIgoreAllUsersGroup.Checked;


            string pathSecrets = txtPathToSecrets.Text;

            if (!File.Exists(pathSecrets))
            {
                MessageBox.Show("Secrets file does not exist at specified path (" + pathSecrets + ")");
                return;
            }


            string pathOutputManifestFile = txtPathToGenerateManifestFile.Text;

            //If they gave us a directory, not a file-name, then add filename to it
            if (Directory.Exists(pathOutputManifestFile))
            {
                pathOutputManifestFile = Path.Combine(
                    pathOutputManifestFile,
                    FileIOHelper.FilenameWithDateTimeUnique("SiteProvisionManifest.xml"));
                //Show it in the UI
                txtPathToGenerateManifestFile.Text = pathOutputManifestFile;
            }


            var pathOutput = Path.GetDirectoryName(pathOutputManifestFile);

            FileIOHelper.CreatePathIfNeeded(pathOutput);


            //Show the user a command line that they can use to run this same work
            GenerateProvisioningCommandLine(
                CommandLineParser.Command_GenerateManifestFromOnlineSite,
                pathSecrets,
                pathOutputManifestFile,
                pathOutput,
                ignoreAllUsersGroup);

            //Run the work
            try
            {
                GenerateManifestFromOnlineSite(
                    statusLogs,
                    pathSecrets,
                    pathOutputManifestFile,
                    ignoreAllUsersGroup);
            }
            catch (Exception exError)
            {
                MessageBox.Show("Error: " + exError.Message);
            }

            UpdateStatusText(statusLogs, true);

            //Open the file explorer to the output directory
            if (Directory.Exists(pathOutput))
            {
                System.Diagnostics.Process.Start(pathOutput);
            }
        }
Beispiel #42
0
    //Update to: -<connection username="" server="preview-online.tableau.com" port="443" directory="/dataserver" dbname="At-Task60days" class="sqlproxy" channel="https">
    private static void RemapDataServerReferences(XmlDocument xmlDoc, ITableauServerSiteInfo serverMapInfo, TaskStatusLogs statusLog)
    {
        var xDataSources = xmlDoc.SelectNodes("workbook/datasources/datasource");

        foreach (XmlNode xnodeDatasource in xDataSources)
        {
            var xnodeConnection = xnodeDatasource.SelectSingleNode("connection");
            //Not all datasources have connection nodes (e.g. the parameters data source). If there is no connection, there is nothing to do
            if (xnodeConnection != null)
            {
                string dbClass = xnodeConnection.Attributes["class"].Value;

                //If its 'sqlproxy' then its a data server connection
                if (dbClass == "sqlproxy")
                {
                    //Start remapping....
                    RemapSingleDataServerConnectionNode(xnodeDatasource, serverMapInfo, statusLog);
                    RemapSingleDataServerRepositoryNode(xmlDoc, xnodeDatasource, serverMapInfo, false, statusLog);
                }
            }
        }
    }
Beispiel #43
0
    /// <summary>
    /// Remaps global references in the workbook that refer to the site/server
    /// </summary>
    /// <param name="xmlDoc"></param>
    /// <param name="serverMapInfo"></param>
    /// <param name="statusLog"></param>
    private static void RemapWorkbookGlobalReferences(XmlDocument xmlDoc, ITableauServerSiteInfo serverMapInfo, TaskStatusLogs statusLog)
    {
        var xnodeWorkbook = xmlDoc.SelectSingleNode("//workbook");

        if (xnodeWorkbook == null)
        {
            statusLog.AddError("Workbook remapper, 'workbook' node not found");
            return;
        }

        //See if there is an an XML base node
        var attrXmlBase = xnodeWorkbook.Attributes["xml:base"];

        if (attrXmlBase != null)
        {
            attrXmlBase.Value = serverMapInfo.ServerNameWithProtocol;
        }

        //We may also have a repository node
        RemapSingleWorkbooksRepositoryNode(xmlDoc, xnodeWorkbook, serverMapInfo, true, statusLog);
    }
Beispiel #44
0
    /// <summary>
    /// Remaps necesary attributes inside of the datasource->connection node to point to a new server
    /// </summary>
    /// <param name="xDSourceConnection"></param>
    /// <param name="serverMapInfo"></param>
    /// <param name="statusLog"></param>
    private static void RemapSingleDataServerConnectionNode(XmlNode xNodeDatasource, ITableauServerSiteInfo serverMapInfo, TaskStatusLogs statusLog)
    {
        //Get the XML sub mode we need
        var xNodeConnection = xNodeDatasource.SelectSingleNode("connection");

        if (xNodeConnection == null)
        {
            statusLog.AddError("Workbook remapper, no 'connection' node found");
            return;
        }
        //====================================================================================
        //PORT NUMBER
        //====================================================================================
        var attrPort = xNodeConnection.Attributes["port"];

        if (attrPort != null)
        {
            if (serverMapInfo.Protocol == ServerProtocol.http)
            {
                attrPort.Value = "80";
            }
            else if (serverMapInfo.Protocol == ServerProtocol.https)
            {
                attrPort.Value = "443";
            }
            else
            {
                statusLog.AddError("Workbook remapper, unknown protocol");
            }
        }
        else
        {
            statusLog.AddError("Workbook remapper, missing attribute 'port'");
        }

        //====================================================================================
        //Server name
        //====================================================================================
        var attrServer = xNodeConnection.Attributes["server"];

        if (attrServer != null)
        {
            attrServer.Value = serverMapInfo.ServerName;
        }
        else
        {
            statusLog.AddError("Workbook remapper, missing attribute 'server'");
        }

        //====================================================================================
        //Channel
        //====================================================================================
        var attrChannel = xNodeConnection.Attributes["channel"];

        if (attrChannel != null)
        {
            if (serverMapInfo.Protocol == ServerProtocol.http)
            {
                attrChannel.Value = "http";
            }
            else if (serverMapInfo.Protocol == ServerProtocol.https)
            {
                attrChannel.Value = "https";
            }
            else
            {
                statusLog.AddError("Workbook remapper, unknown protocol");
            }
        }
        else
        {
            statusLog.AddError("Workbook remapper, missing attribute 'channel'");
        }
    }
    /// <summary>
    /// Synchronous call to test and make sure sign in works
    /// </summary>
    /// <param name="url"></param>
    /// <param name="userId"></param>
    /// <param name="userPassword"></param>
    /// <param name="statusLog"></param>
    public static void VerifySignInPossible(string url, string userId, string userPassword, TaskStatusLogs statusLog)
    {
        var  urlManager = TableauServerUrls.FromContentUrl(url, TaskMasterOptions.RestApiReponsePageSizeDefault);
        var  signIn     = new TableauServerSignIn(urlManager, userId, userPassword, statusLog);
        bool success    = signIn.Execute();

        if (!success)
        {
            throw new Exception("Failed sign in");
        }
    }
    /// <summary>
    /// Remaps the 'repository-location' node of the Data Source XML
    /// </summary>
    /// <param name="xnodeRepository"></param>
    /// <param name="serverMapInfo"></param>
    /// <param name="statusLog"></param>
    private static void RemapSingleWorkbooksRepositoryNode(XmlDocument xmlDoc, XmlNode xNodeDatasource, ITableauServerSiteInfo serverMapInfo, bool ignoreIfMissing, TaskStatusLogs statusLog)
    {
        var siteId = serverMapInfo.SiteId;

        //Get the XML sub mode we need
        var xnodeRepository = xNodeDatasource.SelectSingleNode("repository-location");
        if (xnodeRepository == null)
        {
            if (ignoreIfMissing) return;

            statusLog.AddError("Workbook remapper, no workbook 'repository-location' node found");
            return;
        }

        ///////////////////////////////////////////////////////////////////////////////////////
        helper_SetRespositorySite(xmlDoc, xnodeRepository, serverMapInfo);

        ///////////////////////////////////////////////////////////////////////////////////////
        var attrPath = xnodeRepository.Attributes["path"];
        if (attrPath != null)
        {
            //Is there a site specified
            if (!string.IsNullOrWhiteSpace(siteId))
            {
                attrPath.Value = "/t/" + siteId + "/workbooks";
            }
            else //Default site
            {
                attrPath.Value = "/workbooks";
            }
        }
        else
        {
            statusLog.AddError("Workbook remapper 'path' attribute not found");
        }
    }
Beispiel #47
0
    /// <summary>
    /// Remaps the 'repository-location' node of the Data Source XML
    /// </summary>
    /// <param name="xnodeRepository"></param>
    /// <param name="serverMapInfo"></param>
    /// <param name="statusLog"></param>
    private static void RemapSingleWorkbooksRepositoryNode(XmlDocument xmlDoc, XmlNode xNodeDatasource, ITableauServerSiteInfo serverMapInfo, bool ignoreIfMissing, TaskStatusLogs statusLog)
    {
        var siteId = serverMapInfo.SiteId;

        //Get the XML sub mode we need
        var xnodeRepository = xNodeDatasource.SelectSingleNode("repository-location");

        if (xnodeRepository == null)
        {
            if (ignoreIfMissing)
            {
                return;
            }

            statusLog.AddError("Workbook remapper, no workbook 'repository-location' node found");
            return;
        }

        ///////////////////////////////////////////////////////////////////////////////////////
        helper_SetRespositorySite(xmlDoc, xnodeRepository, serverMapInfo);

        ///////////////////////////////////////////////////////////////////////////////////////
        var attrPath = xnodeRepository.Attributes["path"];

        if (attrPath != null)
        {
            //Is there a site specified
            if (!string.IsNullOrWhiteSpace(siteId))
            {
                attrPath.Value = "/t/" + siteId + "/workbooks";
            }
            else //Default site
            {
                attrPath.Value = "/workbooks";
            }
        }
        else
        {
            statusLog.AddError("Workbook remapper 'path' attribute not found");
        }
    }