mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Updating the gallery module
--HG-- branch : nuget
This commit is contained in:
@@ -12,9 +12,9 @@ namespace Orchard.Packaging {
|
|||||||
public void GetNavigation(NavigationBuilder builder) {
|
public void GetNavigation(NavigationBuilder builder) {
|
||||||
builder.Add(T("Gallery"), "30", menu => menu
|
builder.Add(T("Gallery"), "30", menu => menu
|
||||||
.Add(T("Modules"), "1.0", item => item
|
.Add(T("Modules"), "1.0", item => item
|
||||||
.Action("ModulesIndex", "Gallery", new { area = "Orchard.Packaging" }))
|
.Action("Modules", "Gallery", new { area = "Orchard.Packaging" }))
|
||||||
.Add(T("Themes"), "2.0", item => item
|
.Add(T("Themes"), "2.0", item => item
|
||||||
.Action("ThemesIndex", "Gallery", new { area = "Orchard.Packaging" }))
|
.Action("Themes", "Gallery", new { area = "Orchard.Packaging" }))
|
||||||
.Add(T("Feeds"), "3.0", item => item
|
.Add(T("Feeds"), "3.0", item => item
|
||||||
.Action("Sources", "Gallery", new { area = "Orchard.Packaging" })));
|
.Action("Sources", "Gallery", new { area = "Orchard.Packaging" })));
|
||||||
}
|
}
|
||||||
|
@@ -1,105 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
using Orchard.Commands;
|
|
||||||
using Orchard.Environment.Extensions;
|
|
||||||
using Orchard.Packaging.Services;
|
|
||||||
|
|
||||||
namespace Orchard.Packaging.Commands {
|
|
||||||
[OrchardFeature("Gallery")]
|
|
||||||
public class GalleryCommands : DefaultOrchardCommandHandler {
|
|
||||||
private readonly IPackageManager _packageManager;
|
|
||||||
|
|
||||||
[OrchardSwitch]
|
|
||||||
public string User { get; set; }
|
|
||||||
|
|
||||||
[OrchardSwitch]
|
|
||||||
public string Password { get; set; }
|
|
||||||
|
|
||||||
public GalleryCommands(IPackageManager packageManager) {
|
|
||||||
_packageManager = packageManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if false
|
|
||||||
[CommandHelp("harvest <moduleName>\r\n\t" + "Package a module into a distributable")]
|
|
||||||
[CommandName("harvest")]
|
|
||||||
public void PackageCreate(string moduleName) {
|
|
||||||
var packageData = _packageManager.Harvest(moduleName);
|
|
||||||
if (packageData.PackageStream.CanSeek)
|
|
||||||
packageData.PackageStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
const int chunk = 512;
|
|
||||||
var dataBuffer = new byte[3 * chunk];
|
|
||||||
var charBuffer = new char[4 * chunk + 2];
|
|
||||||
for (; ; ) {
|
|
||||||
var dataCount = packageData.PackageStream.Read(dataBuffer, 0, dataBuffer.Length);
|
|
||||||
if (dataCount <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var charCount = Convert.ToBase64CharArray(dataBuffer, 0, dataCount, charBuffer, 0);
|
|
||||||
Context.Output.Write(charBuffer, 0, charCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[CommandHelp("gallery submit module <moduleName> <feedUrl> /User:<user> /Password:<password>\r\n\t" + "Package a module into a distributable and push it to a feed server.")]
|
|
||||||
[CommandName("gallery submit module")]
|
|
||||||
[OrchardSwitches("User,Password")]
|
|
||||||
public void SubmitModule(string moduleName, string feedUrl) {
|
|
||||||
var packageData = _packageManager.Harvest(moduleName);
|
|
||||||
|
|
||||||
if ( String.IsNullOrWhiteSpace(User) ) {
|
|
||||||
Context.Output.WriteLine(T("Missing or incorrect User"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( String.IsNullOrWhiteSpace(Password) ) {
|
|
||||||
Context.Output.WriteLine(T("Missing or incorrect Password"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
_packageManager.Push(packageData, feedUrl, User, Password);
|
|
||||||
Context.Output.WriteLine(T("Success"));
|
|
||||||
}
|
|
||||||
catch (WebException webException) {
|
|
||||||
string text = "";
|
|
||||||
if (webException.Response != null) {
|
|
||||||
text = new StreamReader(webException.Response.GetResponseStream()).ReadToEnd();
|
|
||||||
}
|
|
||||||
throw new ApplicationException(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandHelp("gallery submit package <filePath> <feedUrl> /User:<user> /Password:<password>\r\n\t" + "Push a packaged module to a feed server.")]
|
|
||||||
[CommandName("gallery submit package")]
|
|
||||||
[OrchardSwitches("User,Password")]
|
|
||||||
public void SubmitPackage(string filePath, string feedUrl) {
|
|
||||||
using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read)) {
|
|
||||||
var packageData = new PackageData {
|
|
||||||
PackageStream = stream
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( String.IsNullOrWhiteSpace(User) ) {
|
|
||||||
Context.Output.WriteLine(T("Missing or incorrect User"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( String.IsNullOrWhiteSpace(Password) ) {
|
|
||||||
Context.Output.WriteLine(T("Missing or incorrect Password"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
_packageManager.Push(packageData, feedUrl, User, Password);
|
|
||||||
Context.Output.WriteLine(T("Success"));
|
|
||||||
}
|
|
||||||
catch (WebException webException) {
|
|
||||||
var text = new StreamReader(webException.Response.GetResponseStream()).ReadToEnd();
|
|
||||||
throw new ApplicationException(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@@ -15,6 +15,7 @@ namespace Orchard.Packaging.Controllers {
|
|||||||
[OrchardFeature("Gallery")]
|
[OrchardFeature("Gallery")]
|
||||||
[Themed, Admin]
|
[Themed, Admin]
|
||||||
public class GalleryController : Controller {
|
public class GalleryController : Controller {
|
||||||
|
|
||||||
private readonly IPackageManager _packageManager;
|
private readonly IPackageManager _packageManager;
|
||||||
private readonly IPackagingSourceManager _packagingSourceManager;
|
private readonly IPackagingSourceManager _packagingSourceManager;
|
||||||
private readonly IExtensionManager _extensionManager;
|
private readonly IExtensionManager _extensionManager;
|
||||||
@@ -34,24 +35,15 @@ namespace Orchard.Packaging.Controllers {
|
|||||||
|
|
||||||
Localizer T { get; set; }
|
Localizer T { get; set; }
|
||||||
|
|
||||||
public ActionResult ModulesIndex() {
|
|
||||||
return Modules(Guid.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionResult ThemesIndex() {
|
|
||||||
return Themes(Guid.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionResult Sources() {
|
public ActionResult Sources() {
|
||||||
return View(new PackagingSourcesViewModel {
|
return View(new PackagingSourcesViewModel {
|
||||||
Sources = _packagingSourceManager.GetSources(),
|
Sources = _packagingSourceManager.GetSources(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActionResult Remove(Guid id) {
|
public ActionResult Remove(int id) {
|
||||||
_packagingSourceManager.RemoveSource(id);
|
_packagingSourceManager.RemoveSource(id);
|
||||||
_notifier.Information(T("The feed has been removed successfully."));
|
_notifier.Information(T("The feed has been removed successfully."));
|
||||||
Update(null);
|
|
||||||
return RedirectToAction("Sources");
|
return RedirectToAction("Sources");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,9 +84,9 @@ namespace Orchard.Packaging.Controllers {
|
|||||||
if ( !ModelState.IsValid )
|
if ( !ModelState.IsValid )
|
||||||
return View(new PackagingAddSourceViewModel { Url = url });
|
return View(new PackagingAddSourceViewModel { Url = url });
|
||||||
|
|
||||||
_packagingSourceManager.AddSource(new PackagingSource { Id = Guid.NewGuid(), FeedUrl = url, FeedTitle = title });
|
_packagingSourceManager.AddSource(title, url);
|
||||||
_notifier.Information(T("The feed has been added successfully."));
|
_notifier.Information(T("The feed has been added successfully."));
|
||||||
Update(null);
|
|
||||||
return RedirectToAction("Sources");
|
return RedirectToAction("Sources");
|
||||||
}
|
}
|
||||||
catch ( Exception exception ) {
|
catch ( Exception exception ) {
|
||||||
@@ -104,32 +96,36 @@ namespace Orchard.Packaging.Controllers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ActionResult Modules(Guid? sourceId) {
|
public ActionResult Modules(int? sourceId) {
|
||||||
var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
|
var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
|
||||||
|
|
||||||
return View("Modules", new PackagingModulesViewModel {
|
var sources = selectedSource != null
|
||||||
Modules = _packagingSourceManager.GetModuleList(selectedSource).Where(p => p.SyndicationItem.Categories.All(c => c.Name == "Orchard Module" || c.Name != "Orchard Theme")),
|
? new [] { selectedSource }
|
||||||
|
: _packagingSourceManager.GetSources()
|
||||||
|
;
|
||||||
|
|
||||||
|
return View("Modules", new PackagingExtensionsViewModel {
|
||||||
|
Extensions = sources.SelectMany(source => _packagingSourceManager.GetModuleList(source)),
|
||||||
Sources = _packagingSourceManager.GetSources().OrderBy(s => s.FeedTitle),
|
Sources = _packagingSourceManager.GetSources().OrderBy(s => s.FeedTitle),
|
||||||
SelectedSource = selectedSource
|
SelectedSource = selectedSource
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActionResult Themes(Guid? sourceId) {
|
public ActionResult Themes(int? sourceId) {
|
||||||
var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
|
var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
|
||||||
|
|
||||||
return View("Themes", new PackagingModulesViewModel {
|
var sources = selectedSource != null
|
||||||
Modules = _packagingSourceManager.GetModuleList(selectedSource).Where(p => p.SyndicationItem.Categories.Any(c => c.Name == "Orchard Theme")),
|
? new[] { selectedSource }
|
||||||
|
: _packagingSourceManager.GetSources()
|
||||||
|
;
|
||||||
|
|
||||||
|
return View("Themes", new PackagingExtensionsViewModel {
|
||||||
|
Extensions = sources.SelectMany(source => _packagingSourceManager.GetThemeList(source)),
|
||||||
Sources = _packagingSourceManager.GetSources().OrderBy(s => s.FeedTitle),
|
Sources = _packagingSourceManager.GetSources().OrderBy(s => s.FeedTitle),
|
||||||
SelectedSource = selectedSource
|
SelectedSource = selectedSource
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActionResult Update(string cameFrom) {
|
|
||||||
_packagingSourceManager.UpdateLists();
|
|
||||||
_notifier.Information(T("List of available modules and themes is updated."));
|
|
||||||
return RedirectToAction(cameFrom == "Themes" ? "ThemesIndex" : "ModulesIndex");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionResult Harvest(string extensionName, string feedUrl) {
|
public ActionResult Harvest(string extensionName, string feedUrl) {
|
||||||
return View(new PackagingHarvestViewModel {
|
return View(new PackagingHarvestViewModel {
|
||||||
ExtensionName = extensionName,
|
ExtensionName = extensionName,
|
||||||
@@ -141,6 +137,7 @@ namespace Orchard.Packaging.Controllers {
|
|||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public ActionResult Harvest(PackagingHarvestViewModel model) {
|
public ActionResult Harvest(PackagingHarvestViewModel model) {
|
||||||
|
#if REFACTORING
|
||||||
model.Sources = _packagingSourceManager.GetSources();
|
model.Sources = _packagingSourceManager.GetSources();
|
||||||
model.Extensions = _extensionManager.AvailableExtensions();
|
model.Extensions = _extensionManager.AvailableExtensions();
|
||||||
|
|
||||||
@@ -164,6 +161,10 @@ namespace Orchard.Packaging.Controllers {
|
|||||||
Update(null);
|
Update(null);
|
||||||
|
|
||||||
return RedirectToAction("Harvest", new { model.ExtensionName, model.FeedUrl });
|
return RedirectToAction("Harvest", new { model.ExtensionName, model.FeedUrl });
|
||||||
|
#else
|
||||||
|
return View();
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActionResult Install(string syndicationId, string cameFrom) {
|
public ActionResult Install(string syndicationId, string cameFrom) {
|
||||||
|
@@ -4,23 +4,20 @@ using Orchard.Environment.Extensions;
|
|||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
using Orchard.Packaging.Services;
|
using Orchard.Packaging.Services;
|
||||||
using Orchard.UI.Notify;
|
|
||||||
|
|
||||||
namespace Orchard.Packaging {
|
namespace Orchard.Packaging {
|
||||||
[OrchardFeature("Gallery")]
|
[OrchardFeature("Gallery")]
|
||||||
public class DefaultPackagingUpdater : IFeatureEventHandler {
|
public class DefaultPackagingUpdater : IFeatureEventHandler {
|
||||||
private readonly IPackagingSourceManager _packagingSourceManager;
|
private readonly IPackagingSourceManager _packagingSourceManager;
|
||||||
private readonly INotifier _notifier;
|
|
||||||
|
|
||||||
public DefaultPackagingUpdater(IPackagingSourceManager packagingSourceManager, INotifier notifier) {
|
public DefaultPackagingUpdater(IPackagingSourceManager packagingSourceManager) {
|
||||||
_packagingSourceManager = packagingSourceManager;
|
_packagingSourceManager = packagingSourceManager;
|
||||||
_notifier = notifier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Localizer T { get; set; }
|
public Localizer T { get; set; }
|
||||||
|
|
||||||
public void Install(Feature feature) {
|
public void Install(Feature feature) {
|
||||||
_packagingSourceManager.AddSource(new PackagingSource { Id = Guid.NewGuid(), FeedTitle = "Orchard Module Gallery", FeedUrl = "http://orchardproject.net/gallery08/feed" });
|
_packagingSourceManager.AddSource( "Orchard Extensions Gallery", "http://feed.nuget.org/ctp2/odata/v1" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Enable(Feature feature) {
|
public void Enable(Feature feature) {
|
||||||
|
16
src/Orchard.Web/Modules/Orchard.Packaging/Migrations.cs
Normal file
16
src/Orchard.Web/Modules/Orchard.Packaging/Migrations.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Orchard.Data.Migration;
|
||||||
|
|
||||||
|
namespace Orchard.Packaging {
|
||||||
|
public class Migrations: DataMigrationImpl {
|
||||||
|
public int Create() {
|
||||||
|
SchemaBuilder.CreateTable("PackagingSourceRecord",
|
||||||
|
table => table
|
||||||
|
.Column<int>("Id", column => column.PrimaryKey().Identity())
|
||||||
|
.Column<string>("FeedTitle", c => c.WithLength(255))
|
||||||
|
.Column<string>("FeedUrl", c => c.WithLength(2048))
|
||||||
|
);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web;
|
||||||
|
|
||||||
|
namespace Orchard.Packaging.Models {
|
||||||
|
public class PackagingSourceRecord {
|
||||||
|
public virtual int Id { get; set; }
|
||||||
|
public virtual string FeedTitle { get; set; }
|
||||||
|
public virtual string FeedUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -36,7 +36,9 @@
|
|||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Data.Services.Client" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Runtime.Serialization" />
|
||||||
<Reference Include="System.ServiceModel" />
|
<Reference Include="System.ServiceModel" />
|
||||||
<Reference Include="System.Web.DynamicData" />
|
<Reference Include="System.Web.DynamicData" />
|
||||||
<Reference Include="System.Web.Entity" />
|
<Reference Include="System.Web.Entity" />
|
||||||
@@ -70,11 +72,12 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="AdminMenu.cs" />
|
<Compile Include="AdminMenu.cs" />
|
||||||
<Compile Include="Commands\GalleryCommands.cs" />
|
|
||||||
<Compile Include="Commands\PackagingCommands.cs" />
|
<Compile Include="Commands\PackagingCommands.cs" />
|
||||||
<Compile Include="Controllers\DownloadStreamResult.cs" />
|
<Compile Include="Controllers\DownloadStreamResult.cs" />
|
||||||
<Compile Include="Controllers\GalleryController.cs" />
|
<Compile Include="Controllers\GalleryController.cs" />
|
||||||
<Compile Include="DefaultPackagingUpdater.cs" />
|
<Compile Include="DefaultPackagingUpdater.cs" />
|
||||||
|
<Compile Include="Migrations.cs" />
|
||||||
|
<Compile Include="Models\PackagingSourceRecord.cs" />
|
||||||
<Compile Include="ResourceManifest.cs" />
|
<Compile Include="ResourceManifest.cs" />
|
||||||
<Compile Include="Services\AtomExtensions.cs" />
|
<Compile Include="Services\AtomExtensions.cs" />
|
||||||
<Compile Include="Services\IPackageBuilder.cs" />
|
<Compile Include="Services\IPackageBuilder.cs" />
|
||||||
@@ -91,7 +94,7 @@
|
|||||||
<Compile Include="Services\PackagingSourceManager.cs" />
|
<Compile Include="Services\PackagingSourceManager.cs" />
|
||||||
<Compile Include="ViewModels\PackagingAddSourceViewModel.cs" />
|
<Compile Include="ViewModels\PackagingAddSourceViewModel.cs" />
|
||||||
<Compile Include="ViewModels\PackagingHarvestViewModel.cs" />
|
<Compile Include="ViewModels\PackagingHarvestViewModel.cs" />
|
||||||
<Compile Include="ViewModels\PackagingModulesViewModel.cs" />
|
<Compile Include="ViewModels\PackagingExtensionsViewModel.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ViewModels\PackagingSourcesViewModel.cs" />
|
<Compile Include="ViewModels\PackagingSourcesViewModel.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -105,9 +108,7 @@
|
|||||||
<Content Include="Views\Gallery\_Subnav.cshtml" />
|
<Content Include="Views\Gallery\_Subnav.cshtml" />
|
||||||
<Content Include="Views\Web.config" />
|
<Content Include="Views\Web.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup />
|
||||||
<Folder Include="Models\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\..\external\nuget\NuPack.Core\NuPack.Core.csproj">
|
<ProjectReference Include="..\..\..\..\external\nuget\NuPack.Core\NuPack.Core.csproj">
|
||||||
<Project>{F879F274-EFA0-4157-8404-33A19B4E6AEC}</Project>
|
<Project>{F879F274-EFA0-4157-8404-33A19B4E6AEC}</Project>
|
||||||
@@ -118,9 +119,6 @@
|
|||||||
<Name>Orchard.Framework</Name>
|
<Name>Orchard.Framework</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Views\Gallery\Themes.cshtml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Content\Web.config">
|
<Content Include="Content\Web.config">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
@@ -136,6 +134,12 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<WCFMetadata Include="Service References\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Views\Gallery\Themes.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
@@ -4,9 +4,6 @@ using System.IO;
|
|||||||
namespace Orchard.Packaging.Services {
|
namespace Orchard.Packaging.Services {
|
||||||
public interface IPackageManager : IDependency {
|
public interface IPackageManager : IDependency {
|
||||||
PackageData Harvest(string extensionName);
|
PackageData Harvest(string extensionName);
|
||||||
PackageData Download(string feedItemId);
|
|
||||||
|
|
||||||
void Push(PackageData packageData, string feedUrl, string login, string password);
|
|
||||||
PackageInfo Install(string packageId, string version, string location, string solutionFolder);
|
PackageInfo Install(string packageId, string version, string location, string solutionFolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,13 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Orchard.Packaging.Models;
|
||||||
|
|
||||||
namespace Orchard.Packaging.Services {
|
namespace Orchard.Packaging.Services {
|
||||||
public interface IPackagingSourceManager : IDependency {
|
public interface IPackagingSourceManager : IDependency {
|
||||||
IEnumerable<PackagingSource> GetSources();
|
IEnumerable<PackagingSourceRecord> GetSources();
|
||||||
void AddSource(PackagingSource source);
|
void AddSource(string feedTitle, string feedUrl);
|
||||||
void RemoveSource(Guid id);
|
void RemoveSource(int id);
|
||||||
void UpdateLists();
|
|
||||||
|
|
||||||
IEnumerable<PackagingEntry> GetModuleList(PackagingSource packagingSource = null);
|
IEnumerable<PackagingEntry> GetModuleList(PackagingSourceRecord packagingSource = null);
|
||||||
|
IEnumerable<PackagingEntry> GetThemeList(PackagingSourceRecord packagingSource = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
using NuGet;
|
|
||||||
using Orchard.Environment.Extensions;
|
using Orchard.Environment.Extensions;
|
||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
|
|
||||||
@@ -13,15 +8,12 @@ namespace Orchard.Packaging.Services {
|
|||||||
private readonly IExtensionManager _extensionManager;
|
private readonly IExtensionManager _extensionManager;
|
||||||
private readonly IPackageBuilder _packageBuilder;
|
private readonly IPackageBuilder _packageBuilder;
|
||||||
private readonly IPackageExpander _packageExpander;
|
private readonly IPackageExpander _packageExpander;
|
||||||
private readonly IPackagingSourceManager _packagingSourceManager;
|
|
||||||
|
|
||||||
public PackageManager(
|
public PackageManager(
|
||||||
IExtensionManager extensionManager,
|
IExtensionManager extensionManager,
|
||||||
IPackagingSourceManager packagingSourceManager,
|
|
||||||
IPackageBuilder packageBuilder,
|
IPackageBuilder packageBuilder,
|
||||||
IPackageExpander packageExpander) {
|
IPackageExpander packageExpander) {
|
||||||
_extensionManager = extensionManager;
|
_extensionManager = extensionManager;
|
||||||
_packagingSourceManager = packagingSourceManager;
|
|
||||||
_packageBuilder = packageBuilder;
|
_packageBuilder = packageBuilder;
|
||||||
_packageExpander = packageExpander;
|
_packageExpander = packageExpander;
|
||||||
}
|
}
|
||||||
@@ -41,48 +33,6 @@ namespace Orchard.Packaging.Services {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Push(PackageData packageData, string feedUrl, string user, string password) {
|
|
||||||
WebRequest request = WebRequest.Create(feedUrl);
|
|
||||||
request.Method = "POST";
|
|
||||||
request.ContentType = "application/x-package";
|
|
||||||
request.Headers.Add("user", Convert.ToBase64String(Encoding.UTF8.GetBytes(user)));
|
|
||||||
request.Headers.Add("password", Convert.ToBase64String(Encoding.UTF8.GetBytes(password)));
|
|
||||||
using ( Stream requestStream = request.GetRequestStream() ) {
|
|
||||||
packageData.PackageStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
packageData.PackageStream.CopyTo(requestStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
using (request.GetResponse()) {
|
|
||||||
// forces request and disposes results
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PackageData Download(string feedItemId) {
|
|
||||||
#if REFACTORING
|
|
||||||
PackagingEntry entry = _packagingSourceManager.GetModuleList().Single(x => x.SyndicationItem.Id == feedItemId);
|
|
||||||
WebRequest request = WebRequest.Create(entry.PackageStreamUri);
|
|
||||||
using (WebResponse response = request.GetResponse()) {
|
|
||||||
using (Stream responseStream = response.GetResponseStream()) {
|
|
||||||
var stream = new MemoryStream();
|
|
||||||
responseStream.CopyTo(stream);
|
|
||||||
Package package = Package.Open(stream);
|
|
||||||
try {
|
|
||||||
return new PackageData {
|
|
||||||
ExtensionName = package.PackageProperties.Identifier,
|
|
||||||
ExtensionVersion = package.PackageProperties.Version,
|
|
||||||
PackageStream = stream
|
|
||||||
};
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
package.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
throw new NotImplementedException();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public PackageInfo Install(string packageId, string version, string location, string solutionFolder) {
|
public PackageInfo Install(string packageId, string version, string location, string solutionFolder) {
|
||||||
return _packageExpander.ExpandPackage(packageId, version, location, solutionFolder);
|
return _packageExpander.ExpandPackage(packageId, version, location, solutionFolder);
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,16 @@
|
|||||||
using System.ServiceModel.Syndication;
|
|
||||||
|
using System;
|
||||||
|
using Orchard.Packaging.Models;
|
||||||
|
|
||||||
namespace Orchard.Packaging.Services {
|
namespace Orchard.Packaging.Services {
|
||||||
public class PackagingEntry {
|
public class PackagingEntry {
|
||||||
public PackagingSource Source { get; set; }
|
public PackagingSourceRecord Source { get; set; }
|
||||||
public SyndicationFeed SyndicationFeed { get; set; }
|
public string Title { get; set; }
|
||||||
public SyndicationItem SyndicationItem { get; set; }
|
public string PackageId { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
public string PackageStreamUri { get; set; }
|
public string PackageStreamUri { get; set; }
|
||||||
|
public DateTime LastUpdated { get; set; }
|
||||||
|
public string Authors { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Orchard.Packaging.Services {
|
namespace Orchard.Packaging.Services {
|
||||||
public class PackagingSource {
|
public class _PackagingSource {
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public string FeedTitle { get; set; }
|
public string FeedTitle { get; set; }
|
||||||
public string FeedUrl { get; set; }
|
public string FeedUrl { get; set; }
|
||||||
|
@@ -1,26 +1,22 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.ServiceModel.Syndication;
|
using NuGet;
|
||||||
using System.Xml;
|
using Orchard.Data;
|
||||||
using System.Xml.Linq;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
using Orchard.Environment.Extensions;
|
using Orchard.Environment.Extensions;
|
||||||
using Orchard.FileSystems.AppData;
|
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
using Orchard.UI.Notify;
|
using Orchard.Packaging.Models;
|
||||||
|
|
||||||
namespace Orchard.Packaging.Services {
|
namespace Orchard.Packaging.Services {
|
||||||
[OrchardFeature("PackagingServices")]
|
[OrchardFeature("PackagingServices")]
|
||||||
public class PackagingSourceManager : IPackagingSourceManager {
|
public class PackagingSourceManager : IPackagingSourceManager {
|
||||||
private static readonly XmlSerializer _sourceSerializer = new XmlSerializer(typeof(List<PackagingSource>), new XmlRootAttribute("Sources"));
|
private const string ModulesFilter = "Orchard.Module.";
|
||||||
private readonly IAppDataFolder _appDataFolder;
|
private const string ThemesFilter = "Orchard.Theme.";
|
||||||
private readonly INotifier _notifier;
|
|
||||||
|
|
||||||
public PackagingSourceManager(IAppDataFolder appDataFolder, INotifier notifier) {
|
private readonly IRepository<PackagingSourceRecord> _packagingSourceRecordRepository;
|
||||||
_appDataFolder = appDataFolder;
|
|
||||||
_notifier = notifier;
|
public PackagingSourceManager(IRepository<PackagingSourceRecord> packagingSourceRecordRepository) {
|
||||||
|
_packagingSourceRecordRepository = packagingSourceRecordRepository;
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,100 +24,49 @@ namespace Orchard.Packaging.Services {
|
|||||||
|
|
||||||
#region IPackagingSourceManager Members
|
#region IPackagingSourceManager Members
|
||||||
|
|
||||||
public IEnumerable<PackagingSource> GetSources() {
|
public IEnumerable<PackagingSourceRecord> GetSources() {
|
||||||
string text = _appDataFolder.ReadFile(GetSourcesPath());
|
return _packagingSourceRecordRepository.Table.ToList();
|
||||||
if ( string.IsNullOrEmpty(text) ) {
|
|
||||||
return Enumerable.Empty<PackagingSource>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var textReader = new StringReader(_appDataFolder.ReadFile(GetSourcesPath()));
|
public void AddSource(string feedTitle, string feedUrl) {
|
||||||
return (IEnumerable<PackagingSource>)_sourceSerializer.Deserialize(textReader);
|
_packagingSourceRecordRepository.Create(new PackagingSourceRecord {FeedTitle = feedTitle, FeedUrl = feedUrl});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddSource(PackagingSource source) {
|
public void RemoveSource(int id) {
|
||||||
SaveSources(GetSources().Concat(new[] { source }).GroupBy(x => x.FeedUrl).Select(g => g.First()));
|
var packagingSource = _packagingSourceRecordRepository.Get(id);
|
||||||
}
|
if(packagingSource != null) {
|
||||||
|
_packagingSourceRecordRepository.Delete(packagingSource);
|
||||||
public void RemoveSource(Guid id) {
|
|
||||||
SaveSources(GetSources().Where(x => x.Id != id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateLists() {
|
|
||||||
foreach ( PackagingSource source in GetSources() ) {
|
|
||||||
UpdateSource(source);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<PackagingEntry> GetModuleList(PackagingSource packagingSource = null) {
|
public IEnumerable<PackagingEntry> GetModuleList(PackagingSourceRecord packagingSource = null) {
|
||||||
return (packagingSource == null ? GetSources() : new[] {packagingSource})
|
return GetExtensionList(ModulesFilter, packagingSource);
|
||||||
|
}
|
||||||
|
public IEnumerable<PackagingEntry> GetThemeList(PackagingSourceRecord packagingSource = null) {
|
||||||
|
return GetExtensionList(ThemesFilter, packagingSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<PackagingEntry> GetExtensionList(string filter = null, PackagingSourceRecord packagingSource = null) {
|
||||||
|
return ( packagingSource == null ? GetSources() : new[] { packagingSource } )
|
||||||
.SelectMany(
|
.SelectMany(
|
||||||
source =>
|
source =>
|
||||||
Bind(ParseFeed(GetModuleListForSource(source)),
|
new DataServicePackageRepository(new Uri(source.FeedUrl))
|
||||||
feed =>
|
.GetPackages()
|
||||||
feed.Items.SelectMany(
|
.Where(p => p.Id.StartsWith(filter ?? String.Empty))
|
||||||
item =>
|
.ToList()
|
||||||
Unit(new PackagingEntry {
|
.Select(p => new PackagingEntry {
|
||||||
|
Title = String.IsNullOrWhiteSpace(p.Title) ? p.Id : p.Title,
|
||||||
|
PackageId = p.Id,
|
||||||
|
PackageStreamUri = p.ProjectUrl != null ? p.ProjectUrl.ToString() : String.Empty,
|
||||||
Source = source,
|
Source = source,
|
||||||
SyndicationFeed = feed,
|
Version = p.Version != null ? p.Version.ToString() : String.Empty,
|
||||||
SyndicationItem = item,
|
Description = p.Description,
|
||||||
PackageStreamUri = item.Links.Where(l => String.IsNullOrEmpty(l.RelationshipType)).FirstOrDefault().GetAbsoluteUri().AbsoluteUri,
|
Authors = p.Authors != null ? String.Join(", ", p.Authors) : String.Empty,
|
||||||
})))).ToArray();
|
})
|
||||||
}
|
).ToArray();
|
||||||
|
|
||||||
private string GetModuleListForSource(PackagingSource source) {
|
|
||||||
if ( !_appDataFolder.FileExists(GetFeedCachePath(source)) ) {
|
|
||||||
UpdateSource(source);
|
|
||||||
}
|
|
||||||
return _appDataFolder.ReadFile(GetFeedCachePath(source));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private static string GetSourcesPath() {
|
|
||||||
return ".Packaging/Sources.xml";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetFeedCachePath(PackagingSource source) {
|
|
||||||
return ".Packaging/Feed." + source.Id.ToString("n") + ".xml";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveSources(IEnumerable<PackagingSource> sources) {
|
|
||||||
var textWriter = new StringWriter();
|
|
||||||
_sourceSerializer.Serialize(textWriter, sources.ToList());
|
|
||||||
|
|
||||||
_appDataFolder.CreateFile(GetSourcesPath(), textWriter.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateSource(PackagingSource source) {
|
|
||||||
try {
|
|
||||||
XDocument feed = XDocument.Load(source.FeedUrl, LoadOptions.PreserveWhitespace);
|
|
||||||
_appDataFolder.CreateFile(GetFeedCachePath(source), feed.ToString(SaveOptions.DisableFormatting));
|
|
||||||
}
|
|
||||||
catch ( Exception e ) {
|
|
||||||
_notifier.Warning(T("Error loading content of feed '{0}': {1}", source.FeedUrl, e.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static XName Atom(string localName) {
|
|
||||||
return AtomExtensions.AtomXName(localName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<T> Unit<T>(T t) where T : class {
|
|
||||||
return t != null ? new[] { t } : Enumerable.Empty<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<T2> Bind<T, T2>(T t, Func<T, IEnumerable<T2>> f) where T : class {
|
|
||||||
return Unit(t).SelectMany(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SyndicationFeed ParseFeed(string content) {
|
|
||||||
if ( string.IsNullOrEmpty(( content )) )
|
|
||||||
return new SyndicationFeed();
|
|
||||||
|
|
||||||
var formatter = new Atom10FeedFormatter<SyndicationFeed>();
|
|
||||||
formatter.ReadFrom(XmlReader.Create(new StringReader(content)));
|
|
||||||
return formatter.Feed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Orchard.Packaging.Models;
|
||||||
|
using Orchard.Packaging.Services;
|
||||||
|
|
||||||
|
namespace Orchard.Packaging.ViewModels {
|
||||||
|
public class PackagingExtensionsViewModel {
|
||||||
|
public IEnumerable<PackagingEntry> Extensions { get; set; }
|
||||||
|
public IEnumerable<PackagingSourceRecord> Sources { get; set; }
|
||||||
|
public PackagingSourceRecord SelectedSource { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@@ -1,11 +1,11 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Orchard.Environment.Extensions.Models;
|
using Orchard.Environment.Extensions.Models;
|
||||||
using Orchard.Packaging.Services;
|
using Orchard.Packaging.Models;
|
||||||
|
|
||||||
namespace Orchard.Packaging.ViewModels {
|
namespace Orchard.Packaging.ViewModels {
|
||||||
public class PackagingHarvestViewModel {
|
public class PackagingHarvestViewModel {
|
||||||
public IEnumerable<PackagingSource> Sources { get; set; }
|
public IEnumerable<PackagingSourceRecord> Sources { get; set; }
|
||||||
public IEnumerable<ExtensionDescriptor> Extensions { get; set; }
|
public IEnumerable<ExtensionDescriptor> Extensions { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
|
@@ -1,10 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Orchard.Packaging.Services;
|
|
||||||
|
|
||||||
namespace Orchard.Packaging.ViewModels {
|
|
||||||
public class PackagingModulesViewModel {
|
|
||||||
public IEnumerable<PackagingEntry> Modules { get; set; }
|
|
||||||
public IEnumerable<PackagingSource> Sources { get; set; }
|
|
||||||
public PackagingSource SelectedSource { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,8 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Orchard.Packaging.Services;
|
using Orchard.Packaging.Models;
|
||||||
|
|
||||||
namespace Orchard.Packaging.ViewModels {
|
namespace Orchard.Packaging.ViewModels {
|
||||||
public class PackagingSourcesViewModel {
|
public class PackagingSourcesViewModel {
|
||||||
public IEnumerable<PackagingSource> Sources { get; set; }
|
public IEnumerable<PackagingSourceRecord> Sources { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,11 +1,10 @@
|
|||||||
@model Orchard.Packaging.ViewModels.PackagingModulesViewModel
|
@model Orchard.Packaging.ViewModels.PackagingExtensionsViewModel
|
||||||
@using System.Linq;
|
@using System.Linq;
|
||||||
|
|
||||||
@{ Style.Require("PackagingAdmin"); }
|
@{ Style.Require("PackagingAdmin"); }
|
||||||
|
|
||||||
<h1>@Html.TitleForPage(T("Browse Gallery - Modules").ToString())</h1>
|
<h1>@Html.TitleForPage(T("Browse Gallery - Modules").Text)</h1>
|
||||||
|
|
||||||
<div class="manage">@Html.ActionLink(T("Refresh").ToString(), "Update", new { cameFrom = "Modules" }, new { @class = "button primaryAction" })</div>
|
|
||||||
@using ( Html.BeginFormAntiForgeryPost(Url.Action("Modules", "Gallery")) ) {
|
@using ( Html.BeginFormAntiForgeryPost(Url.Action("Modules", "Gallery")) ) {
|
||||||
<fieldset class="bulk-actions">
|
<fieldset class="bulk-actions">
|
||||||
<label for="filterResults" class="bulk-filter">@T("Feed:")</label>
|
<label for="filterResults" class="bulk-filter">@T("Feed:")</label>
|
||||||
@@ -20,24 +19,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@if (Model.Modules.Count() > 0) {
|
@if (Model.Extensions.Count() > 0) {
|
||||||
<ul class="contentItems">
|
<ul class="contentItems">
|
||||||
@foreach (var item in Model.Modules) {
|
@foreach (var item in Model.Extensions) {
|
||||||
<li>
|
<li>
|
||||||
<div class="moduleName">
|
<div class="moduleName">
|
||||||
<h2>@(item.SyndicationItem.Title == null ? T("(No title)").Text : item.SyndicationItem.Title.Text)<span> - @T("Version: {0}", item.SyndicationItem.ElementExtensions.ReadElementExtensions<string>("Version", "http://orchardproject.net").FirstOrDefault() ?? T("N/A").Text)</span></h2>
|
<h2>@item.Title<span> - @T("Version: {0}", item.Version)</span></h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="related">
|
<div class="related">
|
||||||
@Html.ActionLink(T("Install").ToString(), "Install", new RouteValueDictionary {{"SyndicationId",item.SyndicationItem.Id},{"cameFrom", "Modules"}})@T(" | ")
|
@Html.ActionLink(T("Install").ToString(), "Install", new RouteValueDictionary {{"SyndicationId", item.PackageId},{"cameFrom", "Modules"}})@T(" | ")
|
||||||
<a href="@item.PackageStreamUri">@T("Download")</a>
|
<a href="@item.PackageStreamUri">@T("Download")</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="properties">
|
<div class="properties">
|
||||||
<p>@(item.SyndicationItem.Summary == null ? T("(No description").Text : item.SyndicationItem.Summary.Text)</p>
|
<p>@(item.Description == null ? T("(No description").Text : item.Description)</p>
|
||||||
<ul class="pageStatus">
|
<ul class="pageStatus">
|
||||||
<li>@T("Last Updated: {0}", item.SyndicationItem.LastUpdatedTime.ToLocalTime())</li>
|
@*todo: (sebros) find a way to get the update date*@<li style="color:red">@T("Last Updated: {0}", DateTime.Now.ToLocalTime())</li>
|
||||||
<li> | @T("Author: {0}", item.SyndicationItem.Authors.Any() ? String.Join(", ", item.SyndicationItem.Authors.Select(a => a.Name)) : T("Unknown").Text)</li>
|
<li> | @T("Author: {0}", item.Authors)</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>}
|
</li>}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
@model Orchard.Packaging.ViewModels.PackagingModulesViewModel
|
@model Orchard.Packaging.ViewModels.PackagingExtensionsViewModel
|
||||||
@{
|
@{
|
||||||
Style.Require("PackagingAdmin");
|
Style.Require("PackagingAdmin");
|
||||||
Style.Require("ThemesAdmin");
|
Style.Require("ThemesAdmin");
|
||||||
|
Reference in New Issue
Block a user