mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-01-19 17:51:45 +08:00
Implementing Orchard.JobsQueue
This commit is contained in:
@@ -230,10 +230,6 @@
|
||||
<Project>{D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB}</Project>
|
||||
<Name>Orchard.Media</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Web\Modules\Orchard.Messaging\Orchard.Messaging.csproj">
|
||||
<Project>{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}</Project>
|
||||
<Name>Orchard.Messaging</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Web\Modules\Orchard.Modules\Orchard.Modules.csproj">
|
||||
<Project>{17F86780-9A1F-4AA1-86F1-875EEC2730C7}</Project>
|
||||
<Name>Orchard.Modules</Name>
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Email.Models;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Email.Services;
|
||||
using Orchard.Events;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Messaging.Services;
|
||||
using Orchard.Workflows.Models;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Email.Activities {
|
||||
public class EmailActivity : Task {
|
||||
private readonly IMessageQueueService _messageQueueManager;
|
||||
public interface IJobsQueueService : IEventHandler {
|
||||
void Enqueue(string message, object parameters, int priority);
|
||||
}
|
||||
|
||||
public EmailActivity(IMessageQueueService messageQueueManager) {
|
||||
_messageQueueManager = messageQueueManager;
|
||||
public class EmailActivity : Task {
|
||||
private readonly IMessageService _messageService;
|
||||
private readonly IJobsQueueService _jobsQueueService;
|
||||
|
||||
public EmailActivity(
|
||||
IMessageService messageService,
|
||||
IJobsQueueService jobsQueueService
|
||||
) {
|
||||
_messageService = messageService;
|
||||
_jobsQueueService = jobsQueueService;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext workflowContext, ActivityContext activityContext) {
|
||||
return new[] { T("Queued") };
|
||||
return new[] { T("Done") };
|
||||
}
|
||||
|
||||
public override string Form {
|
||||
@@ -41,24 +49,27 @@ namespace Orchard.Email.Activities {
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalizedString> Execute(WorkflowContext workflowContext, ActivityContext activityContext) {
|
||||
var priority = activityContext.GetState<int>("Priority");
|
||||
|
||||
var body = activityContext.GetState<string>("Body");
|
||||
var subject = activityContext.GetState<string>("Subject");
|
||||
var recipients = Split(activityContext.GetState<string>("RecipientAddress"));
|
||||
var payload = new EmailMessage {
|
||||
Subject = subject,
|
||||
Body = body,
|
||||
Recipients = recipients
|
||||
var recipients = activityContext.GetState<string>("Recipients");
|
||||
|
||||
var parameters = new Dictionary<string, object> {
|
||||
{"Subject", subject},
|
||||
{"Body", body},
|
||||
{"Recipients", recipients}
|
||||
};
|
||||
|
||||
_messageQueueManager.Enqueue(SmtpMessageChannel.MessageType, payload, priority);
|
||||
var queued = activityContext.GetState<bool>("Queued");
|
||||
|
||||
yield return T("Queued");
|
||||
}
|
||||
if (!queued) {
|
||||
_messageService.Send(SmtpMessageChannel.MessageType, parameters);
|
||||
}
|
||||
else {
|
||||
var priority = activityContext.GetState<int>("Priority");
|
||||
_jobsQueueService.Enqueue("IMessageService.Send", new { type = SmtpMessageChannel.MessageType, parameters = parameters }, priority);
|
||||
}
|
||||
|
||||
private static string[] Split(string value) {
|
||||
return !String.IsNullOrWhiteSpace(value) ? value.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) : new string[0];
|
||||
yield return T("Done");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,35 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Environment.Features;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Email.Forms {
|
||||
public class EmailForm : Component, IFormProvider {
|
||||
private readonly IFeatureManager _featureManager;
|
||||
protected dynamic New { get; set; }
|
||||
|
||||
public EmailForm(IShapeFactory shapeFactory) {
|
||||
public EmailForm(IShapeFactory shapeFactory,
|
||||
IFeatureManager featureManager) {
|
||||
_featureManager = featureManager;
|
||||
New = shapeFactory;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
Func<IShapeFactory, dynamic> formFactory =
|
||||
shape => {
|
||||
var jobsQueueEnabled = _featureManager.GetEnabledFeatures().Any(x => x.Id == "Orchard.JobsQueue");
|
||||
|
||||
var form = New.Form(
|
||||
Id: "EmailActivity",
|
||||
_Type: New.FieldSet(
|
||||
Title: T("Send to"),
|
||||
_RecipientAddress: New.Textbox(
|
||||
Id: "recipient-address",
|
||||
Name: "RecipientAddress",
|
||||
Title: T("Email Address"),
|
||||
_Recipients: New.Textbox(
|
||||
Id: "recipients",
|
||||
Name: "Recipients",
|
||||
Title: T("Email Addresses"),
|
||||
Description: T("Specify a comma-separated list of recipient email addresses. To include a display name, use the following format: John Doe <john.doe@outlook.com>"),
|
||||
Classes: new[] {"large", "text", "tokenized"}),
|
||||
_Subject: New.Textbox(
|
||||
@@ -34,17 +41,27 @@ namespace Orchard.Email.Forms {
|
||||
Id: "Body", Name: "Body",
|
||||
Title: T("Body"),
|
||||
Description: T("The body of the email message."),
|
||||
Classes: new[] {"tokenized"}),
|
||||
_Priority: New.SelectList(
|
||||
Classes: new[] {"tokenized"})
|
||||
));
|
||||
|
||||
if (jobsQueueEnabled) {
|
||||
form._Type._Queued(New.Checkbox(
|
||||
Id: "Queued", Name: "Queued",
|
||||
Title: T("Queued"),
|
||||
Checked: false, Value: "true",
|
||||
Description: T("Check send it as a queued job.")));
|
||||
|
||||
form._Type._Priority(New.SelectList(
|
||||
Id: "priority",
|
||||
Name: "Priority",
|
||||
Title: T("Priority"),
|
||||
Description: ("The priority of this message.")
|
||||
)));
|
||||
));
|
||||
|
||||
form._Type._Priority.Add(new SelectListItem { Value = "-50", Text = T("Low").Text });
|
||||
form._Type._Priority.Add(new SelectListItem { Value = "0", Text = T("Normal").Text });
|
||||
form._Type._Priority.Add(new SelectListItem { Value = "50", Text = T("High").Text });
|
||||
form._Type._Priority.Add(new SelectListItem { Value = "-50", Text = T("Low").Text });
|
||||
form._Type._Priority.Add(new SelectListItem { Value = "0", Text = T("Normal").Text });
|
||||
form._Type._Priority.Add(new SelectListItem { Value = "50", Text = T("High").Text });
|
||||
}
|
||||
|
||||
return form;
|
||||
};
|
||||
@@ -63,12 +80,12 @@ namespace Orchard.Email.Forms {
|
||||
public void Validating(ValidatingContext context) {
|
||||
if (context.FormName != "EmailActivity") return;
|
||||
|
||||
var recipientAddress = context.ValueProvider.GetValue("RecipientAddress").AttemptedValue;
|
||||
var recipients = context.ValueProvider.GetValue("Recipients").AttemptedValue;
|
||||
var subject = context.ValueProvider.GetValue("Subject").AttemptedValue;
|
||||
var body = context.ValueProvider.GetValue("Body").AttemptedValue;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(recipientAddress)) {
|
||||
context.ModelState.AddModelError("RecipientAddress", T("You must specify at least one recipient.").Text);
|
||||
if (String.IsNullOrWhiteSpace(recipients)) {
|
||||
context.ModelState.AddModelError("Recipients", T("You must specify at least one recipient.").Text);
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(subject)) {
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
public class EmailMessage {
|
||||
public string Subject { get; set; }
|
||||
public string Body { get; set; }
|
||||
public string[] Recipients { get; set; }
|
||||
public string Recipients { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,4 @@ Features:
|
||||
Name: Email Messaging
|
||||
FeatureDescription: Email Messaging services.
|
||||
Category: Messaging
|
||||
Dependencies: Orchard.Messaging, Orchard.Workflows
|
||||
Dependencies: Orchard.Workflows
|
||||
|
||||
@@ -106,10 +106,6 @@
|
||||
<Project>{642a49d7-8752-4177-80d6-bfbbcfad3de0}</Project>
|
||||
<Name>Orchard.Forms</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Messaging\Orchard.Messaging.csproj">
|
||||
<Project>{085948ff-0e9b-4a9a-b564-f8b8b4bdddbc}</Project>
|
||||
<Name>Orchard.Messaging</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Workflows\Orchard.Workflows.csproj">
|
||||
<Project>{7059493c-8251-4764-9c1e-2368b8b485bc}</Project>
|
||||
<Name>Orchard.Workflows</Name>
|
||||
|
||||
@@ -12,10 +12,9 @@ namespace Orchard.Email.Services {
|
||||
public MessageChannelSelectorResult GetChannel(string messageType, object payload) {
|
||||
if (messageType == "Email") {
|
||||
var workContext = _workContextAccessor.GetContext();
|
||||
var channel = workContext.Resolve<ISmtpChannel>();
|
||||
return new MessageChannelSelectorResult {
|
||||
Priority = 50,
|
||||
MessageChannel = channel
|
||||
MessageChannel = () => workContext.Resolve<ISmtpChannel>()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
using System.Web.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Logging;
|
||||
@@ -35,17 +37,18 @@ namespace Orchard.Email.Services {
|
||||
_smtpClientField.Value.Dispose();
|
||||
}
|
||||
|
||||
public void Process(string payload) {
|
||||
public void Process(IDictionary<string, object> parameters) {
|
||||
|
||||
|
||||
if (!_smtpSettings.IsValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var emailMessage = JsonConvert.DeserializeObject<EmailMessage>(payload);
|
||||
if (emailMessage == null) {
|
||||
return;
|
||||
}
|
||||
var emailMessage = new EmailMessage {
|
||||
Body = parameters["Body"] as string,
|
||||
Subject = parameters["Subject"] as string,
|
||||
Recipients = parameters["Recipients"] as string
|
||||
};
|
||||
|
||||
if (emailMessage.Recipients.Length == 0) {
|
||||
Logger.Error("Email message doesn't have any recipient");
|
||||
@@ -65,7 +68,7 @@ namespace Orchard.Email.Services {
|
||||
};
|
||||
|
||||
try {
|
||||
foreach (var recipient in emailMessage.Recipients) {
|
||||
foreach (var recipient in emailMessage.Recipients.Split(new [] {',', ';'}, StringSplitOptions.RemoveEmptyEntries)) {
|
||||
mailMessage.To.Add(new MailAddress(recipient));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@* Override this template to alter the email messages sent by the Smtp channel *@
|
||||
<p style="font-family: 'Segoe UI', Arial, helvetica, sans-serif; font-size: 10pt; ">
|
||||
<div style="font-family: 'Segoe UI', Arial, helvetica, sans-serif; font-size: 10pt; ">
|
||||
@Model.Content
|
||||
</p>
|
||||
</div>
|
||||
@@ -1,15 +1,17 @@
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Messaging {
|
||||
namespace Orchard.JobsQueue {
|
||||
[OrchardFeature("Orchard.JobsQueue.UI")]
|
||||
public class AdminMenu : Component, INavigationProvider {
|
||||
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder
|
||||
.AddImageSet("messaging")
|
||||
.Add(T("Message Queue"), "15.0", item => {
|
||||
item.Action("List", "Admin", new { area = "Orchard.Messaging" });
|
||||
.AddImageSet("jobsqueue")
|
||||
.Add(T("Jobs Queue"), "15.0", item => {
|
||||
item.Action("List", "Admin", new { area = "Orchard.JobsQueue" });
|
||||
item.LinkToFirstChild(false);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Localization;
|
||||
using Orchard.JobsQueue.Models;
|
||||
using Orchard.JobsQueue.Services;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.UI.Admin;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.JobsQueue.Controllers {
|
||||
[Admin]
|
||||
[OrchardFeature("Orchard.JobsQueue.UI")]
|
||||
public class AdminController : Controller {
|
||||
private readonly IJobsQueueManager _jobsQueueManager;
|
||||
private readonly IOrchardServices _services;
|
||||
private readonly IJobsQueueProcessor _jobsQueueProcessor;
|
||||
|
||||
public AdminController(
|
||||
IJobsQueueManager jobsQueueManager,
|
||||
IShapeFactory shapeFactory,
|
||||
IOrchardServices services,
|
||||
IJobsQueueProcessor jobsQueueProcessor) {
|
||||
_jobsQueueManager = jobsQueueManager;
|
||||
_services = services;
|
||||
_jobsQueueProcessor = jobsQueueProcessor;
|
||||
New = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public dynamic New { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
public ActionResult Details(int id, string returnUrl) {
|
||||
var job = _jobsQueueManager.GetJob(id);
|
||||
|
||||
if (!Url.IsLocalUrl(returnUrl))
|
||||
returnUrl = Url.Action("List");
|
||||
|
||||
var model = New.ViewModel().Job(job).ReturnUrl(returnUrl);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public ActionResult List(PagerParameters pagerParameters) {
|
||||
var pager = new Pager(_services.WorkContext.CurrentSite, pagerParameters);
|
||||
|
||||
var jobsCount = _jobsQueueManager.GetJobsCount();
|
||||
var jobs = _jobsQueueManager.GetJobs(pager.GetStartIndex(), pager.PageSize).ToList();
|
||||
var model = _services.New.ViewModel()
|
||||
.Pager(_services.New.Pager(pager).TotalItemCount(jobsCount))
|
||||
.JobsQueueStatus(_services.WorkContext.CurrentSite.As<JobsQueueSettingsPart>().Status)
|
||||
.Jobs(jobs)
|
||||
;
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("List")]
|
||||
[FormValueRequired("submit.Filter")]
|
||||
public ActionResult Filter() {
|
||||
return RedirectToAction("List");
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("List")]
|
||||
[FormValueRequired("submit.Resume")]
|
||||
public ActionResult Resume() {
|
||||
_jobsQueueManager.Resume();
|
||||
_services.Notifier.Information(T("The queue has been resumed."));
|
||||
return RedirectToAction("List");
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("List")]
|
||||
[FormValueRequired("submit.Pause")]
|
||||
public ActionResult Pause() {
|
||||
_jobsQueueManager.Pause();
|
||||
_services.Notifier.Information(T("The queue has been paused."));
|
||||
return RedirectToAction("List");
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("List")]
|
||||
[FormValueRequired("submit.Process")]
|
||||
public ActionResult Process() {
|
||||
_jobsQueueProcessor.ProcessQueue();
|
||||
_services.Notifier.Information(T("Processing has started."));
|
||||
return RedirectToAction("List");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.Localization;
|
||||
using Orchard.JobsQueue.Models;
|
||||
using Orchard.JobsQueue.ViewModels;
|
||||
using Orchard.Messaging.Models;
|
||||
|
||||
namespace Orchard.JobsQueue.Drivers {
|
||||
[UsedImplicitly]
|
||||
public class JobsQueueSettingsPartDriver : ContentPartDriver<JobsQueueSettingsPart> {
|
||||
private const string TemplateName = "Parts/JobsQueueSettings";
|
||||
public IOrchardServices Services { get; set; }
|
||||
|
||||
public JobsQueueSettingsPartDriver(IOrchardServices services) {
|
||||
Services = services;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override string Prefix { get { return "JobsQueueSettings"; } }
|
||||
|
||||
protected override DriverResult Editor(JobsQueueSettingsPart part, dynamic shapeHelper) {
|
||||
|
||||
var model = new JobsQueueSettingsPartViewModel {
|
||||
JobsQueueSettings = part
|
||||
};
|
||||
|
||||
return ContentShape("Parts_JobsQueueSettings_Edit", () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(JobsQueueSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
|
||||
var model = new JobsQueueSettingsPartViewModel {
|
||||
JobsQueueSettings = part
|
||||
};
|
||||
|
||||
updater.TryUpdateModel(model, Prefix, null, null);
|
||||
|
||||
return ContentShape("Parts_JobsQueueSettings_Edit", () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Orchard.Messaging.Extensions {
|
||||
namespace Orchard.JobsQueue.Extensions {
|
||||
public static class StringExtensions {
|
||||
public static string TrimSafe(this string value) {
|
||||
return value != null ? value.Trim() : null;
|
||||
@@ -0,0 +1,12 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.JobsQueue.Models;
|
||||
|
||||
namespace Orchard.JobsQueue.Handlers {
|
||||
[UsedImplicitly]
|
||||
public class JobsQueueSettingsPartHandler : ContentHandler {
|
||||
public JobsQueueSettingsPartHandler() {
|
||||
Filters.Add(new ActivatingFilter<JobsQueueSettingsPart>("Site"));
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/Orchard.Web/Modules/Orchard.JobsQueue/Migrations.cs
Normal file
19
src/Orchard.Web/Modules/Orchard.JobsQueue/Migrations.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using Orchard.Data.Migration;
|
||||
|
||||
namespace Orchard.JobsQueue {
|
||||
public class Migrations : DataMigrationImpl {
|
||||
|
||||
public int Create() {
|
||||
SchemaBuilder.CreateTable("QueuedJobRecord", table => table
|
||||
.Column<int>("Id", c => c.Identity().PrimaryKey())
|
||||
.Column<string>("Message", c => c.WithLength(64))
|
||||
.Column<string>("Parameters", c => c.Unlimited())
|
||||
.Column<int>("Priority", c => c.WithDefault(0))
|
||||
.Column<DateTime>("CreatedUtc")
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.Messaging.Models {
|
||||
public class MessageSettingsPart : ContentPart {
|
||||
|
||||
public MessageQueueStatus Status {
|
||||
get { return this.Retrieve(x => x.Status); }
|
||||
set { this.Store(x => x.Status, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.JobsQueue.Models {
|
||||
public class JobsQueueSettingsPart : ContentPart {
|
||||
|
||||
public JobsQueueStatus Status {
|
||||
get { return this.Retrieve(x => x.Status); }
|
||||
set { this.Store(x => x.Status, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Orchard.JobsQueue.Models {
|
||||
public enum JobsQueueStatus {
|
||||
Idle,
|
||||
Paused
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.JobsQueue.Models {
|
||||
public class QueuedJobRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual int Priority { get; set; }
|
||||
public virtual string Message { get; set; }
|
||||
|
||||
[StringLengthMax]
|
||||
public virtual string Parameters { get; set; }
|
||||
|
||||
public virtual DateTime CreatedUtc { get; set; }
|
||||
}
|
||||
}
|
||||
18
src/Orchard.Web/Modules/Orchard.JobsQueue/Module.txt
Normal file
18
src/Orchard.Web/Modules/Orchard.JobsQueue/Module.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
Name: Jobs Queue
|
||||
AntiForgery: enabled
|
||||
Author: The Orchard Team
|
||||
Website: http://orchardproject.net
|
||||
Version: 1.7.2
|
||||
OrchardVersion: 1.7.2
|
||||
Description: This module provides a jobs queue to process jobs asynchronously.
|
||||
Features:
|
||||
Orchard.JobsQueue:
|
||||
Description: Provides a jobs queue to process jobs asynchronously.
|
||||
Category: Developer
|
||||
Dependencies: Settings, Orchard.TaskLease
|
||||
Name: Jobs Queue
|
||||
Orchard.JobsQueue.UI:
|
||||
Description: Provides a UI to manage queued jobs.
|
||||
Category: Developer
|
||||
Dependencies: Orchard.JobsQueue
|
||||
Name: Jobs Queue UI
|
||||
@@ -1,185 +1,173 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>
|
||||
</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}</ProjectGuid>
|
||||
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Orchard.Messaging</RootNamespace>
|
||||
<AssemblyName>Orchard.Messaging</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<OldToolsVersion>4.0</OldToolsVersion>
|
||||
<TargetFrameworkProfile />
|
||||
<UseIISExpress>false</UseIISExpress>
|
||||
<IISExpressSSLPort />
|
||||
<IISExpressAnonymousAuthentication />
|
||||
<IISExpressWindowsAuthentication />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\newtonsoft.json\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.ApplicationServices" />
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Entity" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.XML" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Extensions\StringExtensions.cs" />
|
||||
<Compile Include="Services\DefaultMessageService.cs" />
|
||||
<Compile Include="Services\IMessageChannelSelector.cs" />
|
||||
<Compile Include="Services\IMessageQueueProcessor.cs" />
|
||||
<Compile Include="Services\IMessageQueueService.cs" />
|
||||
<Compile Include="Services\IMessageService.cs" />
|
||||
<Compile Include="Services\NullMessageChannelSelector.cs" />
|
||||
<Compile Include="ViewModels\MessagesFilter.cs" />
|
||||
<Compile Include="Drivers\MessageSettingsPartDriver.cs" />
|
||||
<Compile Include="Handlers\MessageSettingsPartHandler.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Services\MessageQueueProcessor.cs" />
|
||||
<Compile Include="Services\MessageQueueBackgroundTask.cs" />
|
||||
<Compile Include="Services\IMessageChannel.cs" />
|
||||
<Compile Include="Models\MessageQueueStatus.cs" />
|
||||
<Compile Include="Models\QueuedMessageRecord.cs" />
|
||||
<Compile Include="Models\MessageSettingsPart.cs" />
|
||||
<Compile Include="Models\QueuedMessageStatus.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\MessageQueueService.cs" />
|
||||
<Compile Include="ViewModels\MessageSettingsPartViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Module.txt" />
|
||||
<Content Include="Placement.info" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Styles\admin-messaging.min.css">
|
||||
<DependentUpon>admin-messaging.css</DependentUpon>
|
||||
</Content>
|
||||
<Content Include="Styles\Images\queue-controls.png" />
|
||||
<Content Include="Styles\Images\message-status.png" />
|
||||
<Content Include="Styles\admin-messaging.css" />
|
||||
<Content Include="Styles\menu.messaging-admin.css" />
|
||||
<Content Include="Styles\Images\menu.messaging.png" />
|
||||
<Content Include="Styles\workflows-activity-sendmessage.css" />
|
||||
<Content Include="Views\EditorTemplates\Parts\MessageSettings.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
|
||||
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||
<Name>Orchard.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
|
||||
<Project>{9916839c-39fc-4ceb-a5af-89ca7e87119f}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Forms\Orchard.Forms.csproj">
|
||||
<Project>{642a49d7-8752-4177-80d6-bfbbcfad3de0}</Project>
|
||||
<Name>Orchard.Forms</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.TaskLease\Orchard.TaskLease.csproj">
|
||||
<Project>{3F72A4E9-7B72-4260-B010-C16EC54F9BAF}</Project>
|
||||
<Name>Orchard.TaskLease</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Workflows\Orchard.Workflows.csproj">
|
||||
<Project>{7059493c-8251-4764-9c1e-2368b8b485bc}</Project>
|
||||
<Name>Orchard.Workflows</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\EditorTemplates\MessageQueueViewModel.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\List.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Details.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Styles\Web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Scripts\Web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>29609</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>
|
||||
</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}</ProjectGuid>
|
||||
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Orchard.JobsQueue</RootNamespace>
|
||||
<AssemblyName>Orchard.JobsQueue</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<OldToolsVersion>4.0</OldToolsVersion>
|
||||
<TargetFrameworkProfile />
|
||||
<UseIISExpress>false</UseIISExpress>
|
||||
<IISExpressSSLPort />
|
||||
<IISExpressAnonymousAuthentication />
|
||||
<IISExpressWindowsAuthentication />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\newtonsoft.json\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.ApplicationServices" />
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Entity" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.XML" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Extensions\StringExtensions.cs" />
|
||||
<Compile Include="Services\IJobsQueueProcessor.cs" />
|
||||
<Compile Include="Services\IJobsQueueManager.cs" />
|
||||
<Compile Include="Services\IJobsQueueService.cs" />
|
||||
<Compile Include="Drivers\JobsQueueSettingsPartDriver.cs" />
|
||||
<Compile Include="Handlers\JobsQueueSettingsPartHandler.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Services\JobsQueueProcessor.cs" />
|
||||
<Compile Include="Services\JobsQueueBackgroundTask.cs" />
|
||||
<Compile Include="Models\JobsQueueStatus.cs" />
|
||||
<Compile Include="Models\QueuedJobRecord.cs" />
|
||||
<Compile Include="Models\JobsQueueSettingsPart.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\JobsQueueManager.cs" />
|
||||
<Compile Include="Services\JobsQueueService.cs" />
|
||||
<Compile Include="ViewModels\QueuedJobsSettingsPartViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Module.txt" />
|
||||
<Content Include="Placement.info" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Styles\Images\menu.jobsqueue.png" />
|
||||
<Content Include="Styles\Images\queue-controls.png" />
|
||||
<Content Include="Styles\admin-jobsqueue.css" />
|
||||
<Content Include="Styles\menu.jobsqueue-admin.css" />
|
||||
<Content Include="Styles\workflows-activity-sendmessage.css" />
|
||||
<Content Include="Views\EditorTemplates\Parts\JobsQueueSettings.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
|
||||
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||
<Name>Orchard.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
|
||||
<Project>{9916839c-39fc-4ceb-a5af-89ca7e87119f}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Forms\Orchard.Forms.csproj">
|
||||
<Project>{642a49d7-8752-4177-80d6-bfbbcfad3de0}</Project>
|
||||
<Name>Orchard.Forms</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.TaskLease\Orchard.TaskLease.csproj">
|
||||
<Project>{3F72A4E9-7B72-4260-B010-C16EC54F9BAF}</Project>
|
||||
<Name>Orchard.TaskLease</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Workflows\Orchard.Workflows.csproj">
|
||||
<Project>{7059493c-8251-4764-9c1e-2368b8b485bc}</Project>
|
||||
<Name>Orchard.Workflows</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\List.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Details.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Styles\Web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Scripts\Web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>29609</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -1,3 +1,3 @@
|
||||
<Placement>
|
||||
<Place Parts_MessageSettings_Edit="Content:10"/>
|
||||
<Placement>
|
||||
<Place Parts_MessageSettings_Edit="Content:10"/>
|
||||
</Placement>
|
||||
@@ -1,36 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Orchard.Messaging")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyProduct("Orchard")]
|
||||
[assembly: AssemblyCopyright("Copyright © Outercurve Foundation 2009")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("c39c2970-b7a5-466b-8dcb-0fc571a7d8c7")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.7.2")]
|
||||
[assembly: AssemblyFileVersion("1.7.2")]
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Orchard.JobsQueue")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyProduct("Orchard")]
|
||||
[assembly: AssemblyCopyright("Copyright © Outercurve Foundation 2009")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("c39c2970-b7a5-466b-8dcb-0fc571a7d8c7")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.7.2")]
|
||||
[assembly: AssemblyFileVersion("1.7.2")]
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.JobsQueue.Models;
|
||||
|
||||
namespace Orchard.JobsQueue.Services {
|
||||
public interface IJobsQueueManager : IDependency {
|
||||
QueuedJobRecord GetJob(int id);
|
||||
void Delete(QueuedJobRecord job);
|
||||
IEnumerable<QueuedJobRecord> GetJobs(int startIndex, int count);
|
||||
int GetJobsCount();
|
||||
void Resume();
|
||||
void Pause();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Orchard.JobsQueue.Services {
|
||||
public interface IJobsQueueProcessor : ISingletonDependency {
|
||||
void ProcessQueue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Orchard.Events;
|
||||
using Orchard.JobsQueue.Models;
|
||||
|
||||
namespace Orchard.JobsQueue.Services {
|
||||
public interface IJobsQueueService : IEventHandler {
|
||||
QueuedJobRecord Enqueue(string message, object parameters, int priority);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Orchard.Tasks;
|
||||
|
||||
namespace Orchard.JobsQueue.Services {
|
||||
public class JobsQueueBackgroundTask : Component, IBackgroundTask {
|
||||
private readonly IJobsQueueProcessor _jobsQueueProcessor;
|
||||
public JobsQueueBackgroundTask(IJobsQueueProcessor jobsQueueProcessor) {
|
||||
_jobsQueueProcessor = jobsQueueProcessor;
|
||||
}
|
||||
|
||||
public void Sweep() {
|
||||
_jobsQueueProcessor.ProcessQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Data;
|
||||
using Orchard.JobsQueue.Models;
|
||||
using Orchard.Settings;
|
||||
|
||||
namespace Orchard.JobsQueue.Services {
|
||||
public class JobsQueueManager : IJobsQueueManager {
|
||||
private readonly IRepository<QueuedJobRecord> _jobRepository;
|
||||
private readonly JobsQueueSettingsPart _jobsQueueSettingsPart;
|
||||
|
||||
public JobsQueueManager(
|
||||
IRepository<QueuedJobRecord> jobRepository,
|
||||
ISiteService siteService) {
|
||||
_jobRepository = jobRepository;
|
||||
_jobsQueueSettingsPart = siteService.GetSiteSettings().As<JobsQueueSettingsPart>();
|
||||
}
|
||||
|
||||
public void Resume() {
|
||||
_jobsQueueSettingsPart.Status = JobsQueueStatus.Idle;
|
||||
}
|
||||
|
||||
public void Pause() {
|
||||
_jobsQueueSettingsPart.Status = JobsQueueStatus.Paused;
|
||||
}
|
||||
|
||||
public int GetJobsCount() {
|
||||
return GetMessagesQuery().Count();
|
||||
}
|
||||
|
||||
public IEnumerable<QueuedJobRecord> GetJobs(int startIndex, int pageSize) {
|
||||
return GetMessagesQuery()
|
||||
.Skip(startIndex)
|
||||
.Take(pageSize)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public QueuedJobRecord GetJob(int id) {
|
||||
return _jobRepository.Get(id);
|
||||
}
|
||||
|
||||
private IQueryable<QueuedJobRecord> GetMessagesQuery() {
|
||||
var query = _jobRepository
|
||||
.Table
|
||||
.OrderByDescending(x => x.Priority)
|
||||
.ThenByDescending(x => x.CreatedUtc);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
public void Delete(QueuedJobRecord job) {
|
||||
_jobRepository.Delete(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Events;
|
||||
using Orchard.Logging;
|
||||
using Orchard.JobsQueue.Models;
|
||||
using Orchard.Services;
|
||||
using Orchard.TaskLease.Services;
|
||||
|
||||
namespace Orchard.JobsQueue.Services {
|
||||
public class JobsQueueProcessor : IJobsQueueProcessor {
|
||||
private readonly Work<IJobsQueueManager> _jobsQueueManager;
|
||||
private readonly Work<IClock> _clock;
|
||||
private readonly Work<ITaskLeaseService> _taskLeaseService;
|
||||
private readonly IEventBus _eventBus;
|
||||
private readonly ReaderWriterLockSlim _rwl = new ReaderWriterLockSlim();
|
||||
|
||||
public JobsQueueProcessor(
|
||||
Work<IClock> clock,
|
||||
Work<IJobsQueueManager> jobsQueueManager,
|
||||
Work<ITaskLeaseService> taskLeaseService,
|
||||
IEventBus eventBus) {
|
||||
_clock = clock;
|
||||
_jobsQueueManager = jobsQueueManager;
|
||||
_taskLeaseService = taskLeaseService;
|
||||
_eventBus = eventBus;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
public void ProcessQueue() {
|
||||
// prevent two threads on the same machine to process the message queue
|
||||
if (_rwl.TryEnterWriteLock(0)) {
|
||||
try {
|
||||
_taskLeaseService.Value.Acquire("JobsQueueProcessor", _clock.Value.UtcNow.AddMinutes(5));
|
||||
IEnumerable<QueuedJobRecord> messages;
|
||||
|
||||
while ((messages = _jobsQueueManager.Value.GetJobs(0, 10).ToArray()).Any()) {
|
||||
foreach (var message in messages.AsParallel()) {
|
||||
ProcessMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
_rwl.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessMessage(QueuedJobRecord job) {
|
||||
|
||||
Logger.Debug("Processing job {0}.", job.Id);
|
||||
|
||||
try {
|
||||
var payload = JObject.Parse(job.Parameters);
|
||||
var parameters = payload.ToDictionary();
|
||||
|
||||
_eventBus.Notify(job.Message, parameters);
|
||||
|
||||
Logger.Debug("Processed job Id {0}.", job.Id);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Logger.Error(e, "An unexpected error while processing job {0}. Error message: {1}.", job.Id, e);
|
||||
}
|
||||
finally {
|
||||
_jobsQueueManager.Value.Delete(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class JObjectExtensions {
|
||||
|
||||
public static IDictionary<string, object> ToDictionary(this JObject jObject) {
|
||||
return (IDictionary<string, object>)Convert(jObject);
|
||||
}
|
||||
|
||||
private static object Convert(this JToken jToken) {
|
||||
if (jToken == null) {
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
switch (jToken.Type) {
|
||||
case JTokenType.Array:
|
||||
var array = jToken as JArray;
|
||||
return array.Values().Select(Convert).ToArray();
|
||||
case JTokenType.Object:
|
||||
var obj = jToken as JObject;
|
||||
return obj
|
||||
.Properties()
|
||||
.ToDictionary(property => property.Name, property => Convert(property.Value));
|
||||
default:
|
||||
return jToken.ToObject<object>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Newtonsoft.Json;
|
||||
using Orchard.Data;
|
||||
using Orchard.JobsQueue.Models;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.JobsQueue.Services {
|
||||
public class JobsQueueService : IJobsQueueService {
|
||||
private readonly IClock _clock;
|
||||
private readonly IRepository<QueuedJobRecord> _messageRepository;
|
||||
|
||||
public JobsQueueService(
|
||||
IClock clock,
|
||||
IRepository<QueuedJobRecord> messageRepository) {
|
||||
_clock = clock;
|
||||
_messageRepository = messageRepository;
|
||||
}
|
||||
|
||||
public QueuedJobRecord Enqueue(string message, object parameters, int priority) {
|
||||
|
||||
var queuedJob = new QueuedJobRecord {
|
||||
Parameters = JsonConvert.SerializeObject(parameters),
|
||||
Message = message,
|
||||
CreatedUtc = _clock.UtcNow,
|
||||
};
|
||||
|
||||
_messageRepository.Create(queuedJob);
|
||||
|
||||
return queuedJob;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1,27 +1,4 @@
|
||||
.message-status {
|
||||
background: url('./images/message-status.png') no-repeat -17px -17px;
|
||||
padding-left: 25px;
|
||||
line-height: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.message-status.pending {
|
||||
background-position: -17px -17px;
|
||||
}
|
||||
|
||||
.message-status.sending {
|
||||
background-position: -17px -67px;
|
||||
}
|
||||
|
||||
.message-status.sent {
|
||||
background-position: -17px -116px;
|
||||
}
|
||||
|
||||
.message-status.faulted {
|
||||
background-position: -17px -166px;
|
||||
}
|
||||
|
||||
fieldset.summary ul li:after {
|
||||
fieldset.summary ul li:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
@@ -0,0 +1,7 @@
|
||||
.navicon-jobs-queue {
|
||||
background-image: url(./images/menu.jobsqueue.png) !important;
|
||||
}
|
||||
|
||||
.navicon-jobs-queue:hover {
|
||||
background-position: 0 -30px !important;
|
||||
}
|
||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using Autofac;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Orchard.JobsQueue.Models;
|
||||
using Orchard.JobsQueue.Services;
|
||||
using Orchard.Messaging.Models;
|
||||
using Orchard.Messaging.Services;
|
||||
using Orchard.Tests;
|
||||
@@ -10,22 +12,22 @@ using Orchard.Tests;
|
||||
namespace Orchard.Messaging.Tests {
|
||||
[TestFixture]
|
||||
public class MessageQueueProcessorTests : DatabaseEnabledTestsBase {
|
||||
private List<QueuedMessageRecord> _messages;
|
||||
private List<QueuedJobRecord> _messages;
|
||||
|
||||
protected override IEnumerable<Type> DatabaseTypes {
|
||||
get {
|
||||
yield return typeof(QueuedMessageRecord);
|
||||
yield return typeof(QueuedJobRecord);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Register(ContainerBuilder builder) {
|
||||
var messageManagerMock = new Mock<IMessageQueueService>();
|
||||
var messageManagerMock = new Mock<IJobsQueueService>();
|
||||
|
||||
builder.RegisterInstance(messageManagerMock.Object);
|
||||
builder.RegisterType<MessageQueueProcessor>().As<IMessageQueueProcessor>();
|
||||
builder.RegisterType<JobsQueueProcessor>().As<IJobsQueueProcessor>();
|
||||
builder.RegisterType<StubMessageChannel>().As<IMessageChannel>();
|
||||
|
||||
_messages = new List<QueuedMessageRecord> {
|
||||
_messages = new List<QueuedJobRecord> {
|
||||
CreateMessage("Message 1"),
|
||||
CreateMessage("Message 2")
|
||||
};
|
||||
@@ -33,7 +35,7 @@ namespace Orchard.Messaging.Tests {
|
||||
messageManagerMock
|
||||
.Setup(x => x.Enqueue(It.IsAny<string>(), It.IsAny<string>(), 0))
|
||||
.Callback(() => _clock.Advance(TimeSpan.FromSeconds(1)))
|
||||
.Returns(new QueuedMessageRecord ());
|
||||
.Returns(new QueuedJobRecord ());
|
||||
//messageManagerMock.Setup(x => x.EnterProcessingStatus()).Callback(() => {
|
||||
// queue.Record.Status = MessageQueueStatus.Processing;
|
||||
// queue.Record.StartedUtc = _clock.UtcNow;
|
||||
@@ -42,17 +44,14 @@ namespace Orchard.Messaging.Tests {
|
||||
|
||||
[Test]
|
||||
public void ProcessingQueueWithEnoughTimeSendsAllMessages() {
|
||||
var processor = _container.Resolve<IMessageQueueProcessor>();
|
||||
var processor = _container.Resolve<IJobsQueueProcessor>();
|
||||
|
||||
processor.ProcessQueue();
|
||||
|
||||
foreach (var message in _messages) {
|
||||
Assert.That(message.Status, Is.EqualTo(QueuedMessageStatus.Sent));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private QueuedMessageRecord CreateMessage(string subject) {
|
||||
return new QueuedMessageRecord {Id = 1, Type = "Email", Payload = "some payload data"};
|
||||
private QueuedJobRecord CreateMessage(string subject) {
|
||||
return new QueuedJobRecord {Id = 1, Message = "Email", Parameters = "some payload data"};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.JobsQueue.Models;
|
||||
using Orchard.JobsQueue.Services;
|
||||
using Orchard.Messaging.Models;
|
||||
using Orchard.Messaging.Services;
|
||||
using Orchard.Tests;
|
||||
@@ -11,12 +13,12 @@ namespace Orchard.Messaging.Tests {
|
||||
public class MessageQueueTests : DatabaseEnabledTestsBase {
|
||||
protected override IEnumerable<Type> DatabaseTypes {
|
||||
get {
|
||||
yield return typeof(QueuedMessageRecord);
|
||||
yield return typeof(QueuedJobRecord);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Register(ContainerBuilder builder) {
|
||||
builder.RegisterType<MessageQueueService>().As<IMessageQueueService>();
|
||||
builder.RegisterType<JobsQueueService>().As<IJobsQueueService>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,6 +42,10 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\..\lib\moq\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\..\lib\newtonsoft.json\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework">
|
||||
<HintPath>..\..\..\..\..\lib\nunit\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -81,9 +85,9 @@
|
||||
<Project>{6F759635-13D7-4E94-BCC9-80445D63F117}</Project>
|
||||
<Name>Orchard.Tokens</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Messaging.csproj">
|
||||
<ProjectReference Include="..\Orchard.JobsQueue.csproj">
|
||||
<Project>{085948ff-0e9b-4a9a-b564-f8b8b4bdddbc}</Project>
|
||||
<Name>Orchard.Messaging</Name>
|
||||
<Name>Orchard.JobsQueue</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Messaging.Services;
|
||||
using Orchard.Tests.Stubs;
|
||||
|
||||
@@ -16,7 +17,7 @@ namespace Orchard.Messaging.Tests {
|
||||
public void Dispose() {
|
||||
}
|
||||
|
||||
public void Process(string payload) {
|
||||
public void Process(IDictionary<string ,object> parameters) {
|
||||
_clock.Advance(_simulatedProcessingTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Orchard.JobsQueue.Models;
|
||||
|
||||
namespace Orchard.JobsQueue.ViewModels {
|
||||
public class JobsQueueSettingsPartViewModel {
|
||||
public JobsQueueSettingsPart JobsQueueSettings { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
@using Orchard.JobsQueue.Models
|
||||
@{
|
||||
Style.Include("admin-jobsqueue.css");
|
||||
Layout.Title = T("Job Details");
|
||||
var message = (QueuedJobRecord)Model.Job;
|
||||
var returnUrl = (string)Model.ReturnUrl;
|
||||
}
|
||||
<fieldset class="summary">
|
||||
<ul>
|
||||
<li>
|
||||
<label>@T("Priority")</label>
|
||||
<span>@message.Priority</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>@T("Created")</label>
|
||||
<span>@message.CreatedUtc</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>@T("Payload")</label>
|
||||
<span>@message.Parameters</span>
|
||||
</li>
|
||||
</ul>
|
||||
</fieldset>
|
||||
<a href="@returnUrl" class="button">@T("Back")</a>
|
||||
@@ -0,0 +1,65 @@
|
||||
@using Orchard.Localization
|
||||
@using Orchard.JobsQueue.Models
|
||||
@using Orchard.Utility.Extensions
|
||||
@{
|
||||
IEnumerable<QueuedJobRecord> jobs = Model.Jobs;
|
||||
JobsQueueStatus status = Model.JobsQueueStatus;
|
||||
|
||||
Layout.Title = T("Jobs Queue");
|
||||
Style.Include("admin-jobsqueue.css");
|
||||
}
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
<div class="manage">
|
||||
<span class="queue-status @String.Format("{0}", status.ToString().HtmlClassify())">@T("Status: {0}", status)</span>
|
||||
@if (status == JobsQueueStatus.Paused) {
|
||||
<button type="submit" name="submit.Resume" value="resume" class="button">@T("Resume")</button>
|
||||
}
|
||||
else {
|
||||
<button type="submit" name="submit.Pause" value="pause" class="button grey">@T("Pause")</button>
|
||||
<button type="submit" name="submit.Process" value="process">@T("Process Now")</button>
|
||||
}
|
||||
</div>
|
||||
if (!jobs.Any()) {
|
||||
<div class="job info">@T("The queue is empty.")</div>
|
||||
}
|
||||
else {
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@T("Priority")</th>
|
||||
<th>@T("Type")</th>
|
||||
<th>@T("Created")</th>
|
||||
<th>@T("Actions")</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var job in jobs) {
|
||||
LocalizedString priorityName;
|
||||
switch (job.Priority) {
|
||||
case -50:
|
||||
priorityName = T("Low");
|
||||
break;
|
||||
case 0:
|
||||
priorityName = T("Normal");
|
||||
break;
|
||||
case 50:
|
||||
priorityName = T("High");
|
||||
break;
|
||||
default:
|
||||
priorityName = T("None");
|
||||
break;
|
||||
}
|
||||
<tr>
|
||||
<td>@priorityName</td>
|
||||
<td>@job.Message</td>
|
||||
<td>@Display.DateTimeRelative(dateTimeUtc: job.CreatedUtc)</td>
|
||||
<td>
|
||||
<a href="@Url.Action("Details", "Admin", new { job.Id, returnUrl = Request.Url.PathAndQuery })">@T("Details")</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
@Display(Model.Pager)
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
@model MessageSettingsPartViewModel
|
||||
@using Orchard.Messaging.ViewModels;
|
||||
@*
|
||||
<fieldset>
|
||||
<legend>@T("Messaging")</legend>
|
||||
<div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@model Orchard.JobsQueue.ViewModels.JobsQueueSettingsPartViewModel
|
||||
@*
|
||||
<fieldset>
|
||||
<legend>@T("Messaging")</legend>
|
||||
<div>
|
||||
</div>
|
||||
</fieldset>
|
||||
*@
|
||||
@@ -1,37 +1,37 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
<remove name="host"/>
|
||||
<remove name="pages"/>
|
||||
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false"/>
|
||||
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false"/>
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
<system.web.webPages.razor>
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
|
||||
<namespaces>
|
||||
<add namespace="System.Web.Mvc"/>
|
||||
<add namespace="System.Web.Mvc.Ajax"/>
|
||||
<add namespace="System.Web.Mvc.Html"/>
|
||||
<add namespace="System.Web.Routing"/>
|
||||
<add namespace="System.Web.WebPages"/>
|
||||
<add namespace="System.Linq"/>
|
||||
<add namespace="System.Collections.Generic"/>
|
||||
<add namespace="Orchard.Mvc.Html"/>
|
||||
</namespaces>
|
||||
</pages>
|
||||
</system.web.webPages.razor>
|
||||
<system.web>
|
||||
<compilation targetFramework="4.5">
|
||||
<assemblies>
|
||||
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
|
||||
<add assembly="System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
</assemblies>
|
||||
</compilation>
|
||||
</system.web>
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
<remove name="host"/>
|
||||
<remove name="pages"/>
|
||||
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false"/>
|
||||
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false"/>
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
<system.web.webPages.razor>
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
|
||||
<namespaces>
|
||||
<add namespace="System.Web.Mvc"/>
|
||||
<add namespace="System.Web.Mvc.Ajax"/>
|
||||
<add namespace="System.Web.Mvc.Html"/>
|
||||
<add namespace="System.Web.Routing"/>
|
||||
<add namespace="System.Web.WebPages"/>
|
||||
<add namespace="System.Linq"/>
|
||||
<add namespace="System.Collections.Generic"/>
|
||||
<add namespace="Orchard.Mvc.Html"/>
|
||||
</namespaces>
|
||||
</pages>
|
||||
</system.web.webPages.razor>
|
||||
<system.web>
|
||||
<compilation targetFramework="4.5">
|
||||
<assemblies>
|
||||
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
|
||||
<add assembly="System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
</assemblies>
|
||||
</compilation>
|
||||
</system.web>
|
||||
</configuration>
|
||||
@@ -1,90 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Messaging.Models;
|
||||
using Orchard.Messaging.Services;
|
||||
using Orchard.Messaging.ViewModels;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.UI.Admin;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.Messaging.Controllers {
|
||||
[Admin]
|
||||
public class AdminController : Controller {
|
||||
private readonly IMessageQueueService _messageQueueManager;
|
||||
private readonly IOrchardServices _services;
|
||||
private readonly IMessageQueueProcessor _messageQueueProcessor;
|
||||
|
||||
public AdminController(
|
||||
IMessageQueueService messageQueueManager,
|
||||
IShapeFactory shapeFactory,
|
||||
IOrchardServices services,
|
||||
IMessageQueueProcessor messageQueueProcessor) {
|
||||
_messageQueueManager = messageQueueManager;
|
||||
_services = services;
|
||||
_messageQueueProcessor = messageQueueProcessor;
|
||||
New = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public dynamic New { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
public ActionResult Details(int id, string returnUrl) {
|
||||
var message = _messageQueueManager.GetMessage(id);
|
||||
|
||||
if (!Url.IsLocalUrl(returnUrl))
|
||||
returnUrl = Url.Action("List");
|
||||
|
||||
var model = New.ViewModel().Message(message).ReturnUrl(returnUrl);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public ActionResult List(MessagesFilter filter, PagerParameters pagerParameters) {
|
||||
var pager = new Pager(_services.WorkContext.CurrentSite, pagerParameters);
|
||||
|
||||
var messageCount = _messageQueueManager.GetMessagesCount(filter.Status);
|
||||
var messages = _messageQueueManager.GetMessages(filter.Status, pager.GetStartIndex(), pager.PageSize).ToList();
|
||||
var model = _services.New.ViewModel()
|
||||
.Pager(_services.New.Pager(pager).TotalItemCount(messageCount))
|
||||
.MessageQueueStatus(_services.WorkContext.CurrentSite.As<MessageSettingsPart>().Status)
|
||||
.Messages(messages)
|
||||
.Filter(filter);
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("List")]
|
||||
[FormValueRequired("submit.Filter")]
|
||||
public ActionResult Filter(QueuedMessageStatus? status) {
|
||||
return RedirectToAction("List", new { status });
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("List")]
|
||||
[FormValueRequired("submit.Resume")]
|
||||
public ActionResult Resume(QueuedMessageStatus? status) {
|
||||
_messageQueueManager.Resume();
|
||||
_services.Notifier.Information(T("The queue has been resumed."));
|
||||
return RedirectToAction("List", new { status });
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("List")]
|
||||
[FormValueRequired("submit.Pause")]
|
||||
public ActionResult Pause(QueuedMessageStatus? status) {
|
||||
_messageQueueManager.Pause();
|
||||
_services.Notifier.Information(T("The queue has been paused."));
|
||||
return RedirectToAction("List", new { status });
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("List")]
|
||||
[FormValueRequired("submit.Process")]
|
||||
public ActionResult Process(QueuedMessageStatus? status) {
|
||||
_messageQueueProcessor.ProcessQueue();
|
||||
_services.Notifier.Information(T("Processing has started."));
|
||||
return RedirectToAction("List", new { status });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Drivers;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Messaging.Models;
|
||||
using Orchard.Messaging.ViewModels;
|
||||
|
||||
namespace Orchard.Messaging.Drivers {
|
||||
[UsedImplicitly]
|
||||
public class MessageSettingsPartDriver : ContentPartDriver<MessageSettingsPart> {
|
||||
private const string TemplateName = "Parts/MessageSettings";
|
||||
public IOrchardServices Services { get; set; }
|
||||
|
||||
public MessageSettingsPartDriver(IOrchardServices services) {
|
||||
Services = services;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
protected override string Prefix { get { return "MessageSettings"; } }
|
||||
|
||||
protected override DriverResult Editor(MessageSettingsPart part, dynamic shapeHelper) {
|
||||
|
||||
var model = new MessageSettingsPartViewModel {
|
||||
MessageSettings = part
|
||||
};
|
||||
|
||||
return ContentShape("Parts_MessageSettings_Edit", () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
|
||||
}
|
||||
|
||||
protected override DriverResult Editor(MessageSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
|
||||
var model = new MessageSettingsPartViewModel {
|
||||
MessageSettings = part
|
||||
};
|
||||
|
||||
updater.TryUpdateModel(model, Prefix, null, null);
|
||||
|
||||
return ContentShape("Parts_MessageSettings_Edit", () => shapeHelper.EditorTemplate(TemplateName: TemplateName, Model: model, Prefix: Prefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Messaging.Models;
|
||||
|
||||
namespace Orchard.Messaging.Handlers {
|
||||
[UsedImplicitly]
|
||||
public class MessageSettingsPartHandler : ContentHandler {
|
||||
public MessageSettingsPartHandler() {
|
||||
Filters.Add(new ActivatingFilter<MessageSettingsPart>("Site"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using Orchard.Data.Migration;
|
||||
|
||||
namespace Orchard.Messaging {
|
||||
public class Migrations : DataMigrationImpl {
|
||||
|
||||
public int Create() {
|
||||
SchemaBuilder.CreateTable("QueuedMessageRecord", table => table
|
||||
.Column<int>("Id", c => c.Identity().PrimaryKey())
|
||||
.Column<string>("Type", c => c.WithLength(64))
|
||||
.Column<int>("Priority", c => c.WithDefault(0))
|
||||
.Column<string>("Payload", c => c.Unlimited())
|
||||
.Column<string>("Status", c => c.WithLength(64))
|
||||
.Column<string>("Result", c => c.Unlimited())
|
||||
.Column<DateTime>("CreatedUtc")
|
||||
.Column<DateTime>("StartedUtc")
|
||||
.Column<DateTime>("CompletedUtc")
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Orchard.Messaging.Models {
|
||||
public enum MessageQueueStatus {
|
||||
Idle,
|
||||
Paused
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System;
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.Messaging.Models {
|
||||
public class QueuedMessageRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual int Priority { get; set; }
|
||||
public virtual string Type { get; set; }
|
||||
|
||||
[StringLengthMax]
|
||||
public virtual string Payload { get; set; }
|
||||
|
||||
public virtual QueuedMessageStatus Status { get; set; }
|
||||
public virtual DateTime CreatedUtc { get; set; }
|
||||
public virtual DateTime? StartedUtc { get; set; }
|
||||
public virtual DateTime? CompletedUtc { get; set; }
|
||||
|
||||
[StringLengthMax]
|
||||
public virtual string Result { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Orchard.Messaging.Models {
|
||||
public enum QueuedMessageStatus {
|
||||
Pending,
|
||||
Sending,
|
||||
Sent,
|
||||
Faulted
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
Name: Messaging
|
||||
AntiForgery: enabled
|
||||
Author: The Orchard Team
|
||||
Website: http://orchardproject.net
|
||||
Version: 1.7.2
|
||||
OrchardVersion: 1.7.2
|
||||
Description: This module provides the messaging infrastructure that modules can use to send messages.
|
||||
Category: Messaging
|
||||
Features:
|
||||
Orchard.Messaging:
|
||||
Description: Provides the messaging infrastructure that modules can use to send messages.
|
||||
Dependencies: Settings, Orchard.TaskLease
|
||||
@@ -1,28 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Logging;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public class DefaultMessageService : Component, IMessageService {
|
||||
private readonly IEnumerable<IMessageChannelSelector> _messageChannelSelectors;
|
||||
|
||||
public DefaultMessageService(IEnumerable<IMessageChannelSelector> messageChannelSelectors) {
|
||||
_messageChannelSelectors = messageChannelSelectors;
|
||||
}
|
||||
|
||||
public void Send(string type, string payload) {
|
||||
var messageChannelResult = _messageChannelSelectors
|
||||
.Select(x => x.GetChannel(type, payload))
|
||||
.Where(x => x != null)
|
||||
.OrderByDescending(x => x.Priority)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (messageChannelResult == null || messageChannelResult.MessageChannel == null) {
|
||||
Logger.Information("No channels where found to process a message of type {0}", type);
|
||||
return;
|
||||
}
|
||||
|
||||
messageChannelResult.MessageChannel.Process(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace Orchard.Messaging.Services {
|
||||
public interface IMessageQueueProcessor : ISingletonDependency {
|
||||
void ProcessQueue();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Messaging.Models;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public interface IMessageQueueService : IDependency {
|
||||
QueuedMessageRecord Enqueue(string type, object payload, int priority);
|
||||
QueuedMessageRecord GetMessage(int id);
|
||||
IEnumerable<QueuedMessageRecord> GetMessages(QueuedMessageStatus? status, int startIndex, int count);
|
||||
int GetMessagesCount(QueuedMessageStatus? status = null);
|
||||
void Resume();
|
||||
void Pause();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace Orchard.Messaging.Services {
|
||||
public interface IMessageService : IDependency {
|
||||
void Send(string type, string payload);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Orchard.Tasks;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public class MessageQueueBackgroundTask : Component, IBackgroundTask {
|
||||
private readonly IMessageQueueProcessor _messageQueueProcessor;
|
||||
public MessageQueueBackgroundTask(IMessageQueueProcessor messageQueueProcessor) {
|
||||
_messageQueueProcessor = messageQueueProcessor;
|
||||
}
|
||||
|
||||
public void Sweep() {
|
||||
_messageQueueProcessor.ProcessQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Orchard.Environment;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Messaging.Models;
|
||||
using Orchard.Services;
|
||||
using Orchard.TaskLease.Services;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public class MessageQueueProcessor : IMessageQueueProcessor {
|
||||
private readonly Work<IMessageQueueService> _messageQueueService;
|
||||
private readonly Work<IMessageService> _messageService;
|
||||
private readonly Work<IClock> _clock;
|
||||
private readonly Work<ITaskLeaseService> _taskLeaseService;
|
||||
private readonly ReaderWriterLockSlim _rwl = new ReaderWriterLockSlim();
|
||||
|
||||
public MessageQueueProcessor(
|
||||
Work<IMessageQueueService> messageQueueService,
|
||||
Work<IMessageService> messageService,
|
||||
Work<IClock> clock,
|
||||
Work<ITaskLeaseService> taskLeaseService) {
|
||||
_messageQueueService = messageQueueService;
|
||||
_messageService = messageService;
|
||||
_clock = clock;
|
||||
_taskLeaseService = taskLeaseService;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
public void ProcessQueue() {
|
||||
// prevent two threads on the same machine to process the message queue
|
||||
if (_rwl.TryEnterWriteLock(0)) {
|
||||
try {
|
||||
_taskLeaseService.Value.Acquire("MessageQueueProcessor", _clock.Value.UtcNow.AddMinutes(5));
|
||||
IEnumerable<QueuedMessageRecord> messages;
|
||||
|
||||
while ((messages = _messageQueueService.Value.GetMessages(QueuedMessageStatus.Pending, 0, 10).ToArray()).Any()) {
|
||||
foreach (var message in messages.AsParallel()) {
|
||||
ProcessMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
_rwl.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessMessage(QueuedMessageRecord message) {
|
||||
|
||||
message.StartedUtc = _clock.Value.UtcNow;
|
||||
message.Status = QueuedMessageStatus.Sending;
|
||||
|
||||
Logger.Debug("Sending message ID {0}.", message.Id);
|
||||
|
||||
try {
|
||||
_messageService.Value.Send(message.Type, message.Payload);
|
||||
message.Status = QueuedMessageStatus.Sent;
|
||||
Logger.Debug("Sent message ID {0}.", message.Id);
|
||||
}
|
||||
catch (Exception e) {
|
||||
message.Status = QueuedMessageStatus.Faulted;
|
||||
message.Result = e.ToString();
|
||||
Logger.Error(e, "An unexpected error while sending message {0}. Error message: {1}.", message.Id, e);
|
||||
}
|
||||
finally {
|
||||
message.CompletedUtc = _clock.Value.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Data;
|
||||
using Orchard.Messaging.Models;
|
||||
using Orchard.Services;
|
||||
using Orchard.Settings;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public class MessageQueueService : IMessageQueueService {
|
||||
private readonly IClock _clock;
|
||||
private readonly IRepository<QueuedMessageRecord> _messageRepository;
|
||||
private readonly MessageSettingsPart _messageSettingsPart;
|
||||
|
||||
public MessageQueueService(
|
||||
IClock clock,
|
||||
IRepository<QueuedMessageRecord> messageRepository,
|
||||
ISiteService siteService) {
|
||||
_clock = clock;
|
||||
_messageRepository = messageRepository;
|
||||
_messageSettingsPart = siteService.GetSiteSettings().As<MessageSettingsPart>();
|
||||
}
|
||||
|
||||
public QueuedMessageRecord Enqueue(string channelName, object payload, int priority) {
|
||||
|
||||
var queuedMessage = new QueuedMessageRecord {
|
||||
Payload = ToJson(payload),
|
||||
Type = channelName,
|
||||
CreatedUtc = _clock.UtcNow,
|
||||
Status = QueuedMessageStatus.Pending
|
||||
};
|
||||
|
||||
_messageRepository.Create(queuedMessage);
|
||||
|
||||
return queuedMessage;
|
||||
}
|
||||
|
||||
public void Resume() {
|
||||
_messageSettingsPart.Status = MessageQueueStatus.Idle;
|
||||
}
|
||||
|
||||
public void Pause() {
|
||||
_messageSettingsPart.Status = MessageQueueStatus.Paused;
|
||||
}
|
||||
|
||||
public int GetMessagesCount(QueuedMessageStatus? status = null) {
|
||||
return GetMessagesQuery(status).Count();
|
||||
}
|
||||
|
||||
public IEnumerable<QueuedMessageRecord> GetMessages(QueuedMessageStatus? status, int startIndex, int pageSize) {
|
||||
return GetMessagesQuery(status)
|
||||
.Skip(startIndex)
|
||||
.Take(pageSize)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public QueuedMessageRecord GetMessage(int id) {
|
||||
return _messageRepository.Get(id);
|
||||
}
|
||||
|
||||
private IQueryable<QueuedMessageRecord> GetMessagesQuery(QueuedMessageStatus? status) {
|
||||
var query = _messageRepository.Table;
|
||||
|
||||
if (status != null) {
|
||||
query = query.Where(x => x.Status == status.Value);
|
||||
}
|
||||
|
||||
query = query
|
||||
.OrderByDescending(x => x.Priority)
|
||||
.ThenByDescending(x => x.CreatedUtc);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
private static string ToJson(object value) {
|
||||
return value != null ? JsonConvert.SerializeObject(value) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1 +0,0 @@
|
||||
.message-status{background:url('./images/message-status.png') no-repeat -17px -17px;padding-left:25px;line-height:16px;vertical-align:middle}.message-status.pending{background-position:-17px -17px}.message-status.sending{background-position:-17px -67px}.message-status.sent{background-position:-17px -116px}.message-status.faulted{background-position:-17px -166px}fieldset.summary ul li:after{content:".";display:block;height:0;clear:both;visibility:hidden}fieldset.summary ul li label,fieldset.summary ul li span{float:left;display:block}fieldset.summary ul li label{width:200px;font-weight:bold}fieldset.summary ul li label:after{content:":"}span.queue-status{margin-right:1.5em}table.queues{clear:both}table.queues td{vertical-align:middle}ul.horizontal li{float:left}.clear{content:".";display:block;height:0;clear:both;visibility:hidden}
|
||||
@@ -1,7 +0,0 @@
|
||||
.navicon-message-queue {
|
||||
background-image: url(./images/menu.messaging.png) !important;
|
||||
}
|
||||
|
||||
.navicon-message-queue:hover {
|
||||
background-position: 0 -30px !important;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using Orchard.Messaging.Models;
|
||||
|
||||
namespace Orchard.Messaging.ViewModels {
|
||||
public class MessageSettingsPartViewModel {
|
||||
public MessageSettingsPart MessageSettings { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using Orchard.Messaging.Models;
|
||||
|
||||
namespace Orchard.Messaging.ViewModels {
|
||||
public class MessagesFilter {
|
||||
public QueuedMessageStatus? Status { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
@using Orchard.Messaging.Models
|
||||
@using Orchard.Utility.Extensions
|
||||
@{
|
||||
Style.Include("admin-messaging.css", "admin-messaging.min.css");
|
||||
Layout.Title = T("Message Details");
|
||||
var message = (QueuedMessageRecord)Model.Message;
|
||||
var returnUrl = (string)Model.ReturnUrl;
|
||||
}
|
||||
<fieldset class="summary">
|
||||
<ul>
|
||||
<li>
|
||||
<label>@T("Status")</label>
|
||||
<span class="message-status @message.Status.ToString().HtmlClassify()">@message.Status</span>
|
||||
</li>
|
||||
@if (message.Status == QueuedMessageStatus.Faulted) {
|
||||
<li>
|
||||
<label>@T("Error")</label>
|
||||
<span>@message.Result</span>
|
||||
</li>
|
||||
}
|
||||
<li>
|
||||
<label>@T("Priority")</label>
|
||||
<span>@message.Priority</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>@T("Created")</label>
|
||||
<span>@message.CreatedUtc</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>@T("Completed")</label>
|
||||
<span>@message.CompletedUtc</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>@T("Payload")</label>
|
||||
<span>@message.Payload</span>
|
||||
</li>
|
||||
</ul>
|
||||
</fieldset>
|
||||
<a href="@returnUrl" class="button">@T("Back")</a>
|
||||
@@ -1,87 +0,0 @@
|
||||
@using Orchard.Localization
|
||||
@using Orchard.Messaging.Models
|
||||
@using Orchard.Messaging.ViewModels
|
||||
@using Orchard.Utility.Extensions
|
||||
@{
|
||||
IEnumerable<QueuedMessageRecord> messages = Model.Messages;
|
||||
MessagesFilter filter = Model.Filter;
|
||||
MessageQueueStatus status = Model.MessageQueueStatus;
|
||||
|
||||
Layout.Title = T("Message Queue");
|
||||
Style.Include("admin-messaging.css", "admin-messaging.min.css");
|
||||
}
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
<div class="manage">
|
||||
<span class="queue-status @String.Format("{0}", status.ToString().HtmlClassify())">@T("Status: {0}", status)</span>
|
||||
@if (status == MessageQueueStatus.Paused) {
|
||||
<button type="submit" name="submit.Resume" value="resume" class="button">@T("Resume")</button>
|
||||
}
|
||||
else {
|
||||
<button type="submit" name="submit.Pause" value="pause" class="button grey">@T("Pause")</button>
|
||||
<button type="submit" name="submit.Process" value="process">@T("Process Now")</button>
|
||||
}
|
||||
</div>
|
||||
<fieldset class="bulk-actions">
|
||||
<label for="filterResults" class="bulk-filter">@T("Filter")</label>
|
||||
<select id="filterResults" name="status">
|
||||
@Html.SelectOption(filter.Status, default(QueuedMessageStatus?), T("All").ToString())
|
||||
@Html.SelectOption(filter.Status, QueuedMessageStatus.Pending, T("Pending").ToString())
|
||||
@Html.SelectOption(filter.Status, QueuedMessageStatus.Sending, T("Sending").ToString())
|
||||
@Html.SelectOption(filter.Status, QueuedMessageStatus.Sent, T("Sent").ToString())
|
||||
@Html.SelectOption(filter.Status, QueuedMessageStatus.Faulted, T("Faulted").ToString())
|
||||
</select>
|
||||
<button type="submit" name="submit.Filter" value="yes please">@T("Apply")</button>
|
||||
</fieldset>
|
||||
if (!messages.Any()) {
|
||||
if (filter.Status == null) {
|
||||
<div class="message info">@T("There no messages in this queue yet.")</div>
|
||||
}
|
||||
else {
|
||||
<div class="message info">@T("There no messages in the '{0}' status.", filter.Status)</div>
|
||||
}
|
||||
}
|
||||
else {
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@T("Status")</th>
|
||||
<th>@T("Priority")</th>
|
||||
<th>@T("Type")</th>
|
||||
<th>@T("Created")</th>
|
||||
<th>@T("Completed")</th>
|
||||
<th>@T("Actions")</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var message in messages) {
|
||||
LocalizedString priorityName;
|
||||
switch (message.Priority) {
|
||||
case -50:
|
||||
priorityName = T("Low");
|
||||
break;
|
||||
case 0:
|
||||
priorityName = T("Normal");
|
||||
break;
|
||||
case 50:
|
||||
priorityName = T("High");
|
||||
break;
|
||||
default:
|
||||
priorityName = T("None");
|
||||
break;
|
||||
}
|
||||
<tr>
|
||||
<td><span class="message-status @message.Status.ToString().HtmlClassify()">@message.Status</span></td>
|
||||
<td>@priorityName</td>
|
||||
<td>@message.Type</td>
|
||||
<td>@Display.DateTimeRelative(dateTimeUtc: message.CreatedUtc)</td>
|
||||
<td>@Display.DateTimeRelative(dateTimeUtc: message.CompletedUtc)</td>
|
||||
<td>
|
||||
<a href="@Url.Action("Details", "Admin", new { message.Id, returnUrl = Request.Url.PathAndQuery })">@T("Details")</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
@Display(Model.Pager)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
@model Orchard.Messaging.ViewModels.MessageQueueViewModel
|
||||
<fieldset>
|
||||
@Html.HiddenFor(m => m.Id)
|
||||
<div>
|
||||
@Html.LabelFor(m => m.Name)
|
||||
@Html.TextBoxFor(m => m.Name, new { @class = "text medium" })
|
||||
</div>
|
||||
<div>
|
||||
@Html.LabelFor(m => m.TimeSlice)
|
||||
@Html.TextBoxFor(m => m.TimeSlice, new { @class = "text medium" })
|
||||
<span class="hint">@T("The time this queue gets to process per cycle.")</span>
|
||||
</div>
|
||||
<div>
|
||||
@Html.LabelFor(m => m.UpdateFrequency)
|
||||
@Html.TextBoxFor(m => m.UpdateFrequency, new { @class = "text medium" })
|
||||
<span class="hint">@T("The interval this queue is to be processed.")</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -75,7 +75,7 @@ namespace Orchard.Setup.Services {
|
||||
// Core
|
||||
"Common", "Containers", "Contents", "Dashboard", "Feeds", "Navigation", "Reports", "Scheduling", "Settings", "Shapes", "Title",
|
||||
// Modules
|
||||
"Orchard.Pages", "Orchard.ContentPicker", "Orchard.Themes", "Orchard.TaskLease", "Orchard.Messaging", "Orchard.Users", "Orchard.Roles", "Orchard.Modules",
|
||||
"Orchard.Pages", "Orchard.ContentPicker", "Orchard.Themes", "Orchard.Users", "Orchard.Roles", "Orchard.Modules",
|
||||
"PackagingServices", "Orchard.Packaging", "Gallery", "Orchard.Recipes"
|
||||
};
|
||||
|
||||
|
||||
@@ -9,4 +9,4 @@ Features:
|
||||
Orchard.Users:
|
||||
Description: Standard users.
|
||||
Category: Core
|
||||
Dependencies: Settings, Orchard.Messaging
|
||||
Dependencies: Settings
|
||||
|
||||
@@ -123,10 +123,6 @@
|
||||
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Messaging\Orchard.Messaging.csproj">
|
||||
<Project>{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}</Project>
|
||||
<Name>Orchard.Messaging</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Items\Content-User.Edit.cshtml" />
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Web.Security;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
@@ -109,14 +108,15 @@ namespace Orchard.Users.Services {
|
||||
var recipient = GetUser(userName);
|
||||
if (recipient != null) {
|
||||
var template = _shapeFactory.Create("Template_User_Moderated", Arguments.From(createUserParams));
|
||||
template.Metadata.Wrappers.Add("Template_User_Wrapper");
|
||||
|
||||
var payload = new {
|
||||
Subject = T("New account").Text,
|
||||
Body = _shapeDisplay.Display(template),
|
||||
Recipients = new [] { recipient.Email }
|
||||
template.Metadata.Wrappers.Add("Template_User_Wrapper");
|
||||
|
||||
var parameters = new Dictionary<string, object> {
|
||||
{"Subject", T("New account").Text},
|
||||
{"Body", _shapeDisplay.Display(template)},
|
||||
{"Recipients", new [] { recipient.Email }}
|
||||
};
|
||||
_messageService.Send("Email", JsonConvert.SerializeObject(payload));
|
||||
|
||||
_messageService.Send("Email", parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Messaging.Services;
|
||||
@@ -10,9 +9,9 @@ using Orchard.Users.Models;
|
||||
namespace Orchard.Users.Services {
|
||||
public class MissingSettingsBanner : INotificationProvider {
|
||||
private readonly IOrchardServices _orchardServices;
|
||||
private readonly IMessageManager _messageManager;
|
||||
private readonly IMessageChannelManager _messageManager;
|
||||
|
||||
public MissingSettingsBanner(IOrchardServices orchardServices, IMessageManager messageManager) {
|
||||
public MissingSettingsBanner(IOrchardServices orchardServices, IMessageChannelManager messageManager) {
|
||||
_orchardServices = orchardServices;
|
||||
_messageManager = messageManager;
|
||||
T = NullLocalizer.Instance;
|
||||
@@ -28,7 +27,11 @@ namespace Orchard.Users.Services {
|
||||
( registrationSettings.UsersMustValidateEmail ||
|
||||
registrationSettings.NotifyModeration ||
|
||||
registrationSettings.EnableLostPassword ) &&
|
||||
!_messageManager.GetAvailableChannelServices().Contains("email") ) {
|
||||
null == _messageManager.GetMessageChannel("Email", new Dictionary<string, object> {
|
||||
{"Body", ""},
|
||||
{"Subject", "Subject"},
|
||||
{"Recipients", "john.doe@outlook.com"}
|
||||
}) ) {
|
||||
yield return new NotifyEntry { Message = T("Some Orchard.User settings require an Email channel to be enabled."), Type = NotifyType.Warning };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
@@ -20,7 +20,7 @@ namespace Orchard.Users.Services {
|
||||
[UsedImplicitly]
|
||||
public class UserService : IUserService {
|
||||
private static readonly TimeSpan DelayToValidate = new TimeSpan(7, 0, 0, 0); // one week to validate email
|
||||
private static readonly TimeSpan DelayToResetPassword = new TimeSpan(1, 0, 0, 0); // 24 hours to validate email
|
||||
private static readonly TimeSpan DelayToResetPassword = new TimeSpan(1, 0, 0, 0); // 24 hours to reset password
|
||||
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IMembershipService _membershipService;
|
||||
@@ -140,15 +140,15 @@ namespace Orchard.Users.Services {
|
||||
ContactEmail = site.As<RegistrationSettingsPart>().ValidateEmailContactEMail,
|
||||
ChallengeUrl = url
|
||||
}));
|
||||
template.Metadata.Wrappers.Add("Template_User_Wrapper");
|
||||
template.Metadata.Wrappers.Add("Template_User_Wrapper");
|
||||
|
||||
var parameters = new Dictionary<string, object> {
|
||||
{"Subject", T("Verification E-Mail").Text},
|
||||
{"Body", _shapeDisplay.Display(template)},
|
||||
{"Recipients", user.Email}
|
||||
};
|
||||
|
||||
var payload = new {
|
||||
Subject = T("Verification E-Mail").Text,
|
||||
Body = _shapeDisplay.Display(template),
|
||||
Recipients = new[] { user.Email }
|
||||
};
|
||||
|
||||
_messageService.Send("Email", JsonConvert.SerializeObject(payload));
|
||||
_messageService.Send("Email", parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,15 +164,15 @@ namespace Orchard.Users.Services {
|
||||
User = user,
|
||||
LostPasswordUrl = url
|
||||
}));
|
||||
template.Metadata.Wrappers.Add("Template_User_Wrapper");
|
||||
template.Metadata.Wrappers.Add("Template_User_Wrapper");
|
||||
|
||||
var payload = new {
|
||||
Subject = T("Lost password").Text,
|
||||
Body = _shapeDisplay.Display(template),
|
||||
Recipients = new[] { user.Email }
|
||||
};
|
||||
var parameters = new Dictionary<string, object> {
|
||||
{"Subject", T("Lost password").Text},
|
||||
{"Body", _shapeDisplay.Display(template)},
|
||||
{"Recipients", user.Email }
|
||||
};
|
||||
|
||||
_messageService.Send("Email", JsonConvert.SerializeObject(payload));
|
||||
_messageService.Send("Email", parameters);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,13 @@ namespace Orchard.Workflows.Models {
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return State.Value[key];
|
||||
dynamic value = State.Value[key];
|
||||
|
||||
if (value == null) {
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,24 +68,22 @@ namespace Upgrade.Controllers {
|
||||
};
|
||||
|
||||
if (!newState.Body.StartsWith("<p ")) {
|
||||
newState.Body =
|
||||
"<p style=\"font-family:Arial, Helvetica; font-size:10pt;\">"
|
||||
+ newState.Body
|
||||
+ System.Environment.NewLine
|
||||
+ "</p>";
|
||||
newState.Body =
|
||||
newState.Body
|
||||
+ System.Environment.NewLine;
|
||||
}
|
||||
|
||||
if (state.Recipient == "owner") {
|
||||
newState.Recipients = new [] {"{User.Current.Email}"};
|
||||
newState.Recipients = "{User.Current.Email}";
|
||||
}
|
||||
else if (state.Recipient == "author") {
|
||||
newState.Recipients = new[] { "{Content.Author.Email}" };
|
||||
newState.Recipients = "{Content.Author.Email}";
|
||||
}
|
||||
else if (state.Recipient == "admin") {
|
||||
newState.Recipients = new[] { "{Site.SuperUser.Email}" };
|
||||
newState.Recipients = "{Site.SuperUser.Email}";
|
||||
}
|
||||
else if (state.Recipient == "other") {
|
||||
newState.Recipients = state.RecipientOther.Split(',');
|
||||
newState.Recipients = state.RecipientOther;
|
||||
}
|
||||
|
||||
record.State = JsonConvert.SerializeObject(newState);
|
||||
|
||||
@@ -157,8 +157,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Lists", "Orchard.We
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Pages", "Orchard.Web\Modules\Orchard.Pages\Orchard.Pages.csproj", "{3420C92A-747F-4990-BA08-F2C9531E44AD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Messaging", "Orchard.Web\Modules\Orchard.Messaging\Orchard.Messaging.csproj", "{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Localization", "Orchard.Web\Modules\Orchard.Localization\Orchard.Localization.csproj", "{FBC8B571-ED50-49D8-8D9D-64AB7454A0D6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Scripting.Dlr", "Orchard.Web\Modules\Orchard.Scripting.Dlr\Orchard.Scripting.Dlr.csproj", "{2AD6973D-C7BB-416E-89FE-EEE34664E05F}"
|
||||
@@ -235,7 +233,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Templates", "Orchar
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Templates.Tests", "Orchard.Web\Modules\Orchard.Templates\Tests\Orchard.Templates.Tests\Orchard.Templates.Tests.csproj", "{8961613C-0ADD-4DF3-945B-CAE47987CAFD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Messaging.Tests", "Orchard.Web\Modules\Orchard.Messaging\Tests\Orchard.Messaging.Tests.csproj", "{2B4E039D-EDEB-43DD-8F5A-C640035906A9}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.JobsQueue", "Orchard.Web\Modules\Orchard.JobsQueue\Orchard.JobsQueue.csproj", "{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -623,16 +621,6 @@ Global
|
||||
{3420C92A-747F-4990-BA08-F2C9531E44AD}.FxCop|Any CPU.Build.0 = Release|Any CPU
|
||||
{3420C92A-747F-4990-BA08-F2C9531E44AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3420C92A-747F-4990-BA08-F2C9531E44AD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Coverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Coverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.FxCop|Any CPU.Build.0 = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FBC8B571-ED50-49D8-8D9D-64AB7454A0D6}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FBC8B571-ED50-49D8-8D9D-64AB7454A0D6}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{FBC8B571-ED50-49D8-8D9D-64AB7454A0D6}.Coverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -985,16 +973,16 @@ Global
|
||||
{8961613C-0ADD-4DF3-945B-CAE47987CAFD}.FxCop|Any CPU.Build.0 = Release|Any CPU
|
||||
{8961613C-0ADD-4DF3-945B-CAE47987CAFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8961613C-0ADD-4DF3-945B-CAE47987CAFD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.Coverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.Coverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.FxCop|Any CPU.Build.0 = Release|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Coverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Coverage|Any CPU.Build.0 = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.FxCop|Any CPU.Build.0 = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -1024,7 +1012,6 @@ Global
|
||||
{C889167C-E52C-4A65-A419-224B3D1B957D} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{137906EA-15FE-4AD8-A6A0-27528F0477D6} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{3420C92A-747F-4990-BA08-F2C9531E44AD} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{FBC8B571-ED50-49D8-8D9D-64AB7454A0D6} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{2AD6973D-C7BB-416E-89FE-EEE34664E05F} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{99002B65-86F7-415E-BF4A-381AA8AB9CCC} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
@@ -1056,6 +1043,7 @@ Global
|
||||
{CBC7993C-57D8-4A6C-992C-19E849DFE71D} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{36B82383-D69E-4897-A24A-648BABDF80EC} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{10AB3CE2-A720-467F-9EC8-EBB4BAC9A1C9} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||
{ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
{F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
{6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
@@ -1063,7 +1051,6 @@ Global
|
||||
{E07AFA7E-7B36-44C3-A537-AFCCAA93EA7A} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
{2969635F-D9C3-4D01-890D-437B46659690} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
{8961613C-0ADD-4DF3-945B-CAE47987CAFD} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
{2B4E039D-EDEB-43DD-8F5A-C640035906A9} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||
{5E5E7A21-C7B2-44D8-8593-2F9541AE041D} = {383DBA32-4A3E-48D1-AAC3-75377A694452}
|
||||
{33B1BC8D-E292-4972-A363-22056B207156} = {383DBA32-4A3E-48D1-AAC3-75377A694452}
|
||||
{0DFA2E10-96C8-4E05-BC10-B710B97ECCDE} = {383DBA32-4A3E-48D1-AAC3-75377A694452}
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Mvc.Html;
|
||||
using System.Web.Routing;
|
||||
using Orchard.Caching;
|
||||
using Orchard.DisplayManagement.Implementation;
|
||||
using Orchard.Environment;
|
||||
@@ -153,19 +154,21 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
|
||||
// If the View is null, it means that the shape is being executed from a non-view origin / where no ViewContext was established by the view engine, but manually.
|
||||
// Manually creating a ViewContext works when working with Shape methods, but not when the shape is implemented as a Razor view template.
|
||||
// Horrible, but it will have to do for now.
|
||||
result = RenderRazorViewToString(harvestShapeInfo.TemplateVirtualPath, displayContext.Value);
|
||||
result = RenderRazorViewToString(harvestShapeInfo.TemplateVirtualPath, displayContext);
|
||||
}
|
||||
|
||||
Logger.Information("Done rendering template file '{0}'", harvestShapeInfo.TemplateVirtualPath);
|
||||
return result;
|
||||
}
|
||||
|
||||
private IHtmlString RenderRazorViewToString(string path, object model) {
|
||||
private IHtmlString RenderRazorViewToString(string path, DisplayContext context) {
|
||||
using (var sw = new StringWriter()) {
|
||||
var controllerContext = CreateControllerContext();
|
||||
var viewResult = _viewEngine.Value.FindPartialView(controllerContext, path, false);
|
||||
var viewContext = new ViewContext(controllerContext, viewResult.View, new ViewDataDictionary(model), new TempDataDictionary(), sw);
|
||||
viewResult.View.Render(viewContext, sw);
|
||||
|
||||
context.ViewContext.ViewData = new ViewDataDictionary(context.Value);
|
||||
context.ViewContext.TempData = new TempDataDictionary();
|
||||
viewResult.View.Render(context.ViewContext, sw);
|
||||
viewResult.ViewEngine.ReleaseView(controllerContext, viewResult.View);
|
||||
return new HtmlString(sw.GetStringBuilder().ToString());
|
||||
}
|
||||
@@ -173,13 +176,15 @@ namespace Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy {
|
||||
|
||||
private ControllerContext CreateControllerContext() {
|
||||
var controller = new StubController();
|
||||
var httpContext = _workContextAccessor.GetContext().HttpContext;
|
||||
var routeData = httpContext.Request.RequestContext.RouteData;
|
||||
var httpContext = _workContextAccessor.GetContext().Resolve<HttpContextBase>();
|
||||
var requestContext = _workContextAccessor.GetContext().Resolve<RequestContext>();
|
||||
var routeData = requestContext.RouteData;
|
||||
|
||||
if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
|
||||
routeData.Values.Add("controller", controller.GetType().Name.ToLower().Replace("controller", ""));
|
||||
|
||||
controller.ControllerContext = new ControllerContext(httpContext, routeData, controller);
|
||||
controller.ControllerContext.RequestContext = requestContext;
|
||||
return controller.ControllerContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement.Implementation;
|
||||
using Orchard.DisplayManagement.Shapes;
|
||||
|
||||
namespace Orchard.DisplayManagement {
|
||||
@@ -11,43 +7,4 @@ namespace Orchard.DisplayManagement {
|
||||
string Display(object shape);
|
||||
IEnumerable<string> Display(IEnumerable<object> shapes);
|
||||
}
|
||||
|
||||
public class ShapeDisplay : IShapeDisplay {
|
||||
private readonly IDisplayHelperFactory _displayHelperFactory;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly HttpContextBase _httpContextBase;
|
||||
|
||||
public ShapeDisplay(
|
||||
IDisplayHelperFactory displayHelperFactory,
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
HttpContextBase httpContextBase) {
|
||||
_displayHelperFactory = displayHelperFactory;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_httpContextBase = httpContextBase;
|
||||
}
|
||||
|
||||
public string Display(Shape shape) {
|
||||
return Display((object) shape);
|
||||
}
|
||||
|
||||
public string Display(object shape) {
|
||||
var viewContext = new ViewContext { HttpContext = _httpContextBase };
|
||||
viewContext.RouteData.DataTokens["IWorkContextAccessor"] = _workContextAccessor;
|
||||
var display = _displayHelperFactory.CreateHelper(viewContext, new ViewDataContainer());
|
||||
|
||||
return ((DisplayHelper)display).ShapeExecute(shape).ToString();
|
||||
}
|
||||
|
||||
public IEnumerable<string> Display(IEnumerable<object> shapes) {
|
||||
return shapes.Select(Display).ToArray();
|
||||
}
|
||||
|
||||
private class ViewDataContainer : IViewDataContainer {
|
||||
public ViewDataDictionary ViewData { get; set; }
|
||||
|
||||
public ViewDataContainer() {
|
||||
ViewData = new ViewDataDictionary();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,14 @@ using Orchard.DisplayManagement.Descriptors;
|
||||
using Orchard.DisplayManagement.Shapes;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Mvc;
|
||||
|
||||
namespace Orchard.DisplayManagement.Implementation {
|
||||
public class DefaultDisplayManager : IDisplayManager {
|
||||
private readonly Lazy<IShapeTableLocator> _shapeTableLocator;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly IEnumerable<IShapeDisplayEvents> _shapeDisplayEvents;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
// this need to be Shape instead of IShape - cast to interface throws error on clr types like HtmlString
|
||||
private static readonly CallSite<Func<CallSite, object, Shape>> _convertAsShapeCallsite = CallSite<Func<CallSite, object, Shape>>.Create(
|
||||
@@ -28,10 +30,12 @@ namespace Orchard.DisplayManagement.Implementation {
|
||||
public DefaultDisplayManager(
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
IEnumerable<IShapeDisplayEvents> shapeDisplayEvents,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
Lazy<IShapeTableLocator> shapeTableLocator) {
|
||||
_shapeTableLocator = shapeTableLocator;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_shapeDisplayEvents = shapeDisplayEvents;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
T = NullLocalizer.Instance;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
@@ -52,8 +56,10 @@ namespace Orchard.DisplayManagement.Implementation {
|
||||
if (shapeMetadata == null || string.IsNullOrEmpty(shapeMetadata.Type))
|
||||
return CoerceHtmlString(context.Value);
|
||||
|
||||
var workContext = _workContextAccessor.GetContext(context.ViewContext);
|
||||
var shapeTable = _shapeTableLocator.Value.Lookup(workContext.CurrentTheme.Id);
|
||||
var workContext = _workContextAccessor.GetContext();
|
||||
var shapeTable = _httpContextAccessor.Current() != null
|
||||
? _shapeTableLocator.Value.Lookup(workContext.CurrentTheme.Id)
|
||||
: _shapeTableLocator.Value.Lookup(null);
|
||||
|
||||
var displayingContext = new ShapeDisplayingContext {
|
||||
Shape = shape,
|
||||
|
||||
54
src/Orchard/DisplayManagement/ShapeDisplay.cs
Normal file
54
src/Orchard/DisplayManagement/ShapeDisplay.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.DisplayManagement.Implementation;
|
||||
using Orchard.DisplayManagement.Shapes;
|
||||
|
||||
namespace Orchard.DisplayManagement {
|
||||
public class ShapeDisplay : IShapeDisplay {
|
||||
private readonly IDisplayHelperFactory _displayHelperFactory;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly HttpContextBase _httpContextBase;
|
||||
private readonly RequestContext _requestContext;
|
||||
|
||||
public ShapeDisplay(
|
||||
IDisplayHelperFactory displayHelperFactory,
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
HttpContextBase httpContextBase,
|
||||
RequestContext requestContext) {
|
||||
_displayHelperFactory = displayHelperFactory;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_httpContextBase = httpContextBase;
|
||||
_requestContext = requestContext;
|
||||
}
|
||||
|
||||
public string Display(Shape shape) {
|
||||
return Display((object) shape);
|
||||
}
|
||||
|
||||
public string Display(object shape) {
|
||||
var viewContext = new ViewContext {
|
||||
HttpContext = _httpContextBase,
|
||||
RequestContext = _requestContext
|
||||
};
|
||||
viewContext.RouteData.DataTokens["IWorkContextAccessor"] = _workContextAccessor;
|
||||
var display = _displayHelperFactory.CreateHelper(viewContext, new ViewDataContainer());
|
||||
|
||||
return ((DisplayHelper)display).ShapeExecute(shape).ToString();
|
||||
}
|
||||
|
||||
public IEnumerable<string> Display(IEnumerable<object> shapes) {
|
||||
return shapes.Select(Display).ToArray();
|
||||
}
|
||||
|
||||
private class ViewDataContainer : IViewDataContainer {
|
||||
public ViewDataDictionary ViewData { get; set; }
|
||||
|
||||
public ViewDataContainer() {
|
||||
ViewData = new ViewDataDictionary();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/Orchard/Messaging/Services/DefaultMessageService.cs
Normal file
24
src/Orchard/Messaging/Services/DefaultMessageService.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Logging;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public class DefaultMessageService : Component, IMessageService {
|
||||
private readonly IMessageChannelManager _messageChannelManager;
|
||||
|
||||
public DefaultMessageService(IMessageChannelManager messageChannelManager) {
|
||||
_messageChannelManager = messageChannelManager;
|
||||
}
|
||||
|
||||
public void Send(string type, IDictionary<string, object> parameters) {
|
||||
var messageChannel = _messageChannelManager.GetMessageChannel(type, parameters);
|
||||
|
||||
if (messageChannel == null) {
|
||||
Logger.Information("No channels where found to process a message of type {0}", type);
|
||||
return;
|
||||
}
|
||||
|
||||
messageChannel.Process(parameters);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public interface IMessageChannel : IDependency {
|
||||
void Process(string payload);
|
||||
void Process(IDictionary<string, object> parameters);
|
||||
}
|
||||
}
|
||||
26
src/Orchard/Messaging/Services/IMessageChannelProvider.cs
Normal file
26
src/Orchard/Messaging/Services/IMessageChannelProvider.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public interface IMessageChannelManager : IDependency {
|
||||
IMessageChannel GetMessageChannel(string type, IDictionary<string, object> parameters);
|
||||
}
|
||||
|
||||
public class MessageChannelManager : IMessageChannelManager {
|
||||
private readonly IEnumerable<IMessageChannelSelector> _messageChannelSelectors;
|
||||
|
||||
public MessageChannelManager(IEnumerable<IMessageChannelSelector> messageChannelSelectors) {
|
||||
_messageChannelSelectors = messageChannelSelectors;
|
||||
}
|
||||
|
||||
public IMessageChannel GetMessageChannel(string type, IDictionary<string, object> parameters) {
|
||||
var messageChannelResult = _messageChannelSelectors
|
||||
.Select(x => x.GetChannel(type, parameters))
|
||||
.Where(x => x != null)
|
||||
.OrderByDescending(x => x.Priority)
|
||||
.FirstOrDefault();
|
||||
|
||||
return messageChannelResult == null ? null : messageChannelResult.MessageChannel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
namespace Orchard.Messaging.Services {
|
||||
using System;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public interface IMessageChannelSelector : IDependency {
|
||||
MessageChannelSelectorResult GetChannel(string messageType, object payload);
|
||||
}
|
||||
|
||||
public class MessageChannelSelectorResult {
|
||||
public int Priority { get; set; }
|
||||
public IMessageChannel MessageChannel { get; set; }
|
||||
public Func<IMessageChannel> MessageChannel { get; set; }
|
||||
}
|
||||
}
|
||||
8
src/Orchard/Messaging/Services/IMessageService.cs
Normal file
8
src/Orchard/Messaging/Services/IMessageService.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Events;
|
||||
|
||||
namespace Orchard.Messaging.Services {
|
||||
public interface IMessageService : IEventHandler {
|
||||
void Send(string type, IDictionary<string, object> parameters);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Web;
|
||||
using System.Web.Instrumentation;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Autofac;
|
||||
@@ -46,8 +49,9 @@ namespace Orchard.Mvc {
|
||||
// which requires activating the Site content item, which in turn requires a UrlHelper, which in turn requires a RequestContext,
|
||||
// thus preventing a StackOverflowException.
|
||||
var baseUrl = new Func<string>(() => siteService.GetSiteSettings().BaseUrl);
|
||||
|
||||
return new HttpContextPlaceholder(baseUrl);
|
||||
var httpContextBase = new HttpContextPlaceholder(baseUrl);
|
||||
context.Resolve<IWorkContextAccessor>().CreateWorkContextScope(httpContextBase);
|
||||
return httpContextBase;
|
||||
}
|
||||
|
||||
static RequestContext RequestContextFactory(IComponentContext context) {
|
||||
@@ -82,6 +86,7 @@ namespace Orchard.Mvc {
|
||||
/// </summary>
|
||||
class HttpContextPlaceholder : HttpContextBase {
|
||||
private readonly Lazy<string> _baseUrl;
|
||||
private readonly IDictionary _items = new Dictionary<object, object>();
|
||||
|
||||
public HttpContextPlaceholder(Func<string> baseUrl) {
|
||||
_baseUrl = new Lazy<string>(baseUrl);
|
||||
@@ -96,6 +101,14 @@ namespace Orchard.Mvc {
|
||||
public override HttpResponseBase Response {
|
||||
get { return new HttpResponsePlaceholder(); }
|
||||
}
|
||||
|
||||
public override IDictionary Items {
|
||||
get { return _items; }
|
||||
}
|
||||
|
||||
public override PageInstrumentationService PageInstrumentation {
|
||||
get { return new PageInstrumentationService(); }
|
||||
}
|
||||
}
|
||||
|
||||
private class HttpResponsePlaceholder : HttpResponseBase {
|
||||
@@ -162,6 +175,10 @@ namespace Orchard.Mvc {
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsLocal {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,6 +221,7 @@
|
||||
<Compile Include="DisplayManagement\Implementation\IShapeFactoryEvents.cs" />
|
||||
<Compile Include="DisplayManagement\INamedEnumerable.cs" />
|
||||
<Compile Include="DisplayManagement\IShapeDisplay.cs" />
|
||||
<Compile Include="DisplayManagement\ShapeDisplay.cs" />
|
||||
<Compile Include="DisplayManagement\Shapes\Composite.cs" />
|
||||
<Compile Include="DisplayManagement\Shapes\ShapeDebugView.cs" />
|
||||
<Compile Include="DisplayManagement\Shapes\ITagBuilderFactory.cs" />
|
||||
@@ -275,6 +276,12 @@
|
||||
<Compile Include="Logging\OrchardLog4netFactory.cs" />
|
||||
<Compile Include="Logging\OrchardLog4netLogger.cs" />
|
||||
<Compile Include="Messaging\Services\DefaultMessageManager.cs" />
|
||||
<Compile Include="Messaging\Services\DefaultMessageService.cs" />
|
||||
<Compile Include="Messaging\Services\IMessageChannel.cs" />
|
||||
<Compile Include="Messaging\Services\IMessageChannelProvider.cs" />
|
||||
<Compile Include="Messaging\Services\IMessageChannelSelector.cs" />
|
||||
<Compile Include="Messaging\Services\IMessageService.cs" />
|
||||
<Compile Include="Messaging\Services\NullMessageChannelSelector.cs" />
|
||||
<Compile Include="Mvc\DataAnnotations\LocalizedRegularExpressionAttribute.cs" />
|
||||
<Compile Include="Mvc\DataAnnotations\LocalizedStringMaxLengthAttribute.cs" />
|
||||
<Compile Include="Mvc\DataAnnotations\LocalizedRangeAttribute.cs" />
|
||||
|
||||
Reference in New Issue
Block a user