Пример #1
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));

            if (virtualPath[0] != '~')
            {
                virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
            }

            if (!ThemeHelper.PathIsInheritableThemeFile(virtualPath))
            {
                return(null);
            }

            bool isExplicit = false;

            virtualPath = ThemeHelper.TokenizePath(virtualPath, out var requestedThemeName, out var relativePath, out var query);

            Func <InheritedThemeFileResult> nullOrFile = () =>
            {
                return(isExplicit
                                        ? new InheritedThemeFileResult {
                    IsExplicit = true, OriginalVirtualPath = virtualPath, Query = query
                }
                                        : null);
            };

            ThemeManifest currentTheme = ResolveTheme(requestedThemeName, relativePath, query, out isExplicit);

            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());
                }
            }
            else if (isExplicit && currentTheme.BaseTheme != null)
            {
                // A file from the base theme has been requested
                currentTheme = currentTheme.BaseTheme;
            }

            var fileKey = new FileKey(currentTheme.ThemeName, relativePath, query);
            InheritedThemeFileResult result;

            using (_rwLock.GetUpgradeableReadLock())
            {
                if (!_files.TryGetValue(fileKey, out result))
                {
                    using (_rwLock.GetWriteLock())
                    {
                        // ALWAYS begin the search with the current working theme's location!
                        string actualLocation = LocateFile(currentTheme.ThemeName, relativePath, out var resultVirtualPath, out var resultPhysicalPath);

                        if (actualLocation != null)
                        {
                            result = new InheritedThemeFileResult
                            {
                                RelativePath        = relativePath,
                                OriginalVirtualPath = virtualPath,
                                ResultVirtualPath   = resultVirtualPath,
                                ResultPhysicalPath  = resultPhysicalPath,
                                OriginalThemeName   = requestedThemeName,
                                ResultThemeName     = actualLocation,
                                IsExplicit          = isExplicit,
                                Query = query
                            };
                        }

                        _files[fileKey] = result;
                    }
                }
            }

            if (result == null)
            {
                return(nullOrFile());
            }

            return(result);
        }
Пример #2
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.ArgumentNotEmpty(() => 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
            {
                bool isLess;
                bool isBundle;
                if (ThemeHelper.IsStyleSheet(relativePath, out isLess, out isBundle) && isLess)
                {
                    // special consideration for 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 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 (isLess && query != null && query.StartsWith("explicit", StringComparison.OrdinalIgnoreCase))
                {
                    // special case to support LESS @import declarations
                    // within inherited LESS files. Snenario: an inheritor wishes to
                    // include the same file from it's base theme (e.g. custom.less) just to tweak it
                    // a bit for his child theme. Without the 'explicit' query the resolution starting point
                    // for custom.less 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);
        }
Пример #3
0
        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);
            }
        }