Content model for data store.
Inheritance: IDisposable
Beispiel #1
0
        /// <summary>
        /// Gets the folder to.
        /// </summary>
        /// <param name="destPhysicalFolder">The destination physical folder.</param>
        /// <param name="sourceFolder">The source folder.</param>
        protected virtual void GetFolderTo(string destPhysicalFolder, ContentModel sourceFolder)
        {
            var result = this.List(sourceFolder);
            foreach (var content in result)
            {
                // a folder, do recursive call
                if (content.Path.EndsWith("/", StringComparison.OrdinalIgnoreCase))
                {
                    this.GetFolderTo(destPhysicalFolder, content);
                }
                else
                {
                    string contentPhysicalPath = System.IO.Path.Combine(
                        destPhysicalFolder, content.Path.TrimStart('/').Replace("/", "\\"));
                    string directoryName = System.IO.Path.GetDirectoryName(contentPhysicalPath);
                    this.Retrieve(content, true);

                    if (!System.IO.Directory.Exists(directoryName))
                    {
                        System.IO.Directory.CreateDirectory(directoryName);
                    }

                    System.IO.File.WriteAllBytes(contentPhysicalPath, content.Data);
                }
            }
        }
Beispiel #2
0
        public void TestContentModelCorrectlyParseFileName()
        {
            // Arrange
            var model = new ContentModel();
            var expected = "three";

            // Act
            model.Path = "/test/one/two/three";

            // Assert
            Assert.AreEqual(expected, model.FileName);
        }
Beispiel #3
0
        public void TestPathSetMethodCorrectlyNormalizePath()
        {
            // Arrange
            var model = new ContentModel();
            var expected = "/test/one/two/three";

            // Act
            model.Path = "test\\one//two/three";

            // Assert
            Assert.AreEqual(expected, model.Path);
        }
        public void TestListRootPathReturnsTwoItem()
        {
            // Arrange
            var repo =
                new FileContentRepository(System.IO.Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "CmsContent"));
            var model = new ContentModel() { Host = "localhost", Path = "/" };

            // Act
            var result = repo.List(model);

            // Assert
            Assert.IsTrue(result.Count() == 2);
        }
        public void TestDelete()
        {
            // Arrange
            var repo =
                new SqlContentRepository(new SqlDataRepository(), "DefaultDatabase", "CmsContent", string.Empty);
            var model = new ContentModel() { Host = "localhost", Path = "/test/test/article" };

            // Act
            repo.Remove(model);
            var result = repo.Exists(model);

            // Assert
            Assert.IsFalse(result);
        }
        public void TestDeleteFile()
        {
            // Arrange
            var repo =
                new FileContentRepository(System.IO.Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "CmsContent"));
            var model = new ContentModel() { Host = "localhost", Path = "/test/test/article" };

            // Act
            repo.Remove(model);
            var result = repo.Exists(model);

            // Assert
            Assert.IsFalse(result);
        }
