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));
        }
Exemple #2
0
        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>&nbsp;{0}</span>".FormatCurrent(info.Manifest.ThemeName);
                return(MvcHtmlString.Create(control.ToString() + chainInfo));
            }
            else
            {
                return(control);
            }
        }
Exemple #4
0
        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);
        }
Exemple #5
0
        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);
            }
        }
Exemple #7
0
        /// <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>&nbsp;{0}</span>".FormatCurrent(info.Manifest.ThemeName);
                return(MvcHtmlString.Create(control.ToString() + chainInfo));
            }
            else
            {
                return(control);
            }
        }