/// <summary> /// Save a file to a blog item /// </summary> /// <param name="id">The blog item to save the file against</param> /// <param name="file">The file to be saved</param> /// <returns>The saved file</returns> public override BlogFile SaveFile(String id, BlogFile file) { try { // Make sure that there is an identifier file.Id = ((file.Id ?? "") == "") ? NewId() : file.Id; // Generate the path for the file item String fileLocation = BlogFilePath(id, file.Id, Path.GetExtension(file.Filename).Replace(".", "")); // Calculate the relative directory based on the path String combinedPath = Path.Combine(Configuration.Environment.WebRootPath, fileLocation); // Get the directory portion from the combined Path String fileDirectory = Path.GetDirectoryName(combinedPath); // If the directory doesn't exist then create it if (!Directory.Exists(fileDirectory)) { Directory.CreateDirectory(fileDirectory); } // Write the file contents File.WriteAllBytes(combinedPath, file.Content); } catch (Exception ex) { throw BlogException.Passthrough(ex, new CouldNotSaveBlogException(ex)); } // Send the file back return(file); }
/// <summary> /// Write an item to disk with a given path from a given object type /// </summary> /// <typeparam name="T">The object type to be written to disk</typeparam> /// <param name="path">The path to write the item to</param> private Boolean Write <T>(String path, T toWrite) where T : IBlogBase { try { // Calculate the relative directory based on the path String combinedPath = Path.Combine(Configuration.Environment.WebRootPath, path); // Get the filename from the combined path String fileName = Path.GetFileName(combinedPath); // Get the directory alone from the combined path String pathAlone = (fileName != "") ? Path.GetDirectoryName(combinedPath) : combinedPath; // Check to make sure the directory exists if (!Directory.Exists(pathAlone)) { Directory.CreateDirectory(pathAlone); } // Write the Xml to disk File.WriteAllText(combinedPath, toWrite.ToXmlString()); // Check if the file exists after the write return(File.Exists(combinedPath)); } catch (Exception ex) { // Throw that the file could not be saved throw BlogException.Passthrough(ex, new CouldNotSaveBlogException(ex)); } }
/// <summary> /// Load the content of the file to the object /// </summary> /// <param name="id">The blog item to get the file from</param> /// <param name="file">The file to have the content populated into</param> /// <returns>The populated Blog File object</returns> public override BlogFile LoadFile(String id, BlogFile file) { try { // Generate the path for the file item String fileLocation = BlogFilePath(id, file.Id, Path.GetExtension(file.Filename).Replace(".", "")); // Calculate the relative directory based on the path String combinedPath = Path.Combine(Configuration.Environment.WebRootPath, fileLocation); // Get the directory portion from the combined Path String fileDirectory = Path.GetDirectoryName(combinedPath); // If the directory doesn't exist then create it if (Directory.Exists(fileDirectory)) { // Read the file contents file.Content = File.ReadAllBytes(combinedPath); } } catch (Exception ex) { throw BlogException.Passthrough(ex, new CouldNotLoadBlogException(ex)); } // Send the file back return(file); }
/// <summary> /// Process / fill a template with a set of key value pairs /// </summary> /// <param name="key"></param> /// <param name="values"></param> /// <returns></returns> public String Process(BlogViewTemplatePart key, List <BlogViewTemplateReplacement> values) { // Get the content (will return an empty content template if it fails) try { // Get the content IHtmlContent content = Get(key); // Something to process? String renderedContent = (content != null) ? HtmlHelpers.GetString(content) : ""; if (renderedContent != "") { // For each replacement text values.ForEach(replacement => { // Replace the value and check if it needs encoding for anti XSS or not renderedContent = renderedContent.Replace(ReplacementStartMarker + replacement.SearchString + ReplacementEndMarker, replacement.Encode ? WebUtility.HtmlEncode(replacement.Content) : replacement.Content); }); } // Send the rendered content back return(renderedContent); } catch (Exception ex) { throw BlogException.Passthrough(ex, new HtmlTemplateNotProcessedBlogException(ex)); } }
/// <summary> /// Get the instance of the data provider from an input string /// </summary> /// <param name="type">The string representing the data provider (Name of the provider)</param> /// <returns>The implementation of the data provider</returns> public IBlogDataProvider Get(String type) { // Create an instance of the implementation from containing assembly IBlogDataProvider provider = null; try { // Loop the available assemblies (not in current usually, might even be a custom provider // so don't make assumptions about where it is) foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { provider = (IBlogDataProvider)assembly.CreateInstance(type); if (provider != null) { break; } } // Raise an error if no provider could be found if (provider == null) { throw new NoProviderFoundBlogException(); } } catch (Exception ex) { // Wrap the exception correctly if it is not a blog exception type throw BlogException.Passthrough(ex, new ErrorFindingProviderBlogException(ex)); } // Send the provider back return(provider); }
/// <summary> /// Read an item from disk with a given path from a given object type /// </summary> /// <typeparam name="T">The object type to be read from disk</typeparam> /// <param name="path">The path to read the item from</param> private T Read <T>(String path) where T : IBlogBase, new() { try { // Calculate the relative directory based on the path String combinedPath = Path.Combine(Configuration.Environment.WebRootPath, path); // Get the filename from the combined path String fileName = Path.GetFileName(combinedPath); // Get the directory alone from the combined path String pathAlone = (fileName != "") ? Path.GetDirectoryName(combinedPath) : combinedPath; // Write the Xml to disk String XmlString = File.ReadAllText(combinedPath); // Generate a new object T result = new T(); // Populate the object by calling the extension method to cast the incoming string result = (T)result.FromXmlString(XmlString); // Send the result back return(result); } catch (Exception ex) { // Throw that the file could not be saved throw BlogException.Passthrough(ex, new CouldNotLoadBlogException(ex)); } }
private const String blogUserFilename = "{0}.xml"; // The file name for the user details /// <summary> /// Override for the base delete functionality /// </summary> /// <returns></returns> public override Boolean Delete(IList <IBlogHeader> items, Boolean permanent) { try { // List of filename to remove should it be a hard delete (done before becuase the // items won't exist in the index after the base method call) List <String> filesToDelete = permanent ? ((List <IBlogHeader>)items).Select(x => BlogItemFilename(x.Id)).ToList <String>() : null; // Any files attached to the blogs that might be removed if (permanent) { ((List <IBlogHeader>)items).ForEach( header => { // Get the item to check to see if there are any other files to remove IBlogItem item = base.Load(header); if (item != null) { item.Files.ForEach(file => DeleteFile(header.Id, file)); } } ); } // Call the base implementation to handle the headers etc. if (base.Delete(items, permanent)) { // Hard delete? Remove the files .. if (permanent && filesToDelete != null) { // Remove the blog files itself filesToDelete.ForEach(file => { try { // Attempt to delete the file from disk .. File.Delete(file); } catch { // Move to the next one if failed to delete, it has no real impact on the system } }); } // Save the index regardless on a hard or soft delete return(WriteBlogIndex()); } // Failed if it gets to here return(false); } catch (Exception ex) { throw BlogException.Passthrough(ex, new CouldNotRemoveBlogException(ex)); } }
public void Exception_External_Is_Not_Passed_Through() { // Arrange // Act Exception result = BlogException.Passthrough(new NotImplementedException(), new GeneralBlogException()); // Assert Assert.IsType <GeneralBlogException>(result); }
public void Exception_Internal_Is_Passed_Through() { // Arrange // Act Exception result = BlogException.Passthrough(new NoProviderFoundBlogException(), new GeneralBlogException()); // Assert Assert.IsType <NoProviderFoundBlogException>(result); }
public void Exception_Is_Internal_Negative() { // Arrange // Act Boolean result = BlogException.IsInternal(new NotImplementedException() { }); // Assert Assert.False(result); }
public void Exception_Is_Internal_Positive() { // Arrange // Act Boolean result = BlogException.IsInternal(new GeneralBlogException() { }); // Assert Assert.True(result); }
/// <summary> /// Initialise call made by the factory /// </summary> public override void Initialise() { // Initialise the users file if there is not already one present in the correct location Boolean usersInitialised = InitialiseUsers(); if (usersInitialised) { // Check to see if there is an a file containing the index to load to initialise the blog try { BlogIndex foundIndex = ReadBlogIndex(); items = foundIndex; items.Initialised = true; } catch (Exception ex) { // Could not load error? if (ex.GetType() == typeof(CouldNotLoadBlogException)) { // File was not found so create a blank index file if (ex.InnerException != null && ex.InnerException.GetType() == typeof(FileNotFoundException)) { // Try and write the blank index try { items = new BlogIndex(); // Generate the new index to be saved if (WriteBlogIndex()) { items.Initialised = true; } } catch (Exception ex2) { throw BlogException.Passthrough(ex, new CouldNotSaveBlogException(ex2)); // Could not do save of the index } } } else { throw BlogException.Passthrough(ex, new CouldNotLoadBlogException(ex)); // Not a handled issue (such as no index so create one) } } } // No item index and not initialised or the users were not initialised then raise an error if (!usersInitialised || items == null || !items.Initialised) { throw new NotInitialisedBlogException(); } }
/// <summary> /// Load the templates from a stream (which needs to be in the appropriate Json format) /// </summary> /// <param name="stream">A stream object that contains the loader object</param> /// <returns>Success Or Failure</returns> public Boolean Load(Stream stream) { BlogViewTemplateLoader templateLoader = null; // Define the template loader outside the try try { Templates = new Dictionary <BlogViewTemplatePart, IHtmlContent>(); // Empty list of templates by default // Convert stream to string using (StreamReader reader = new StreamReader(stream)) { // Parse the XML stream as an XDocument to query XDocument doc = XDocument.Parse(reader.ReadToEnd()); // Query the XDocument and pull out the new template loader item // for each node of type "Item" in the XDocument templateLoader = new BlogViewTemplateLoader() { Items = doc.Descendants("item").Select(item => new BlogViewTemplateLoaderItem() { // Get the Id by analysing the attributes portion Id = (BlogViewTemplatePart)item.Attribute("id").Value. GetValueFromDescription <BlogViewTemplatePart>(), // Get the content by selecting all sub-nodes and pulling the CData value out for the first item it finds Content = item.Elements("content").Select( node => node.Value ).First() ?? "" }).ToList() }; // Nothing to work with? Raise the error if (templateLoader == null || templateLoader.Items == null || templateLoader.Items.Count == 0) { throw new CastObjectBlogException(); } } } catch (Exception ex) { // Decide whether or not to pass the origional or inner exception to the user throw BlogException.Passthrough(ex, new HtmlTemplateLoadFailureBlogException(ex)); } // Call the loader with the object and not the stream return(Load(templateLoader)); }
/// <summary> /// Load a list of users from the XML Location /// </summary> /// <returns>A list of users</returns> private BlogUsers LoadUsers() { BlogUsers users = new BlogUsers(); // The default return parameter try { // Check to see if the file exists if (!File.Exists(UserFileLocation)) { users = Read <BlogUsers>(UserFileLocation); } } catch (Exception ex) { // Bounce up the chain the error about loading the user's list (and not because it wasn't there) throw BlogException.Passthrough(ex, new UserBlogException("Failed to connect to the users repository")); } return(users); // Return the list of users }
/// <summary> /// Wrapper for registering a controller around the testable functionality /// as the controller discovery cannot be tested within the context of the tests /// </summary> /// <param name="instanceType">The new blog controller (or type) to be registered</param> /// <param name="blogs">The list of blogs to register against (strictly ref isn't needed but added to make clear)</param> public Boolean Register(Type instanceType, ref Dictionary <String, IBlog> blogs) { // The result which is failed by default Boolean result = false; try { // Try and scan the attributes from the blog controller instance BlogSetupAttribute[] setupAttributes = (BlogSetupAttribute[])instanceType.GetCustomAttributes(typeof(BlogSetupAttribute), false); BlogSEOAttribute[] seoAttributes = (BlogSEOAttribute[])instanceType.GetCustomAttributes(typeof(BlogSEOAttribute), false); // Call the testable functionality from this wrapper with the derived attributes result = Register(instanceType.Name, setupAttributes, seoAttributes, ref blogs); } catch (Exception ex) { // Tell the caller we could not initialise the blog controller throw BlogException.Passthrough(ex, new NotInitialisedBlogException(ex)); } return(result); // Send back the result }
/// <summary> /// Load the item from disk /// </summary> /// <param name="request">The header of the item that is to be loaded</param> /// <returns>The blog item that was found</returns> public override IBlogItem Load(IBlogHeader request) { try { // Get the header record part from the base load IBlogItem headerOnly = base.Load(request); // Did it give us a valid header to now go and load the content from disk? if (headerOnly.Header.Id != "") { return(ReadBlogItem(headerOnly.Header)); } // Only should get to here if something has gone wrong throw new CouldNotLoadBlogException(); // Failed to load the content so error } catch (Exception ex) { // Something went wrong, explain why throw BlogException.Passthrough(ex, new CouldNotLoadBlogException(ex)); } }
/// <summary> /// Overload to take the setup attributes and blog id so that tests can /// be ran against them without needing it scan for the blog controller /// </summary> /// <param name="instanceName">The name of the blog controller that inherits the base class</param> /// <param name="setupAttributes">The collection of attributes that setup the blog</param> /// <param name="seoAttributes">The collection of seo attributes for the blog</param> /// <param name="blogs">The dictionary to add the blog to once it is set up</param> /// <returns></returns> public Boolean Register(String instanceName, BlogSetupAttribute[] setupAttributes, BlogSEOAttribute[] seoAttributes, ref Dictionary <String, IBlog> blogs) { // Define the result as failed by default Boolean result = false; // Check to see if the blogs dictionary has been instantiated (is not from the startup routine) blogs = blogs ?? new Dictionary <string, IBlog>(); // If the base controller hasn't been set up yet the go gather the information it // needs to connect to the blogs that have been assigned for it to manage if (!blogs.ContainsKey(instanceName)) { try { // Get all of the blog setup attributes for this controller / blog combination if (setupAttributes.Length > 0) { // Get the blog Id from the calling class custom parameters String blogId = setupAttributes[0].BlogId; // Work out and create an instance of the correct data provider IBlogDataProvider blogDataProvider = (new BlogDataProviderFactory()).Get(setupAttributes[0].Provider); if (blogDataProvider != null) { blogDataProvider.ConnectionString = new BlogDataProviderConnectionString(setupAttributes[0].ProviderConnectionString); // Initialise the data provider blogDataProvider.Initialise(); // Get all of the blog setup attributes for this controller / blog combination BlogSEOSettings blogSEOSettings = (seoAttributes.Length > 0) ? seoAttributes[0].SEOSettings : new BlogSEOSettings() { }; // Construct the parameters for setting up the blog IBlogParameters blogParameters = new BlogParameters() { Id = blogId, Provider = blogDataProvider, SEOSettings = blogSEOSettings }; // Assign the instantiated blog class to the static array of blogs blogs[instanceName] = new Blog(blogParameters); // Success result = true; } } } catch (Exception ex) { // Tell the caller we could not initialise the blog controller throw BlogException.Passthrough(ex, new NotInitialisedBlogException(ex)); } } // Return if it was successful return(result); }
/// <summary> /// When the action is executed, set up anything needed for any subsequent actions /// </summary> /// <param name="context">The execution context</param> public override void OnActionExecuting(ActionExecutingContext context) { // Is a redirect? Boolean isRedirect = false; if (context.HttpContext.Request.Query.ContainsKey("redirect")) { Boolean.TryParse(context.HttpContext.Request.Query["redirect"], out isRedirect); } // Is it a password change? if (context.HttpContext.Request.Path.Value.Contains($"auth/passwordchange")) { isRedirect = true; } try { // Get a new login manager instance to check against the current blog // with the current session context loginManager = new BlogLoginManager(Current, context.HttpContext); if (loginManager != null) { // Handle the logins (tokens etc.) loginManager.HandleTokens(); // Current user? Is there some reason to redirect? if (loginManager.CurrentUser != null) { // Password change required and not already on the auth screen? if (loginManager.CurrentUser.PasswordChange && !isRedirect) { // Redirect .. context.Result = new RedirectResult($"{BaseUrl}/auth/login/?redirect=true"); return; } } } else { throw new UserBlogException("Login Manager could not be initialised."); } // Check to see if we have any security attributes applied to the current method ControllerActionDescriptor controllerDescriptor = (ControllerActionDescriptor)context.ActionDescriptor; if (controllerDescriptor != null) // Did we get a controller descriptor? { // Get the required security level if there is one BlogSecurityAttribute[] securityAttributes = (BlogSecurityAttribute[])controllerDescriptor.MethodInfo.GetCustomAttributes(typeof(BlogSecurityAttribute), false); if (securityAttributes.Length != 0) { // Loop the items and see if any fail to meet criteria, if so then set the redirect result foreach (BlogSecurityAttribute attrib in securityAttributes) { // Check against the current user for the security level needed if (loginManager.CurrentUser == null || loginManager.CurrentUser.Permissions == null || !loginManager.CurrentUser.Permissions.Contains(attrib.Permission)) { context.Result = new RedirectResult($"{BaseUrl}"); // Redirect to the user to the home page } } } } } catch (Exception ex) { // Throw the exception wrapped (if needed) in a non-initialised exception throw BlogException.Passthrough(ex, new UserBlogException(ex)); } // Call the base method base.OnActionExecuting(context); }
/// <summary> /// Save a blog item /// </summary> /// <param name="item">The item to be saved</param> /// <returns>The item once it has been saved</returns> public override IBlogItem Save(IBlogItem item) { try { // New Item? Boolean isNew = (item.Header.Id ?? "") == ""; // The base class will save the item to the in-memory header // so we don't want to pass the content in to this. We only want to save // the content to the file, so copy and update the item without the content // to the index IBlogItem headerRecord = item.Duplicate(); headerRecord.Content = ""; // Remove the content from being saved to the header record headerRecord.Files = new List <BlogFile>(); // The header record doesn't need the file listing for the header record only IBlogItem response = base.Save(headerRecord); // Make sure we have an Id // Successfully saved? if (response != null && response.Header != null && response.Header.Id != "") { // Make sure that the origional record that is about to be writen has an associated Id with it item.Header.Id = response.Header.Id; // If we have any file attachments to save we need to do this now // If the item.Files is blank then see if we already have files to attach if (item.Files != null) { item.Files.ForEach(file => { // Anything to write? if (file.Content != null && file.Content.Length > 0) { file = SaveFile(item.Header.Id, file); } }); } else { // Sort out the file array if (isNew && item.Files == null) { item.Files = response.Files = new List <BlogFile>(); } else if (!isNew && item.Files == null) { // Get the old blog back again to get the file listing etc. response = Load(response.Header); item.Files = new List <BlogFile>(); if (response != null && response.Header != null && response.Header.Id != "") { item.Files.AddRange(response.Files); } } } // Write the blog item to disk if (WriteBlogItem(item)) { // Try and save the header records to disk so any updates are cached there too if (WriteBlogIndex()) { return(response); } } } } catch (Exception ex) { throw BlogException.Passthrough(ex, new CouldNotSaveBlogException(ex)); } // Got to here so must have failed throw new CouldNotSaveBlogException(); }