#17282 - Gallery modules improvements

--HG--
branch : dev
This commit is contained in:
Sebastien Ros
2011-02-07 14:17:48 -08:00
parent b74514ba4d
commit b53f2e6a36
15 changed files with 343 additions and 370 deletions

View File

@@ -143,7 +143,6 @@
<Compile Include="Indexing\LuceneIndexProviderTests.cs" />
<Compile Include="Indexing\LuceneSearchBuilderTests.cs" />
<Compile Include="Media\Services\MediaServiceTests.cs" />
<Compile Include="Packaging\Services\PackagingServicesTests.cs" />
<Compile Include="Scripting.Dlr\EvaluatorTests.cs" />
<Compile Include="Scripting\EvaluatorTestsBase.cs" />
<Compile Include="Scripting\EvaluatorTests.cs" />

View File

@@ -1,146 +0,0 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Autofac;
using Moq;
using NUnit.Framework;
using Orchard.Caching;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentManagement.MetaData.Services;
using Orchard.ContentManagement.Records;
using Orchard.Core.Settings.Handlers;
using Orchard.Core.Settings.Metadata;
using Orchard.Core.Settings.Models;
using Orchard.Core.Settings.Services;
using Orchard.Data;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement.Implementation;
using Orchard.Environment;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Packaging.Services;
using Orchard.Security;
using Orchard.Security.Permissions;
using Orchard.Security.Providers;
using Orchard.Settings;
using Orchard.Tests.Modules.Users;
using Orchard.Tests.Stubs;
using Orchard.UI.Notify;
using Orchard.Users.Handlers;
using Orchard.Users.Models;
using Orchard.Users.Services;
namespace Orchard.Tests.Modules.Packaging.Services {
public class PackagingServicesTests : DatabaseEnabledTestsBase {
private Mock<IAuthorizer> _authorizer;
private Mock<WorkContext> _workContext;
private IUser _currentUser;
public override void Register(ContainerBuilder builder) {
builder.RegisterType<SiteService>().As<ISiteService>();
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
builder.RegisterType(typeof(SettingsFormatter))
.As(typeof(IMapper<XElement, SettingsDictionary>))
.As(typeof(IMapper<SettingsDictionary, XElement>));
builder.RegisterType<ContentDefinitionManager>().As<IContentDefinitionManager>();
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
builder.RegisterType<DefaultContentQuery>().As<IContentQuery>().InstancePerDependency();
builder.RegisterType<MembershipService>().As<IMembershipService>();
builder.RegisterType<UserService>().As<IUserService>();
builder.RegisterType<UserPartHandler>().As<IContentHandler>();
builder.RegisterType<OrchardServices>().As<IOrchardServices>();
builder.RegisterType<TransactionManager>().As<ITransactionManager>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
builder.RegisterType<SiteSettingsPartHandler>().As<IContentHandler>();
builder.RegisterType<PackagingServices>().As<IPackagingServices>();
builder.RegisterInstance(new Mock<INotifier>().Object);
builder.RegisterInstance(new Mock<IContentDisplay>().Object);
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<Signals>().As<ISignals>();
builder.RegisterType<DefaultEncryptionService>().As<IEncryptionService>();
builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled());
_authorizer = new Mock<IAuthorizer>();
builder.RegisterInstance(_authorizer.Object);
_workContext = new Mock<WorkContext>();
_workContext.Setup(w => w.GetState<ISite>(It.Is<string>(s => s == "CurrentSite"))).Returns(() => _container.Resolve<ISiteService>().GetSiteSettings());
_workContext.Setup(w => w.GetState<IUser>(It.Is<string>(s => s == "CurrentUser"))).Returns(() => _currentUser);
var _workContextAccessor = new Mock<IWorkContextAccessor>();
_workContextAccessor.Setup(w => w.GetContext()).Returns(_workContext.Object);
builder.RegisterInstance(_workContextAccessor.Object).As<IWorkContextAccessor>();
}
protected override IEnumerable<Type> DatabaseTypes {
get {
return new[] { typeof(UserPartRecord),
typeof(SiteSettingsPartRecord),
typeof(RegistrationSettingsPartRecord),
typeof(ContentTypeRecord),
typeof(ContentItemRecord),
typeof(ContentItemVersionRecord),
};
}
}
[Test]
public void CanManagePackagesTest() {
const string superUsername = "admin";
const string regularUsername = "user1";
IPackagingServices packagingServices = _container.Resolve<IPackagingServices>();
IOrchardServices orchardServices = _container.Resolve<IOrchardServices>();
ShellSettings shellSettings = _container.Resolve<ShellSettings>();
shellSettings.Name = ShellSettings.DefaultName;
IUser regularUser = CreateUser(regularUsername);
IUser superUser = CreateUser(superUsername);
orchardServices.WorkContext.CurrentSite.As<SiteSettingsPart>().SuperUser = superUsername;
_currentUser = regularUser;
_session.Flush();
// Setup authorizer to return false
_authorizer.Setup(x => x.Authorize(It.IsAny<Permission>(), It.IsAny<LocalizedString>())).Returns(false);
// Test regular user without permission explicit assigned and which is not the default admin
Assert.That(packagingServices.CanManagePackages(), Is.False);
// Test regular user with permission explicit assigned and which is not the default admin
_authorizer.Setup(x => x.Authorize(It.IsAny<Permission>(), It.IsAny<LocalizedString>())).Returns(true);
Assert.That(packagingServices.CanManagePackages(), Is.True);
// Test super user that even without permission explicit assigned should be able to manage packages
_authorizer.Setup(x => x.Authorize(It.IsAny<Permission>(), It.IsAny<LocalizedString>())).Returns(false);
Assert.That(packagingServices.CanManagePackages(), Is.False);
_currentUser = superUser;
Assert.That(packagingServices.CanManagePackages(), Is.True);
// Test with super user from another tenant site
shellSettings.Name = "tenantsite2";
_session.Flush();
Assert.That(packagingServices.CanManagePackages(), Is.False);
}
private IUser CreateUser(string username) {
var manager = _container.Resolve<IContentManager>();
UserPart userPart = manager.New<UserPart>("User");
userPart.Record = new UserPartRecord { UserName = username, NormalizedUserName = username, Email = string.Format("{0}@orcharproject.com", username) };
manager.Create(userPart.ContentItem);
return userPart;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

View File

@@ -3,16 +3,21 @@ using System.Collections.Generic;
using System.Linq;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Routing;
using System.Xml.Linq;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Packaging.GalleryServer;
using Orchard.Packaging.Models;
using Orchard.Packaging.Services;
using Orchard.Packaging.ViewModels;
using Orchard.Security;
using Orchard.Themes;
using Orchard.UI.Admin;
using Orchard.UI.Navigation;
using Orchard.UI.Notify;
using IPackageManager = Orchard.Packaging.Services.IPackageManager;
@@ -20,23 +25,18 @@ namespace Orchard.Packaging.Controllers {
[OrchardFeature("Gallery")]
[Themed, Admin]
public class GalleryController : Controller {
private readonly IPackagingServices _packagingServices;
private readonly ShellSettings _shellSettings;
private readonly IPackageManager _packageManager;
private readonly IPackagingSourceManager _packagingSourceManager;
private readonly INotifier _notifier;
public GalleryController(
IPackagingServices packagingServices,
ShellSettings shellSettings,
IPackageManager packageManager,
IPackagingSourceManager packagingSourceManager,
INotifier notifier,
IOrchardServices services) {
_packagingServices = packagingServices;
_shellSettings = shellSettings;
_packageManager = packageManager;
_packagingSourceManager = packagingSourceManager;
_notifier = notifier;
Services = services;
T = NullLocalizer.Instance;
@@ -48,7 +48,7 @@ namespace Orchard.Packaging.Controllers {
public ILogger Logger { get; set; }
public ActionResult Sources() {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to list sources")))
return new HttpUnauthorizedResult();
return View(new PackagingSourcesViewModel {
@@ -57,16 +57,16 @@ namespace Orchard.Packaging.Controllers {
}
public ActionResult Remove(int id) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to remove sources")))
return new HttpUnauthorizedResult();
_packagingSourceManager.RemoveSource(id);
_notifier.Information(T("The feed has been removed successfully."));
Services.Notifier.Information(T("The feed has been removed successfully."));
return RedirectToAction("Sources");
}
public ActionResult AddSource() {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to add sources")))
return new HttpUnauthorizedResult();
return View(new PackagingAddSourceViewModel());
@@ -74,7 +74,7 @@ namespace Orchard.Packaging.Controllers {
[HttpPost]
public ActionResult AddSource(string url) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to add sources")))
return new HttpUnauthorizedResult();
try {
@@ -109,29 +109,34 @@ namespace Orchard.Packaging.Controllers {
return View(new PackagingAddSourceViewModel { Url = url });
_packagingSourceManager.AddSource(title, url);
_notifier.Information(T("The feed has been added successfully."));
Services.Notifier.Information(T("The feed has been added successfully."));
return RedirectToAction("Sources");
}
catch (Exception exception) {
_notifier.Error(T("Adding feed failed: {0}", exception.Message));
Services.Notifier.Error(T("Adding feed failed: {0}", exception.Message));
return View(new PackagingAddSourceViewModel { Url = url });
}
}
public ActionResult Modules(int? sourceId) {
return ListExtensions(sourceId, DefaultExtensionTypes.Module, "Modules", source => _packagingSourceManager.GetModuleList(source).ToArray());
}
public ActionResult Themes(int? sourceId) {
return ListExtensions(sourceId, DefaultExtensionTypes.Theme, "Themes", source => _packagingSourceManager.GetThemeList(source).ToArray());
}
protected ActionResult ListExtensions(int? sourceId, string extensionType, string returnView, Func<PackagingSource, PackagingEntry[]> getList) {
if (!_packagingServices.CanManagePackages())
public ActionResult Modules(PackagingExtensionsOptions options, PagerParameters pagerParameters) {
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to list Modules")))
return new HttpUnauthorizedResult();
var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
var pager = new Pager(Services.WorkContext.CurrentSite, pagerParameters);
return ListExtensions(options, DefaultExtensionTypes.Module, pager);
}
public ActionResult Themes(PackagingExtensionsOptions options, PagerParameters pagerParameters) {
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to list Themes")))
return new HttpUnauthorizedResult();
var pager = new Pager(Services.WorkContext.CurrentSite, pagerParameters);
return ListExtensions(options, DefaultExtensionTypes.Theme, pager);
}
protected ActionResult ListExtensions(PackagingExtensionsOptions options, string packageType, Pager pager) {
var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == options.SourceId).FirstOrDefault();
var sources = selectedSource != null
? new[] { selectedSource }
@@ -139,26 +144,86 @@ namespace Orchard.Packaging.Controllers {
;
IEnumerable<PackagingEntry> extensions = null;
int totalCount = 0;
foreach (var source in sources) {
try {
var sourceExtensions = getList(source);
var sourceExtensions = _packagingSourceManager.GetExtensionList(
source,
packages => {
packages = packages.Where(p => p.PackageType == packageType);
switch (options.Order) {
case PackagingExtensionsOrder.Downloads:
packages = packages.OrderByDescending(p => p.DownloadCount).ThenBy(p => p.Title);
break;
case PackagingExtensionsOrder.Ratings:
packages = packages.OrderByDescending(p => p.Rating).ThenBy(p => p.Title);
break;
case PackagingExtensionsOrder.Alphanumeric:
packages = packages.OrderBy(p => p.Title);
break;
}
if(pager.PageSize != 0) {
packages = packages.Skip((pager.Page - 1)*pager.PageSize).Take(pager.PageSize);
}
return packages;
}
).ToArray();
// count packages separately to prevent loading everything just to count
totalCount += _packagingSourceManager.GetExtensionCount(
source,
packages => packages.Where(p => p.PackageType == packageType)
);
extensions = extensions == null ? sourceExtensions : extensions.Concat(sourceExtensions);
// apply another paging rule in case there were multiple sources
if (sources.Count() > 1) {
switch (options.Order) {
case PackagingExtensionsOrder.Downloads:
extensions = extensions.OrderByDescending(p => p.DownloadCount).ThenBy(p => p.Title);
break;
case PackagingExtensionsOrder.Ratings:
extensions = extensions.OrderByDescending(p => p.Rating).ThenBy(p => p.Title);
break;
case PackagingExtensionsOrder.Alphanumeric:
extensions = extensions.OrderBy(p => p.Title);
break;
}
if (pager.PageSize != 0) {
extensions = extensions.Take(pager.PageSize);
}
}
}
catch (Exception ex) {
Logger.Error(ex, "Error loading extensions from gallery source '{0}'. {1}.", source.FeedTitle, ex.Message);
_notifier.Error(T("Error loading extensions from gallery source '{0}'. {1}.", source.FeedTitle, ex.Message));
Services.Notifier.Error(T("Error loading extensions from gallery source '{0}'. {1}.", source.FeedTitle, ex.Message));
}
}
return View(returnView, new PackagingExtensionsViewModel {
Extensions = extensions ?? new PackagingEntry[] { },
extensions = extensions ?? new PackagingEntry[0];
var pagerShape = Services.New.Pager(pager).TotalItemCount(totalCount);
// maintain previous route data when generating page links
var routeData = new RouteData();
routeData.Values.Add("Options.Order", options.Order);
pagerShape.RouteData(routeData);
return View(packageType == DefaultExtensionTypes.Theme ? "Themes" : "Modules", new PackagingExtensionsViewModel {
Extensions = extensions,
Sources = _packagingSourceManager.GetSources().OrderBy(s => s.FeedTitle),
SelectedSource = selectedSource
SelectedSource = selectedSource,
Pager = pagerShape,
Options = options
});
}
public ActionResult Install(string packageId, string version, int sourceId, string redirectTo) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to add sources")))
return new HttpUnauthorizedResult();
var source = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
@@ -171,16 +236,16 @@ namespace Orchard.Packaging.Controllers {
_packageManager.Install(packageId, version, source.FeedUrl, HostingEnvironment.MapPath("~/"));
if (packageId.StartsWith(PackagingSourceManager.GetExtensionPrefix(DefaultExtensionTypes.Theme))) {
_notifier.Information(T("The theme has been successfully installed. It can be enabled in the \"Themes\" page accessible from the menu."));
Services.Notifier.Information(T("The theme has been successfully installed. It can be enabled in the \"Themes\" page accessible from the menu."));
}
else if (packageId.StartsWith(PackagingSourceManager.GetExtensionPrefix(DefaultExtensionTypes.Module))) {
_notifier.Information(T("The module has been successfully installed. Its features can be enabled in the \"Configuration | Features\" page accessible from the menu."));
Services.Notifier.Information(T("The module has been successfully installed. Its features can be enabled in the \"Configuration | Features\" page accessible from the menu."));
}
}
catch (Exception exception) {
_notifier.Error(T("Package installation failed."));
Services.Notifier.Error(T("Package installation failed."));
for (Exception scan = exception; scan != null; scan = scan.InnerException) {
_notifier.Error(T("{0}", scan.Message));
Services.Notifier.Error(T("{0}", scan.Message));
}
}

View File

@@ -4,12 +4,14 @@ using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using NuGet;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.AppData;
using Orchard.Localization;
using Orchard.Mvc.Extensions;
using Orchard.Packaging.Services;
using Orchard.Security;
using Orchard.Themes;
using Orchard.UI.Admin;
using Orchard.UI.Notify;
@@ -21,19 +23,19 @@ namespace Orchard.Packaging.Controllers {
[Themed, Admin]
public class PackagingServicesController : Controller {
private readonly IPackagingServices _packagingServices;
private readonly ShellSettings _shellSettings;
private readonly IPackageManager _packageManager;
private readonly IAppDataFolderRoot _appDataFolderRoot;
private readonly INotifier _notifier;
public PackagingServicesController(
IPackagingServices packagingServices,
ShellSettings shellSettings,
IPackageManager packageManager,
INotifier notifier,
IAppDataFolderRoot appDataFolderRoot,
IOrchardServices services) {
_packagingServices = packagingServices;
_shellSettings = shellSettings;
_packageManager = packageManager;
_notifier = notifier;
_appDataFolderRoot = appDataFolderRoot;
@@ -46,7 +48,7 @@ namespace Orchard.Packaging.Controllers {
public IOrchardServices Services { get; set; }
public ActionResult AddTheme(string returnUrl) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to add themes")))
return new HttpUnauthorizedResult();
return View();
@@ -54,7 +56,7 @@ namespace Orchard.Packaging.Controllers {
[HttpPost, ActionName("AddTheme")]
public ActionResult AddThemePOST(string returnUrl) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to add themes")))
return new HttpUnauthorizedResult();
return InstallPackage(returnUrl, Request.RawUrl);
@@ -62,14 +64,14 @@ namespace Orchard.Packaging.Controllers {
[HttpPost, ActionName("RemoveTheme")]
public ActionResult RemoveThemePOST(string themeId, string returnUrl, string retryUrl) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to remove themes")))
return new HttpUnauthorizedResult();
return UninstallPackage(PackageBuilder.BuildPackageId(themeId, DefaultExtensionTypes.Theme), returnUrl, retryUrl);
}
public ActionResult AddModule(string returnUrl) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to add modules")))
return new HttpUnauthorizedResult();
return View();
@@ -77,14 +79,14 @@ namespace Orchard.Packaging.Controllers {
[HttpPost, ActionName("AddModule")]
public ActionResult AddModulePOST(string returnUrl) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to add modules")))
return new HttpUnauthorizedResult();
return InstallPackage(returnUrl, Request.RawUrl);
}
public ActionResult InstallPackage(string returnUrl, string retryUrl) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to install packages")))
return new HttpUnauthorizedResult();
try {
@@ -117,7 +119,7 @@ namespace Orchard.Packaging.Controllers {
}
public ActionResult UninstallPackage(string id, string returnUrl, string retryUrl) {
if (!_packagingServices.CanManagePackages())
if (_shellSettings.Name != ShellSettings.DefaultName || !Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to uninstall packages")))
return new HttpUnauthorizedResult();
try {

View File

@@ -77,7 +77,6 @@
<Compile Include="Services\IPackageBuilder.cs" />
<Compile Include="Services\IPackageInstaller.cs" />
<Compile Include="Services\IPackageManager.cs" />
<Compile Include="Services\IPackagingServices.cs" />
<Compile Include="Services\IPackagingSourceManager.cs" />
<Compile Include="Services\NugetLogger.cs" />
<Compile Include="Services\PackageBuilder.cs" />
@@ -85,7 +84,6 @@
<Compile Include="Services\PackageInstaller.cs" />
<Compile Include="Services\PackageManager.cs" />
<Compile Include="Services\PackagingEntry.cs" />
<Compile Include="Services\PackagingServices.cs" />
<Compile Include="Services\PackagingSourceManager.cs" />
<Compile Include="ViewModels\PackagingAddSourceViewModel.cs" />
<Compile Include="ViewModels\PackagingHarvestViewModel.cs" />
@@ -96,6 +94,7 @@
<ItemGroup>
<Content Include="Content\Images\imagePlaceholder.png" />
<Content Include="Content\Images\moduleDefaultIcon.png" />
<Content Include="Content\Images\stars.png" />
<Content Include="Module.txt" />
<Content Include="Service References\GalleryServer\Reference.datasvcmap">
<Generator>DataServiceClientGenerator</Generator>

View File

@@ -1,13 +0,0 @@
namespace Orchard.Packaging.Services {
/// <summary>
/// Provides generic packaging related methods.
/// </summary>
public interface IPackagingServices : IDependency {
/// <summary>
/// Verifies if the current user is allowed to manage packages. The super user of the default tenant site is always allowed.
/// </summary>
/// <returns>True if the allowed; false otherwise.</returns>
bool CanManagePackages();
}
}

View File

@@ -1,4 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Orchard.Packaging.GalleryServer;
using Orchard.Packaging.Models;
namespace Orchard.Packaging.Services {
@@ -7,7 +11,7 @@ namespace Orchard.Packaging.Services {
void AddSource(string feedTitle, string feedUrl);
void RemoveSource(int id);
IEnumerable<PackagingEntry> GetModuleList(PackagingSource packagingSource = null);
IEnumerable<PackagingEntry> GetThemeList(PackagingSource packagingSource = null);
IEnumerable<PackagingEntry> GetExtensionList(PackagingSource packagingSource = null, Func<IQueryable<PublishedPackage>, IQueryable<PublishedPackage>> query = null);
int GetExtensionCount(PackagingSource packagingSource = null, Func<IQueryable<PublishedPackage>, IQueryable<PublishedPackage>> query = null);
}
}

View File

@@ -17,5 +17,6 @@ namespace Orchard.Packaging.Services {
public string IconUrl { get; set; }
public double Rating { get; set; }
public int RatingsCount { get; set; }
public int DownloadCount { get; set; }
}
}

View File

@@ -1,39 +0,0 @@
using Orchard.Environment;
using Orchard.Environment.Configuration;
using Orchard.Localization;
namespace Orchard.Packaging.Services {
/// <summary>
/// Provides generic packaging related methods.
/// </summary>
public class PackagingServices : IPackagingServices {
private readonly IOrchardServices _orchardServices;
private readonly ShellSettings _shellSettings;
public PackagingServices(IOrchardServices orchardServices,
ShellSettings shellSettings) {
_orchardServices = orchardServices;
_shellSettings = shellSettings;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
/// <summary>
/// Verifies if the current user is allowed to manage packages. The super user of the default tenant site is always allowed.
/// </summary>
/// <returns>True if the allowed; False otherwise.</returns>
public bool CanManagePackages() {
// Check if super user for default tenant site
if (_shellSettings.Name == ShellSettings.DefaultName
&& _orchardServices.WorkContext.CurrentUser.UserName == _orchardServices.WorkContext.CurrentSite.SuperUser) {
return true;
}
// Check if it has permission explicitly assigned
return _orchardServices.Authorizer.Authorize(Permissions.ManagePackages, T("Not authorized to manage packages."));
}
}
}

View File

@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Orchard.Data;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.Localization;
using Orchard.Packaging.GalleryServer;
using Orchard.Packaging.Models;
@@ -42,30 +42,42 @@ namespace Orchard.Packaging.Services {
}
}
public IEnumerable<PackagingEntry> GetModuleList(PackagingSource packagingSource = null) {
return GetExtensionList(DefaultExtensionTypes.Module, packagingSource);
}
public IEnumerable<PackagingEntry> GetThemeList(PackagingSource packagingSource = null) {
return GetExtensionList(DefaultExtensionTypes.Theme, packagingSource);
}
private IEnumerable<PackagingEntry> GetExtensionList(string filter = null, PackagingSource packagingSource = null) {
public IEnumerable<PackagingEntry> GetExtensionList(PackagingSource packagingSource = null, Func<IQueryable<PublishedPackage>, IQueryable<PublishedPackage>> query = null) {
return (packagingSource == null ? GetSources() : new[] {packagingSource})
.SelectMany(
source => {
GalleryFeedContext galleryFeedContext = new GalleryFeedContext(new Uri(source.FeedUrl));
return galleryFeedContext.Packages
.Where(p => p.PackageType == filter)
.ToList()
.Select(p => {
PublishedScreenshot firstScreenshot = galleryFeedContext.Screenshots
.Where(s => s.PublishedPackageId == p.Id && s.PublishedPackageVersion == p.Version)
.ToList()
.FirstOrDefault();
return CreatePackageEntry(p, firstScreenshot, packagingSource, galleryFeedContext.GetReadStreamUri(p));
});
var galleryFeedContext = new GalleryFeedContext(new Uri(source.FeedUrl));
IQueryable<PublishedPackage> packages = galleryFeedContext.Packages;
if (query != null) {
packages = query(packages);
}
return packages.ToList().Select(
p => {
PublishedScreenshot firstScreenshot = galleryFeedContext.Screenshots
.Where(s => s.PublishedPackageId == p.Id && s.PublishedPackageVersion == p.Version)
.ToList()
.FirstOrDefault();
return CreatePackageEntry(p, firstScreenshot, packagingSource, galleryFeedContext.GetReadStreamUri(p));
});
}
).ToArray();
);
}
public int GetExtensionCount(PackagingSource packagingSource = null, Func<IQueryable<PublishedPackage>, IQueryable<PublishedPackage>> query = null) {
return (packagingSource == null ? GetSources() : new[] { packagingSource })
.Sum( source => {
var galleryFeedContext = new GalleryFeedContext(new Uri(source.FeedUrl));
IQueryable<PublishedPackage> packages = galleryFeedContext.Packages;
if (query != null) {
packages = query(packages);
}
return packages.Count();
}
);
}
private static PackagingEntry CreatePackageEntry(PublishedPackage package, PublishedScreenshot screenshot, PackagingSource source, Uri downloadUri) {
@@ -91,7 +103,8 @@ namespace Orchard.Packaging.Services {
IconUrl = iconUrl,
FirstScreenshot = firstScreenshot,
Rating = package.Rating,
RatingsCount = package.RatingsCount
RatingsCount = package.RatingsCount,
DownloadCount = package.DownloadCount
};
}

View File

@@ -47,3 +47,18 @@
width: 171px;
height: 128px;
}
.ratings {
background:url(../Content/Images/stars.png) repeat-x;
position: relative;
height: 14px;
width: 75px;
display: block;
float: left;
clear: both;
}
.score {
background: url(../Content/Images/stars.png) repeat-x 0 -14px;
position: absolute;
width: 45px;
}

View File

@@ -7,5 +7,18 @@ namespace Orchard.Packaging.ViewModels {
public IEnumerable<PackagingEntry> Extensions { get; set; }
public IEnumerable<PackagingSource> Sources { get; set; }
public PackagingSource SelectedSource { get; set; }
public PackagingExtensionsOptions Options { get; set; }
public dynamic Pager { get; set; }
}
public class PackagingExtensionsOptions {
public int? SourceId { get; set; }
public PackagingExtensionsOrder Order { get; set; }
}
public enum PackagingExtensionsOrder {
Downloads,
Ratings,
Alphanumeric
}
}

View File

@@ -1,7 +1,17 @@
@model Orchard.Packaging.ViewModels.PackagingExtensionsViewModel
@using Orchard.Packaging.ViewModels;
@using System.Linq;
@{ Style.Require("PackagingAdmin"); }
@{
Style.Require("PackagingAdmin");
var pageSizes = new List<int?>() { 10, 50, 100 };
var defaultPageSize = WorkContext.CurrentSite.PageSize;
if(!pageSizes.Contains(defaultPageSize)) {
pageSizes.Add(defaultPageSize);
}
}
<h1>@Html.TitleForPage(T("Browse Gallery - Modules").Text)</h1>
@@ -14,56 +24,77 @@
@Html.SelectOption(source.Id, Model.SelectedSource != null && Model.SelectedSource.Id == source.Id, source.FeedTitle)
}
</select>
</fieldset>
<fieldset class="bulk-actions">
<label for="filterResults">@T("Sort by:")</label>
<select id="filterResults" name="@Html.NameOf(m => m.Options.Order)">
@Html.SelectOption(Model.Options.Order, PackagingExtensionsOrder.Downloads, T("Downloads").ToString())
@Html.SelectOption(Model.Options.Order, PackagingExtensionsOrder.Ratings, T("Ratings").ToString())
@Html.SelectOption(Model.Options.Order, PackagingExtensionsOrder.Alphanumeric, T("Alphanumeric").ToString())
</select>
<input type="hidden" name="Page" value="1" />
<label for="pageSize">@T("Show:")</label>
<select id="pageSize" name="PageSize">
@Html.SelectOption((int)Model.Pager.PageSize, 0, T("All").ToString())
@foreach(int size in pageSizes.OrderBy(p => p)) {
@Html.SelectOption((int)Model.Pager.PageSize, size, size.ToString())
}
<select>
<button type="submit">@T("Apply")</button>
</fieldset>
}
@if (Model.Extensions.Count() > 0) {
<ul class="contentItems">
@foreach (var item in Model.Extensions) {
<li>
@{
string iconUrl = @item.IconUrl;
if (string.IsNullOrWhiteSpace(iconUrl)) {
iconUrl = Href("../../Content/Images/ModuleDefaultIcon.png");
}
}
<div class="iconThumbnail">
<div class="extensionDetails column">
<div class="extensionName">
@if (!string.IsNullOrWhiteSpace(item.GalleryDetailsUrl)) {
<a href="@item.GalleryDetailsUrl">
<h2>@item.Title<span> - @T("Version: {0}", item.Version)</span></h2>
</a>
} else {
<h2>@item.Title<span> - @T("Version: {0}", item.Version)</span></h2>
}
</div>
<div class="related">
@Html.ActionLink(T("Install").ToString(), "Install", new RouteValueDictionary { { "packageId", item.PackageId }, { "version", item.Version }, { "sourceId", item.Source.Id }, { "redirectTo", "Modules" } })@T(" | ")
<a href="@item.PackageStreamUri">@T("Download")</a>
</div>
<div class="properties">
<p>@(item.Description == null ? T("(No description").Text : item.Description)</p>
<ul class="pageStatus">
<li>@T("Last Updated: {0}", DateTime.Now.ToLocalTime())</li>
<li>&nbsp;&#124;&nbsp;@T("Author: {0}", !string.IsNullOrEmpty(item.Authors) ? item.Authors : T("Unknown").ToString())</li>
<li>&nbsp;&#124;&nbsp;@T("Rating: {0}", item.Rating)</li>
<li>&nbsp;&#124;&nbsp;@T("Ratings Count: {0}", item.RatingsCount)</li>
<li>&nbsp;&#124;&nbsp;@T("Website: ")
@if (!string.IsNullOrEmpty(item.ProjectUrl)) { <a href="@item.ProjectUrl">@item.ProjectUrl</a> } else { @T("Unknown").ToString() }
</li>
</ul>
</div>
</div>
<div class="extensionThumbnail column">
<img src="@iconUrl" class="thumbnail" alt="module" />
</div>
</div>
</li>}
</ul>
if (Model.Extensions.Count() > 0) {
<ul class="contentItems">
@foreach (var item in Model.Extensions) {
<li>
@{
string iconUrl = @item.IconUrl;
if (string.IsNullOrWhiteSpace(iconUrl)) {
iconUrl = Href("../../Content/Images/ModuleDefaultIcon.png");
}
}
<div class="iconThumbnail">
<div class="extensionDetails column">
<div class="extensionName">
@if (!string.IsNullOrWhiteSpace(item.GalleryDetailsUrl)) {
<a href="@item.GalleryDetailsUrl">
<h2>@item.Title<span> - @T("Version: {0}", item.Version)</span></h2>
</a>
} else {
<h2>@item.Title<span> - @T("Version: {0}", item.Version)</span></h2>
}
</div>
<div class="ratings" style="width:@(15*5)px" title="@T("Ratings: {0} ({1})", item.Rating, item.RatingsCount)">
<div class="score" style="width:@(15*(item.Rating))px">&nbsp;</div>
</div>
<div class="related">
@Html.ActionLink(T("Install").ToString(), "Install", new RouteValueDictionary { { "packageId", item.PackageId }, { "version", item.Version }, { "sourceId", item.Source.Id }, { "redirectTo", "Modules" } })@T(" | ")
<a href="@item.PackageStreamUri">@T("Download")</a>
</div>
<div class="properties">
<p>@(item.Description == null ? T("(No description").Text : item.Description)</p>
<ul class="pageStatus">
<li>@T("Last Updated: {0}", DateTime.Now.ToLocalTime())</li>
<li>&nbsp;&#124;&nbsp;@T("Author: {0}", !string.IsNullOrEmpty(item.Authors) ? item.Authors : T("Unknown").ToString())</li>
<li>&nbsp;&#124;&nbsp;@T("Downloads: {0}", item.DownloadCount)</li>
<li>&nbsp;&#124;&nbsp;@T("Website: ")
@if (!string.IsNullOrEmpty(item.ProjectUrl)) { <a href="@item.ProjectUrl">@item.ProjectUrl</a> } else { @T("Unknown").ToString() }
</li>
</ul>
</div>
</div>
<div class="extensionThumbnail column">
<img src="@iconUrl" class="thumbnail" alt="module" />
</div>
</div>
</li>}
</ul>
}
@Display(Model.Pager)
}

View File

@@ -1,7 +1,17 @@
@model Orchard.Packaging.ViewModels.PackagingExtensionsViewModel
@using Orchard.Packaging.ViewModels;
@using System.Linq;
@{ Style.Require("PackagingAdmin"); }
@{
Style.Require("PackagingAdmin");
var pageSizes = new List<int?>() { 10, 50, 100 };
var defaultPageSize = WorkContext.CurrentSite.PageSize;
if(!pageSizes.Contains(defaultPageSize)) {
pageSizes.Add(defaultPageSize);
}
}
<h1>@Html.TitleForPage(T("Browse Gallery - Themes").Text)</h1>
@@ -14,63 +24,82 @@
@Html.SelectOption(source.Id, Model.SelectedSource != null && Model.SelectedSource.Id == source.Id, source.FeedTitle)
}
</select>
</fieldset>
<fieldset class="bulk-actions">
<label for="filterResults">@T("Sort by:")</label>
<select id="filterResults" name="@Html.NameOf(m => m.Options.Order)">
@Html.SelectOption(Model.Options.Order, PackagingExtensionsOrder.Downloads, T("Downloads").ToString())
@Html.SelectOption(Model.Options.Order, PackagingExtensionsOrder.Ratings, T("Ratings").ToString())
@Html.SelectOption(Model.Options.Order, PackagingExtensionsOrder.Alphanumeric, T("Alphanumeric").ToString())
</select>
<input type="hidden" name="Page" value="1" />
<label for="pageSize">@T("Show:")</label>
<select id="pageSize" name="PageSize">
@Html.SelectOption((int)Model.Pager.PageSize, 0, T("All").ToString())
@foreach(int size in pageSizes.OrderBy(p => p)) {
@Html.SelectOption((int)Model.Pager.PageSize, size, size.ToString())
}
<select>
<button type="submit">@T("Apply")</button>
</fieldset>
}
@if (Model.Extensions.Count() > 0) {
<ul class="contentItems theme">
@foreach (var item in Model.Extensions) {
<li>
@{
string extensionClass = "iconThumbnail";
string iconUrl = @item.IconUrl;
if (!string.IsNullOrWhiteSpace(@item.FirstScreenshot)) {
iconUrl = @item.FirstScreenshot;
extensionClass = "screenshotThumbnail";
}
else if (string.IsNullOrWhiteSpace(iconUrl)) {
iconUrl = Href("../../Content/Images/imagePlaceholder.png");
extensionClass = "screenshotThumbnail";
}
}
<div class="@extensionClass">
<div class="extensionDetails column">
<div class="extensionName">
@if (!string.IsNullOrWhiteSpace(item.GalleryDetailsUrl)) {
<a href="@item.GalleryDetailsUrl">
<h2>@item.Title<span> - @T("Version: {0}", item.Version)</span></h2>
</a>
} else {
<h2>@item.Title<span> - @T("Version: {0}", item.Version)</span></h2>
}
</div>
<div class="related">
@Html.ActionLink(T("Install").ToString(), "Install", new RouteValueDictionary {{"packageId", item.PackageId}, {"version", item.Version}, {"sourceId", item.Source.Id}, {"redirectTo", "Themes"}})@T(" | ")
<a href="@item.PackageStreamUri">@T("Download")</a>
</div>
<div class="properties">
<p>@(item.Description == null ? T("(No description").Text : item.Description)</p>
<ul class="pageStatus">
<li>@T("Last Updated: {0}", DateTime.Now.ToLocalTime())</li>
<li>&nbsp;&#124;&nbsp;@T("Author: {0}", !string.IsNullOrEmpty(item.Authors) ? item.Authors : T("Unknown").ToString())</li>
<li>&nbsp;&#124;&nbsp;@T("Rating: {0}", item.Rating)</li>
<li>&nbsp;&#124;&nbsp;@T("Ratings Count: {0}", item.RatingsCount)</li>
<li>&nbsp;&#124;&nbsp;@T("Website: ")
@if(!string.IsNullOrEmpty(item.ProjectUrl)) { <a href="@item.ProjectUrl">@item.ProjectUrl</a> }
else { @T("Unknown").ToString() }
</li>
</ul>
</div>
</div>
<div class="extensionThumbnail column">
<img src="@iconUrl" class="thumbnail" alt="theme" />
</div>
</div>
</li>}
</ul>
if (Model.Extensions.Count() > 0) {
<ul class="contentItems theme">
@foreach (var item in Model.Extensions) {
<li>
@{
string extensionClass = "iconThumbnail";
string iconUrl = @item.IconUrl;
if (!string.IsNullOrWhiteSpace(@item.FirstScreenshot)) {
iconUrl = @item.FirstScreenshot;
extensionClass = "screenshotThumbnail";
}
else if (string.IsNullOrWhiteSpace(iconUrl)) {
iconUrl = Href("../../Content/Images/imagePlaceholder.png");
extensionClass = "screenshotThumbnail";
}
}
<div class="@extensionClass">
<div class="extensionDetails column">
<div class="extensionName">
@if (!string.IsNullOrWhiteSpace(item.GalleryDetailsUrl)) {
<a href="@item.GalleryDetailsUrl">
<h2>@item.Title<span> - @T("Version: {0}", item.Version)</span></h2>
</a>
} else {
<h2>@item.Title<span> - @T("Version: {0}", item.Version)</span></h2>
}
</div>
<div class="ratings" style="width:@(15*5)px" title="@T("Ratings: {0} ({1})", item.Rating, item.RatingsCount)">
<div class="score" style="width:@(15*(item.Rating))px">&nbsp;</div>
</div>
<div class="related">
@Html.ActionLink(T("Install").ToString(), "Install", new RouteValueDictionary {{"packageId", item.PackageId}, {"version", item.Version}, {"sourceId", item.Source.Id}, {"redirectTo", "Themes"}})@T(" | ")
<a href="@item.PackageStreamUri">@T("Download")</a>
</div>
<div class="properties">
<p>@(item.Description == null ? T("(No description").Text : item.Description)</p>
<ul class="pageStatus">
<li>@T("Last Updated: {0}", DateTime.Now.ToLocalTime())</li>
<li>&nbsp;&#124;&nbsp;@T("Author: {0}", !string.IsNullOrEmpty(item.Authors) ? item.Authors : T("Unknown").ToString())</li>
<li>&nbsp;&#124;&nbsp;@T("Downloads: {0}", item.DownloadCount)</li>
<li>&nbsp;&#124;&nbsp;@T("Website: ")
@if(!string.IsNullOrEmpty(item.ProjectUrl)) { <a href="@item.ProjectUrl">@item.ProjectUrl</a> }
else { @T("Unknown").ToString() }
</li>
</ul>
</div>
</div>
<div class="extensionThumbnail column">
<img src="@iconUrl" class="thumbnail" alt="theme" />
</div>
</div>
</li>}
</ul>
}
}