/// <summary>
        /// Read shopify theme settings from 'config' folder
        /// </summary>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public IDictionary <string, object> GetSettings(string defaultValue = null)
        {
            var cacheKey = CacheKey.With(GetType(), "GetSettings", CurrentThemeSettingPath, defaultValue);

            return(_memoryCache.GetOrCreateExclusive(cacheKey, (cacheItem) =>
            {
                cacheItem.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(CurrentThemeSettingPath) }));
                var retVal = new Dictionary <string, object>().WithDefaultValue(defaultValue);
                //Load all data from current theme config
                var resultSettings = InnerGetAllSettings(_themeBlobProvider, CurrentThemeSettingPath);
                if (resultSettings == null && BaseThemeSettingPath != null)
                {
                    resultSettings = InnerGetAllSettings(_themeBlobProvider, BaseThemeSettingPath);
                }
                if (resultSettings != null)
                {
                    //Get actual preset from merged config
                    var currentPreset = resultSettings.GetValue("current");
                    if (currentPreset is JValue)
                    {
                        var currentPresetName = ((JValue)currentPreset).Value.ToString();
                        if (!(resultSettings.GetValue("presets") is JObject presets) || !presets.Children().Any())
                        {
                            throw new StorefrontException("Setting presets not defined");
                        }

                        IList <JProperty> allPresets = presets.Children().Cast <JProperty>().ToList();
                        resultSettings = allPresets.FirstOrDefault(p => p.Name == currentPresetName).Value as JObject;
                        if (resultSettings == null)
                        {
                            throw new StorefrontException($"Setting preset with name '{currentPresetName}' not found");
                        }
                    }
        /// <summary>
        /// Read shopify theme settings from 'config' folder
        /// </summary>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public IDictionary <string, object> GetSettings(string defaultValue = null)
        {
            var cacheKey = CacheKey.With(GetType(), "GetSettings", CurrentThemeSettingPath, defaultValue);

            return(_memoryCache.GetOrCreateExclusive(cacheKey, cacheItem =>
            {
                cacheItem.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(CurrentThemeSettingPath) }));

                JObject result;
                var baseThemeSettings = new JObject();
                var currentThemeSettings = result = InnerGetAllSettings(_themeBlobProvider, CurrentThemeSettingPath);

                //Try to load settings from base theme path and merge them with resources for local theme
                if ((_options.MergeBaseSettings || currentThemeSettings == null) && !string.IsNullOrEmpty(BaseThemeSettingPath))
                {
                    cacheItem.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(BaseThemeSettingPath) }));
                    baseThemeSettings = InnerGetAllSettings(_themeBlobProvider, BaseThemeSettingPath);
                }

                result = _options.MergeBaseSettings
                    ? SettingsManager.Merge(baseThemeSettings, currentThemeSettings ?? new JObject())
                    : SettingsManager.ReadSettings(currentThemeSettings ?? new JObject()).CurrentPreset.Json;

                return result.ToObject <Dictionary <string, object> >().ToDictionary(x => x.Key, x => x.Value).WithDefaultValue(defaultValue);
            }));
        }
예제 #3
0
        /// <summary>
        /// Render template by content and parameters
        /// </summary>
        /// <param name="templateContent"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public ValueTask<string> RenderTemplateAsync(string templateContent, string templatePath, object context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (!(context is IScriptObject scriptObject))
            {
                throw new StorefrontException($"{ nameof(context) } must implement IScriptObject");
            }

            if (string.IsNullOrEmpty(templateContent))
            {
                return new ValueTask<string>(templateContent);
            }

            var isLiquidTemplate = _isLiquid.Match(templateContent);
            if (!isLiquidTemplate.Success)
            {
                return new ValueTask<string>(templateContent);
            }

            //TODO: Handle _options.RethrowLiquidRenderErrors
            var cacheKey = CacheKey.With(GetType(), "ParseTemplate", templatePath ?? templateContent);
            var parsedTemplate = _memoryCache.GetOrCreate(cacheKey, (cacheItem) =>
            {
                if (!string.IsNullOrEmpty(templatePath))
                {
                    cacheItem.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(templatePath) }));
                }
                else
                {
                    cacheItem.AddExpirationToken(ThemeEngineCacheRegion.CreateChangeToken());
                }
                return Template.ParseLiquid(templateContent, templatePath);
            });

            if (parsedTemplate.HasErrors)
            {
                throw new InvalidOperationException(string.Join("\n", parsedTemplate.Messages));
            }


            var templateContext = new TemplateContext()
            {
                TemplateLoader = this,
                EnableRelaxedMemberAccess = true,
                NewLine = Environment.NewLine,
                TemplateLoaderLexerOptions = new LexerOptions
                {
                    Mode = ScriptMode.Liquid
                }
            };
            templateContext.PushGlobal(scriptObject);

            var result = parsedTemplate.Render(templateContext);
            return new ValueTask<string>(result);
        }
예제 #4
0
        /// <summary>
        /// Read localization resources
        /// </summary>
        /// <returns></returns>
        public JObject ReadLocalization()
        {
            var cacheKey = CacheKey.With(GetType(), "ReadLocalization", CurrentThemeLocalePath, WorkContext.CurrentLanguage.CultureName);

            return(_memoryCache.GetOrCreate(cacheKey, (cacheItem) =>
            {
                cacheItem.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(CurrentThemeLocalePath + "/*") }));
                return InnerReadLocalization(_themeBlobProvider, CurrentThemeLocalePath, WorkContext.CurrentLanguage) ?? new JObject();
            }));
        }
