diff --git a/OurUmbraco/Repository/Controllers/PackageRepositoryController.cs b/OurUmbraco/Repository/Controllers/PackageRepositoryController.cs index d05dff3c..615e63e4 100644 --- a/OurUmbraco/Repository/Controllers/PackageRepositoryController.cs +++ b/OurUmbraco/Repository/Controllers/PackageRepositoryController.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Cache; using Umbraco.Web.WebApi; using System.Net.Http; using System.Net; +using Umbraco.Core; namespace OurUmbraco.Repository.Controllers { @@ -87,13 +88,26 @@ namespace OurUmbraco.Repository.Controllers TimeSpan.FromMinutes(1)); //cache for 1 min } - public Models.PackageDetails GetDetails(Guid id) + /// + /// Returns the package details for the package Id passed in and ensures that + /// the resulting ZipUrl is the compatible package for the version passed in. + /// + /// + /// The umbraco version requesting the details, if null than the ZipUrl will be the latest package zip + /// + public PackageDetails GetDetails(Guid id, string version = null) { + System.Version parsed = null; + if (version.IsNullOrWhiteSpace() == false) + { + System.Version.TryParse(version, out parsed); + } + //return the results, but cache for 1 minute - var key = string.Format("PackageRepositoryController.GetDetails.{0}", id); + var key = string.Format("PackageRepositoryController.GetDetails.{0}.{1}", id, parsed == null ? string.Empty : parsed.ToString(3)); var package = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem (key, - () => Service.GetDetails(id), + () => Service.GetDetails(id, parsed), TimeSpan.FromMinutes(1)); //cache for 1 min if (package == null) @@ -103,5 +117,7 @@ namespace OurUmbraco.Repository.Controllers return package; } + + } } \ No newline at end of file diff --git a/OurUmbraco/Repository/Models/Package.cs b/OurUmbraco/Repository/Models/Package.cs index f0668a09..a94cfc10 100644 --- a/OurUmbraco/Repository/Models/Package.cs +++ b/OurUmbraco/Repository/Models/Package.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace OurUmbraco.Repository.Models { @@ -27,18 +28,13 @@ namespace OurUmbraco.Repository.Models public string Category { get; set; } + /// + /// The latest version of this package + /// public string LatestVersion { get; set; } public PackageOwnerInfo OwnerInfo { get; set; } - - /// - /// This is the minimum Umbraco version that this package supports - /// - /// - /// This could be null if it is a legacy package - /// - public string MinimumVersion { get; set; } - + public long Score { get; set; } } } \ No newline at end of file diff --git a/OurUmbraco/Repository/Models/PackageDetails.cs b/OurUmbraco/Repository/Models/PackageDetails.cs index 8f85678e..0d695952 100644 --- a/OurUmbraco/Repository/Models/PackageDetails.cs +++ b/OurUmbraco/Repository/Models/PackageDetails.cs @@ -24,7 +24,6 @@ namespace OurUmbraco.Repository.Models this.Name = package.Name; this.Icon = package.Icon; this.LatestVersion = package.LatestVersion; - this.MinimumVersion = package.MinimumVersion; this.OwnerInfo = package.OwnerInfo; } } @@ -35,6 +34,11 @@ namespace OurUmbraco.Repository.Models public List Compatibility { get; set; } + /// + /// A list of all supported/targeted Umbraco versions for all files for this package + /// + public List TargetedUmbracoVersions { get; set; } + public List ExternalSources { get; set; } public string LicenseName { get; set; } diff --git a/OurUmbraco/Repository/Services/PackageRepositoryService.cs b/OurUmbraco/Repository/Services/PackageRepositoryService.cs index f738fd67..67a33da2 100644 --- a/OurUmbraco/Repository/Services/PackageRepositoryService.cs +++ b/OurUmbraco/Repository/Services/PackageRepositoryService.cs @@ -145,7 +145,14 @@ namespace OurUmbraco.Repository.Services }; } - public PackageDetails GetDetails(Guid id) + /// + /// Returns the package details for the package Id passed in and ensures that + /// the resulting ZipUrl is the compatible package for the version passed in. + /// + /// + /// The umbraco version requesting the details, if null than the ZipUrl will be the latest package zip + /// + public PackageDetails GetDetails(Guid id, System.Version version) { // [LK:2016-06-13@CGRT16] We're using XPath as we experienced issues with query Examine for GUIDs, // (it might worth but we were up against the clock). @@ -156,16 +163,14 @@ namespace OurUmbraco.Repository.Services if (item == null) return null; - return MapContentToPackageDetails(item); + return MapContentToPackageDetails(item, version); } private Models.Package MapContentToPackage(IPublishedContent content) { if (content == null) return null; - - var wikiFiles = WikiFile.CurrentFiles(content.Id); - + return new Models.Package { Category = content.Parent.Name, @@ -177,29 +182,76 @@ namespace OurUmbraco.Repository.Services Name = content.Name, Icon = GetThumbnailUrl(BASE_URL + content.GetPropertyValue("defaultScreenshotPath", "/css/img/package2.png"), 154, 281), LatestVersion = content.GetPropertyValue("version"), - MinimumVersion = GetMinimumVersion(content.GetPropertyValue("file"), wikiFiles.Where(x => x.FileType.InvariantEquals("package"))), OwnerInfo = GetPackageOwnerInfo(content.GetPropertyValue("owner"), content.GetPropertyValue("openForCollab", false), content.Id), Url = string.Concat(BASE_URL, content.Url) }; } - private PackageDetails MapContentToPackageDetails(IPublishedContent content) + private PackageDetails MapContentToPackageDetails(IPublishedContent content, System.Version currentUmbracoVersion) { if (content == null) return null; - + var package = MapContentToPackage(content); - var packageDetails = new PackageDetails(package); + var wikiFiles = WikiFile.CurrentFiles(content.Id); - packageDetails.Compatibility = GetPackageCompatibility(content); - packageDetails.NetVersion = content.GetPropertyValue("dotNetVersion"); - packageDetails.LicenseName = content.GetPropertyValue("licenseName"); - packageDetails.LicenseUrl = content.GetPropertyValue("licenseUrl"); - packageDetails.Description = content.GetPropertyValue("description").CleanHtmlAttributes(); - packageDetails.Images = GetPackageImages(wikiFiles.Where(x => x.FileType.InvariantEquals("screenshot")), 154, 281); - packageDetails.ExternalSources = GetExternalSources(content); - packageDetails.ZipUrl = string.Concat(BASE_URL, "/FileDownload?id=", content.GetPropertyValue("file")); + //TODO: SD: I dunno where these come from or if we care about it? + //var deliCompatVersions = Utils.GetProjectCompatibleVersions(content.Id) ?? new List(); + + var allPackageFiles = wikiFiles.Where(x => x.FileType.InvariantEquals("package")).ToArray(); + + var strictPackageFileVersions = GetAllStrictSupportedPackageVersions(allPackageFiles).ToArray(); + + var packageDetails = new PackageDetails(package) + { + TargetedUmbracoVersions = GetAllFilePackageVersions(allPackageFiles).Select(x => x.ToString(3)).ToList(), + Compatibility = GetPackageCompatibility(content), + NetVersion = content.GetPropertyValue("dotNetVersion"), + LicenseName = content.GetPropertyValue("licenseName"), + LicenseUrl = content.GetPropertyValue("licenseUrl"), + Description = content.GetPropertyValue("description").CleanHtmlAttributes(), + Images = GetPackageImages(wikiFiles.Where(x => x.FileType.InvariantEquals("screenshot")), 154, 281), + ExternalSources = GetExternalSources(content) + }; + + //if no version or not strict version dependencies, return the current release file + if (currentUmbracoVersion == null || strictPackageFileVersions.Length == 0) + { + var currentReleaseFile = content.GetPropertyValue("file"); + packageDetails.ZipUrl = string.Concat(BASE_URL, "/FileDownload?id=", currentReleaseFile); + } + else + { + //order from latest package version and lastest min umb version and start from the top, we want to return the most recent + //available package version for the current Umbraco version + var packageVersions = strictPackageFileVersions + .OrderByDescending(x => x.Item2) + .ThenByDescending(x => x.Item3); + + int found = -1; + foreach (var pckVersion in packageVersions) + { + //if this version will work with the umbraco version, then use it + if (pckVersion.Item3 >= currentUmbracoVersion) + { + found = pckVersion.Item1; + break; + } + } + + if (found != -1) + { + //got one! so use it's id for the file download + packageDetails.ZipUrl = string.Concat(BASE_URL, "/FileDownload?id=", found); + } + else + { + //not compatible! + return null; + } + } + packageDetails.Created = content.CreateDate; return packageDetails; @@ -222,29 +274,71 @@ namespace OurUmbraco.Repository.Services .ToList(); } - private string GetMinimumVersion(int filePropertyValue, IEnumerable packages) + /// + /// Based on all of the files that this package go retrieve all targeted Umbraco versions + /// + /// + /// + private IEnumerable GetAllFilePackageVersions(IEnumerable packages) { - var currentVersion = filePropertyValue; - var latest = packages.FirstOrDefault(x => x.Id == currentVersion); + var allVersions = packages + .Select(x => ConvertToVersion(x.Version.Version)) + .WhereNotNull() + .Distinct() + .OrderBy(x => x) + .ToArray(); - if (latest == null || string.IsNullOrWhiteSpace(latest.Version.Version) || latest.Version.Version.InvariantEquals("nan")) - return null; + return allVersions; + } - if (latest.Version.Version.InvariantStartsWith("v")) + /// + /// Based on all of the files that this package go retrieve all targeted Umbraco versions + /// + /// + /// + /// A tuple: the file Id, the package version, the min umbraco version + /// + private IEnumerable> GetAllStrictSupportedPackageVersions(IEnumerable packages) + { + var allVersions = packages + .Where(x => x.MinimumVersionStrict.IsNullOrWhiteSpace() == false) + .Select(x => Tuple.Create(x.Id, ConvertToVersion(x.Version.Version), ConvertToVersion(x.MinimumVersionStrict))) + .Where(x => x.Item2 != null && x.Item3 != null) + .ToArray(); + return allVersions; + } + + /// + /// Try to convert the string to a real version based on legacy version formats + /// + /// + /// + private System.Version ConvertToVersion(string ver) + { + string normalized = ver; + if (ver.InvariantStartsWith("v")) { - var legacyFormat = latest.Version.Version; - - if (legacyFormat.InvariantStartsWith("v4")) + if (ver.InvariantStartsWith("v4")) { - return string.Concat(legacyFormat.Replace("v4", "4."), ".0"); + normalized = string.Concat(ver.Replace("v4", "4."), ".0"); } else { - return string.Join(".", legacyFormat.ToCharArray().Skip(1)); + normalized = string.Join(".", ver.ToCharArray().Skip(1)); } } - return latest.Version.Version; + if (normalized.EndsWith(".x")) + { + normalized = normalized.TrimEnd(".x").EnsureEndsWith(".0"); + } + + System.Version result; + if (System.Version.TryParse(normalized, out result)) + { + return result; + } + return null; } private PackageOwnerInfo GetPackageOwnerInfo(int ownerId, bool openForCollab, int contentId)