public override VirtualFile GetFile(string virtualPath) { if (ThemeHelper.PathIsThemeVars(virtualPath)) { var theme = ThemeHelper.ResolveCurrentTheme(); int storeId = ThemeHelper.ResolveCurrentStoreId(); return(new ThemeVarsVirtualFile(virtualPath, theme.ThemeName, storeId)); } var result = GetResolveResult(virtualPath); if (result != null) { if (!result.IsExplicit) { return(new InheritedVirtualThemeFile(result)); } else { virtualPath = result.OriginalVirtualPath; } } return(_previous.GetFile(virtualPath)); }
public override VirtualFile GetFile(string virtualPath) { var styleResult = ThemeHelper.IsStyleSheet(virtualPath); if (styleResult != null) { if (styleResult.IsThemeVars) { var theme = ThemeHelper.ResolveCurrentTheme(); int storeId = ThemeHelper.ResolveCurrentStoreId(); return(new ThemeVarsVirtualFile(virtualPath, styleResult.Extension, theme.ThemeName, storeId)); } else if (styleResult.IsModuleImports) { return(new ModuleImportsVirtualFile(virtualPath, ThemeHelper.IsAdminArea())); } } var result = GetResolveResult(virtualPath); if (result != null) { if (!result.IsExplicit) { return(new InheritedVirtualThemeFile(result)); } else { virtualPath = result.OriginalVirtualPath; } } return(_previous.GetFile(virtualPath)); }
public static MvcHtmlString ThemeVarEditor(this HtmlHelper html, ThemeVariableInfo info, object value) { Guard.ArgumentNotNull(info, "info"); string expression = html.NameForThemeVar(info); var strValue = string.Empty; var arrValue = value as string[]; if (arrValue != null) { strValue = arrValue.Length > 0 ? arrValue[0] : value.ToString(); } else { strValue = value.ToString(); } var currentTheme = ThemeHelper.ResolveCurrentTheme(); var isDefault = strValue.IsCaseInsensitiveEqual(info.DefaultValue); MvcHtmlString control; if (info.Type == ThemeVariableType.Color) { control = html.ColorBox(expression, strValue, info.DefaultValue); } else if (info.Type == ThemeVariableType.Boolean) { control = html.CheckBox(expression, strValue.ToBool()); } else if (info.Type == ThemeVariableType.Select) { control = ThemeVarSelectEditor(html, info, expression, strValue); } else { control = html.TextBox(expression, isDefault ? "" : strValue, new { placeholder = info.DefaultValue }); } if (currentTheme != info.Manifest) { // the variable is inherited from a base theme: display an info badge var chainInfo = "<span class='themevar-chain-info'><i class='fa fa-chain fa-flip-horizontal'></i> {0}</span>".FormatCurrent(info.Manifest.ThemeName); return(MvcHtmlString.Create(control.ToString() + chainInfo)); } else { return(control); } }
private ThemeManifest ResolveTheme(string requestedThemeName, string relativePath, string query, out bool isExplicit) { isExplicit = false; ThemeManifest currentTheme; var isAdmin = EngineContext.Current.Resolve <IWorkContext>().IsAdmin; // ThemeHelper.IsAdminArea() if (isAdmin) { currentTheme = _themeRegistry.GetThemeManifest(requestedThemeName); } else { var styleResult = ThemeHelper.IsStyleSheet(relativePath); var isPreprocessor = styleResult != null && styleResult.IsPreprocessor; if (isPreprocessor) { // special consideration for SASS/LESS files: they can be validated // in the backend. For validation, a "theme" query is appended // to the url. During validation we must work with the actual // requested theme instead of dynamically resolving the working theme. var httpContext = HttpContext.Current; if (httpContext != null && httpContext.Request != null) { var qs = httpContext.Request.QueryString; if (qs["theme"].HasValue()) { EngineContext.Current.Resolve <IThemeContext>().SetRequestTheme(qs["theme"]); } } } if (isPreprocessor && query != null && query.StartsWith("explicit", StringComparison.OrdinalIgnoreCase)) { // special case to support SASS/LESS @import declarations // within inherited SASS/LESS files. Snenario: an inheritor wishes to // include the same file from it's base theme (e.g. custom.scss) just to tweak it // a bit for his child theme. Without the 'explicit' query the resolution starting point // for custom.scss would be the CURRENT theme's folder, and NOT the requested one's, // which inevitably would result in a cyclic dependency. currentTheme = _themeRegistry.GetThemeManifest(requestedThemeName); isExplicit = true; } else { currentTheme = ThemeHelper.ResolveCurrentTheme(); } } return(currentTheme); }
public static MvcHtmlString ThemeVarChainInfo(this HtmlHelper html, ThemeVariableInfo info) { Guard.NotNull(info, "info"); var currentTheme = ThemeHelper.ResolveCurrentTheme(); if (currentTheme != info.Manifest) { // the variable is inherited from a base theme: display an info badge var chainInfo = "<span class='themevar-chain-info'><i class='fa fa-chain fa-flip-horizontal'></i><span class='pl-1'>{0}</span></span>".FormatCurrent(info.Manifest.ThemeName); return(MvcHtmlString.Create(chainInfo)); } return(MvcHtmlString.Empty); }
public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) { bool isLess; bool isBundle; if (!ThemeHelper.IsStyleSheet(virtualPath, out isLess, out isBundle)) { return(GetCacheDependencyInternal(virtualPath, virtualPathDependencies, utcStart)); } else { if (!isLess && !isBundle) { // it's a static css file (no bundle, no less) return(GetCacheDependencyInternal(virtualPath, virtualPathDependencies, utcStart)); } var arrPathDependencies = virtualPathDependencies.Cast <string>().ToArray(); // determine the virtual themevars.less import reference var themeVarsFile = arrPathDependencies.Where(x => ThemeHelper.PathIsThemeVars(x)).FirstOrDefault(); if (themeVarsFile.IsEmpty()) { // no themevars import... so no special considerations here return(GetCacheDependencyInternal(virtualPath, virtualPathDependencies, utcStart)); } // exclude the themevars import from the file dependencies list, // 'cause this one cannot be monitored by the physical file system var fileDependencies = arrPathDependencies.Except(new string[] { themeVarsFile }); if (arrPathDependencies.Any()) { int storeId = ThemeHelper.ResolveCurrentStoreId(); var theme = ThemeHelper.ResolveCurrentTheme(); // invalidate the cache when variables change string cacheKey = AspNetCache.BuildKey(FrameworkCacheConsumer.BuildThemeVarsCacheKey(theme.ThemeName, storeId)); var cacheDependency = new CacheDependency(MapDependencyPaths(fileDependencies), new string[] { cacheKey }, utcStart); return(cacheDependency); } return(null); } }
/// <summary> /// Tries to resolve a file up in the current theme's hierarchy chain. /// </summary> /// <param name="virtualPath">The original virtual path of the theme file</param> /// <returns> /// If the current working themme is based on another theme AND the requested file /// was physically found in the theme's hierarchy chain, an instance of <see cref="InheritedThemeFileResult" /> will be returned. /// In any other case the return value is <c>null</c>. /// </returns> public InheritedThemeFileResult Resolve(string virtualPath) { Guard.NotEmpty(virtualPath, nameof(virtualPath)); virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); if (!ThemeHelper.PathIsInheritableThemeFile(virtualPath)) { return(null); } bool isExplicit = false; string requestedThemeName; string relativePath; string query; virtualPath = TokenizePath(virtualPath, out requestedThemeName, out relativePath, out query); Func <InheritedThemeFileResult> nullOrFile = () => { if (isExplicit) { return(new InheritedThemeFileResult { IsExplicit = true, OriginalVirtualPath = virtualPath }); } return(null); }; ThemeManifest currentTheme; var isAdmin = EngineContext.Current.Resolve <IWorkContext>().IsAdmin; // ThemeHelper.IsAdminArea() if (isAdmin) { currentTheme = _themeRegistry.GetThemeManifest(requestedThemeName); } else { var styleResult = ThemeHelper.IsStyleSheet(relativePath); var isPreprocessor = styleResult != null && styleResult.IsPreprocessor; if (isPreprocessor) { // special consideration for SASS/LESS files: they can be validated // in the backend. For validation, a "theme" query is appended // to the url. During validation we must work with the actual // requested theme instead of dynamically resolving the working theme. var httpContext = HttpContext.Current; if (httpContext != null && httpContext.Request != null) { var qs = httpContext.Request.QueryString; if (qs["theme"].HasValue()) { EngineContext.Current.Resolve <IThemeContext>().SetRequestTheme(qs["theme"]); } } } if (isPreprocessor && query != null && query.StartsWith("explicit", StringComparison.OrdinalIgnoreCase)) { // special case to support SASS/LESS @import declarations // within inherited SASS/LESS files. Snenario: an inheritor wishes to // include the same file from it's base theme (e.g. custom.scss) just to tweak it // a bit for his child theme. Without the 'explicit' query the resolution starting point // for custom.scss would be the CURRENT theme's folder, and NOT the requested one's, // which inevitably would result in a cyclic dependency. currentTheme = _themeRegistry.GetThemeManifest(requestedThemeName); isExplicit = true; } else { currentTheme = ThemeHelper.ResolveCurrentTheme(); } if (currentTheme.BaseTheme == null) { // dont't bother resolving files: the current theme is not inherited. // Let the current VPP do the work. return(nullOrFile()); } } if (!currentTheme.ThemeName.Equals(requestedThemeName, StringComparison.OrdinalIgnoreCase)) { if (!_themeRegistry.IsChildThemeOf(currentTheme.ThemeName, requestedThemeName)) { return(nullOrFile()); } } var fileKey = new FileKey(currentTheme.ThemeName, relativePath); var result = _files.GetOrAdd(fileKey, (k) => { // ALWAYS begin the search with the current working theme's location! string resultVirtualPath; string resultPhysicalPath; string actualLocation = LocateFile(currentTheme.ThemeName, relativePath, out resultVirtualPath, out resultPhysicalPath); if (actualLocation != null) { return(new InheritedThemeFileResult { RelativePath = relativePath, OriginalVirtualPath = virtualPath, ResultVirtualPath = resultVirtualPath, ResultPhysicalPath = resultPhysicalPath, OriginalThemeName = requestedThemeName, ResultThemeName = actualLocation }); } return(null); }); if (result == null) { return(nullOrFile()); } return(result); }
public static MvcHtmlString ThemeVarEditor(this HtmlHelper html, ThemeVariableInfo info, object value) { Guard.NotNull(info, "info"); string expression = html.NameForThemeVar(info); var strValue = string.Empty; var arrValue = value as string[]; if (arrValue != null) { strValue = arrValue.Length > 0 ? arrValue[0] : value.ToString(); } else { strValue = value.ToString(); } var currentTheme = ThemeHelper.ResolveCurrentTheme(); var isDefault = strValue.IsCaseInsensitiveEqual(info.DefaultValue); var isValidColor = info.Type == ThemeVariableType.Color && ((strValue.HasValue() && ThemeVarsRepository.IsValidColor(strValue)) || (strValue.IsEmpty() && ThemeVarsRepository.IsValidColor(info.DefaultValue))); MvcHtmlString control; if (isValidColor) { control = html.ColorBox(expression, strValue, info.DefaultValue); } else if (info.Type == ThemeVariableType.Boolean) { var locService = EngineContext.Current.Resolve <ILocalizationService>(); control = html.CheckBox(expression, strValue.ToBool()); var custom = "<label class='switch'>{0}<span class='switch-toggle' data-on='{1}' data-off='{2}'></span></label>".FormatInvariant( control.ToString(), locService.GetResource("Common.On").Truncate(3), locService.GetResource("Common.Off").Truncate(3)); control = MvcHtmlString.Create(custom); } else if (info.Type == ThemeVariableType.Select) { control = ThemeVarSelectEditor(html, info, expression, strValue); } else { control = html.TextBox(expression, isDefault ? "" : strValue, new { placeholder = info.DefaultValue, @class = "form-control" }); } if (currentTheme != info.Manifest) { // the variable is inherited from a base theme: display an info badge var chainInfo = "<span class='themevar-chain-info'><i class='fa fa-chain fa-flip-horizontal'></i> {0}</span>".FormatCurrent(info.Manifest.ThemeName); return(MvcHtmlString.Create(control.ToString() + chainInfo)); } else { return(control); } }