예제 #5
0
        /// <summary>
        /// Return hash of requested asset (used for file versioning)
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public string GetAssetHash(string filePath)
        {
            var cacheKey = CacheKey.With(GetType(), "GetAssetHash", filePath);

            return(_memoryCache.GetOrCreate(cacheKey, (cacheEntry) =>
            {
                cacheEntry.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(filePath) }));

                using (var stream = GetAssetStream(filePath))
                {
                    var hashAlgorithm = CryptoConfig.AllowOnlyFipsAlgorithms ? (SHA256) new SHA256CryptoServiceProvider() : new SHA256Managed();
                    return WebEncoders.Base64UrlEncode(hashAlgorithm.ComputeHash(stream));
                }
            }));
        }
        private string ReadTemplateByPath(string templatePath)
        {
            if (string.IsNullOrEmpty(templatePath))
            {
                throw new ArgumentNullException(nameof(templatePath));
            }

            var cacheKey = CacheKey.With(GetType(), "ReadTemplateByName", templatePath);

            return(_memoryCache.GetOrCreateExclusive(cacheKey, (cacheItem) =>
            {
                cacheItem.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(templatePath) }));
                using (var stream = _themeBlobProvider.OpenRead(templatePath))
                {
                    return stream.ReadToString();
                }
            }));
        }
예제 #7
0
        /// <summary>
        /// Return hash of requested asset (used for file versioning)
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public string GetAssetHash(string filePath)
        {
            var cacheKey = CacheKey.With(GetType(), "GetAssetHash", filePath);
            return _memoryCache.GetOrCreateExclusive(cacheKey, (cacheEntry) =>
            {
                cacheEntry.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(filePath) }));

                using (var stream = GetAssetStreamAsync(filePath).GetAwaiter().GetResult())
                {
                    if (stream == null)
                    {
                        throw new StorefrontException($"Theme resource for path '{filePath}' not found");
                    }
                    var hashAlgorithm = CryptoConfig.AllowOnlyFipsAlgorithms ? (SHA256)new SHA256CryptoServiceProvider() : new SHA256Managed();
                    return WebEncoders.Base64UrlEncode(hashAlgorithm.ComputeHash(stream));
                }
            });
        }
        /// <summary>
        /// Read localization resources
        /// </summary>
        /// <returns></returns>
        public JObject ReadLocalization()
        {
            var cacheKey = CacheKey.With(GetType(), "ReadLocalization", CurrentThemeLocalePath, WorkContext.CurrentLanguage.CultureName);

            return(_memoryCache.GetOrCreateExclusive(cacheKey, (cacheItem) =>
            {
                var result = new JObject();
                cacheItem.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(CurrentThemeLocalePath + "/*") }));
                //Try to load localization resources from base theme path and merge them with resources for local theme
                if (BaseThemeLocalePath != null)
                {
                    cacheItem.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(BaseThemeLocalePath + "/*") }));
                    result = InnerReadLocalization(_themeBlobProvider, BaseThemeLocalePath, WorkContext.CurrentLanguage) ?? new JObject();
                }
                result.Merge(InnerReadLocalization(_themeBlobProvider, CurrentThemeLocalePath, WorkContext.CurrentLanguage) ?? new JObject(), new JsonMergeSettings {
                    MergeArrayHandling = MergeArrayHandling.Merge
                });
                return result;
            }));
        }
예제 #9
0
        private string ReadTemplateByName(string templateName)
        {
            var templatePath = ResolveTemplatePath(templateName);

            if (string.IsNullOrEmpty(templatePath))
            {
                throw new FileSystemException($"The template '{templateName}' was not found. The following locations were searched:<br/>{string.Join("<br/>", DiscoveryPaths)}");
            }

            var cacheKey = CacheKey.With(GetType(), "ReadTemplateByName", templatePath);

            return(_memoryCache.GetOrCreate(cacheKey, (cacheItem) =>
            {
                cacheItem.AddExpirationToken(new CompositeChangeToken(new[] { ThemeEngineCacheRegion.CreateChangeToken(), _themeBlobProvider.Watch(templatePath) }));
                using (var stream = _themeBlobProvider.OpenRead(templatePath))
                {
                    return stream.ReadToString();
                }
            }));
        }
예제 #10
0
        public bool IsFeatureActive(string featureName)
        {
            var cacheKey = CacheKey.With(GetType(), nameof(IsFeatureActive), featureName);

            return(_memoryCache.GetOrCreateExclusive(cacheKey, cacheEntry =>
            {
                var changeToken = ThemeEngineCacheRegion.CreateChangeToken();
                var watchChangeToken = _themeBlobProvider.Watch(CurrentThemeSettingPath);
                var tokens = new[]
                {
                    changeToken, watchChangeToken
                };
                var compositeChangeToken = new CompositeChangeToken(tokens);
                cacheEntry.AddExpirationToken(compositeChangeToken);

                var settingJObject = InnerGetAllSettings(_themeBlobProvider, CurrentThemeSettingPath);
                var result = _featuresAgent.IsActive(featureName, settingJObject);
                return result;
            }));
        }
예제 #11
0
        public ActionResult ResetCache()
        {
            //TODO: Replace to some other (maybe with using reflection)
            ThemeEngineCacheRegion.ExpireRegion();
            CartCacheRegion.ExpireRegion();
            CatalogCacheRegion.ExpireRegion();
            ContentBlobCacheRegion.ExpireRegion();
            CustomerCacheRegion.ExpireRegion();
            MarketingCacheRegion.ExpireRegion();
            PricingCacheRegion.ExpireRegion();
            QuoteCacheRegion.ExpireRegion();
            RecommendationsCacheRegion.ExpireRegion();
            StaticContentCacheRegion.ExpireRegion();
            StoreCacheRegion.ExpireRegion();
            TaxCacheRegion.ExpireRegion();
            SubscriptionCacheRegion.ExpireRegion();
            SecurityCacheRegion.ExpireRegion();

            return(StoreFrontRedirect("~/"));
        }