mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-23 04:43:35 +08:00
Bug fix
Re-activate the slug constraint for pages. Published pages now should get their friendly slug on the front-end. --HG-- extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4045974
This commit is contained in:
@@ -88,6 +88,7 @@
|
||||
<Compile Include="Services\SlugConstraint.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Services\SlugConstraintUpdator.cs" />
|
||||
<Compile Include="ViewModels\PageCreateViewModel.cs" />
|
||||
<Compile Include="ViewModels\PageEditViewModel.cs" />
|
||||
<Compile Include="ViewModels\PagesViewModel.cs" />
|
||||
|
@@ -1,20 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Pages.Models;
|
||||
using Orchard.Core.Common.Records;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Services;
|
||||
using Orchard.Tasks.Scheduling;
|
||||
|
||||
namespace Orchard.Pages.Services {
|
||||
public class PageService : IPageService {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IPublishingTaskManager _publishingTaskManager;
|
||||
private readonly ISlugConstraint _slugConstraint;
|
||||
|
||||
public PageService(IContentManager contentManager, IPublishingTaskManager publishingTaskManager) {
|
||||
public PageService(IContentManager contentManager, IPublishingTaskManager publishingTaskManager, ISlugConstraint slugConstraint) {
|
||||
_contentManager = contentManager;
|
||||
_publishingTaskManager = publishingTaskManager;
|
||||
_slugConstraint = slugConstraint;
|
||||
}
|
||||
|
||||
public IEnumerable<Page> Get() {
|
||||
@@ -22,24 +24,16 @@ namespace Orchard.Pages.Services {
|
||||
}
|
||||
|
||||
public IEnumerable<Page> Get(PageStatus status) {
|
||||
IEnumerable<ContentItem> contentItems;
|
||||
|
||||
switch (status) {
|
||||
case PageStatus.All:
|
||||
contentItems = _contentManager.Query(VersionOptions.Latest, "page").List();
|
||||
break;
|
||||
return _contentManager.Query<Page>(VersionOptions.Latest).List();
|
||||
case PageStatus.Published:
|
||||
contentItems = _contentManager.Query(VersionOptions.Published, "page").List();
|
||||
break;
|
||||
return _contentManager.Query<Page>(VersionOptions.Published).List();
|
||||
case PageStatus.Offline:
|
||||
contentItems = _contentManager.Query(VersionOptions.Latest, "page").List().Where(ci => !ci.VersionRecord.Published);
|
||||
break;
|
||||
return _contentManager.Query<Page>(VersionOptions.Latest).Where<ContentPartVersionRecord>(ci => !ci.ContentItemVersionRecord.Published).List();
|
||||
default:
|
||||
contentItems = new List<Page>().Cast<ContentItem>();
|
||||
break;
|
||||
return Enumerable.Empty<Page>();
|
||||
}
|
||||
|
||||
return contentItems.Select(ci => ci.As<Page>());
|
||||
}
|
||||
|
||||
public Page Get(int id) {
|
||||
@@ -79,6 +73,7 @@ namespace Orchard.Pages.Services {
|
||||
public void Publish(Page page) {
|
||||
_publishingTaskManager.DeleteTasks(page.ContentItem);
|
||||
_contentManager.Publish(page.ContentItem);
|
||||
_slugConstraint.AddPublishedSlug(page.Slug);
|
||||
}
|
||||
|
||||
public void Publish(Page page, DateTime scheduledPublishUtc) {
|
||||
@@ -87,6 +82,7 @@ namespace Orchard.Pages.Services {
|
||||
|
||||
public void Unpublish(Page page) {
|
||||
_contentManager.Unpublish(page.ContentItem);
|
||||
_slugConstraint.RemovePublishedSlug(page.Slug);
|
||||
}
|
||||
|
||||
public DateTime? GetScheduledPublishUtc(Page page) {
|
||||
|
@@ -3,34 +3,72 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
using Orchard.Logging;
|
||||
|
||||
namespace Orchard.Pages.Services {
|
||||
public interface ISlugConstraint : IRouteConstraint, ISingletonDependency {
|
||||
void SetCurrentlyPublishedSlugs(IEnumerable<string> slugs);
|
||||
string LookupPublishedSlug(string slug);
|
||||
void AddPublishedSlug(string slug);
|
||||
void RemovePublishedSlug(string slug);
|
||||
}
|
||||
|
||||
public class SlugConstraint : ISlugConstraint {
|
||||
/// <summary>
|
||||
/// Singleton object, per Orchard Shell instance. We need to protect concurrent access to the
|
||||
/// dictionary.
|
||||
/// </summary>
|
||||
private readonly object _syncLock = new object();
|
||||
private IDictionary<string, string> _currentlyPublishedSlugs = new Dictionary<string, string>();
|
||||
|
||||
public SlugConstraint() {
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void SetCurrentlyPublishedSlugs(IEnumerable<string> values) {
|
||||
_currentlyPublishedSlugs = values
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(value => value, StringComparer.OrdinalIgnoreCase);
|
||||
// Make a copy to avoid performing potential lazy computation inside the lock
|
||||
string[] valuesArray = values.ToArray();
|
||||
|
||||
lock (_syncLock) {
|
||||
_currentlyPublishedSlugs = valuesArray
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(value => value, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
Logger.Debug("Publishing slugs: {0}", string.Join(", ", valuesArray));
|
||||
}
|
||||
|
||||
public string LookupPublishedSlug(string slug) {
|
||||
string actual;
|
||||
if (_currentlyPublishedSlugs.TryGetValue(slug, out actual))
|
||||
return actual;
|
||||
return slug;
|
||||
lock (_syncLock) {
|
||||
string actual;
|
||||
if (_currentlyPublishedSlugs.TryGetValue(slug, out actual))
|
||||
return actual;
|
||||
return slug;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPublishedSlug(string slug) {
|
||||
lock (_syncLock) {
|
||||
_currentlyPublishedSlugs.Add(slug, slug);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePublishedSlug(string slug) {
|
||||
lock (_syncLock) {
|
||||
_currentlyPublishedSlugs.Remove(slug);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
|
||||
object value;
|
||||
if (values.TryGetValue(parameterName, out value)) {
|
||||
var parameterValue = Convert.ToString(value);
|
||||
return _currentlyPublishedSlugs.ContainsKey(parameterValue);
|
||||
|
||||
lock (_syncLock) {
|
||||
return _currentlyPublishedSlugs.ContainsKey(parameterValue);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
using System.Linq;
|
||||
using Orchard.Extensions;
|
||||
using Orchard.Tasks;
|
||||
|
||||
namespace Orchard.Pages.Services {
|
||||
public class SlugConstraintUpdator : ExtensionManagerEvents, IBackgroundTask {
|
||||
private readonly ISlugConstraint _slugConstraint;
|
||||
private readonly IPageService _pageService;
|
||||
|
||||
public SlugConstraintUpdator(ISlugConstraint slugConstraint, IPageService pageService) {
|
||||
_slugConstraint = slugConstraint;
|
||||
_pageService = pageService;
|
||||
}
|
||||
|
||||
public override void Activated(ExtensionEventContext context) {
|
||||
if (context.Extension.Descriptor.Name == "Orchard.Pages") {
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public void Sweep() {
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void Refresh() {
|
||||
_slugConstraint.SetCurrentlyPublishedSlugs(_pageService.Get(PageStatus.Published).Select(page => page.Slug));
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@ using Orchard.Security.Permissions;
|
||||
|
||||
namespace Orchard.Roles {
|
||||
[UsedImplicitly]
|
||||
public class Extension : IExtensionManagerEvents {
|
||||
public class Extension : ExtensionManagerEvents {
|
||||
private readonly IRoleService _roleService;
|
||||
private readonly IEnumerable<IPermissionProvider> _permissionProviders;
|
||||
|
||||
@@ -18,10 +18,7 @@ namespace Orchard.Roles {
|
||||
_permissionProviders = permissionProviders;
|
||||
}
|
||||
|
||||
public void Enabling(ExtensionEventContext context) {
|
||||
}
|
||||
|
||||
public void Enabled(ExtensionEventContext context) {
|
||||
public override void Enabled(ExtensionEventContext context) {
|
||||
// when another package is being enabled, locate matching permission providers
|
||||
var providersForEnabledPackage =
|
||||
_permissionProviders.Where(x => x.PackageName == context.Extension.Descriptor.Name);
|
||||
@@ -47,11 +44,5 @@ namespace Orchard.Roles {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Disabling(ExtensionEventContext context) {
|
||||
}
|
||||
|
||||
public void Disabled(ExtensionEventContext context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -38,7 +38,8 @@ namespace Orchard.Environment {
|
||||
}
|
||||
|
||||
protected virtual void Initialize() {
|
||||
var shell = CreateShell();
|
||||
var shellContainer = CreateShellContainer();
|
||||
var shell = shellContainer.Resolve<IOrchardShell>();
|
||||
shell.Activate();
|
||||
_current = shell;
|
||||
|
||||
@@ -46,7 +47,14 @@ namespace Orchard.Environment {
|
||||
_controllerBuilder.SetControllerFactory(new OrchardControllerFactory());
|
||||
ServiceLocator.SetLocator(t => _containerProvider.RequestContainer.Resolve(t));
|
||||
|
||||
// fire off one-time install events on an alternate container
|
||||
// Fire off one-time install events on an alternate container
|
||||
HackInstallSimulation();
|
||||
|
||||
// Activate extensions inside shell container
|
||||
HackSimulateExtensionActivation(shellContainer);
|
||||
}
|
||||
|
||||
private void HackInstallSimulation() {
|
||||
var tempContainer = CreateShellContainer();
|
||||
var containerProvider = new FiniteContainerProvider(tempContainer);
|
||||
try {
|
||||
@@ -60,7 +68,19 @@ namespace Orchard.Environment {
|
||||
containerProvider.DisposeRequestContainer();
|
||||
tempContainer.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void HackSimulateExtensionActivation(IContainer shellContainer) {
|
||||
var containerProvider = new FiniteContainerProvider(shellContainer);
|
||||
try {
|
||||
var requestContainer = containerProvider.RequestContainer;
|
||||
|
||||
var hackInstallationGenerator = requestContainer.Resolve<IHackInstallationGenerator>();
|
||||
hackInstallationGenerator.GenerateActivateEvents();
|
||||
}
|
||||
finally {
|
||||
containerProvider.DisposeRequestContainer();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void EndRequest() {
|
||||
|
@@ -1,10 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Utility;
|
||||
|
||||
namespace Orchard.Extensions {
|
||||
public interface IHackInstallationGenerator : IDependency {
|
||||
void GenerateInstallEvents();
|
||||
void GenerateActivateEvents();
|
||||
}
|
||||
|
||||
public class HackInstallationGenerator : IHackInstallationGenerator {
|
||||
@@ -22,7 +24,6 @@ namespace Orchard.Extensions {
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void GenerateInstallEvents() {
|
||||
|
||||
//TEMP: this is really part of the extension manager's job. an extension
|
||||
// install event is being simulated here on each web app startup
|
||||
var enabled = new List<ExtensionEntry>();
|
||||
@@ -37,5 +38,18 @@ namespace Orchard.Extensions {
|
||||
_extensionEvents.Invoke(x => x.Enabled(context), Logger);
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateActivateEvents() {
|
||||
//TEMP: This is really part of the extension manager's job.
|
||||
var extensions = _extensionManager.ActiveExtensions().ToReadOnlyCollection();
|
||||
foreach (var extension in extensions) {
|
||||
var context = new ExtensionEventContext {
|
||||
Extension = extension,
|
||||
EnabledExtensions = extensions,
|
||||
};
|
||||
_extensionEvents.Invoke(x => x.Activating(context), Logger);
|
||||
_extensionEvents.Invoke(x => x.Activated(context), Logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,36 @@ namespace Orchard.Extensions {
|
||||
void Enabled(ExtensionEventContext context);
|
||||
void Disabling(ExtensionEventContext context);
|
||||
void Disabled(ExtensionEventContext context);
|
||||
void Activating(ExtensionEventContext context);
|
||||
void Activated(ExtensionEventContext context);
|
||||
void Deactivating(ExtensionEventContext context);
|
||||
void Deactivated(ExtensionEventContext context);
|
||||
}
|
||||
|
||||
public abstract class ExtensionManagerEvents : IExtensionManagerEvents {
|
||||
public virtual void Enabling(ExtensionEventContext context) {
|
||||
}
|
||||
|
||||
public virtual void Enabled(ExtensionEventContext context) {
|
||||
}
|
||||
|
||||
public virtual void Disabling(ExtensionEventContext context) {
|
||||
}
|
||||
|
||||
public virtual void Disabled(ExtensionEventContext context) {
|
||||
}
|
||||
|
||||
public virtual void Activating(ExtensionEventContext context) {
|
||||
}
|
||||
|
||||
public virtual void Activated(ExtensionEventContext context) {
|
||||
}
|
||||
|
||||
public virtual void Deactivating(ExtensionEventContext context) {
|
||||
}
|
||||
|
||||
public virtual void Deactivated(ExtensionEventContext context) {
|
||||
}
|
||||
}
|
||||
|
||||
public class ExtensionEventContext {
|
||||
|
@@ -23,5 +23,4 @@ namespace Orchard.Tasks {
|
||||
_tasks.Invoke(task => task.Sweep(), Logger);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ namespace Orchard.Tasks {
|
||||
_timer = new Timer();
|
||||
_timer.Elapsed += Elapsed;
|
||||
Logger = NullLogger.Instance;
|
||||
Interval = TimeSpan.FromSeconds(30);
|
||||
Interval = TimeSpan.FromMinutes(5);
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
Reference in New Issue
Block a user