/// <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);
        }
Beispiel #2
0
        /// <summary>
        /// Combines (and minifies) the content of resources and saves the combinations
        /// </summary>
        /// <param name="resources">Resources to combine</param>
        /// <param name="hashCode">Just so it shouldn't be recalculated</param>
        /// <param name="resourceType">Type of the resources</param>
        /// <param name="settings">Combination settings</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, int hashCode, 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);

                //var requiredContext = new ResourceRequiredContext();
                //var resourceManifest = new ResourceManifest();
                //requiredContext.Resource = resourceManifest.DefineResource(ResourceTypeHelper.EnumToStringType(resourceType), resource.Resource.Name);
                //requiredContext.Resource.SetUrl(resource.Resource.GetFullPath());
                //requiredContext.Settings = new RequireSettings();
                //requiredContext.Settings.Culture = resource.Settings.Culture;
                //requiredContext.Settings.Condition = resource.Settings.Condition;
                //requiredContext.Settings.Attributes = new Dictionary<string, string>(resource.Settings.Attributes);
                //combinatorResource.RequiredContext = requiredContext;

                combinatorResources.Add(combinatorResource);
            }

            var combinedContent = new StringBuilder(1000);

            Action <CombinatorResource> saveCombination =
                (combinedResource) =>
            {
                if (combinedResource == null)
                {
                    return;
                }
                // Don't save emtpy resources
                if (combinedContent.Length == 0 && !combinedResource.IsOriginal)
                {
                    return;
                }

                combinedResource.Content = combinedContent.ToString();
                _cacheFileService.Save(hashCode, combinedResource);

                combinedContent.Clear();
            };


            Regex currentSetRegex = null;

            for (int i = 0; i < combinatorResources.Count; i++)
            {
                var resource          = combinatorResources[i];
                var previousResource  = (i != 0) ? combinatorResources[i - 1] : null;
                var absoluteUrlString = "";

                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);
                        }

                        // 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);
                        }

                        // 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)
                            {
                                saveCombination(previousResource);
                            }
                        }

                        _resourceProcessingService.ProcessResource(resource, combinedContent, settings);
                    }
                    else
                    {
                        // This is a fully excluded resource
                        if (previousResource != null)
                        {
                            saveCombination(previousResource);
                        }
                        resource.IsOriginal = true;
                        saveCombination(resource);
                        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]);
        }