public override IList<ResourceRequiredContext> BuildRequiredResources(string stringResourceType) { // It's necessary to make a copy since making a change to the local variable also changes the private one. var resources = new List<ResourceRequiredContext>(base.BuildRequiredResources(stringResourceType)); var settingsPart = _cacheManager.Get("Piedone.Combinator.CombinatorSettingsPart", ctx => { _combinatorEventMonitor.MonitorConfigurationChanged(ctx); return _siteService.GetSiteSettings().As<CombinatorSettingsPart>(); }); if (resources.Count == 0 || Orchard.UI.Admin.AdminFilter.IsApplied(_httpContextAccessor.Current().Request.RequestContext) && !settingsPart.EnableForAdmin) return resources; var resourceType = ResourceTypeHelper.StringTypeToEnum(stringResourceType); try { var settings = new CombinatorSettings { CombineCDNResources = settingsPart.CombineCdnResources, ResourceDomain = settingsPart.ResourceDomain, EmbedCssImages = settingsPart.EmbedCssImages, EmbeddedImagesMaxSizeKB = settingsPart.EmbeddedImagesMaxSizeKB, GenerateImageSprites = settingsPart.GenerateImageSprites, MinifyResources = settingsPart.MinifyResources }; if (!String.IsNullOrEmpty(settingsPart.CombinationExcludeRegex)) settings.CombinationExcludeFilter = new Regex(settingsPart.CombinationExcludeRegex); if (!String.IsNullOrEmpty(settingsPart.EmbedCssImagesStylesheetExcludeRegex)) settings.EmbedCssImagesStylesheetExcludeFilter = new Regex(settingsPart.EmbedCssImagesStylesheetExcludeRegex); if (!String.IsNullOrEmpty(settingsPart.MinificationExcludeRegex)) settings.MinificationExcludeFilter = new Regex(settingsPart.MinificationExcludeRegex); if (!String.IsNullOrEmpty(settingsPart.ResourceSetRegexes)) { var setRegexes = new List<Regex>(); foreach (var regex in settingsPart.ResourceSetRegexes.Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) { if (!String.IsNullOrEmpty(regex)) setRegexes.Add(new Regex(regex)); } settings.ResourceSetFilters = setRegexes.ToArray(); } DiscoverResourceOverrides(resources, resourceType); IList<ResourceRequiredContext> result; if (resourceType == ResourceType.Style) result = _combinatorService.CombineStylesheets(resources, settings); else if (resourceType == ResourceType.JavaScript) result = _combinatorService.CombineScripts(resources, settings); else return base.BuildRequiredResources(stringResourceType); RemoveOriginalResourceShapes(result, resourceType); return result; } catch (Exception ex) { if (ex.IsFatal()) throw; Logger.Error(ex, "Error when combining " + resourceType + " files"); return base.BuildRequiredResources(stringResourceType); } }
public void MinificationExclusionWorks() { _resourceRepository.FillWithTestStyles(); var settings = new CombinatorSettings { MinificationExcludeFilter = new Regex("test\\.css"), MinifyResources = true }; var resource = _resourceRepository.GetResource("~/Modules/Piedone.Combinator/Styles/test.css"); _resourceProcessingService.ProcessResource(resource, new StringBuilder(), settings); Assert.That(resource.Content.StartsWith("minified:") && resource.Content.Contains("/Modules/Piedone.Combinator/Styles/test.css"), Is.False); resource = _resourceRepository.GetResource("~/Modules/Piedone.Combinator/Styles/test2.css"); _resourceProcessingService.ProcessResource(resource, new StringBuilder(), settings); Assert.That(resource.Content.StartsWith("minified:") && resource.Content.Contains("/Modules/Piedone.Combinator/Styles/test2.css"), Is.True); }
/// <summary> /// Combines (and minifies) the content of resources and saves the combinations. /// </summary> /// <param name="resources">Resources to combine.</param> /// <param name="fingerprint">Just so it shouldn't be recalculated.</param> /// <param name="resourceType">Type of the resources.</param> /// <param name="settings">Combination setting.s</param> /// <exception cref="ApplicationException">Thrown if there was a problem with a resource file (e.g. it was missing or could not be opened).</exception> private void Combine(IList<ResourceRequiredContext> resources, string fingerprint, ResourceType resourceType, ICombinatorSettings settings) { if (resources.Count == 0) return; var combinatorResources = new List<CombinatorResource>(resources.Count); foreach (var resource in resources) { var combinatorResource = _combinatorResourceManager.ResourceFactory(resourceType); // Copying the context so the original one won't be touched. combinatorResource.FillRequiredContext( resource.Resource.Name, resource.Resource.GetFullPath(), resource.Settings.Culture, resource.Settings.Condition, resource.Settings.Attributes, resource.Resource.TagBuilder.Attributes); combinatorResource.IsRemoteStorageResource = settings.RemoteStorageUrlPattern != null && settings.RemoteStorageUrlPattern.IsMatch(combinatorResource.AbsoluteUrl.ToString()); combinatorResources.Add(combinatorResource); } var combinedContent = new StringBuilder(1000); var resourceBaseUri = settings.ResourceBaseUri != null ? settings.ResourceBaseUri : new Uri(_hca.Current().Request.Url, "/"); Action<CombinatorResource, List<CombinatorResource>> saveCombination = (combinedResource, containedResources) => { if (combinedResource == null) return; // Don't save emtpy resources. if (combinedContent.Length == 0 && !combinedResource.IsOriginal) return; if (!containedResources.Any()) containedResources = new List<CombinatorResource> { combinedResource }; var bundleFingerprint = containedResources.GetCombinatorResourceListFingerprint(settings); var localSettings = new CombinatorSettings(settings); if (localSettings.EnableResourceSharing && settings.ResourceSharingExcludeFilter != null) { foreach (var resource in containedResources) { if (localSettings.EnableResourceSharing) { localSettings.EnableResourceSharing = !settings.ResourceSharingExcludeFilter.IsMatch(resource.AbsoluteUrl.ToString()); } } } if (!combinedResource.IsOriginal) { combinedResource.Content = combinedContent.ToString(); if (combinedResource.Type == ResourceType.Style && !string.IsNullOrEmpty(combinedResource.Content) && settings.GenerateImageSprites) { _resourceProcessingService.ReplaceCssImagesWithSprite(combinedResource); } combinedResource.Content = "/*" + Environment.NewLine + "Resource bundle created by Combinator (http://combinator.codeplex.com/)" + Environment.NewLine + Environment.NewLine + "Resources in this bundle:" + Environment.NewLine + string.Join(Environment.NewLine, containedResources.Select(resource => { var url = resource.AbsoluteUrl.ToString(); if (localSettings.EnableResourceSharing && !resource.IsCdnResource && !resource.IsRemoteStorageResource) { var uriBuilder = new UriBuilder(url); uriBuilder.Host = "DefaultTenant"; url = uriBuilder.Uri.ToStringWithoutScheme(); } return "- " + url; })) + Environment.NewLine + "*/" + Environment.NewLine + Environment.NewLine + Environment.NewLine + combinedResource.Content; } // We save a bundle now. First the bundle should be saved separately under its unique name, then for this resource list. if (bundleFingerprint != fingerprint && containedResources.Count > 1) { if (!_cacheFileService.Exists(bundleFingerprint, localSettings)) { _cacheFileService.Save(bundleFingerprint, combinedResource, localSettings); } // Overriding the url for the resource in this resource list with the url of the set. combinedResource.IsOriginal = true; // The following should fetch one result theoretically but can more if the above Exists()-Save() happens // in multiple requests at the same time. var set = _cacheFileService.GetCombinedResources(bundleFingerprint, localSettings).First(); combinedResource.LastUpdatedUtc = set.LastUpdatedUtc; if (IsOwnedResource(set)) { if (settings.ResourceBaseUri != null && !set.IsRemoteStorageResource) { combinedResource.RequiredContext.Resource.SetUrl(UriHelper.Combine(resourceBaseUri.ToStringWithoutScheme(), set.AbsoluteUrl.PathAndQuery)); } else if (set.IsRemoteStorageResource) { combinedResource.IsRemoteStorageResource = true; combinedResource.RequiredContext.Resource.SetUrl(set.AbsoluteUrl.ToStringWithoutScheme()); } else { combinedResource.RequiredContext.Resource.SetUrl(set.RelativeUrl.ToString()); } AddTimestampToUrlIfNecessary(combinedResource); } else { combinedResource.RequiredContext.Resource.SetUrl(set.AbsoluteUrl.ToStringWithoutScheme()); } } _cacheFileService.Save(fingerprint, combinedResource, localSettings); combinedContent.Clear(); containedResources.Clear(); }; Regex currentSetRegex = null; var resourcesInCombination = new List<CombinatorResource>(); for (int i = 0; i < combinatorResources.Count; i++) { var resource = combinatorResources[i]; var previousResource = (i != 0) ? combinatorResources[i - 1] : null; var absoluteUrlString = ""; var saveOriginalResource = false; try { absoluteUrlString = resource.AbsoluteUrl.ToString(); if (settings.CombinationExcludeFilter == null || !settings.CombinationExcludeFilter.IsMatch(absoluteUrlString)) { // If this resource differs from the previous one in terms of settings or CDN they can't be combined if (previousResource != null && (!previousResource.SettingsEqual(resource) || (previousResource.IsCdnResource != resource.IsCdnResource && !settings.CombineCdnResources))) { saveCombination(previousResource, resourcesInCombination); previousResource = null; // So it doesn't get combined again in if (saveOriginalResource) below. } // If this resource is in a different set than the previous, they can't be combined if (currentSetRegex != null && !currentSetRegex.IsMatch(absoluteUrlString)) { currentSetRegex = null; saveCombination(previousResource, resourcesInCombination); } // Calculate if this resource is in a set if (currentSetRegex == null && settings.ResourceSetFilters != null && settings.ResourceSetFilters.Length > 0) { int r = 0; while (currentSetRegex == null && r < settings.ResourceSetFilters.Length) { if (settings.ResourceSetFilters[r].IsMatch(absoluteUrlString)) currentSetRegex = settings.ResourceSetFilters[r]; r++; } // The previous resource is in a different set or in no set so it can't be combined with this resource if (currentSetRegex != null && previousResource != null && resourcesInCombination.Any()) { saveCombination(previousResource, resourcesInCombination); } } // Nesting resources in such blocks is needed because some syntax is valid if in its own file but not anymore // when bundled. if (resourceType == ResourceType.JavaScript) combinedContent.Append("{"); combinedContent.Append(Environment.NewLine); _resourceProcessingService.ProcessResource(resource, combinedContent, settings); // This can be because e.g. it's a CDN resource and CDN combination is disabled. if (resource.IsOriginal) saveOriginalResource = true; combinedContent.Append(Environment.NewLine); if (resourceType == ResourceType.JavaScript) combinedContent.Append("}"); combinedContent.Append(Environment.NewLine); resourcesInCombination.Add(resource); } else saveOriginalResource = true; if (saveOriginalResource) { // This is a fully excluded resource if (previousResource != null) saveCombination(previousResource, resourcesInCombination); resource.IsOriginal = true; saveCombination(resource, resourcesInCombination); combinatorResources[i] = null; // So previous resource detection works correctly } } catch (Exception ex) { if (ex.IsFatal()) throw; throw new OrchardException(T("Processing of resource {0} failed.", absoluteUrlString), ex); } } saveCombination(combinatorResources[combinatorResources.Count - 1], resourcesInCombination); }
private void SaveTestResources() { var settings = new CombinatorSettings { ResourceBaseUri = new Uri("http://localhost") }; var resource1 = _resourceRepository.NewResource(ResourceType.Style); resource1.Content = "test"; _cacheFileService.Save(_cssResourcesFingerprint, resource1, settings); _cacheFileService.Save(_jsResourceFingerprint, _resourceRepository.NewResource(ResourceType.JavaScript), settings); _cacheFileService.Save(_jsResourceFingerprint, _resourceRepository.NewResource(ResourceType.JavaScript), settings); ClearSession(); }
public void SpriteGenerationRuns() { _resourceRepository.Clear(); CombinatorResource resource; var type = ResourceType.Style; resource = _resourceRepository.SaveResource("~/Modules/Piedone.Combinator/Styles/1.css", type); var settings = new CombinatorSettings { GenerateImageSprites = true }; var combinedResources = _combinatorService.CombineStylesheets(_resourceRepository.GetResources(type), settings); resource = _resourceRepository.GetResource(combinedResources.First()); Assert.That(resource.Content.Contains("ImageSprite"), Is.True); }
public void SetsAreSplit() { _resourceRepository.Clear(); CombinatorResource resource; var type = ResourceType.Style; resource = _resourceRepository.SaveResource("~/Modules/Piedone.Combinator/Styles/1.css", type); resource = _resourceRepository.SaveResource("~/Modules/Piedone.Combinator/Styles/2.css", type); resource = _resourceRepository.SaveResource("~/Modules/Piedone.Combinator/Styles/3.css", type); resource = _resourceRepository.SaveResource("~/Modules/Piedone.Combinator/Styles/4.css", type); resource = _resourceRepository.SaveResource("~/Modules/Piedone.Combinator/Styles/5.css", type); resource = _resourceRepository.SaveResource("~/Modules/Piedone.Combinator/Styles/6.css", type); var settings = new CombinatorSettings { ResourceSetFilters = new Regex[] { new Regex("1"), new Regex("4|5") } }; var combinedResources = _combinatorService.CombineStylesheets(_resourceRepository.GetResources(type), settings); Assert.That(combinedResources.Count, Is.EqualTo(4)); }
public override IList<ResourceRequiredContext> BuildRequiredResources(string stringResourceType) { // It's necessary to make a copy since making a change to the local variable also changes the private one. var resources = new List<ResourceRequiredContext>(base.BuildRequiredResources(stringResourceType)); var settingsPart = _cacheManager.Get("Piedone.Combinator.CombinatorSettingsPart", ctx => { _combinatorEventMonitor.MonitorConfigurationChanged(ctx); return _siteService.GetSiteSettings().As<CombinatorSettingsPart>(); }); if (resources.Count == 0 || Orchard.UI.Admin.AdminFilter.IsApplied(_httpContextAccessor.Current().Request.RequestContext) && !settingsPart.EnableForAdmin) return resources; var resourceType = ResourceTypeHelper.StringTypeToEnum(stringResourceType); try { var settings = new CombinatorSettings { CombineCDNResources = settingsPart.CombineCDNResources, EmbedCssImages = settingsPart.EmbedCssImages, EmbeddedImagesMaxSizeKB = settingsPart.EmbeddedImagesMaxSizeKB, MinifyResources = settingsPart.MinifyResources }; if (!String.IsNullOrEmpty(settingsPart.CombinationExcludeRegex)) settings.CombinationExcludeFilter = new Regex(settingsPart.CombinationExcludeRegex); if (!String.IsNullOrEmpty(settingsPart.EmbedCssImagesStylesheetExcludeRegex)) settings.EmbedCssImagesStylesheetExcludeFilter = new Regex(settingsPart.EmbedCssImagesStylesheetExcludeRegex); if (!String.IsNullOrEmpty(settingsPart.MinificationExcludeRegex)) settings.MinificationExcludeFilter = new Regex(settingsPart.MinificationExcludeRegex); if (!String.IsNullOrEmpty(settingsPart.ResourceSetRegexes)) { var setRegexes = new List<Regex>(); foreach (var regex in settingsPart.ResourceSetRegexes.Trim().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) { if (!String.IsNullOrEmpty(regex)) setRegexes.Add(new Regex(regex)); } settings.ResourceSetFilters = setRegexes.ToArray(); } if (resourceType == ResourceType.Style) { // Checking for overridden stylesheets var currentTheme = _themeManager.GetRequestTheme(_httpContextAccessor.Current().Request.RequestContext); var shapeTable = _shapeTableLocator.Lookup(currentTheme.Id); foreach (var resource in resources) { var shapeName = StylesheetBindingStrategy.GetAlternateShapeNameFromFileName(resource.Resource.GetFullPath()); // Simply included CDN stylesheets are not in the ShapeTable, so we have to check if (shapeTable.Bindings.ContainsKey("Style__" + shapeName)) { var binding = shapeTable.Bindings["Style__" + shapeName].BindingSource; resource.Resource.SetUrl(binding, null); } } return _combinatorService.CombineStylesheets(resources, settings); } else if (resourceType == ResourceType.JavaScript) { return _combinatorService.CombineScripts(resources, settings); } return base.BuildRequiredResources(stringResourceType); } catch (Exception ex) { if (ex.IsFatal()) throw; Logger.Error(ex, "Error when combining " + resourceType + " files"); return base.BuildRequiredResources(stringResourceType); } }
public override IList<ResourceRequiredContext> BuildRequiredResources(string stringResourceType) { // It's necessary to make a copy since making a change to the local variable also changes the private one. var resources = new List<ResourceRequiredContext>(base.BuildRequiredResources(stringResourceType)); var settingsPart = _siteService.GetSiteSettings().As<CombinatorSettingsPart>(); if (resources.Count == 0 || Orchard.UI.Admin.AdminFilter.IsApplied(_httpContextAccessor.Current().Request.RequestContext) && !settingsPart.EnableForAdmin) return resources; var resourceType = ResourceTypeHelper.StringTypeToEnum(stringResourceType); try { Uri resourceBaseUri = null; if (!string.IsNullOrEmpty(settingsPart.ResourceBaseUrl)) resourceBaseUri = UriHelper.CreateUri(settingsPart.ResourceBaseUrl); var settings = new CombinatorSettings { CombineCdnResources = settingsPart.CombineCdnResources, ResourceBaseUri = resourceBaseUri, EmbedCssImages = settingsPart.EmbedCssImages, EmbeddedImagesMaxSizeKB = settingsPart.EmbeddedImagesMaxSizeKB, GenerateImageSprites = settingsPart.GenerateImageSprites, MinifyResources = settingsPart.MinifyResources, EnableResourceSharing = settingsPart.EnableResourceSharing }; if (!string.IsNullOrEmpty(settingsPart.CombinationExcludeRegex)) settings.CombinationExcludeFilter = new Regex(settingsPart.CombinationExcludeRegex); if (!string.IsNullOrEmpty(settingsPart.RemoteStorageUrlRegex)) settings.RemoteStorageUrlPattern = new Regex(settingsPart.RemoteStorageUrlRegex); if (!string.IsNullOrEmpty(settingsPart.EmbedCssImagesStylesheetExcludeRegex)) settings.EmbedCssImagesStylesheetExcludeFilter = new Regex(settingsPart.EmbedCssImagesStylesheetExcludeRegex); if (!string.IsNullOrEmpty(settingsPart.MinificationExcludeRegex)) settings.MinificationExcludeFilter = new Regex(settingsPart.MinificationExcludeRegex); if (!string.IsNullOrEmpty(settingsPart.ResourceSharingExcludeRegex)) settings.ResourceSharingExcludeFilter = new Regex(settingsPart.ResourceSharingExcludeRegex); if (!string.IsNullOrEmpty(settingsPart.ResourceSetRegexes)) { var setRegexes = new List<Regex>(); foreach (var regex in settingsPart.ResourceSetRegexesEnumerable) { if (!string.IsNullOrEmpty(regex)) setRegexes.Add(new Regex(regex)); } settings.ResourceSetFilters = setRegexes.ToArray(); } DiscoverResourceOverrides(resources, resourceType); IList<ResourceRequiredContext> result; if (resourceType == ResourceType.Style) result = _combinatorService.CombineStylesheets(resources, settings); else if (resourceType == ResourceType.JavaScript) result = _combinatorService.CombineScripts(resources, settings); else return base.BuildRequiredResources(stringResourceType); RemoveOriginalResourceShapes(result, resourceType); return result; } catch (Exception ex) { if (ex.IsFatal()) throw; Logger.Error(ex, "Error when combining " + resourceType + " files"); return base.BuildRequiredResources(stringResourceType); } }