Beispiel #7
0
        /// <summary>
        /// Populates the data.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="content">The content.</param>
        /// <param name="tableName">Name of the table.</param>
        /// <param name="cachePath">The cache path.</param>
        /// <returns>
        /// Content model with populated data stream.
        /// </returns>
        public virtual ContentModel PopulateData(DapperContext context, ContentModel content, string tableName, string cachePath)
        {
            this.CacheData(context, content, tableName, cachePath);

            if (!string.IsNullOrEmpty(cachePath))
            {
                var localPath = this.ResolvePath(content, cachePath);

                // return a stream
                content.DataStream = new System.IO.FileStream(
                    localPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            }

            return content;
        }
Beispiel #8
0
        public void TestContentModelCorrectlyParseParentDirectory()
        {
            // Arrange
            var model = new ContentModel();
            var expected = "/test/one/two/";

            // Act
            model.Path = "/test/one/two/three";

            // Assert
            Assert.AreEqual(expected, model.ParentPath);

            // Arrange
            expected = "/";

            // Act
            model.Path = string.Empty;

            // Assert
            Assert.AreEqual("/", model.ParentPath);
        }
Beispiel #9
0
        /// <summary>
        /// Caches the data.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="content">The content.</param>
        /// <param name="tableName">Name of the table.</param>
        /// <param name="cachePath">The cache path.</param>
        public virtual void CacheData(DapperContext context, ContentModel content, string tableName, string cachePath)
        {
            if (!string.IsNullOrEmpty(content.DataIdString))
            {
                var data =
                    context.Query<ContentModel>(
                        string.Format("SELECT Data, DataLength FROM {0} WHERE IdString = @DataIdString", tableName), content).FirstOrDefault();

                if (data != null)
                {
                    content.Data = data.Data;
                    content.DataLength = data.DataLength;
                }
            }

            if (string.IsNullOrEmpty(cachePath))
            {
                return;
            }

            // determine if local content exists or is out of date
            var localPath = this.ResolvePath(content, cachePath);
            var canCache = !File.Exists(localPath);
            if (!canCache)
            {
                var lastWriteTime = File.GetLastWriteTime(localPath);
                canCache = lastWriteTime < (content.ModifyDate ?? content.CreateDate);
            }

            if (canCache)
            {
                var localDir = System.IO.Path.GetDirectoryName(localPath);
                if (!System.IO.Directory.Exists(localDir))
                {
                    System.IO.Directory.CreateDirectory(localDir);
                }

                System.IO.File.WriteAllBytes(localPath, content.Data ?? new byte[0]);
            }
        }
Beispiel #10
0
        /// <summary>
        /// Gets the folder.
        /// </summary>
        /// <param name="folder">The folder.</param>
        /// <returns>
        /// Path to temp file that is a zip of the folder content.
        /// </returns>
        public virtual string GetFolder(ContentModel folder)
        {
            var fileName = Guid.NewGuid().ToString();
            var tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), fileName);
            System.IO.Directory.CreateDirectory(tempPath);

            this.GetFolderTo(tempPath, folder);

            // zip up folder
            var tempFile = tempPath + ".zip";
            ZipFile.CreateFromDirectory(tempPath, tempFile, CompressionLevel.Fastest, false);

            try
            {
                System.IO.Directory.Delete(tempPath, true);
            }
            catch
            {
                // just try to delete the temp folder we just created, do nothing if error
            }

            return tempFile;
        }
        /// <summary>
        /// Lists the specified path.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="uri">The URI.</param>
        /// <returns>List of content models.</returns>
        public IQueryable<ContentModel> List(string path, Uri uri)
        {
            // make sure it's a folder listing
            var model = new ContentModel
            {
                Host = this.GetTenantHost(uri),
                Path = this.Normalize(path).Trim().TrimEnd('/') + "/"
            };

            this.OnContentEvent("Listing", model);
            var result = this.ContentRepository.List(model).Where(h => !HiddenFolderChars.IsMatch(h.ParentPath));
            this.OnContentEvent(new ContentConnectorEventArgument("Listed", model) { Extra = result });
            return result;
        }
        /// <summary>
        /// Deletes the specified model.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="uri">The URI.</param>
        /// <returns>Content model result.</returns>
        /// <exception cref="System.Web.HttpException">500;Unable to delete protected path '/'.</exception>
        public virtual ContentModel Delete(string path, Uri uri)
        {
            var model = new ContentModel()
                            {
                                Path = this.Normalize(path),
                                Host = this.GetTenantHost(uri)
                            };

            this.OnContentEvent("Deleting", model);
            var thePath = model.Path.TrimEnd('/');
            if (thePath.Equals(string.Empty, StringComparison.OrdinalIgnoreCase))
            {
                throw new HttpException(500, "Unable to delete root path '/'.");
            }
            else if (thePath.Equals("/page", StringComparison.OrdinalIgnoreCase))
            {
                throw new HttpException(500, "Unable to delete path '/page'.");
            }
            else if (thePath.Equals("/content", StringComparison.OrdinalIgnoreCase))
            {
                throw new HttpException(500, "Unable to delete path '/content'.");
            }

            this.ContentRepository.Remove(model);

            this.OnContentEvent("Deleted", model);
            return model;
        }
        /// <summary>
        /// Creates the or update.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="uri">The URI.</param>
        /// <param name="data">The data.</param>
        /// <returns>
        /// Content model result.
        /// </returns>
        /// <exception cref="System.ArgumentException">Cannot update root path.;path</exception>
        public virtual ContentModel CreateOrUpdate(string path, Uri uri, byte[] data)
        {
            var model = new ContentModel()
            {
                Path = this.Normalize(path),
                Data = data,
                Host = this.GetTenantHost(uri),
                CreateBy = System.Threading.Thread.CurrentPrincipal.Identity.Name,
                ModifyBy = System.Threading.Thread.CurrentPrincipal.Identity.Name
            };

            this.OnContentEvent("CreateOrUpdating", model);

            // empty files are allowed, otherwise throw exception somewhere here
            // root folder creation is not allow
            if (string.Compare(model.Path, "/", StringComparison.OrdinalIgnoreCase) == 0)
            {
                throw new HttpException(500, "Cannot update root path.");
            }

            this.ContentRepository.Save(model);

            this.OnContentEvent("CreateOrUpdated", model);
            return model;
        }
        /// <summary>
        /// Create the file.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="data">The data.</param>
        /// <param name="uri">The URI.</param>
        /// <returns>Content model result.</returns>
        /// <exception cref="System.Web.HttpException">500;Cannot create or overwrite an existing content of path:  + path</exception>
        public virtual ContentModel Create(string path, string data, Uri uri)
        {
            var model = new ContentModel()
                            {
                                Path = this.Normalize(path),
                                Host = this.GetTenantHost(uri)
                            };

            this.OnContentEvent("Creating", model);
            if (this.ContentRepository.Exists(model))
            {
                throw new HttpException(500, "Cannot create or overwrite an existing content: " + path);
            }

            var result = this.CreateOrUpdate(path, data, uri);
            this.OnContentEvent("Created", result);
            return result;
        }
