/// <summary> /// Constructor /// </summary> /// <param name="onlineUrls"></param> /// <param name="login"></param> /// <param name="workbook"></param> /// <param name="localSavePath"></param> public DownloadWorkbookThumbnail( TableauServerUrls onlineUrls, TableauServerSignIn login, SiteWorkbook workbook, string localSavePath) : base(login) { _onlineUrls = onlineUrls; _workbook = workbook; _localSavePathRoot = localSavePath; }
/// <summary> /// URL to download a workbook's view /// </summary> /// <param name="siteUrlSegment"></param> /// <returns></returns> public string Url_WorkbookViewThumbnailDownload(TableauServerSignIn session, SiteWorkbook contentInfo, string viewId) { string workingText = _urlDownloadWorkbookViewThumbnailTemplate; workingText = workingText.Replace("{{iwsSiteId}}", session.SiteId); workingText = workingText.Replace("{{iwsRepositoryId}}", contentInfo.Id); workingText = workingText.Replace("{{iwsViewId}}", viewId); ValidateTemplateReplaceComplete(workingText); return(workingText); }
private SiteWorkbook ParseWorkbookElement(XElement element) { try { var workbookXml = element.ToXmlNode(); var workbook = new SiteWorkbook(workbookXml, xmlNamespace); return(workbook); } catch (Exception e) { Console.WriteLine(e); throw; } }
/// <summary> /// Add data source connection data /// </summary> /// <param name="thisWorkbook"></param> private void AddWorkbookConnectionData(SiteWorkbook thisWorkbook) { var dataConnections = thisWorkbook.DataConnections; if (dataConnections == null) { return; } //Write out details for each data connection foreach (var thisConnection in dataConnections) { this.AddKeyValuePairs( new string[] { ContentType //1 , ContentId //2 , ContentConnectionType //3 , ContentConnectionServer //4 , ContentConnectionPort //5 , ContentConnectionUserName //6 , ContentWorkbookId //7 , ContentWorkbookName //8 , ContentProjectId //9 , ContentProjectName //10 , ContentOwnerId //11 , DeveloperNotes //12 }, new string[] { "data-connection" //1 , thisConnection.Id //2 , thisConnection.ConnectionType //3 , thisConnection.ServerAddress //4 , thisConnection.ServerPort //5 , thisConnection.UserName //6 , thisWorkbook.Id //7 , thisWorkbook.Name //8 , thisWorkbook.ProjectId //9 , thisWorkbook.ProjectName //10 , thisWorkbook.OwnerId //11 , thisWorkbook.DeveloperNotes //12 }); } }
/// <summary> /// Constructor /// </summary> /// <param name="onlineUrls"></param> /// <param name="login"></param> /// <param name="workbook"></param> /// <param name="localSavePath"></param> public DownloadWorkbookViewThumbnail( TableauServerUrls onlineUrls, TableauServerSignIn login, SiteWorkbook workbook, string viewId, string localSavePath) : base(login) { if (string.IsNullOrWhiteSpace(viewId)) { var errorText = "Download workbook view thumbnail, viewId cannot be blank. Workbook: " + workbook.Id; login.StatusLog.AddError(errorText); throw new Exception(errorText); } _onlineUrls = onlineUrls; _workbook = workbook; _localSavePathRoot = localSavePath; _viewId = viewId; }
/// <summary> /// If we have Project Mapping information, generate a project based path for the download /// </summary> /// <param name="basePath">File system location which will be the root of project paths.</param> /// <param name="workbook">Workbook record.</param> /// <param name="statusLog">Logging object.</param> /// <returns></returns> public static string EnsureProjectBasedPath(string basePath, SiteWorkbook workbook, TaskStatusLogs statusLog) { //If we have no project list to do lookups in then just return the base path if (workbook == null) { return(basePath); } //Turn the project name into a directory name var safeDirectoryName = GenerateWindowsSafeFilename(workbook.Name); var pathWithProject = Path.Combine(basePath, safeDirectoryName); //If needed, create the directory if (!Directory.Exists(pathWithProject)) { Directory.CreateDirectory(pathWithProject); } return(pathWithProject); }
/// <summary> /// Build a list of Workbooks /// </summary> /// <param name="xmlWorkbooksList"></param> /// <returns></returns> private List <SiteWorkbook> GenerateWorkbooksList(XmlNodeList xmlWorkbooksList) { var onlineWorkbooks = new List <SiteWorkbook>(); //Get information for each of the data sources foreach (XmlNode itemXml in xmlWorkbooksList) { try { var thisItem = new SiteWorkbook(itemXml); onlineWorkbooks.Add(thisItem); } catch { AppDiagnostics.Assert(false, "1024-1113: Workbook parse error"); _onlineSession.StatusLog.AddError("1024-1113: Error parsing workbook: " + itemXml.InnerXml); } } //end: foreach return(onlineWorkbooks); }
/// <summary> /// Downloads a single request for 1 workbook /// </summary> /// <param name="workbookId"></param> /// <returns></returns> public SiteWorkbook ExecuteRequest_SingleWorkbook(string workbookId) { //Sanity check if (string.IsNullOrWhiteSpace(workbookId)) { _onlineSession.StatusLog.AddError("Workbook ID required to query workbooks"); } //Create a web request, in including the users logged-in auth information in the request headers var urlQuery = _onlineUrls.Url_WorkbookInfo(_onlineSession, workbookId); _onlineSession.StatusLog.AddStatus("Web request: " + urlQuery, -10); var xmlDoc = ResourceSafe_PerformWebRequest_GetXmlDocument(urlQuery, "get workbooks list"); //var webRequest = CreateLoggedInWebRequest(urlQuery); //webRequest.Method = "GET"; //var response = GetWebReponseLogErrors(webRequest, "get workbooks list"); //var xmlDoc = GetWebResponseAsXml(response); //Get all the workbook nodes var nsManager = XmlHelper.CreateTableauXmlNamespaceManager("iwsOnline"); var workbooks = xmlDoc.SelectNodes("//iwsOnline:workbook", nsManager); //Get information for each of the data sources foreach (XmlNode itemXml in workbooks) { try { var wb = new SiteWorkbook(itemXml); //There is ONLY one workbook, so return it return(wb); } catch { AppDiagnostics.Assert(false, "Workbook parse error"); _onlineSession.StatusLog.AddError("Error parsing workbook: " + itemXml.InnerXml); return(null); } } //end: foreach return(null); }
/// <summary> /// Save Workbook metadata in a XML file along-side the workbook file /// </summary> /// <param name="wb">Information about the workbook we have downloaded</param> /// <param name="localWorkbookPath">Local path to the twb/twbx of the workbook</param> /// <param name="userLookups">If non-NULL contains the mapping of users/ids so we can look up the owner</param> internal static void CreateSettingsFile(SiteWorkbook wb, string localWorkbookPath, KeyedLookup<SiteUser> userLookups) { string contentOwnerName = null; //Start off assuming we have no content owner information if(userLookups != null) { contentOwnerName = helper_LookUpOwnerId(wb.OwnerId, userLookups); } var xml = System.Xml.XmlWriter.Create(PathForSettingsFile(localWorkbookPath)); xml.WriteStartDocument(); xml.WriteStartElement(XmlElement_WorkbookInfo); XmlHelper.WriteValueElement(xml, XmlElement_ShowTabsInWorkbook, wb.ShowTabs); //If we have an owner name, write it out if (!string.IsNullOrWhiteSpace(contentOwnerName)) { XmlHelper.WriteValueElement(xml, XmlElement_ContentOwner, contentOwnerName); } xml.WriteEndElement(); //end: WorkbookInfo xml.WriteEndDocument(); xml.Close(); }
/// <summary> /// Get a page's worth of Workbook listings /// </summary> /// <param name="onlineWorkbooks"></param> /// <param name="pageToRequest">Page # we are requesting (1 based)</param> /// <param name="totalNumberPages">Total # of pages of data that Server can return us</param> private void ExecuteRequest_ForPage(List <SiteWorkbook> onlineWorkbooks, int pageToRequest, out int totalNumberPages) { int pageSize = _onlineUrls.PageSize; //Create a web request, in including the users logged-in auth information in the request headers var urlQuery = _onlineUrls.Url_WorkbooksListForUser(_onlineSession, _userIdForWorkbookQuery, pageSize, pageToRequest, SortDirectiveForContentQuery()); _onlineSession.StatusLog.AddStatus("Web request: " + urlQuery, -10); var xmlDoc = ResourceSafe_PerformWebRequest_GetXmlDocument(urlQuery, "get workbooks list"); //var webRequest = CreateLoggedInWebRequest(urlQuery); //webRequest.Method = "GET"; //var response = GetWebReponseLogErrors(webRequest, "get workbooks list"); //var xmlDoc = GetWebResponseAsXml(response); //Get all the workbook nodes var nsManager = XmlHelper.CreateTableauXmlNamespaceManager("iwsOnline"); var workbooks = xmlDoc.SelectNodes("//iwsOnline:workbook", nsManager); //Get information for each of the data sources foreach (XmlNode itemXml in workbooks) { try { var ds = new SiteWorkbook(itemXml); onlineWorkbooks.Add(ds); } catch { AppDiagnostics.Assert(false, "Workbook parse error"); _onlineSession.StatusLog.AddError("Error parsing workbook: " + itemXml.InnerXml); } } //end: foreach //------------------------------------------------------------------- //Get the updated page-count //------------------------------------------------------------------- totalNumberPages = DownloadPaginationHelper.GetNumberOfPagesFromPagination( xmlDoc.SelectSingleNode("//iwsOnline:pagination", nsManager), pageSize); }
/// <summary> /// Save Workbook metadata in a XML file along-side the workbook file /// </summary> /// <param name="wb">Information about the workbook we have downloaded</param> /// <param name="localWorkbookPath">Local path to the twb/twbx of the workbook</param> /// <param name="userLookups">If non-NULL contains the mapping of users/ids so we can look up the owner</param> internal static void CreateSettingsFile(SiteWorkbook wb, string localWorkbookPath, KeyedLookup <SiteUser> userLookups) { string contentOwnerName = null; //Start off assuming we have no content owner information if (userLookups != null) { contentOwnerName = helper_LookUpOwnerId(wb.OwnerId, userLookups); } var xml = System.Xml.XmlWriter.Create(PathForSettingsFile(localWorkbookPath)); xml.WriteStartDocument(); xml.WriteStartElement(XmlElement_WorkbookInfo); XmlHelper.WriteValueElement(xml, XmlElement_ShowTabsInWorkbook, wb.ShowTabs); //If we have an owner name, write it out if (!string.IsNullOrWhiteSpace(contentOwnerName)) { XmlHelper.WriteValueElement(xml, XmlElement_ContentOwner, contentOwnerName); } xml.WriteEndElement(); //end: WorkbookInfo xml.WriteEndDocument(); xml.Close(); }
/// <summary> /// Get a page's worth of Workbook listings /// </summary> /// <param name="onlineWorkbooks"></param> /// <param name="pageToRequest">Page # we are requesting (1 based)</param> /// <param name="totalNumberPages">Total # of pages of data that Server can return us</param> private void ExecuteRequest_ForPage(List<SiteWorkbook> onlineWorkbooks, int pageToRequest, out int totalNumberPages) { int pageSize = _onlineUrls.PageSize; //Create a web request, in including the users logged-in auth information in the request headers var urlQuery = _onlineUrls.Url_WorkbooksListForUser(_onlineSession, _userId, pageSize, pageToRequest); var webRequest = CreateLoggedInWebRequest(urlQuery); webRequest.Method = "GET"; _onlineSession.StatusLog.AddStatus("Web request: " + urlQuery, -10); var response = GetWebReponseLogErrors(webRequest, "get workbooks list"); var xmlDoc = GetWebResponseAsXml(response); //Get all the workbook nodes var nsManager = XmlHelper.CreateTableauXmlNamespaceManager("iwsOnline"); var workbooks = xmlDoc.SelectNodes("//iwsOnline:workbook", nsManager); //Get information for each of the data sources foreach (XmlNode itemXml in workbooks) { try { var ds = new SiteWorkbook(itemXml); onlineWorkbooks.Add(ds); } catch { AppDiagnostics.Assert(false, "Workbook parse error"); _onlineSession.StatusLog.AddError("Error parsing workbook: " + itemXml.InnerXml); } } //end: foreach //------------------------------------------------------------------- //Get the updated page-count //------------------------------------------------------------------- totalNumberPages = DownloadPaginationHelper.GetNumberOfPagesFromPagination( xmlDoc.SelectSingleNode("//iwsOnline:pagination", nsManager), pageSize); }
/// <summary> /// URL to download a workbook /// </summary> /// <param name="siteUrlSegment"></param> /// <returns></returns> public string Url_WorkbookDownload(TableauServerSignIn session, SiteWorkbook contentInfo) { string workingText = _urlDownloadWorkbookTemplate; workingText = workingText.Replace("{{iwsSiteId}}", session.SiteId); workingText = workingText.Replace("{{iwsRepositoryId}}", contentInfo.Id); ValidateTemplateReplaceComplete(workingText); return workingText; }
/// <summary> /// Assign ownership /// </summary> /// <param name="workbook"></param> /// <param name="publishSettings"></param> /// <param name="siteUsers"></param> /// <returns>TRUE: The server content has the correct owner now. FALSE: We were unable to give the server content the correct owner</returns> private bool AttemptOwnerReassignment(SiteWorkbook workbook, WorkbookPublishSettings publishSettings, IEnumerable <SiteUser> siteUsers) { this.StatusLog.AddStatusHeader("Attempting ownership assignement for Workbook " + workbook.Name + "/" + workbook.Id); //Something went wrong if we don't have a set of site users to do the look up if (siteUsers == null) { throw new ArgumentException("No set of site users provided for lookup"); } //Look the local meta data to see what the desired name is var desiredOwnerName = publishSettings.OwnerName; if (string.IsNullOrEmpty(desiredOwnerName)) { this.StatusLog.AddStatus("Skipping owner assignment. The local file system has no metadata with an owner information for " + workbook.Name); LogManualAction_ReassignOwnership(workbook.Name, "none specified", "No client ownership information was specified"); return(true); //Since there is no ownership preference stated locally, then ownership we assigned during upload was fine. } //Look on the list of users in the target site/server, and try to find a match // //NOTE: We are doing a CASE INSENSITIVE name comparison. We assume that there are not 2 users with the same name on server w/differet cases // Because if this, we want to be flexible and allow that our source/destination servers may have the user name specified with a differnt // case. -- This is less secure than a case-sensitive comparison, but is almost always what we want when porting content between servers var desiredServerUser = SiteUser.FindUserWithName(siteUsers, desiredOwnerName, StringComparison.InvariantCultureIgnoreCase); if (desiredServerUser == null) { this.StatusLog.AddError("The local file has a workbook/user mapping: " + workbook.Name + "/" + desiredOwnerName + ", but this user does not exist on the target site"); LogManualAction_ReassignOwnership(workbook.Name, desiredOwnerName, "The target site does not contain a user name that matches the owner specified by the local metadata"); return(false); //Not a run time error, but we have manual steps to perform } //If the server content is already correct, then there is nothing to do if (desiredServerUser.Id == workbook.OwnerId) { this.StatusLog.AddStatus("Workbook " + workbook.Name + "/" + workbook.Id + ", already has correct ownership. No update requried"); return(true); } //Lets tell server to update the owner var changeOwnership = new SendUpdateWorkbookOwner(_onlineSession, workbook.Id, desiredServerUser.Id); SiteWorkbook updatedWorkbook; try { this.StatusLog.AddStatus("Server request to change Workbook ownership, wb: " + workbook.Name + "/" + workbook.Id + ", user:"******"/" + desiredServerUser.Id); updatedWorkbook = changeOwnership.ExecuteRequest(); } catch (Exception exChangeOnwnerhsip) { throw exChangeOnwnerhsip; //Unexpected error, send it upward } //Sanity check the result we got back: Double check to make sure we have the expected owner. if (updatedWorkbook.OwnerId != desiredServerUser.Id) { this.StatusLog.AddError("Unexpected server error! Updated workbook Owner Id does not match expected. wb: " + workbook.Name + "/" + workbook.Id + ", " + "expected user: "******", " + "actual user: " + updatedWorkbook.OwnerId ); } return(true); }
/// <summary> /// Assign ownership /// </summary> /// <param name="workbook"></param> /// <param name="publishSettings"></param> /// <param name="siteUsers"></param> /// <returns>TRUE: The server content has the correct owner now. FALSE: We were unable to give the server content the correct owner</returns> private bool AttemptOwnerReassignment(SiteWorkbook workbook, WorkbookPublishSettings publishSettings, IEnumerable<SiteUser> siteUsers) { this.StatusLog.AddStatusHeader("Attempting ownership assignement for Workbook " + workbook.Name + "/" + workbook.Id); //Something went wrong if we don't have a set of site users to do the look up if (siteUsers == null) { throw new ArgumentException("No set of site users provided for lookup"); } //Look the local meta data to see what the desired name is var desiredOwnerName = publishSettings.OwnerName; if(string.IsNullOrEmpty(desiredOwnerName)) { this.StatusLog.AddStatus("Skipping owner assignment. The local file system has no metadata with an owner information for " + workbook.Name); LogManualAction_ReassignOwnership(workbook.Name, "none specified", "No client ownership information was specified"); return true; //Since there is no ownership preference stated locally, then ownership we assigned during upload was fine. } //Look on the list of users in the target site/server, and try to find a match // //NOTE: We are doing a CASE INSENSITIVE name comparison. We assume that there are not 2 users with the same name on server w/differet cases // Because if this, we want to be flexible and allow that our source/destination servers may have the user name specified with a differnt // case. -- This is less secure than a case-sensitive comparison, but is almost always what we want when porting content between servers var desiredServerUser = SiteUser.FindUserWithName(siteUsers, desiredOwnerName, StringComparison.InvariantCultureIgnoreCase); if(desiredServerUser == null) { this.StatusLog.AddError("The local file has a workbook/user mapping: " + workbook.Name + "/" + desiredOwnerName + ", but this user does not exist on the target site"); LogManualAction_ReassignOwnership(workbook.Name, desiredOwnerName, "The target site does not contain a user name that matches the owner specified by the local metadata"); return false; //Not a run time error, but we have manual steps to perform } //If the server content is already correct, then there is nothing to do if(desiredServerUser.Id == workbook.OwnerId) { this.StatusLog.AddStatus("Workbook " + workbook.Name + "/" + workbook.Id + ", already has correct ownership. No update requried"); return true; } //Lets tell server to update the owner var changeOwnership = new SendUpdateWorkbookOwner(_onlineUrls, _onlineSession, workbook.Id, desiredServerUser.Id); SiteWorkbook updatedWorkbook; try { this.StatusLog.AddStatus("Server request to change Workbook ownership, wb: " + workbook.Name + "/" + workbook.Id + ", user:"******"/" + desiredServerUser.Id); updatedWorkbook = changeOwnership.ExecuteRequest(); } catch (Exception exChangeOnwnerhsip) { throw exChangeOnwnerhsip; //Unexpected error, send it upward } //Sanity check the result we got back: Double check to make sure we have the expected owner. if(updatedWorkbook.OwnerId != desiredServerUser.Id) { this.StatusLog.AddError("Unexpected server error! Updated workbook Owner Id does not match expected. wb: " + workbook.Name + "/" + workbook.Id + ", " + "expected user: "******", " + "actual user: " + updatedWorkbook.OwnerId ); } return true; }
/// <summary> /// Constructor /// </summary> /// <param name="onlineUrls">Tableau Server Information</param> /// <param name="logInInfo">Tableau Sign In Information</param> /// <param name="workbook">IEnumerable of SiteWorkbook objects</param> /// <param name="localSavePath">Local path where the workbooks should be saved</param> public DownloadWorkbook(TableauServerUrls onlineUrls, TableauServerSignIn logInInfo, SiteWorkbook workbook, string localSavePath) : base(logInInfo) { _onlineUrls = onlineUrls; _workbook = workbook; _localSavePath = localSavePath; }
/// <summary> /// Update the owner of a single workbook /// </summary> /// <param name="siteSignIn"></param> /// <param name="contentItem"></param> /// <param name="userOldOwner"></param> /// <param name="userNewOwner"></param> private void Execute_ProvisionOwnership_SingleUserChange_SingleWorkbook_inner(TableauServerSignIn siteSignIn, SiteWorkbook contentItem, SiteUser userOldOwner, SiteUser userNewOwner) { var updateContentOwner = new SendUpdateWorkbookOwner(siteSignIn, contentItem.Id, userNewOwner.Id); updateContentOwner.ExecuteRequest(); CSVRecord_ContentOwnershipModified("workbook", contentItem.Name, userOldOwner.Name, userNewOwner.Name); }
/// <summary> /// Add data source connection data /// </summary> /// <param name="thisWorkbook"></param> private void AddWorkbookConnectionData(SiteWorkbook thisWorkbook) { var dataConnections = thisWorkbook.DataConnections; if(dataConnections == null) { return; } //Write out details for each data connection foreach (var thisConnection in dataConnections) { this.AddKeyValuePairs( new string[] { ContentType //1 ,ContentId //2 ,ContentConnectionType //3 ,ContentConnectionServer //4 ,ContentConnectionPort //5 ,ContentConnectionUserName//6 ,ContentWorkbookId //7 ,ContentWorkbookName //8 ,ContentProjectId //9 ,ContentProjectName //10 ,ContentOwnerId //11 ,DeveloperNotes //12 }, new string[] { "data-connection" //1 ,thisConnection.Id //2 ,thisConnection.ConnectionType //3 ,thisConnection.ServerAddress //4 ,thisConnection.ServerPort //5 ,thisConnection.UserName //6 ,thisWorkbook.Id //7 ,thisWorkbook.Name //8 ,thisWorkbook.ProjectId //9 ,thisWorkbook.ProjectName //10 ,thisWorkbook.OwnerId //11 ,thisWorkbook.DeveloperNotes //12 }); } }
/// <summary> /// Change the ownership for a single Workbook /// </summary> /// <param name="contentItem"></param> /// <param name="userOldOwner"></param> /// <param name="userNewOwner"></param> private void Execute_ProvisionOwnership_SingleUserChange_SingleWorkbook(TableauServerSignIn siteSignIn, SiteWorkbook contentItem, SiteUser userOldOwner, SiteUser userNewOwner) { try { Execute_ProvisionOwnership_SingleUserChange_SingleWorkbook_inner(siteSignIn, contentItem, userOldOwner, userNewOwner); } catch (Exception ex) { _statusLogs.AddError("Error attempting to change content ownership, " + "workbook: " + contentItem.Name + "from: " + userOldOwner.Name + ", to:" + userNewOwner.Name + ", error: " + ex.ToString()); CSVRecord_ErrorUpdatingContentOwnership("workbook", contentItem.Name, userOldOwner.Name, userNewOwner.Name, ex.Message); } }