/// <summary><![CDATA[ /// Process Carbide dependencies on startup /// ]]></summary> private void InjectCarbideDependencies(object sender, EventArgs e) { var tabExists = false; var file = "/config/Dashboard.config"; var aliases = Config.GetConfigFileValues(file, "//dashBoard/section/@alias"); foreach (var alias in aliases) { if (alias == "CarbideContentTools") { tabExists = true; break; } } if (Config.GetKeyValue <bool>("Tabs.Content.Enabled", true, "Fynydd.Carbide")) { if (tabExists == false) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(StorageHelpers.MapPath(file)); XmlNode root = xmlDoc.DocumentElement; XmlDocument fragment = new XmlDocument(); fragment.LoadXml(StorageHelpers.CarbideEmbeddedHtml("DashControl.xml")); XmlNode tab = xmlDoc.ImportNode(fragment.DocumentElement, true); root.InsertBefore(tab, root.FirstChild); xmlDoc.Save(StorageHelpers.MapPath(file)); } } else { if (tabExists == true) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(StorageHelpers.MapPath(file)); XmlNodeList xnList = xmlDoc.SelectNodes("//dashBoard/section[@alias='CarbideContentTools']"); foreach (XmlNode node in xnList) { node.ParentNode.RemoveChild(node); } xmlDoc.Save(StorageHelpers.MapPath(file)); } } // Remove event hook to ensure it only runs once UmbracoApplicationBase.ApplicationInit -= InjectCarbideDependencies; }
/// <summary><![CDATA[ /// Retrieve XML node data from a config file. /// ]]></summary> /// <param name="configFilePath">Web style path to the config file</param> /// <param name="nodePath">XPath for node selection</param> /// <returns>Enumerable string array of values</returns> public static string[] GetConfigFileValues(string configFilePath, string nodePath) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(StorageHelpers.MapPath(configFilePath)); XmlNodeList nodeList = xmlDoc.DocumentElement.SelectNodes(nodePath); List <string> result = new List <string>(); foreach (XmlNode node in nodeList) { result.Add(node.InnerText); } return(result.ToArray()); }
/// <summary><![CDATA[ /// Generate CSV data from a SQL Server data request, and write it to a file. Converts quotation marks to """. /// ]]></summary> /// <param name="select">SQL Server command to execute, which retrieves a dataset.</param> /// <param name="connectionStringName">Connection string name in the Web.config file.</param> /// <param name="includeHeaders">Include column names as a header row in the CSV file.</param> /// <param name="alwaysUseQuotes">Wrap all values in quotation marks. False will omit quotation marks around numeric values.</param> /// <param name="filePath">Web-style path and filename for the output of the data.</param> /// <returns>True if the file was written successfully.</returns> public static bool ExportCSV(string select, string connectionStringName, bool includeHeaders, bool alwaysUseQuotes, string filePath) { bool result = false; StringBuilder CSV = new StringBuilder(); CSV = GenerateCSV(select, connectionStringName, includeHeaders, alwaysUseQuotes); StorageHelpers.WriteFile(filePath, CSV.ToString()); if (StorageHelpers.FileExists(filePath)) { result = true; } return(result); }
/// <summary><![CDATA[ /// Wrapper method for building CSS from SCSS files, with partial injection. /// Will check and build every "Scss.BuildCheck.Seconds" when debug=true if there are file changes (production). /// Will check and build every page load when debug=true if there are file changes. /// Requires the following Carbide config items: /// ]]></summary> /// <example> /// <code><![CDATA[ /// <add key="Scss.BuildCheck.Seconds" value="3600" /> /// <add key="Scss.Root" value="/scss/" /> /// <add key="Scss.Partials.Root" value="/scss/custom/" /> /// <add key="Scss.Filename.Base" value="application" /> /// <add key="Scss.Output.Root" value="/css/" /> /// ]]></code> /// </example> public static void RunConfiguredScssBuild() { var scssPath = Config.GetKeyValue("Scss.Root", "/scss/", "Fynydd.Carbide"); var scssPartialsPath = Config.GetKeyValue("Scss.Partials.Root", "/scss/custom/", "Fynydd.Carbide"); var scssFilenameBase = Config.GetKeyValue("Scss.Filename.Base", "application", "Fynydd.Carbide"); var scssOutputPath = Config.GetKeyValue("Scss.Output.Root", "/css/", "Fynydd.Carbide"); var debugging = AppStateHelpers.IsDebugging(); var buildScss = debugging; // Always run in debug mode if (debugging == false) { // Only run the check periodically on production unless the file doesn't exist if (FileExists(scssOutputPath + scssFilenameBase + ".css") == false) { buildScss = true; } TemporalHelpers.TaskIntervalInit("ScssProductionBuild", Config.GetKeyValue <double>("Scss.BuildCheck.Seconds", 60 * 60 * 24, "Fynydd.Carbide")); if (TemporalHelpers.TaskShouldBeRun("ScssProductionBuild")) { buildScss = true; } } if (buildScss == true) { if (debugging == false) { TemporalHelpers.TaskIntervalStart("ScssProductionBuild"); } StorageHelpers.InjectScssPartials(scssPath, scssFilenameBase + ".scss", scssPartialsPath); StorageHelpers.BuildScss(scssPath + scssFilenameBase + ".scss", scssOutputPath + scssFilenameBase + ".css", debugMode: debugging); if (debugging == false) { TemporalHelpers.TaskIntervalStop("ScssProductionBuild"); } } }
public HttpResponseMessage Svg(int id, bool clean = false, string color = "") // /umbraco/api/carbidefile/svg/?id=1024&color=008BFF { string result = ""; try { var umbracoHelper = new UmbracoHelper(Carbide.ContextHelpers.EnsureUmbracoContext()); var svg = umbracoHelper.TypedMedia(id); if (svg != null) { result = StorageHelpers.ReadFile(svg.Url); if (result.Length > 0) { if (result.Contains("<svg ")) { if (clean) { result = MediaHelpers.CleanSvg(result, removeStyles: false, fixStyles: false, removeXmlHeader: false); } if (color != "" && color.Length < 10) { result = MediaHelpers.CleanSvg(result, removeStyles: true, fixStyles: false, removeXmlHeader: false); result = Regex.Replace(result, "#[0-9a-fA-F]{6,8}", color.FixHexColor(), RegexOptions.Singleline); result = result.Replace("<svg ", "<svg style=\"fill: " + color.FixHexColor() + ";\" "); } } } } } catch { } var response = new HttpResponseMessage(HttpStatusCode.OK); response.Content = new StringContent(result, Encoding.UTF8, "image/svg+xml"); return(response); }
/// <summary><![CDATA[ /// Remove comments and style attributes from SVG markup. /// ]]></summary> /// <param name="svg">SVG markup to clean.</param> /// <param name="removeStyles">If true, svg style property is removed.</param> /// <param name="fixStyles">Ensure unique svg id and style classes are scoped to that id.</param> /// <param name="removeXmlHeader">Removes the XML document header.</param> /// <returns>Cleaned SVG markup.</returns> public static string CleanSvg(string svg, bool removeStyles = false, bool fixStyles = true, bool removeXmlHeader = true) { var result = svg; var svgId = "SVG" + StorageHelpers.MakeUniqueFilenameFromExtension("").TrimEnd("."); // Remove comments result = Regex.Replace(result, "<!--.*?-->", String.Empty, RegexOptions.Singleline); if (removeXmlHeader) { // Remove XML header result = Regex.Replace(result, @"<[\?]xml.*?[\?]>", String.Empty, RegexOptions.Singleline); } // Remove styles? if (removeStyles) { // Removes embedded style blocks // result = Regex.Replace(result, "<style.*?</style>", String.Empty, RegexOptions.Singleline); MatchCollection matches = Regex.Matches(result, @"<svg[^>]* (style=['\""].*?['\""])[^>]*>", RegexOptions.Singleline); if (matches.Count > 0) { foreach (Match match in matches) { foreach (Group group in match.Groups) { if (!group.Value.StartsWith("<svg")) { result = result.Replace(group.Value, ""); } } } } } // Fix embedded styles to class names are scoped locally else { if (fixStyles) { // Assign a unique id to prevent in-page conflicts MatchCollection matches = Regex.Matches(result, @"<svg[^>]* id=['\""](.*?)['\""][^>]*>", RegexOptions.Singleline); if (matches.Count > 0) { foreach (Match match in matches) { foreach (Group group in match.Groups) { if (!group.Value.StartsWith("<svg")) { result = result.Replace("id=\"" + group.Value, "id=\"" + svgId); result = result.Replace("id='" + group.Value, "id='" + svgId); } } } } else { result = result.Replace("<svg ", "<svg id=\"" + svgId + "\" "); } if (result.Contains("<style")) { matches = Regex.Matches(result, @"\s*>*}*(\.[\w\d-]*)\s*{.*?", RegexOptions.Singleline); foreach (Match match in matches) { foreach (Group group in match.Groups) { if (!group.Value.EndsWith("{")) { result = result.Replace(group.Value, "#" + svgId + " " + group.Value); } } } } } } return(result.Trim()); }
/// <summary><![CDATA[ /// Include a URL asset into a web page using relative paths. Optionally add dynamic cachebuster and minify JS and CSS files. /// ]]></summary> /// <param name="url">UrlHelper object</param> /// <param name="contentPath">Relative path to the asset</param> /// <param name="addCacheBuster">If true, appends a cachebuster to the generated URL from the file modification date</param> /// <param name="minify">If true minifies CSS and JS assets as new files and points the URL to them instead</param> /// <param name="fallback">If cachebuster fails, use this fallback value</param> /// <returns></returns> public static string Content(this UrlHelper url, string contentPath, bool addCacheBuster = false, bool minify = false, string fallback = "") { var queryString = contentPath.QueryString(); var filePath = contentPath.RemoveQueryString(); try { if (minify && !filePath.StartsWith("_carbide.generated.")) { if (filePath.EndsWith(".js") || filePath.EndsWith(".css")) { bool proceed = true; var newContentpath = ""; if (filePath.Contains("/")) { newContentpath = filePath.Substring(0, filePath.LastIndexOf("/") + 1) + "_carbide.generated." + filePath.Substring(filePath.LastIndexOf("/") + 1); } else { newContentpath = "_carbide.generated." + filePath; } FileInfo fileInfo = new FileInfo(StorageHelpers.MapPath(filePath)); DateTime lastModified = fileInfo.LastWriteTime; var item = fileInfo.Length + "|" + lastModified.DateFormat(Carbide.Constants.DateFormats.Utc); if (HttpContext.Current.Application.KeyExists(StorageHelpers.ConvertFilePathToKey(filePath))) { if (HttpContext.Current.Application[StorageHelpers.ConvertFilePathToKey(filePath)].ToString() == item) { if (StorageHelpers.FileExists(newContentpath)) { filePath = newContentpath; proceed = false; } } else { if (StorageHelpers.FileExists(filePath) == false) { filePath = newContentpath; proceed = false; } } } else { if (StorageHelpers.FileExists(filePath) == false) { proceed = false; } } if (proceed) { if (StorageHelpers.FileExists(newContentpath)) { StorageHelpers.DeleteFiles(newContentpath); } var minified = ""; if (filePath.EndsWith(".js")) { var jsc = new JavaScriptCompressor(); minified = jsc.Compress(StorageHelpers.ReadFile(filePath)); } if (filePath.EndsWith(".css")) { var cssc = new CssCompressor(); minified = cssc.Compress(StorageHelpers.ReadFile(filePath)); } StorageHelpers.WriteFile(newContentpath, minified); Debug.WriteLine("MINIFIED TO " + newContentpath); fileInfo = new FileInfo(StorageHelpers.MapPath(filePath)); lastModified = fileInfo.LastWriteTime; item = fileInfo.Length + "|" + lastModified.DateFormat(Carbide.Constants.DateFormats.Utc); HttpContext.Current.Application[StorageHelpers.ConvertFilePathToKey(filePath)] = item; filePath = newContentpath; } else { Debug.WriteLine("SKIPPED MINIFICATION FOR " + filePath); } } } if (addCacheBuster) { queryString += (queryString.Contains("?") ? "&" : "?") + "_cachebuster=" + StorageHelpers.MakeCacheBuster(filePath, fallback); } } catch (Exception e) { Debug.WriteLine(e); } return(url.Content(filePath + queryString)); }
public HttpResponseMessage RebuildImageCache() // /umbraco/api/carbidesupport/rebuildimagecache/ { string result = ""; if (HttpContext.Current.Application["RebuildCacheStatus"] == null) { var context = HttpContext.Current; context.Application["RebuildCacheStatus"] = "running"; context.Application["RebuildCacheHistory"] = "<h4 style=\"font-size: 1.1rem; margin-bottom: 1.5rem;\">Started " + TemporalHelpers.DateFormat(DateTime.Now, DateFormats.European).ToUpper() + " @ " + TemporalHelpers.TimeFormat(DateTime.Now, TimeFormats.SqlMilitary) + "</h4>"; result = context.Application["RebuildCacheHistory"].ToString(); Thread workerThread = new Thread(new ThreadStart(() => { StopWatch timer = new StopWatch(); StopWatch timer2 = new StopWatch(); try { timer.Start(); context.Server.ScriptTimeout = 100000; context.Application["RebuildCacheHistory"] += "<ol style=\"padding: 0.25rem 0 0 1rem;\">"; context.Application["RebuildCacheHistory"] += "<li style=\"padding-bottom: 1rem;\">Clearing cached images... "; timer2.Start(); foreach (var folder in StorageHelpers.GetFolders("/App_Data/cache/")) { StorageHelpers.DeleteDirectory("/App_Data/cache/" + folder); if (StorageHelpers.DirectoryExists("/App_Data/cache/" + folder)) { // Retry up to 5 times after pausing... int retry = 0; string original = context.Application["RebuildCacheHistory"].ToString(); while (retry < 5) { retry++; context.Application["RebuildCacheHistory"] = original + "cache/" + folder + " retry " + retry; TemporalHelpers.PauseExecution(1); if (StorageHelpers.DirectoryExists("/App_Data/cache/" + folder)) { StorageHelpers.DeleteDirectory("/App_Data/cache/" + folder); } if (!StorageHelpers.DirectoryExists("/App_Data/cache/" + folder)) { retry = 5; } } context.Application["RebuildCacheHistory"] = original; if (StorageHelpers.DirectoryExists("/App_Data/cache/" + folder)) { context.Application["RebuildCacheHistory"] += "<strong style='color:#b94a48;'>cache/" + folder + " locked...</strong> "; } } } timer2.Stop(); context.Application["RebuildCacheHistory"] += "<strong>completed in " + timer2.GetSeconds <int>() + " seconds</strong></li>"; timer.Stop(); context.Application.SafeRemove("RebuildCacheStatus"); context.Application["RebuildCacheHistory"] += "</ol>"; context.Application["RebuildCacheHistory"] += "<h4 style=\"font-size: 1.1rem;\">Finished in " + timer.GetSeconds <int>() + " seconds</h4>"; } catch (Exception e) { timer.Stop(); timer2.Stop(); context.Application.SafeRemove("RebuildCacheStatus"); context.Application["RebuildCacheHistory"] = "</li></ol><p><strong>Error in " + timer.GetSeconds <int>() + " seconds on " + TemporalHelpers.DateFormat(DateTime.Now, DateFormats.European).ToUpper() + " @ " + TemporalHelpers.TimeFormat(DateTime.Now, TimeFormats.SqlMilitary) + "</strong></p>" + e.Message; result = context.Application["RebuildCacheHistory"].ToString(); } })) { IsBackground = true }; workerThread.Start(); while (HttpContext.Current.Application["RebuildCacheStatus"] == null) { // Wait for worker thread to start up and initialize System.Threading.Thread.Sleep(50); } } else { result = HttpContext.Current.Application["RebuildCacheHistory"].ToString(); } var response = new HttpResponseMessage(HttpStatusCode.OK); response.Content = new StringContent(result, Encoding.UTF8, "text/plain"); return(response); }
/// <summary><![CDATA[ /// Read a folder of filenames (SCSS partials) and inject them into a SCSS file as import statements. /// Add the following to your SCSS file so the method knows where to inject the import statements, /// including the leading "// ": /// // $CARBIDE_PARTIALS:BEGIN /// // $CARBIDE_PARTIALS:END /// ]]></summary> /// <param name="scssPath">Relative web path to the SCSS files (e.g. "/scss/").</param> /// <param name="scssFilename">File name for the SCSS file in which to inject the partials as import statements (e.g. "application.scss").</param> /// <param name="partialPath">Relative web path to the folder containing the SCSS partials to read (e.g. "/scss/custom/").</param> public static void InjectScssPartials(string scssPath, string scssFilename, string partialPath) { try { var _scssPath = "/" + scssPath.ToLower().Trim('/') + "/"; var _partialPath = "/" + partialPath.ToLower().Trim('/') + "/"; _partialPath = _partialPath.Replace(_scssPath, ""); var scss = StorageHelpers.ReadFile(_scssPath + scssFilename); string[] delims = { "// $CARBIDE_PARTIALS:BEGIN", "// $CARBIDE_PARTIALS:END" }; if (scss.Length > (delims[0].Length + delims[1].Length)) { if (scss.Contains(delims[0]) && scss.Contains(delims[1])) { if (scss.IndexOf(delims[0]) < scss.IndexOf(delims[1])) { List <string> chunks = new List <string>(scss.Split(delims, StringSplitOptions.RemoveEmptyEntries)); var oldList = ""; if (chunks.Count == 3) { oldList = chunks[1]; chunks.RemoveAt(1); } if (chunks.Count == 2) { var inject = ""; ArrayList folders = GetWebFolders(_scssPath + _partialPath, includeRoot: true); foreach (var folder in folders) { var _files = StorageHelpers.GetFiles(folder.ToString(), "*.scss"); _files.Sort(); if (_files.Count > 0) { foreach (var file in _files) { inject += "@import \"" + folder.ToString().Replace(_scssPath, "") + file + "\";\r\n"; } } } // Only write imports if there are changes... if (oldList == "" || inject.ToLower().Trim().Replace("\r\n", "|").Replace("\r", "|").Replace("\n", "|") != oldList.ToLower().Trim().Replace("\r\n", "|").Replace("\r", "|").Replace("\n", "|")) { var finalFile = chunks[0] + delims[0] + "\r\n" + inject + delims[1] + chunks[1]; StorageHelpers.WriteFile(_scssPath + scssFilename, finalFile); Debug.WriteLine("SCSS import changes detected, writing to " + _scssPath + scssFilename); } else { Debug.WriteLine("No SCSS import changes detected in " + _scssPath + scssFilename + ", skipping"); } } } } } } catch (Exception e) { Debug.WriteLine("EXCEPTION: Carbide.StorageHelpers.InjectScssPartials() - " + e.Message); } }