Beispiel #15
0
        public virtual ActionResult CreatePage(string path, string data)
        {
            if (!path.ToLowerInvariant().EndsWith("_default.htm") && !path.ToLowerInvariant().EndsWith("_default.vash"))
            {
                throw new HttpException(500, "Page path must ends with _default.htm or _default.vash: " + path);
            }

            var model = new ContentModel() { Path = this.ContentConnector.Normalize(path) };
            var folderPath = model.ParentPath;

            this.Create(folderPath + "app.js", string.Empty, null);
            this.Create(folderPath + "app.css", string.Empty, null);
            this.Create(folderPath + "layout.vash", @"<!DOCTYPE html>
            <html lang=""en"">
            <head>
            <meta charset=""utf-8"" />
            <title>@model.title</title>
            <link   type=""text/css"" rel=""stylesheet"" href=""app.css"">
            <script type=""text/javascript"" src=""app.js""></script>
            </head>
            <body>
            @html.block('content')
            </body>
            </html>", null);

            return this.Create(model.Path, data, null);
        }
Beispiel #16
0
        /// <summary>
        /// Uploads the specified upload.
        /// </summary>
        /// <param name="file">The upload.</param>
        /// <param name="path">The path.</param>
        /// <returns>Parent path.</returns>
        protected virtual string Upload(HttpPostedFileBase file, string path)
        {
            var contentModel = new ContentModel()
            {
                Path = this.ContentConnector.Normalize(path),
                CreateBy = this.User.Identity.Name,
                ModifyBy = this.User.Identity.Name
            };

            // if it's a file path then get the folder
            if (!contentModel.Path.EndsWith("/"))
            {
                contentModel.Path = contentModel.Path.Substring(0, contentModel.Path.LastIndexOf('/'));
            }

            var fileName = (file.FileName + string.Empty).Replace("\\", "/").Replace("//", "/");
            contentModel.Path = string.Concat(contentModel.Path, "/", fileName.IndexOf('/') >= 0 ? System.IO.Path.GetFileName(file.FileName) : file.FileName);

            this.ContentConnector.CreateOrUpdate(contentModel.Path, this.Request.Url, file.InputStream.ReadAll());

            return contentModel.ParentPath;
        }
Beispiel #17
0
 /// <summary>
 /// Check for exist of content.
 /// </summary>
 /// <param name="content">The content - requires host, path, and name property.</param>
 /// <returns>
 /// true if content exists.
 /// </returns>
 public bool Exists(ContentModel content)
 {
     var path = this.ResolvePath(content);
     return content.Path.EndsWith("/", StringComparison.OrdinalIgnoreCase) ? System.IO.Directory.Exists(path) : File.Exists(path);
 }
Beispiel #18
0
        /// <summary>
        /// Caches the retrieve.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns>
        /// Cache retrieve.
        /// </returns>
        protected virtual string CacheRetrieve(string filename)
        {
            var path = filename;
            var content = new ContentModel() { Path = path, Host = this.api.tenantHost };

            // search the shared folder for file
            if (!this.connector.ContentRepository.Exists(content))
            {
                content.Path = "/page/shared/" + content.FileName;
            }

            // connector should be good for handling and retrieve error
            var contentResult = this.connector.Retrieve(content.Path, this.api.request.url);
            contentResult.SetDataFromStream();
            var result = System.Text.Encoding.UTF8.GetString(contentResult.Data);

            return result;
        }
        /// <summary>
        /// Retrieves the specified path.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="uri">The URI.</param>
        /// <returns>Content result.</returns>
        /// <exception cref="System.Web.HttpException">404;PhunCms path not found.</exception>
        public virtual ContentModel Retrieve(string path, Uri uri)
        {
            var content = new ContentModel()
            {
                Path = this.Normalize(path),
                Host = this.GetTenantHost(uri)
            };

            this.OnContentEvent("Retrieving", content);

            if (content.Path.EndsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                var localFilePath = this.ContentRepository.GetFolder(content);

                content.DataStream = new System.IO.FileStream(localFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                content.Path = content.Path.Replace("/", "_") + ".zip";

                this.OnContentEvent("RetrievedFolder", content);
                return content;
            }

            var result = this.ContentRepository.Retrieve(content, true);

            if (result.DataLength == null)
            {
                throw new HttpException(404, "PhunCms path not found: " + path);
            }

            this.OnContentEvent("Retrieved", result);
            return result;
        }
Beispiel #20
0
        /// <summary>
        /// Saves the specified content.
        /// </summary>
        /// <param name="content">The content - requires host, path, and name property.</param>
        public void Save(ContentModel content)
        {
            var pathAndName = this.ResolvePath(content);
            var path = Path.GetDirectoryName(pathAndName);
            var isValidChildOfBasePath =
                System.IO.Path.GetFullPath(path)
                      .StartsWith(System.IO.Path.GetFullPath(this.basePath), StringComparison.OrdinalIgnoreCase);

            if (!isValidChildOfBasePath)
            {
                return;
            }

            // make sure directories exist
            if (!System.IO.Directory.Exists(path))
            {
                System.IO.Directory.CreateDirectory(path);
            }

            // write all data
            File.WriteAllBytes(pathAndName, content.Data);
        }
Beispiel #21
0
 /// <summary>
 /// Lists the specified content.Path
 /// </summary>
 /// <param name="content">The content.</param>
 /// <returns>
 /// Enumerable to content model.
 /// </returns>
 public abstract System.Linq.IQueryable<ContentModel> List(ContentModel content);
Beispiel #22
0
        /// <summary>
        /// Resolves the path.
        /// </summary>
        /// <param name="content">The content.</param>
        /// <returns>
        /// The full path to the content or file.
        /// </returns>
        /// <exception cref="System.ArgumentException">Content is required.;content
        /// or
        /// Content path is required or not valid:  + content.Path;content.Path</exception>
        private string ResolvePath(ContentModel content)
        {
            bool isFolder = content.Path.EndsWith("/", StringComparison.OrdinalIgnoreCase);

            if (content == null)
            {
                throw new ArgumentException("Content is required.", "content");
            }

            if (string.IsNullOrEmpty(content.Host))
            {
                content.Host = this.defaultHost;
            }

            var path = this.NormalizedPath(content.Path);

            // add: 'basePath\host\contentPath'
            var result = string.Concat(this.basePath, "\\", content.Host, "\\", path.Trim('/').Replace("/", "\\"));

            // result full path must be more than 3 characters
            if (result.Length <= 3)
            {
                throw new ArgumentException("Illegal path length detected: " + content.Path, "path");
            }

            if (isFolder)
            {
                result = result + "\\";
            }

            return result;
        }
 /// <summary>
 /// Called when [content event].
 /// </summary>
 /// <param name="eventName">Name of the event.</param>
 /// <param name="model">The model.</param>
 public void OnContentEvent(string eventName, ContentModel model)
 {
     this.OnContentEvent(new ContentConnectorEventArgument(eventName, model));
 }
        /// <summary>
        /// Phuns the partial.
        /// </summary>
        /// <param name="contentName">Name of the content.</param>
        /// <param name="url">The URL.</param>
        /// <returns>
        /// Partial content.
        /// </returns>
        /// <exception cref="System.ArgumentException">contentName is required.</exception>
        protected internal virtual string PhunPartial(string contentName, Uri url)
        {
            if (string.IsNullOrEmpty(contentName))
            {
                throw new ArgumentException("contentName is required.");
            }

            var result = string.Empty;
            var config = this.ContentConfig ?? Bootstrapper.Default.ContentConfig;
            var content = new ContentModel()
            {
                Path = this.Normalize(
                          "/page" + (contentName.Contains("/") ? contentName : url.AbsolutePath + "/" + contentName)),
                Host = this.GetTenantHost(url)
            };

            config.ContentRepository.Retrieve(content, true);
            if (content.DataLength != null)
            {
                content.SetDataFromStream();
                result = System.Text.Encoding.UTF8.GetString(content.Data).GetHtmlBody();
            }

            return result;
        }
        /// <summary>
        /// View content.
        /// </summary>
        /// <param name="httpContext">The HTTP context.</param>
        public virtual ContentModel RenderPage(HttpContextBase httpContext)
        {
            var path = httpContext.Request.QueryString["path"];
            if (string.IsNullOrEmpty(path))
            {
                path = (httpContext.Request.Path + string.Empty).Trim();
            }

            // if somehow, CMS Resource URL get routed here then intercept
            if (this.Config.IsResourceRoute(path))
            {
                this.Config.GetResourceFile(path).WriteFile(httpContext);
                return null;
            }

            if (!path.EndsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                httpContext.Response.RedirectPermanent(path + "/");
                return null;
            }

            path = path.TrimEnd('/');
            var tenantHost = this.GetTenantHost(httpContext.Request.Url);
            var model = new ContentModel()
            {
                Host = tenantHost,
                Path = this.ResolvePath(path, tenantHost, httpContext)
            };

            this.OnContentEvent("PageRendering", model);
            if (model.Path.EndsWith(".htm"))
            {
                // set response 302
                return this.ContentRepository.Retrieve(model, true);
            }

            // now that it is a vash file, attempt to render the file
            this.TemplateHandler.Render(model, this, httpContext);

            this.OnContentEvent("PageRendered", model);

            return null;
        }
Beispiel #26
0
 /// <summary>
 /// Populate or gets the content provided specific host, path, and name property.
 /// </summary>
 /// <param name="content">The content - requires host, path, and name property.</param>
 /// <param name="includeData">if set to <c>true</c> [include data].</param>
 /// <returns>
 /// The <see cref="ContentModel" /> that was passed in.
 /// </returns>
 public abstract ContentModel Retrieve(ContentModel content, bool includeData = true);
        /// <summary>
        /// Checks the page.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="tenantHost">The tenant host.</param>
        /// <param name="extension">The extension.</param>
        /// <returns></returns>
        private string CheckPage(string path, string tenantHost, string extension)
        {
            // only normalize after determine that it is not a CMS Resource URL
            // it must also start with page path
            var result = string.Concat("/page", this.Normalize(path));
            var newModel = new ContentModel()
            {
                Host = tenantHost,
                Path = result
            };

            if (!result.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
            {

                newModel.Path += extension;
                if (this.ContentRepository.Exists(newModel))
                {
                    result = newModel.Path;
                }
                else
                {
                    newModel.Path = result + "/_default" + extension;
                    result = this.ContentRepository.Exists(newModel) ? newModel.Path : "==NOT FOUND==";
                }
            }

            return result;
        }
        public void TestListRootPathReturnsTwoItem()
        {
            // Arrange
            var repo =
                new SqlContentRepository(new SqlDataRepository(), "DefaultDatabase", "CmsContent", string.Empty);
            var model = new ContentModel() { Host = "localhost", Path = "/" };

            // Act
            repo.Save(
                    new ContentModel()
                    {
                        Host = "localhost",
                        Path = "/test/foo/article",
                        Data = System.Text.Encoding.UTF8.GetBytes("test"),
                        CreateBy = string.Empty
                    });
            var result = repo.List(model);

            // Assert
            Assert.AreEqual(2, result.Count());
        }
Beispiel #29
0
        /// <summary>
        /// Tries the set304.
        /// </summary>
        /// <param name="content">The content.</param>
        /// <returns>True if content has not been modified since last retrieve.</returns>
        protected virtual bool TrySet304(ContentModel content, double hours = 24)
        {
            var context = this.HttpContext;

            if (this.ContentConnector.Config.DisableResourceCache || !Bootstrapper.Default.ContentRegEx.IsMatch(content.FileName))
            {
                context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                context.Response.Cache.SetExpires(DateTime.MinValue);
                return false;
            }

            // allow static file to access core
            context.Response.AddHeader("Access-Control-Allow-Origin", "*");
            var currentDate = content.ModifyDate ?? DateTime.Now;
            context.Response.Cache.SetLastModified(currentDate);
            context.Response.Cache.SetCacheability(HttpCacheability.Public);
            context.Response.Cache.SetExpires(DateTime.Now.AddHours(hours));

            DateTime previousDate;
            string data = context.Request.Headers["If-Modified-Since"] + string.Empty;
            if (DateTime.TryParse(data, out previousDate))
            {
                if (currentDate > previousDate.AddMilliseconds(100))
                {
                    context.Response.StatusCode = 304;
                    context.Response.StatusDescription = "Not Modified";
                    return true;
                }
            }

            return false;
        }
Beispiel #30
0
        /// <summary>
        /// Lists the specified content.Path
        /// </summary>
        /// <param name="content">The content.</param>
        /// <returns>
        /// Enumerable to content model.
        /// </returns>
        public override IQueryable<ContentModel> List(ContentModel content)
        {
            var path = this.ResolvePath(content);
            var result = new List<ContentModel>();
            var isValidChildOfBasePath = System.IO.Path.GetFullPath(path)
                        .StartsWith(System.IO.Path.GetFullPath(this.basePath), StringComparison.OrdinalIgnoreCase);

            // don't do anything for invalid path
            if (!isValidChildOfBasePath)
            {
                return result.AsQueryable();
            }

            // only proceed if it is a folder
            if (content.Path.EndsWith("/", StringComparison.OrdinalIgnoreCase))
            {
                var directory = new DirectoryInfo(path);
                if (!directory.Exists)
                {
                    return result.AsQueryable();
                }

                // build directory result
                foreach (var dir in directory.GetDirectories())
                {
                    result.Add(
                        new ContentModel()
                            {
                                Host = content.Host,
                                Path = string.Concat(content.Path, dir.Name, "/"),
                                CreateDate = directory.CreationTime,
                                ModifyDate = directory.LastWriteTime
                            });
                }

                // build file result
                foreach (var file in directory.GetFiles())
                {
                    result.Add(
                        new ContentModel()
                            {
                                Host = content.Host,
                                Path = string.Concat(content.Path, file.Name),
                                CreateDate = file.CreationTime,
                                ModifyDate = file.LastWriteTime
                            });
                }
            }

            return result.AsQueryable();
        }