diff --git a/OurUmbraco.Site/macroScripts/repository-view-project.cshtml b/OurUmbraco.Site/macroScripts/repository-view-project.cshtml index 0a29ba3f..297a5c24 100644 --- a/OurUmbraco.Site/macroScripts/repository-view-project.cshtml +++ b/OurUmbraco.Site/macroScripts/repository-view-project.cshtml @@ -1,4 +1,6 @@ @using OurUmbraco.Project +@using OurUmbraco.Repository.Services +@using Umbraco.Core @using Umbraco.Web @{ System.Web.Helpers.AntiForgeryConfig.SuppressXFrameOptionsHeader = true; @@ -7,6 +9,7 @@ int projectId; if (int.TryParse(Request.QueryString["project_id"], out projectId) == false) { + //TODO: Exception probably ! return; } @@ -14,6 +17,11 @@ var callback = Request.QueryString["callback"]; var Project = ProjectsProvider.GetListing(projectId, false); + if (Project == null) + { + throw new InvalidOperationException("No project found with id " + projectId); + } + var node = new umbraco.NodeFactory.Node(projectId); var parentId = node.Parent.Id; var categoryName = node.Parent.Name; @@ -30,10 +38,21 @@ descCssClass = "wrap"; } + var umbracoHelper = new UmbracoHelper(UmbracoContext.Current); + var packageRepoService = new PackageRepositoryService(umbracoHelper, umbracoHelper.MembershipHelper, ApplicationContext.Current.DatabaseContext); + //This doesn't matter what we set it to so long as it's below 7.5 since that is the version we introduce strict dependencies + var currUmbracoVersion = new Version(4, 0, 0); + var packageDetails = packageRepoService.GetDetails(Project.ProjectGuid, currUmbracoVersion); + + if (packageDetails == null) + { + throw new InvalidOperationException("No package found with id " + Project.ProjectGuid); + } + int currentReleaseFile = 0; if (int.TryParse(Project.CurrentReleaseFile, out currentReleaseFile)) { - var file = Project.PackageFile.Where(x => x.Id == currentReleaseFile).FirstOrDefault(); + var file = Project.PackageFile.FirstOrDefault(x => x.Id == currentReleaseFile); if (file != null) { var ct = "This project is compaitible with "; @@ -66,27 +85,33 @@ } }
- @if (currentReleaseFile != 0) + @if (string.IsNullOrWhiteSpace(packageDetails.ZipUrl) == false) {

@Project.Name Install package

} + else if (currentReleaseFile != 0) + { + Unfortunately, this package is not compatible with the version of Umbraco you are running.
+ Go to the package page to learn more + } else { Unfortunately, this package has no current release file to install at the moment.
- Go to the package page to learn more} + Go to the package page to learn more + }
@@ -94,10 +119,10 @@
Project owner:
@{ - var umbracoHelper = new UmbracoHelper(UmbracoContext.Current); + var ownerName = umbracoHelper.MembershipHelper.GetById(Project.VendorId).Name; } -
@ownerName
+
@ownerName
Package downloads
@Project.Downloads
@@ -119,9 +144,9 @@ @if (Project.Stable) {
Is Stable:
-
- Project is stable -
+
+ Project is stable +
}
Current version
@@ -130,9 +155,9 @@ @if (!string.IsNullOrEmpty(Project.LicenseName)) {
License
-
- @Project.LicenseName -
+
+ @Project.LicenseName +
}
@@ -158,7 +183,7 @@ image.Path.EndsWith("jpeg")) { - + } } diff --git a/OurUmbraco/Repository/Controllers/PackageRepositoryController.cs b/OurUmbraco/Repository/Controllers/PackageRepositoryController.cs index 615e63e4..6a6cd79e 100644 --- a/OurUmbraco/Repository/Controllers/PackageRepositoryController.cs +++ b/OurUmbraco/Repository/Controllers/PackageRepositoryController.cs @@ -102,9 +102,21 @@ namespace OurUmbraco.Repository.Controllers { System.Version.TryParse(version, out parsed); } + else + { + //if the version is null then the current umbraco version must be 7.5.x because this endpoint was only ever used by 7.5.x and 7.6.x and above will always + // suppy the version, therefore we can assume that this is 7.5.x, let's make it 7.5.13 which is the latest 7.5 we have right now + parsed = new System.Version(7, 5, 13); + } + + //this should never be null, if it is return not found + if (parsed == null) + { + throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + } //return the results, but cache for 1 minute - var key = string.Format("PackageRepositoryController.GetDetails.{0}.{1}", id, parsed == null ? string.Empty : parsed.ToString(3)); + var key = string.Format("PackageRepositoryController.GetDetails.{0}.{1}", id, parsed.ToString(3)); var package = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem (key, () => Service.GetDetails(id, parsed), diff --git a/OurUmbraco/Repository/Models/PackageDetails.cs b/OurUmbraco/Repository/Models/PackageDetails.cs index 6ca18040..2bce790c 100644 --- a/OurUmbraco/Repository/Models/PackageDetails.cs +++ b/OurUmbraco/Repository/Models/PackageDetails.cs @@ -48,5 +48,7 @@ namespace OurUmbraco.Repository.Models public string NetVersion { get; set; } public string ZipUrl { get; set; } + + public int ZipFileId { get; set; } } } \ No newline at end of file diff --git a/OurUmbraco/Repository/Projects.cs b/OurUmbraco/Repository/Projects.cs index f6965d56..18c9df2d 100644 --- a/OurUmbraco/Repository/Projects.cs +++ b/OurUmbraco/Repository/Projects.cs @@ -352,9 +352,12 @@ namespace OurUmbraco.Repository if (xpn.MoveNext()) { - if (xpn.Current is IHasXmlNode) + var xmlNode = xpn.Current as IHasXmlNode; + if (xmlNode != null) { - Node node = new Node(((IHasXmlNode)xpn.Current).GetNode()); + //This will get the latest marked file id + + Node node = new Node(xmlNode.GetNode()); string fileId = safeProperty(node, "file"); int _id; diff --git a/OurUmbraco/Repository/Services/PackageRepositoryService.cs b/OurUmbraco/Repository/Services/PackageRepositoryService.cs index d243bdfa..271a76a9 100644 --- a/OurUmbraco/Repository/Services/PackageRepositoryService.cs +++ b/OurUmbraco/Repository/Services/PackageRepositoryService.cs @@ -29,7 +29,7 @@ using Umbraco.Web.Security; namespace OurUmbraco.Repository.Services { - internal class PackageRepositoryService + public class PackageRepositoryService { private readonly DatabaseContext DatabaseContext; private readonly MembershipHelper MembershipHelper; @@ -154,6 +154,8 @@ namespace OurUmbraco.Repository.Services /// public PackageDetails GetDetails(Guid id, System.Version version) { + if (version == null) throw new ArgumentNullException(nameof(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). // The XPath 'translate' is being used to force the 'packageGuid' to be lowercase for comparison. @@ -188,10 +190,10 @@ namespace OurUmbraco.Repository.Services } private PackageDetails MapContentToPackageDetails(IPublishedContent content, System.Version currentUmbracoVersion) - { - if (content == null) - return null; - + { + if (currentUmbracoVersion == null) throw new ArgumentNullException(nameof(currentUmbracoVersion)); + if (content == null) return null; + var package = MapContentToPackage(content); var wikiFiles = WikiFile.CurrentFiles(content.Id); @@ -201,6 +203,7 @@ namespace OurUmbraco.Repository.Services var allPackageFiles = wikiFiles.Where(x => x.FileType.InvariantEquals("package")).ToArray(); + //get the strict packages in the correct desc order var strictPackageFileVersions = GetAllStrictSupportedPackageVersions(allPackageFiles).ToArray(); var packageDetails = new PackageDetails(package) @@ -213,35 +216,65 @@ namespace OurUmbraco.Repository.Services 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 version75 = new System.Version(7, 5, 0); + + //this is the file marked as the current/latest release + var currentReleaseFile = content.GetPropertyValue("file"); + + if (strictPackageFileVersions.Length == 0) { + //if there are no strict package files then return the latest package file + + packageDetails.ZipUrl = string.Concat(BASE_URL, "/FileDownload?id=", currentReleaseFile); + packageDetails.ZipFileId = currentReleaseFile; + } + else if (currentUmbracoVersion < version75) + { + //if the umbraco version is < 7.5 it means that strict package formats are not supported + //TODO: Now we have to do the opposite of below and filter out any package file versions that have strict // umbraco dependencies applied. Anything that has 7.5 (which would be the very minimum strict dependency) we can check for - // and then we must also consider that version 7.5.x will never send up a `currentUmbracoVersion` string, however, - // we will know if it is 7.5.x if it comes from PackageRepositoryController.GetDetails since that was the minimum umbraco version - // that used this endpoint! - var currentReleaseFile = content.GetPropertyValue("file"); - packageDetails.ZipUrl = string.Concat(BASE_URL, "/FileDownload?id=", currentReleaseFile); + //these are ordered by package version desc + var nonStrictPackageFiles = GetNonStrictSupportedPackageVersions(wikiFiles).ToArray(); + + if (nonStrictPackageFiles.Length == 0) + { + //Looks like there's nothing compatible + return null; + } + + //there might be a case where the 'current release file' is not the latest version found, so let's check if + //the latest release file is included in the non-strict packages and if so we'll use that, otherwise we'll use the latest + var found = nonStrictPackageFiles.FirstOrDefault(x => x.PackageId == currentReleaseFile); + if (found != null) + { + //it's included in the non strict packages so use it + packageDetails.ZipUrl = string.Concat(BASE_URL, "/FileDownload?id=", currentReleaseFile); + packageDetails.ZipFileId = currentReleaseFile; + } + else + { + //use the latest available package version + packageDetails.ZipUrl = string.Concat(BASE_URL, "/FileDownload?id=", nonStrictPackageFiles[0].PackageId); + packageDetails.ZipFileId = nonStrictPackageFiles[0].PackageId; + } } 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); + //this package has some strict version dependency files, so we need to figure out which one is + // compatible with the current umbraco version passed in and also use the latest available + // package version that is compatible. int found = -1; - foreach (var pckVersion in packageVersions) + foreach (var pckVersion in strictPackageFileVersions) { //if this version will work with the umbraco version, then use it - if (pckVersion.Item3 >= currentUmbracoVersion) + if (pckVersion.MinUmbracoVersion >= currentUmbracoVersion) { - found = pckVersion.Item1; + found = pckVersion.PackageId; break; } } @@ -250,6 +283,7 @@ namespace OurUmbraco.Repository.Services { //got one! so use it's id for the file download packageDetails.ZipUrl = string.Concat(BASE_URL, "/FileDownload?id=", found); + packageDetails.ZipFileId = found; } else { @@ -302,18 +336,86 @@ namespace OurUmbraco.Repository.Services /// /// /// - /// A tuple: the file Id, the package version, the min umbraco version + /// 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 /// - private IEnumerable> GetAllStrictSupportedPackageVersions(IEnumerable packages) + 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) + .Select(x => new PackageVersionSupport(x.Id, ConvertToVersion(x.Version.Version), ConvertToVersion(x.MinimumVersionStrict))) + .Where(x => x.PackageVersion != null && x.MinUmbracoVersion != null) + .OrderByDescending(x => x.PackageVersion) + .ThenByDescending(x => x.MinUmbracoVersion) .ToArray(); return allVersions; } + /// + /// Based on all of the files that this package go retrieve all non targeted Umbraco versions + /// + /// + /// + /// Order from latest package version and start from the top, we want to return the most recent + /// available package version for the current Umbraco version + /// + private IEnumerable GetNonStrictSupportedPackageVersions(IEnumerable packages) + { + var allVersions = packages + .Where(x => x.MinimumVersionStrict.IsNullOrWhiteSpace()) + .Select(x => new PackageVersionSupport(x.Id, ConvertToVersion(x.Version.Version), null)) + .Where(x => x.PackageVersion != null) + .OrderByDescending(x => x.PackageVersion) + .ToArray(); + return allVersions; + } + + private class PackageVersionSupport : IEquatable + { + private readonly int _packageId; + + public PackageVersionSupport(int packageId, System.Version packageVersion, System.Version minUmbracoVersion) + { + _packageId = packageId; + MinUmbracoVersion = minUmbracoVersion; + PackageVersion = packageVersion; + } + + public int PackageId + { + get { return _packageId; } + } + + public System.Version MinUmbracoVersion { get; private set; } + public System.Version PackageVersion { get; private set; } + + public bool Equals(PackageVersionSupport other) + { + return _packageId == other._packageId; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + return obj is PackageVersionSupport && Equals((PackageVersionSupport) obj); + } + + public override int GetHashCode() + { + return _packageId; + } + + public static bool operator ==(PackageVersionSupport left, PackageVersionSupport right) + { + return left.Equals(right); + } + + public static bool operator !=(PackageVersionSupport left, PackageVersionSupport right) + { + return !left.Equals(right); + } + } + /// /// Try to convert the string to a real version based on legacy version formats /// diff --git a/OurUmbraco/Repository/webservices/repository.asmx.cs b/OurUmbraco/Repository/webservices/repository.asmx.cs index 44c22afe..f4fb5f93 100644 --- a/OurUmbraco/Repository/webservices/repository.asmx.cs +++ b/OurUmbraco/Repository/webservices/repository.asmx.cs @@ -4,8 +4,11 @@ using System.ComponentModel; using System.IO; using System.Web.Services; using System.Xml.XPath; +using OurUmbraco.Repository.Services; using OurUmbraco.Wiki.BusinessLogic; using umbraco.cms.businesslogic.web; +using Umbraco.Core; +using Umbraco.Web; namespace OurUmbraco.Repository.webservices { @@ -76,12 +79,76 @@ namespace OurUmbraco.Repository.webservices } } + /// + /// This will return the byte array for the package file for the package id specified + /// + /// + /// The Version of the umbraco install requesting the package file, this is in the normaly version format such as 7.6.0 + /// + /// + /// This endpoint is new and is only referenced from 7.5.14 and above + /// + [WebMethod] + public byte[] GetPackageFile(string packageGuid, string umbracoVersion) + { + var umbHelper = new UmbracoHelper(UmbracoContext.Current); + var pckRepoService = new PackageRepositoryService(umbHelper, umbHelper.MembershipHelper, ApplicationContext.Current.DatabaseContext); + + System.Version currUmbracoVersion; + if (!System.Version.TryParse(umbracoVersion, out currUmbracoVersion)) + throw new InvalidOperationException("Could not parse the version specified " + umbracoVersion); + + var guid = new Guid(packageGuid); + var details = pckRepoService.GetDetails(guid, currUmbracoVersion); + if (details == null) + throw new InvalidOperationException("No package found with id " + packageGuid); + + var wf = new WikiFile(details.ZipFileId); + if (wf == null) + throw new InvalidOperationException("Could not find wiki file by id " + details.ZipFileId); + wf.UpdateDownloadCounter(true, true); + + return wf.ToByteArray(); + } + + /// + /// This will return the byte array for the package file for the package id specified - since this endpoint is ONLY used + /// for Umbraco installs that are less than 7.6.0 and only when legacy XML schema is enabled, we know that this is only used for old umbraco versions + /// + /// + /// [WebMethod] public byte[] fetchPackage(string packageGuid) { - return OurUmbraco.Repository.Packages.PackageFileByGuid(new Guid(packageGuid)).ToByteArray(); + var umbHelper = new UmbracoHelper(UmbracoContext.Current); + var pckRepoService = new PackageRepositoryService(umbHelper, umbHelper.MembershipHelper, ApplicationContext.Current.DatabaseContext); + + //This doesn't matter what we set it to so long as it's below 7.5 since that is the version we introduce strict dependencies + var currUmbracoVersion = new System.Version(4, 0, 0); + var guid = new Guid(packageGuid); + var details = pckRepoService.GetDetails(guid, currUmbracoVersion); + if (details == null) + throw new InvalidOperationException("No package found with id " + packageGuid); + + var wf = new WikiFile(details.ZipFileId); + if (wf == null) + throw new InvalidOperationException("Could not find wiki file by id " + details.ZipFileId); + wf.UpdateDownloadCounter(true, true); + + return wf.ToByteArray(); } + /// + /// This will return the byte array for the package file for the package id specified - since this endpoint is ONLY used + /// for Umbraco installs that are less than 7.6.0 and only when legacy XML schema is enabled, we know that this is only used for old umbraco versions + /// + /// + /// + /// This is a strange Umbraco version parameter - but it's not really an umbraco version, it's a special/odd version format like Version41 + /// + /// The repoVersion will never be null for for 7.5.x and this method will never be used by 7.6+ + /// + /// [WebMethod] public byte[] fetchPackageByVersion(string packageGuid, string repoVersion) { @@ -112,51 +179,56 @@ namespace OurUmbraco.Repository.webservices break; } - WikiFile wf = OurUmbraco.Repository.Packages.PackageFileByGuid(new Guid(packageGuid)); + var umbHelper = new UmbracoHelper(UmbracoContext.Current); + var pckRepoService = new PackageRepositoryService(umbHelper, umbHelper.MembershipHelper, ApplicationContext.Current.DatabaseContext); + //This doesn't matter what we set it to so long as it's below 7.5 since that is the version we introduce strict dependencies + //Side note: Umbraco 7.5.0 uses this endpoint but since 7.5.0 is already out there's nothing we can do about this, if we pass in 7.5.x then + // the logic will use strict file versions which we cannot do because this endpoint is also used for < 7.5! + // The worst that can happen in this case is that a strict package dependency has been made on 7.5 and then an umbraco 7.5 package + // requests the file, well it won't get it because this will only return non strict packages. + var currUmbracoVersion = new System.Version(4, 0, 0); + var guid = new Guid(packageGuid); + var details = pckRepoService.GetDetails(guid, currUmbracoVersion); + if (details == null) + return new byte[0]; - if (wf != null) + var wf = new WikiFile(details.ZipFileId); + + //WikiFile wf = OurUmbraco.Repository.Packages.PackageFileByGuid(new Guid(packageGuid)); + + //if the package doesn't care about the umbraco version needed... + if (wf.Version.Version == "nan") + return wf.ToByteArray(); + + //if v45 + if (version == "v45") { + int v = 0; + if (int.TryParse(wf.Version.Version.Replace("v", ""), out v)) + if (v >= 45) + return wf.ToByteArray(); - //if the package doesn't care about the umbraco version needed... - if (wf.Version.Version == "nan") + if (wf.Version.Version == "v47" || wf.Version.Version == "v45") return wf.ToByteArray(); - - //if v45 - if (version == "v45") + if (wf.Version.Version != version && wf.Version.Version != "nan") { - int v = 0; - if (int.TryParse(wf.Version.Version.Replace("v", ""), out v)) - if (v >= 45) - return wf.ToByteArray(); - - - if (wf.Version.Version == "v47" || wf.Version.Version == "v45") - return wf.ToByteArray(); - else if (wf.Version.Version != version && wf.Version.Version != "nan") - { - wf = WikiFile.FindPackageForUmbracoVersion(wf.NodeId, version); - return wf.ToByteArray(); - } + wf = WikiFile.FindPackageForUmbracoVersion(wf.NodeId, version); + return wf.ToByteArray(); } } - return new byte[0]; - - /* - if (wf.Version.Version != version && wf.Version.Version != "nan") - wf = uWiki.Businesslogic.WikiFile.FindPackageForUmbracoVersion(wf.NodeId, version); - - - if(wf != null) - return wf.ToByteArray(); - else - return new byte[0];*/ + return new byte[0]; } - + /// + /// SD: Pretty sure this is no longer used/needed it should probably be removed - maybe really old versions might try to use this? + /// + /// + /// + /// [WebMethod] public byte[] fetchProtectedPackage(string packageGuid, string memberKey) { @@ -225,24 +297,6 @@ namespace OurUmbraco.Repository.webservices return item; } - private static umbraco.cms.businesslogic.contentitem.ContentItem repositoryContentItem(string guid) - { - - umbraco.cms.businesslogic.contentitem.ContentItem item = new umbraco.cms.businesslogic.contentitem.ContentItem(1052); - - XPathNodeIterator xpn = umbraco.library.GetXmlNodeByXPath("descendant::node[data [@alias = 'repositoryGuid'] = '" + guid + "']"); - - if (xpn.MoveNext()) - { - - int id = int.Parse(xpn.Current.GetAttribute("id", "")); ; - - item = new umbraco.cms.businesslogic.contentitem.ContentItem(id); - } - - return item; - } - private static string md5(string input) {