Refactoring Orchard.Messaging

This commit is contained in:
Sebastien Ros 2014-01-07 18:20:15 -08:00
parent 9d7450d444
commit eba14590b6
78 changed files with 737 additions and 1584 deletions

View File

@ -1,19 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Email.Models;
using Orchard.Email.Services;
using Orchard.Localization;
using Orchard.Messaging.Models;
using Orchard.Messaging.Services;
using Orchard.Workflows.Models;
using Orchard.Workflows.Services;
namespace Orchard.Email.Activities {
public class EmailActivity : Task {
private readonly IMessageQueueManager _messageQueueManager;
private readonly IMessageQueueService _messageQueueManager;
public EmailActivity(IMessageQueueManager messageQueueManager) {
public EmailActivity(IMessageQueueService messageQueueManager) {
_messageQueueManager = messageQueueManager;
T = NullLocalizer.Instance;
}
@ -43,21 +41,24 @@ namespace Orchard.Email.Activities {
}
public override IEnumerable<LocalizedString> Execute(WorkflowContext workflowContext, ActivityContext activityContext) {
var recipientAddresses = Split(activityContext.GetState<string>("RecipientAddress")).ToList();
var priority = activityContext.GetState<int>("Priority");
var body = activityContext.GetState<string>("Body");
var subject = activityContext.GetState<string>("Subject");
var queueId = activityContext.GetState<int?>("Queue") ?? _messageQueueManager.GetDefaultQueue().Id;
var priorityId = activityContext.GetState<int>("Priority");
var recipients = recipientAddresses.Select(x => new MessageRecipient(x));
var priority = _messageQueueManager.GetPriority(priorityId);
var payload = new EmailMessage(subject, body);
_messageQueueManager.Send(recipients, EmailMessageChannel.ChannelName, payload, priority, queueId);
var recipients = Split(activityContext.GetState<string>("RecipientAddress"));
var payload = new EmailMessage {
Subject = subject,
Body = body,
Recipients = recipients
};
_messageQueueManager.Enqueue(SmtpMessageChannel.MessageType, payload, priority);
yield return T("Queued");
}
private static IEnumerable<string> Split(string value) {
return !String.IsNullOrWhiteSpace(value) ? value.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) : Enumerable.Empty<string>();
private static string[] Split(string value) {
return !String.IsNullOrWhiteSpace(value) ? value.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) : new string[0];
}
}
}

View File

@ -1,140 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Core.Common.Models;
using Orchard.Messaging.Events;
using Orchard.Messaging.Models;
using Orchard.Messaging.Services;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Security;
using Orchard.Workflows.Models;
using Orchard.Workflows.Services;
namespace Orchard.Email.Activities {
[Obsolete("Use the new EmailActivity instead.")]
public class MailActivity : Task {
private readonly IMessageManager _messageManager;
private readonly IOrchardServices _orchardServices;
private readonly IMembershipService _membershipService;
public const string MessageType = "ActionEmail_Deprecated";
public MailActivity(
IMessageManager messageManager,
IOrchardServices orchardServices,
IMembershipService membershipService) {
_messageManager = messageManager;
_orchardServices = orchardServices;
_membershipService = membershipService;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext workflowContext, ActivityContext activityContext) {
return new[] { T("Sent") };
}
public override string Form {
get {
return "ActivityActionEmail";
}
}
public override LocalizedString Category {
get { return T("Messaging"); }
}
public override string Name {
get { return "SendEmailDeprecated"; }
}
public override LocalizedString Description {
get { return T("Sends an e-mail to a specific user (deprecated)."); }
}
public override IEnumerable<LocalizedString> Execute(WorkflowContext workflowContext, ActivityContext activityContext) {
string recipient = activityContext.GetState<string>("Recipient");
var properties = new Dictionary<string, string> {
{"Body", activityContext.GetState<string>("Body")},
{"Subject", activityContext.GetState<string>("Subject")},
{"RecipientOther",activityContext.GetState<string>("RecipientOther")}
};
if (recipient == "owner") {
var content = workflowContext.Content;
if (content.Has<CommonPart>()) {
var owner = content.As<CommonPart>().Owner;
if (owner != null && owner.ContentItem != null && owner.ContentItem.Record != null) {
_messageManager.Send(owner.ContentItem.Record, MessageType, "email", properties);
}
_messageManager.Send(
SplitEmail(owner.As<IUser>().Email), MessageType, "email", properties);
}
}
else if (recipient == "author") {
var user = _orchardServices.WorkContext.CurrentUser;
// can be null if user is anonymous
if (user != null && !String.IsNullOrWhiteSpace(user.Email)) {
_messageManager.Send(user.ContentItem.Record, MessageType, "email", properties);
}
}
else if (recipient == "admin") {
var username = _orchardServices.WorkContext.CurrentSite.SuperUser;
var user = _membershipService.GetUser(username);
// can be null if user is no super user is defined
if (user != null && !String.IsNullOrWhiteSpace(user.Email)) {
_messageManager.Send(user.ContentItem.Record, MessageType, "email", properties);
}
}
else if (recipient == "other") {
_messageManager.Send(SplitEmail(activityContext.GetState<string>("RecipientOther")), MessageType, "email", properties);
}
yield return T("Sent");
}
private static IEnumerable<string> SplitEmail(string commaSeparated) {
return commaSeparated.Split(new[] { ',', ';' });
}
}
[Obsolete]
public class MailActionsHandler : IMessageEventHandler {
public MailActionsHandler() {
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public void Sending(MessageContext context) {
if (context.MessagePrepared)
return;
if ((context.Recipients == null || !context.Recipients.Any()) &&
(context.Addresses == null || !context.Addresses.Any())) {
return;
}
switch (context.Type) {
case MailActivity.MessageType:
context.MailMessage.Subject = context.Properties["Subject"];
context.MailMessage.Body = context.Properties["Body"];
FormatEmailBody(context);
context.MessagePrepared = true;
break;
}
}
private static void FormatEmailBody(MessageContext context) {
context.MailMessage.Body = "<p style=\"font-family:Arial, Helvetica; font-size:10pt;\">" + context.MailMessage.Body + "</p>";
}
public void Sent(MessageContext context) {
}
}
}

View File

@ -1,22 +1,15 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.Mvc;
using Orchard.DisplayManagement;
using Orchard.Forms.Services;
using Orchard.Localization;
using Orchard.Messaging.Models;
using Orchard.Messaging.Services;
namespace Orchard.Email.Forms {
public class EmailForm : Component, IFormProvider {
private readonly IMessageQueueManager _messageQueueManager;
protected dynamic New { get; set; }
public EmailForm(IShapeFactory shapeFactory, IMessageQueueManager messageQueueManager) {
public EmailForm(IShapeFactory shapeFactory) {
New = shapeFactory;
_messageQueueManager = messageQueueManager;
}
public void Describe(DescribeContext context) {
@ -46,19 +39,12 @@ namespace Orchard.Email.Forms {
Id: "priority",
Name: "Priority",
Title: T("Priority"),
Description: ("The priority of this message."),
Items: GetPriorities())));
Description: ("The priority of this message.")
)));
var queues = GetQueues();
if (queues.Count() > 1) {
form._Queue = New.SelectList(
Id: "queue",
Name: "Queue",
Title: T("Queue"),
Description: ("The queue to add this message to."),
Items: queues);
}
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("Hight").Text });
return form;
};
@ -66,19 +52,6 @@ namespace Orchard.Email.Forms {
context.Form("EmailActivity", formFactory);
}
private IEnumerable<SelectListItem> GetPriorities() {
var priorities = _messageQueueManager.GetPriorities().ToList();
if (!priorities.Any())
priorities = _messageQueueManager.CreateDefaultPriorities().ToList();
return priorities.Select(x => new SelectListItem { Text = x.DisplayText, Value = x.Id.ToString(CultureInfo.InvariantCulture) }).ToList();
}
private IEnumerable<SelectListItem> GetQueues() {
var queues = _messageQueueManager.GetQueues().ToList();
if (!queues.Any())
queues = new List<MessageQueue> {_messageQueueManager.CreateDefaultQueue()};
return queues.Select(x => new SelectListItem {Text = x.Name, Value = x.Id.ToString(CultureInfo.InvariantCulture)}).ToList();
}
}
public class EmailFormValidator : IFormEventHandler {

View File

@ -85,22 +85,22 @@ namespace Orchard.Email.Forms {
if (context.FormName != "ActivityActionEmail") return;
var recipientFormValue = context.ValueProvider.GetValue("Recipient");
var recipient = recipientFormValue != null ? recipientFormValue.AttemptedValue : String.Empty;
if (recipient == String.Empty) {
context.ModelState.AddModelError("Recipient", T("You must select at least one recipient").Text);
}
if (context.ValueProvider.GetValue("Subject").AttemptedValue == String.Empty) {
context.ModelState.AddModelError("Subject", T("You must provide a Subject").Text);
}
if (context.ValueProvider.GetValue("Body").AttemptedValue == String.Empty) {
context.ModelState.AddModelError("Body", T("You must provide a Body").Text);
}
if (context.ValueProvider.GetValue("RecipientOther").AttemptedValue == String.Empty && recipient == "other") {
context.ModelState.AddModelError("RecipientOther", T("You must provide an e-mail address").Text);
var recipient = recipientFormValue != null ? recipientFormValue.AttemptedValue : String.Empty;
if (recipient == String.Empty) {
context.ModelState.AddModelError("Recipient", T("You must select at least one recipient").Text);
}
if (context.ValueProvider.GetValue("Subject").AttemptedValue == String.Empty) {
context.ModelState.AddModelError("Subject", T("You must provide a Subject").Text);
}
if (context.ValueProvider.GetValue("Body").AttemptedValue == String.Empty) {
context.ModelState.AddModelError("Body", T("You must provide a Body").Text);
}
if (context.ValueProvider.GetValue("RecipientOther").AttemptedValue == String.Empty && recipient == "other") {
context.ModelState.AddModelError("RecipientOther", T("You must provide an e-mail address").Text);
}
}

View File

@ -1,15 +1,7 @@
namespace Orchard.Email.Models {
public class EmailMessage {
public EmailMessage() {
}
public EmailMessage(string subject, string body) {
Subject = subject;
Body = body;
}
public string Subject { get; set; }
public string Body { get; set; }
public string[] Recipients { get; set; }
}
}

View File

@ -10,4 +10,4 @@ Features:
Name: Email Messaging
FeatureDescription: Email Messaging services.
Category: Messaging
Dependencies: Orchard.Messaging.Queuing, Orchard.Workflows
Dependencies: Orchard.Messaging, Orchard.Workflows

View File

@ -49,6 +49,10 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<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.Data.DataSetExtensions" />
<Reference Include="System.Web.ApplicationServices" />
@ -63,7 +67,6 @@
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="Activities\MailActivity.cs" />
<Compile Include="Activities\EmailActivity.cs" />
<Compile Include="Forms\MailForms.cs" />
<Compile Include="Forms\EmailForm.cs" />
@ -75,7 +78,8 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Rules\MailActions.cs" />
<Compile Include="Rules\MailForms.cs" />
<Compile Include="Services\EmailMessageChannel.cs" />
<Compile Include="Services\DefaultEmailMessageChannelSelector.cs" />
<Compile Include="Services\SmtpMessageChannel.cs" />
<Compile Include="Services\EmailMessageEventHandler.cs" />
<Compile Include="Services\EmailMessagingChannel.cs" />
<Compile Include="Services\MissingSettingsBanner.cs" />

View File

@ -16,6 +16,7 @@ namespace Orchard.Email.Rules {
void Describe(dynamic describe);
}
[Obsolete]
public class MailActions : IActionProvider {
private readonly IMessageManager _messageManager;
private readonly IOrchardServices _orchardServices;
@ -85,6 +86,7 @@ namespace Orchard.Email.Rules {
}
}
[Obsolete]
public class MailActionsHandler : IMessageEventHandler {
public MailActionsHandler() {
T = NullLocalizer.Instance;

View File

@ -0,0 +1,25 @@
using Orchard.ContentManagement;
using Orchard.Email.Models;
using Orchard.Messaging.Services;
namespace Orchard.Email.Services {
public class DefaultEmailMessageChannelSelector : Component, IMessageChannelSelector {
private readonly IOrchardServices _services;
public const string ChannelName = "Email";
public DefaultEmailMessageChannelSelector(IOrchardServices services) {
_services = services;
}
public MessageChannelSelectorResult GetChannel(string messageType, object payload) {
if (messageType == "Email") {
return new MessageChannelSelectorResult {
Priority = 50,
MessageChannel = new SmtpMessageChannel(_services.WorkContext.CurrentSite.As<SmtpSettingsPart>())
};
}
return null;
}
}
}

View File

@ -1,77 +0,0 @@
using System;
using System.Net;
using System.Net.Mail;
using Orchard.ContentManagement;
using Orchard.Logging;
using Orchard.Email.Models;
using Orchard.Messaging.Services;
using Orchard.Messaging.Models;
namespace Orchard.Email.Services {
public interface IEmailMessageChannel : IMessageChannel { }
public class EmailMessageChannel : MessageChannelBase, IEmailMessageChannel {
private readonly IOrchardServices _services;
private readonly Lazy<SmtpClient> _smtpClientField;
public const string ChannelName = "Email";
public EmailMessageChannel(IOrchardServices services) {
_services = services;
_smtpClientField = new Lazy<SmtpClient>(CreateSmtpClient);
Logger = NullLogger.Instance;
}
public override string Name {
get { return ChannelName; }
}
private SmtpClient SmtpClient {
get { return _smtpClientField.Value; }
}
protected override void Dispose(bool disposing) {
if (!disposing) return;
if (!_smtpClientField.IsValueCreated) return;
_smtpClientField.Value.Dispose();
}
public override void Send(QueuedMessage message) {
var smtpSettings = _services.WorkContext.CurrentSite.As<SmtpSettingsPart>();
if (smtpSettings == null || !smtpSettings.IsValid()) return;
var emailPayload = message.GetPayload<EmailMessage>();
var mailMessage = new MailMessage {
From = new MailAddress(smtpSettings.Address),
Subject = emailPayload.Subject,
Body = emailPayload.Body,
IsBodyHtml = emailPayload.Body != null && emailPayload.Body.Contains("<") && emailPayload.Body.Contains(">")
};
foreach (var recipient in message.Recipients) {
mailMessage.To.Add(new MailAddress(recipient.AddressOrAlias));
}
SmtpClient.Send(mailMessage);
}
private SmtpClient CreateSmtpClient() {
var smtpSettings = _services.WorkContext.CurrentSite.As<SmtpSettingsPart>();
var smtpClient = new SmtpClient {
UseDefaultCredentials = !smtpSettings.RequireCredentials
};
if (!smtpClient.UseDefaultCredentials && !String.IsNullOrWhiteSpace(smtpSettings.UserName)) {
smtpClient.Credentials = new NetworkCredential(smtpSettings.UserName, smtpSettings.Password);
}
if (smtpSettings.Host != null)
smtpClient.Host = smtpSettings.Host;
smtpClient.Port = smtpSettings.Port;
smtpClient.EnableSsl = smtpSettings.EnableSsl;
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
return smtpClient;
}
}
}

View File

@ -6,6 +6,7 @@ using Orchard.Messaging.Models;
using Orchard.Security;
namespace Orchard.Email.Services {
[Obsolete]
public class EmailMessageEventHandler : IMessageEventHandler {
private readonly IContentManager _contentManager;

View File

@ -10,7 +10,7 @@ using Orchard.Messaging.Services;
using Orchard.Messaging.Models;
namespace Orchard.Email.Services {
[Obsolete("Use EmailMessageChannel instead.")]
[Obsolete]
public class EmailMessagingChannel : IMessagingChannel {
private readonly IOrchardServices _orchardServices;

View File

@ -0,0 +1,81 @@
using System;
using System.Net;
using System.Net.Mail;
using Newtonsoft.Json;
using Orchard.Logging;
using Orchard.Email.Models;
using Orchard.Messaging.Services;
namespace Orchard.Email.Services {
public class SmtpMessageChannel : Component, IMessageChannel, IDisposable {
private readonly SmtpSettingsPart _smtpSettings;
private readonly Lazy<SmtpClient> _smtpClientField;
public static readonly string MessageType = "Email";
public SmtpMessageChannel(SmtpSettingsPart smtpSettings) {
_smtpSettings = smtpSettings;
_smtpClientField = new Lazy<SmtpClient>(CreateSmtpClient);
}
public void Dispose() {
if (!_smtpClientField.IsValueCreated) {
return;
}
_smtpClientField.Value.Dispose();
}
public void Process(string payload) {
if (!_smtpSettings.IsValid()) {
return;
}
var emailMessage = JsonConvert.DeserializeObject<EmailMessage>(payload);
if (emailMessage == null) {
return;
}
if (emailMessage.Recipients.Length == 0) {
Logger.Error("Email message doesn't have any recipient");
return;
}
var mailMessage = new MailMessage {
From = new MailAddress(_smtpSettings.Address),
Subject = emailMessage.Subject,
Body = emailMessage.Body,
IsBodyHtml = emailMessage.Body != null && emailMessage.Body.Contains("<") && emailMessage.Body.Contains(">")
};
try {
foreach (var recipient in emailMessage.Recipients) {
mailMessage.To.Add(new MailAddress(recipient));
}
_smtpClientField.Value.Send(mailMessage);
}
catch (Exception e) {
Logger.Error(e, "Could not send email");
}
}
private SmtpClient CreateSmtpClient() {
var smtpClient = new SmtpClient {
UseDefaultCredentials = !_smtpSettings.RequireCredentials
};
if (!smtpClient.UseDefaultCredentials && !String.IsNullOrWhiteSpace(_smtpSettings.UserName)) {
smtpClient.Credentials = new NetworkCredential(_smtpSettings.UserName, _smtpSettings.Password);
}
if (_smtpSettings.Host != null) {
smtpClient.Host = _smtpSettings.Host;
}
smtpClient.Port = _smtpSettings.Port;
smtpClient.EnableSsl = _smtpSettings.EnableSsl;
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
return smtpClient;
}
}
}

View File

@ -1,33 +1,16 @@
using System.Linq;
using Orchard.Environment.Extensions;
using Orchard.Messaging.Services;
using Orchard.UI.Navigation;
using Orchard.UI.Navigation;
namespace Orchard.Messaging {
[OrchardFeature("Orchard.Messaging.Queuing")]
public class AdminMenu : Component, INavigationProvider {
private readonly IMessageQueueManager _messageQueueManager;
public AdminMenu(IMessageQueueManager messageQueueManager) {
_messageQueueManager = messageQueueManager;
}
public string MenuName { get { return "admin"; } }
public void GetNavigation(NavigationBuilder builder) {
var queues = _messageQueueManager.GetQueues().ToList();
builder
.AddImageSet("messaging")
.Add(T("Messaging"), "5.0", item => {
if (queues.Count == 1) {
item.Action("List", "AdminQueue", new { area = "Orchard.Messaging", id = queues.First().Id });
item.LinkToFirstChild(false);
}
else {
item.Action("Index", "AdminQueue", new { area = "Orchard.Messaging" });
}
item.Add(T("Priorities"), "1.1", subItem => subItem
.Action("Index", "AdminPriority", new { area = "Orchard.Messaging" }));
.Add(T("Message Queue"), "15.0", item => {
item.Action("List", "Admin", new { area = "Orchard.Messaging" });
item.LinkToFirstChild(false);
});
}
}

View File

@ -0,0 +1,90 @@
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 });
}
}
}

View File

@ -1,31 +0,0 @@
using System.Web.Mvc;
using Orchard.DisplayManagement;
using Orchard.Environment.Extensions;
using Orchard.Messaging.Services;
using Orchard.UI.Admin;
namespace Orchard.Messaging.Controllers {
[OrchardFeature("Orchard.Messaging.Queuing")]
[Admin]
public class AdminMessageController : Controller {
private readonly IMessageQueueManager _messageQueueManager;
public AdminMessageController(IMessageQueueManager messageQueueManager, IShapeFactory shapeFactory) {
_messageQueueManager = messageQueueManager;
New = shapeFactory;
}
public dynamic New { get; set; }
public ActionResult Details(int id, string returnUrl) {
var message = _messageQueueManager.GetMessage(id);
if (!Url.IsLocalUrl(returnUrl))
returnUrl = Url.Action("List", "AdminQueue", new {message.Queue.Id});
var model = New.ViewModel().Message(message).ReturnUrl(returnUrl);
return View(model);
}
}
}

View File

@ -1,86 +0,0 @@
using System.Linq;
using System.Web.Mvc;
using Orchard.DisplayManagement;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Messaging.Extensions;
using Orchard.Messaging.Services;
using Orchard.Messaging.ViewModels;
using Orchard.UI.Admin;
using Orchard.UI.Notify;
namespace Orchard.Messaging.Controllers {
[OrchardFeature("Orchard.Messaging.Queuing")]
[Admin]
public class AdminPriorityController : Controller {
private readonly IMessageQueueManager _messageQueueManager;
private readonly INotifier _notifier;
public AdminPriorityController(IMessageQueueManager messageQueueManager, IShapeFactory shapeFactory, INotifier notifier) {
_messageQueueManager = messageQueueManager;
_notifier = notifier;
New = shapeFactory;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public dynamic New { get; set; }
public ActionResult Index() {
var priorities = _messageQueueManager.GetPriorities().ToList();
var model = New.ViewModel().Priorities(priorities);
return View(model);
}
[HttpPost]
public ActionResult Delete(int id) {
var priority = _messageQueueManager.GetPriority(id);
_messageQueueManager.DeletePriority(priority);
_notifier.Information(T("That priority has been deleted."));
return RedirectToAction("Index");
}
public ActionResult Create() {
return View(new MessagePriorityViewModel());
}
[HttpPost]
public ActionResult Create(MessagePriorityViewModel model) {
if (!ModelState.IsValid)
return View(model);
var priority = _messageQueueManager.CreatePriority(model.Name.TrimSafe(), model.DisplayText.TrimSafe(), model.Value);
_notifier.Information(T("Your Priority has been created."));
return RedirectToAction("Edit", new { priority.Id });
}
public ActionResult Edit(int id) {
var priority = _messageQueueManager.GetPriority(id);
if (priority == null || priority.Archived)
return HttpNotFound();
var model = new MessagePriorityViewModel {
Id = priority.Id,
Name = priority.Name,
DisplayText = priority.DisplayText,
Value = priority.Value
};
return View(model);
}
[HttpPost]
public ActionResult Edit(MessagePriorityViewModel model) {
if (!ModelState.IsValid)
return View(model);
var priority = _messageQueueManager.GetPriority(model.Id);
priority.Name = model.Name.TrimSafe();
priority.DisplayText = model.DisplayText.TrimSafe();
priority.Value = model.Value;
_notifier.Information(T("Your Priority has been updated."));
return RedirectToAction("Edit", new { priority.Id });
}
}
}

View File

@ -1,138 +0,0 @@
using System.Linq;
using System.Web.Mvc;
using Orchard.Environment.Extensions;
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 {
[OrchardFeature("Orchard.Messaging.Queuing")]
[Admin]
public class AdminQueueController : Controller {
private readonly IMessageQueueManager _messageQueueManager;
private readonly IOrchardServices _services;
private readonly IMessageQueueProcessor _messageQueueProcessor;
public AdminQueueController(IMessageQueueManager messageQueueManager, IOrchardServices services, IMessageQueueProcessor messageQueueProcessor) {
_messageQueueManager = messageQueueManager;
_services = services;
_messageQueueProcessor = messageQueueProcessor;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public ActionResult Index() {
var queues = _messageQueueManager.GetQueues().ToList();
if (queues.Count == 1) {
return RedirectToAction("List", new {id = queues.First().Id});
}
var queueShapes = queues.Select(x => _services.New.Queue(x)
.Pending(_messageQueueManager.CountMessages(x.Id, QueuedMessageStatus.Pending))
.Faulted(_messageQueueManager.CountMessages(x.Id, QueuedMessageStatus.Faulted))
.Sent(_messageQueueManager.CountMessages(x.Id, QueuedMessageStatus.Sent))).ToList();
var model = _services.New.ViewModel()
.Queues(queueShapes);
return View(model);
}
public ActionResult Edit(int id, string returnUrl) {
var queue = _messageQueueManager.GetQueue(id);
var model = new MessageQueueViewModel {
Id = queue.Id,
Name = queue.Name,
ReturnUrl = returnUrl
};
return View(model);
}
[HttpPost]
public ActionResult Edit(MessageQueueViewModel model) {
if (!ModelState.IsValid)
return View(model);
CreateOrUpdateQueue(model);
_services.Notifier.Information(T("Your queue has been updated."));
return Url.IsLocalUrl(model.ReturnUrl) ? (ActionResult) Redirect(model.ReturnUrl) : RedirectToAction("Edit", new {id = model.Id});
}
public ActionResult Create() {
return View(new MessageQueueViewModel());
}
[HttpPost]
public ActionResult Create(MessageQueueViewModel model) {
if (!ModelState.IsValid)
return View(model);
var queue = CreateOrUpdateQueue(model);
_services.Notifier.Information(T("Your queue has been created."));
return RedirectToAction("Edit", new { id = queue.Id });
}
public ActionResult List(int id, MessagesFilter filter, PagerParameters pagerParameters) {
var pager = new Pager(_services.WorkContext.CurrentSite, pagerParameters);
var queue = _messageQueueManager.GetQueue(id);
if (queue == null)
return HttpNotFound();
var messageCount = _messageQueueManager.CountMessages(queue.Id, filter.Status);
var messages = _messageQueueManager.GetMessages(queue.Id, filter.Status, pager.GetStartIndex(), pager.PageSize).ToList();
var model = _services.New.ViewModel()
.Pager(_services.New.Pager(pager).TotalItemCount(messageCount))
.Queue(queue)
.Messages(messages)
.Filter(filter);
return View(model);
}
[HttpPost, ActionName("List")]
[FormValueRequired("submit.Filter")]
public ActionResult Filter(int id, QueuedMessageStatus? status) {
return RedirectToAction("List", new {id, status});
}
[HttpPost, ActionName("List")]
[FormValueRequired("submit.Resume")]
public ActionResult Resume(int id, QueuedMessageStatus? status) {
var queue = _messageQueueManager.GetQueue(id);
_messageQueueManager.Resume(queue);
_services.Notifier.Information(T("The queue has been resumed."));
return RedirectToAction("List", new { id, status });
}
[HttpPost, ActionName("List")]
[FormValueRequired("submit.Pause")]
public ActionResult Pause(int id, QueuedMessageStatus? status) {
var queue = _messageQueueManager.GetQueue(id);
_messageQueueManager.Pause(queue);
_services.Notifier.Information(T("The queue has been paused."));
return RedirectToAction("List", new { id, status });
}
[HttpPost, ActionName("List")]
[FormValueRequired("submit.Process")]
public ActionResult Process(int id, QueuedMessageStatus? status) {
_messageQueueProcessor.ProcessQueues();
_services.Notifier.Information(T("Processing has started."));
return RedirectToAction("List", new { id, status });
}
private MessageQueue CreateOrUpdateQueue(MessageQueueViewModel model) {
var queue = _messageQueueManager.GetQueue(model.Id) ?? _messageQueueManager.CreateQueue();
queue.Name = model.Name;
return queue;
}
}
}

View File

@ -3,18 +3,15 @@ using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.Localization;
using Orchard.Messaging.Models;
using Orchard.Messaging.Services;
using Orchard.Messaging.ViewModels;
namespace Orchard.Messaging.Drivers {
[UsedImplicitly]
public class MessageSettingsPartDriver : ContentPartDriver<MessageSettingsPart> {
private const string TemplateName = "Parts/MessageSettings";
private readonly IMessageManager _messageQueueManager;
public IOrchardServices Services { get; set; }
public MessageSettingsPartDriver(IOrchardServices services, IMessageManager messageQueueManager) {
_messageQueueManager = messageQueueManager;
public MessageSettingsPartDriver(IOrchardServices services) {
Services = services;
T = NullLocalizer.Instance;
}
@ -26,7 +23,6 @@ namespace Orchard.Messaging.Drivers {
protected override DriverResult Editor(MessageSettingsPart part, dynamic shapeHelper) {
var model = new MessageSettingsPartViewModel {
ChannelServices = _messageQueueManager.GetAvailableChannelServices(),
MessageSettings = part
};
@ -35,7 +31,6 @@ namespace Orchard.Messaging.Drivers {
protected override DriverResult Editor(MessageSettingsPart part, IUpdateModel updater, dynamic shapeHelper) {
var model = new MessageSettingsPartViewModel {
ChannelServices = _messageQueueManager.GetAvailableChannelServices(),
MessageSettings = part
};

View File

@ -0,0 +1,23 @@
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;
}
}
}

View File

@ -1,14 +0,0 @@
using Orchard.Data.Migration;
namespace Orchard.Messaging.Migrations {
public class MessagingMigrations : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("MessageSettingsPartRecord", table => table
.ContentPartRecord()
.Column<string>("DefaultChannelService", c => c.WithLength(64)));
return 1;
}
}
}

View File

@ -1,50 +0,0 @@
using System;
using Orchard.Data.Migration;
using Orchard.Environment.Extensions;
using Orchard.Messaging.Services;
namespace Orchard.Messaging.Migrations {
[OrchardFeature("Orchard.Messaging.Queuing")]
public class MessagingQueuingMigrations : DataMigrationImpl {
private readonly IMessageQueueManager _messageQueueManager;
public MessagingQueuingMigrations(IMessageQueueManager messageQueueManager) {
_messageQueueManager = messageQueueManager;
}
public int Create() {
SchemaBuilder.CreateTable("MessagePriority", table => table
.Column<int>("Id", c => c.Identity().PrimaryKey())
.Column<int>("Value", c => c.NotNull())
.Column<string>("Name", c => c.WithLength(50))
.Column<string>("DisplayText", c => c.WithLength(50))
.Column<bool>("Archived", c => c.NotNull())
.Column<DateTime>("ArchivedUtc"));
SchemaBuilder.CreateTable("MessageQueueRecord", table => table
.Column<int>("Id", c => c.Identity().PrimaryKey())
.Column<string>("Name", c => c.WithLength(50))
.Column<string>("Status", c => c.WithLength(50))
.Column<DateTime>("StartedUtc")
.Column<DateTime>("EndedUtc"));
SchemaBuilder.CreateTable("QueuedMessageRecord", table => table
.Column<int>("Id", c => c.Identity().PrimaryKey())
.Column<int>("QueueId", c => c.NotNull())
.Column<int>("Priority_Id")
.Column<string>("ChannelName", c => c.WithLength(50))
.Column<string>("Recipients", c => c.Unlimited())
.Column<string>("Payload", c => c.Unlimited())
.Column<string>("Status", c => c.WithLength(50))
.Column<DateTime>("CreatedUtc")
.Column<DateTime>("StartedUtc")
.Column<DateTime>("CompletedUtc")
.Column<string>("Result", c => c.Unlimited()));
_messageQueueManager.CreateDefaultQueue();
_messageQueueManager.CreateDefaultPriorities();
return 1;
}
}
}

View File

@ -1,16 +0,0 @@
using System;
namespace Orchard.Messaging.Models {
public class MessagePriority {
public virtual int Id { get; set; }
public virtual int Value { get; set; }
public virtual string Name { get; set; }
public virtual string DisplayText { get; set; }
public virtual bool Archived { get; set; }
public virtual DateTime? ArchivedUtc { get; set; }
public override string ToString() {
return String.Format("{0} - {1}", Value, Name);
}
}
}

View File

@ -1,39 +0,0 @@
using System;
namespace Orchard.Messaging.Models {
public class MessageQueue {
public MessageQueue(MessageQueueRecord record) {
Record = record;
}
public MessageQueueRecord Record { get; private set; }
public int Id {
get { return Record.Id; }
}
public string Name {
get { return Record.Name; }
set { Record.Name = value; }
}
public MessageQueueStatus Status {
get { return Record.Status; }
internal set { Record.Status = value; }
}
public DateTime? StartedUtc {
get { return Record.StartedUtc; }
internal set { Record.StartedUtc = value; }
}
public DateTime? EndedUtc {
get { return Record.EndedUtc; }
internal set { Record.EndedUtc = value; }
}
public override string ToString() {
return String.Format("{0} - {1}", Name, Status);
}
}
}

View File

@ -1,15 +0,0 @@
using System;
namespace Orchard.Messaging.Models {
public class MessageQueueRecord {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual MessageQueueStatus Status { get; set; }
public virtual DateTime? StartedUtc { get; set; }
public virtual DateTime? EndedUtc { get; set; }
public override string ToString() {
return String.Format("{0} - {1}", Name, Status);
}
}
}

View File

@ -1,7 +1,6 @@
namespace Orchard.Messaging.Models {
public enum MessageQueueStatus {
Idle,
Processing,
Paused
}
}

View File

@ -1,15 +0,0 @@
namespace Orchard.Messaging.Models {
public class MessageRecipient {
public string AddressOrAlias { get; set; }
public MessageRecipient() {}
public MessageRecipient(string addressOrAlias) {
AddressOrAlias = addressOrAlias;
}
public override string ToString() {
return AddressOrAlias;
}
}
}

View File

@ -1,15 +1,11 @@
using System.ComponentModel.DataAnnotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement;
namespace Orchard.Messaging.Models {
public class MessageSettingsPart : ContentPart {
public const ushort DefaultChannelServiceLength = 64;
[StringLength(DefaultChannelServiceLength)]
public string DefaultChannelService {
get { return this.Retrieve(x => x.DefaultChannelService); }
set { this.Store(x => x.DefaultChannelService, value); }
public MessageQueueStatus Status {
get { return this.Retrieve(x => x.Status); }
set { this.Store(x => x.Status, value); }
}
}
}

View File

@ -1,80 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Orchard.Messaging.Services;
namespace Orchard.Messaging.Models {
public class QueuedMessage {
// ReSharper disable InconsistentNaming
public Lazy<MessageQueue> QueueField;
public Lazy<IMessageChannel> ChannelField;
public Lazy<IEnumerable<MessageRecipient>> RecipientsField;
// ReSharper restore InconsistentNaming
public QueuedMessage(QueuedMessageRecord record) {
Record = record;
}
public QueuedMessageRecord Record {
get; private set;
}
public int Id {
get { return Record.Id; }
}
public MessagePriority Priority {
get { return Record.Priority; }
set { Record.Priority = value; }
}
public QueuedMessageStatus Status {
get { return Record.Status; }
internal set { Record.Status = value; }
}
public string Result {
get { return Record.Result; }
set { Record.Result = value; }
}
public DateTime CreatedUtc {
get { return Record.CreatedUtc; }
}
public DateTime? StartedUtc {
get { return Record.StartedUtc; }
internal set { Record.StartedUtc = value; }
}
public DateTime? CompletedUtc {
get { return Record.CompletedUtc; }
internal set { Record.CompletedUtc = value; }
}
public MessageQueue Queue {
get { return QueueField.Value; }
}
public IMessageChannel Channel {
get { return ChannelField.Value; }
}
public IEnumerable<MessageRecipient> Recipients {
get { return RecipientsField.Value; }
}
public T GetPayload<T>() {
return Record.Payload != null ? JsonConvert.DeserializeObject<T>(Record.Payload) : default(T);
}
public void SetPayload<T>(T value) {
Record.Payload = !ReferenceEquals(value, default(T)) ? JsonConvert.SerializeObject(value) : null;
}
public override string ToString() {
return String.Format("Recipients: {0}", String.Join(", ", Recipients.Select(x => x.ToString())));
}
}
}

View File

@ -4,13 +4,9 @@ using Orchard.Data.Conventions;
namespace Orchard.Messaging.Models {
public class QueuedMessageRecord {
public virtual int Id { get; set; }
public virtual int QueueId { get; set; }
public virtual MessagePriority Priority { get; set; }
public virtual string ChannelName { get; set; }
[StringLengthMax]
public virtual string Recipients { get; set; }
public virtual int Priority { get; set; }
public virtual string Type { get; set; }
[StringLengthMax]
public virtual string Payload { get; set; }

View File

@ -4,14 +4,9 @@ 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 such as Workflows can use. It is only useful as a dependency for other modules that implement specific channels, such as e-mail or Twitter.
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 such as Workflows can use. It is only useful as a dependency for other modules that implement specific channels, such as e-mail or Twitter.
Dependencies: Settings
Orchard.Messaging.Queuing
Name: Message Queuing
Description: Provides message queuing facilities and APIs, enabling the queing of messages to be processed at regular intervals. Includes support for prioritized messages and templates messages using shapes.
Category: Messaging
Dependencies: Orchard.Messaging, Orchard.Forms, Orchard.Workflows, Orchard.jQuery
Description: Provides the messaging infrastructure that modules can use to send messages.
Dependencies: Settings, Orchard.TaskLease

View File

@ -70,32 +70,27 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminQueueController.cs" />
<Compile Include="Controllers\AdminMessageController.cs" />
<Compile Include="Controllers\AdminPriorityController.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="ViewModels\MessagePriorityViewModel.cs" />
<Compile Include="ViewModels\MessageQueueViewModel.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\MessagingQueuingMigrations.cs" />
<Compile Include="Migrations\MessagingMigrations.cs" />
<Compile Include="Models\MessageQueue.cs" />
<Compile Include="Migrations.cs" />
<Compile Include="Services\MessageQueueProcessor.cs" />
<Compile Include="Services\MessageChannelBase.cs" />
<Compile Include="Services\MessageQueueBackgroundTask.cs" />
<Compile Include="Services\IMessageChannel.cs" />
<Compile Include="Models\QueuedMessage.cs" />
<Compile Include="Models\MessagePriority.cs" />
<Compile Include="Models\MessageQueueRecord.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\MessageQueueManager.cs" />
<Compile Include="Models\MessageRecipient.cs" />
<Compile Include="Services\MessageQueueService.cs" />
<Compile Include="ViewModels\MessageSettingsPartViewModel.cs" />
</ItemGroup>
<ItemGroup>
@ -103,10 +98,6 @@
<Content Include="Placement.info" />
</ItemGroup>
<ItemGroup>
<Content Include="Scripts\submit.js" />
<Content Include="Scripts\submit.min.js">
<DependentUpon>submit.js</DependentUpon>
</Content>
<Content Include="Styles\admin-messaging.min.css">
<DependentUpon>admin-messaging.css</DependentUpon>
</Content>
@ -131,6 +122,10 @@
<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>
@ -139,50 +134,22 @@
<ItemGroup>
<Content Include="web.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AdminQueue\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AdminQueue\Create.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\EditorTemplates\MessageQueueViewModel.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AdminQueue\Edit.cshtml" />
<Content Include="Views\Admin\List.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AdminQueue\List.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AdminMessage\Details.cshtml" />
<Content Include="Views\Admin\Details.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Styles\Web.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Activity-SendMessage.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Scripts\submit.min.js.map">
<DependentUpon>submit.js</DependentUpon>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Scripts\Web.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AdminPriority\Create.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AdminPriority\Edit.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\AdminPriority\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\EditorTemplates\MessagePriorityViewModel.cshtml" />
</ItemGroup>
<ItemGroup />
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@ -1,8 +0,0 @@
(function($) {
$(function() {
$("#layout-content").on("click", ".submit-form", function (e) {
$(this).parents("form:first").submit();
e.preventDefault();
});
});
})(jQuery);

View File

@ -1,2 +0,0 @@
(function(n){n(function(){n("#layout-content").on("click",".submit-form",function(t){n(this).parents("form:first").submit(),t.preventDefault()})})})(jQuery);
//# sourceMappingURL=submit.min.js.map

View File

@ -1,8 +0,0 @@
{
"version":3,
"file":"submit.min.js",
"lineCount":1,
"mappings":"CAAC,QAAQ,CAACA,CAAD,CAAI,CACTA,CAAC,CAAC,QAAQ,CAAA,CAAG,CACTA,CAAC,CAAC,iBAAD,CAAmBC,GAAG,CAAC,OAAO,CAAE,cAAc,CAAE,QAAS,CAACC,CAAD,CAAI,CAC1DF,CAAC,CAAC,IAAD,CAAMG,QAAQ,CAAC,YAAD,CAAcC,OAAO,CAAA,CAAE,CACtCF,CAACG,eAAe,CAAA,CAF0C,CAAvC,CADd,CAAZ,CADQ,EAOX,CAACC,MAAD,CAAQ",
"sources":["submit.js"],
"names":["$","on","e","parents","submit","preventDefault","jQuery"]
}

View File

@ -0,0 +1,28 @@
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);
}
}
}

View File

@ -1,9 +1,5 @@
using System;
using Orchard.Messaging.Models;
namespace Orchard.Messaging.Services {
public interface IMessageChannel : IDependency, IDisposable {
string Name { get; }
void Send(QueuedMessage message);
public interface IMessageChannel : IDependency {
void Process(string payload);
}
}

View File

@ -0,0 +1,10 @@
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; }
}
}

View File

@ -0,0 +1,5 @@
namespace Orchard.Messaging.Services {
public interface IMessageQueueProcessor : ISingletonDependency {
void ProcessQueue();
}
}

View File

@ -0,0 +1,13 @@
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();
}
}

View File

@ -0,0 +1,5 @@
namespace Orchard.Messaging.Services {
public interface IMessageService : IDependency {
void Send(string type, string payload);
}
}

View File

@ -1,24 +0,0 @@
using System;
using Orchard.Messaging.Models;
namespace Orchard.Messaging.Services {
public abstract class MessageChannelBase : Component, IMessageChannel {
~MessageChannelBase() {
Dispose(false);
}
public abstract string Name { get; }
public abstract void Send(QueuedMessage message);
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
public override string ToString() {
return Name;
}
protected virtual void Dispose(bool disposing) { }
}
}

View File

@ -1,8 +1,6 @@
using Orchard.Environment.Extensions;
using Orchard.Tasks;
using Orchard.Tasks;
namespace Orchard.Messaging.Services {
[OrchardFeature("Orchard.Messaging.Queuing")]
public class MessageQueueBackgroundTask : Component, IBackgroundTask {
private readonly IMessageQueueProcessor _messageQueueProcessor;
public MessageQueueBackgroundTask(IMessageQueueProcessor messageQueueProcessor) {
@ -10,7 +8,7 @@ namespace Orchard.Messaging.Services {
}
public void Sweep() {
_messageQueueProcessor.ProcessQueues();
_messageQueueProcessor.ProcessQueue();
}
}
}

View File

@ -1,284 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Orchard.Data;
using Orchard.Environment.Extensions;
using Orchard.Messaging.Models;
using Orchard.Services;
namespace Orchard.Messaging.Services {
public interface IMessageQueueManager : IDependency {
QueuedMessage Send<T>(MessageRecipient recipient, string channelName, T payload, MessagePriority priority = null, int? queueId = null);
QueuedMessage Send<T>(IEnumerable<MessageRecipient> recipients, string channelName, T payload, MessagePriority priority = null, int? queueId = null);
QueuedMessage Send<T>(string recipient, string channelName, T payload, MessagePriority priority = null, int? queueId = null);
QueuedMessage Send<T>(IEnumerable<string> recipients, string channelName, T payload, MessagePriority priority = null, int? queueId = null);
MessageQueue GetQueue(int id);
MessageQueue GetDefaultQueue();
MessagePriority GetPriority(int id);
MessagePriority GetPriority(string name);
IEnumerable<MessagePriority> GetPriorities();
MessagePriority GetDefaultPriority();
IEnumerable<MessagePriority> CreateDefaultPriorities();
void DeletePriority(MessagePriority priority);
IEnumerable<MessageQueue> GetIdleQueues();
void EnterProcessingStatus(MessageQueue queue);
void ExitProcessingStatus(MessageQueue queue);
IEnumerable<MessageQueue> GetQueues();
int CountMessages(int queueId, QueuedMessageStatus? status = null);
IQueryable<QueuedMessage> GetMessages(int queueId, QueuedMessageStatus? status = null, int startIndex = 0, int pageSize = 10);
IEnumerable<QueuedMessage> GetPendingMessages(int queueId, int pageSize = 10);
QueuedMessage GetMessage(int id);
MessageQueue CreateQueue();
MessageQueue CreateDefaultQueue();
IMessageChannel GetChannel(string name);
IEnumerable<IMessageChannel> GetChannels();
void Resume(MessageQueue queue);
void Pause(MessageQueue queue);
MessagePriority CreatePriority(string name, string displayText, int rank);
}
[OrchardFeature("Orchard.Messaging.Queuing")]
public class MessageQueueManager : IMessageQueueManager {
private readonly IClock _clock;
private readonly IRepository<MessageQueueRecord> _queueRepository;
private readonly IRepository<QueuedMessageRecord> _messageRepository;
private readonly IRepository<MessagePriority> _priorityRepository;
public MessageQueueManager(
IClock clock,
IRepository<MessageQueueRecord> queueRepository,
IRepository<QueuedMessageRecord> messageRepository,
IRepository<MessagePriority> priorityRepository,
IEnumerable<IMessageChannel> channels) {
_clock = clock;
_queueRepository = queueRepository;
_messageRepository = messageRepository;
_priorityRepository = priorityRepository;
ChannelsDictionary = channels.ToDictionary(x => x.Name);
}
public QueuedMessage Send<T>(MessageRecipient recipient, string channelName, T payload, MessagePriority priority = null, int? queueId = null) {
return Send(new[] {recipient}, channelName, payload, priority, queueId);
}
public QueuedMessage Send<T>(string recipient, string channelName, T payload, MessagePriority priority = null, int? queueId = null) {
return Send(new[] { recipient }, channelName, payload, priority, queueId);
}
public QueuedMessage Send<T>(IEnumerable<string> recipients, string channelName, T payload, MessagePriority priority = null, int? queueId = null) {
return Send(recipients.Select(x => new MessageRecipient(x)), channelName, payload, priority, queueId);
}
public QueuedMessage Send<T>(IEnumerable<MessageRecipient> recipients, string channelName, T payload, MessagePriority priority = null, int? queueId = null) {
var queue = queueId != null ? GetQueue(queueId.Value) ?? GetDefaultQueue() : GetDefaultQueue();
var queuedMessage = new QueuedMessageRecord {
Payload = ToJson(payload),
Recipients = ToJson(recipients.ToList()),
ChannelName = channelName,
Priority = priority ?? GetDefaultPriority(),
QueueId = queue.Id,
CreatedUtc = _clock.UtcNow,
Status = QueuedMessageStatus.Pending
};
_messageRepository.Create(queuedMessage);
return ActivateMessage(queuedMessage);
}
public IMessageChannel GetChannel(string name) {
return ChannelsDictionary[name];
}
public IEnumerable<IMessageChannel> GetChannels() {
return ChannelsDictionary.Select(x => x.Value);
}
public void Resume(MessageQueue queue) {
if(queue.Status != MessageQueueStatus.Paused)
throw new InvalidOperationException("Cannot resume a queue that is not paused.");
queue.Status = MessageQueueStatus.Idle;
}
public void Pause(MessageQueue queue) {
if (queue.Status == MessageQueueStatus.Paused)
throw new InvalidOperationException("Cannot resume a queue that is already paused.");
queue.Status = MessageQueueStatus.Paused;
}
public MessagePriority CreatePriority(string name, string displayText, int value) {
var priority = new MessagePriority {
Name = name,
DisplayText = displayText,
Value = value
};
_priorityRepository.Create(priority);
return priority;
}
public IDictionary<string, IMessageChannel> ChannelsDictionary { get; private set; }
public MessageQueue GetDefaultQueue() {
return ActivateQueue(_queueRepository.Table.FirstOrDefault() ?? CreateDefaultQueue());
}
public MessagePriority GetPriority(int id) {
return _priorityRepository.Get(id);
}
public MessagePriority GetPriority(string name) {
return _priorityRepository.Get(x => x.Name == name);
}
public IEnumerable<MessagePriority> GetPriorities() {
return _priorityRepository.Table.Where(x => !x.Archived).OrderBy(x => x.Value).ToList();
}
public MessageQueue GetQueue(int id) {
var record = _queueRepository.Get(id);
return record != null ? ActivateQueue(record) : null;
}
public MessagePriority GetDefaultPriority() {
return _priorityRepository.Table.OrderBy(x => x.Value).FirstOrDefault() ?? CreateDefaultPriorities().First();
}
public IEnumerable<MessagePriority> CreateDefaultPriorities() {
var priorities = new List<MessagePriority> {
new MessagePriority {
Name = "Low",
DisplayText = "Low",
Value = 1
},
new MessagePriority {
Name = "Normal",
DisplayText = "Normal",
Value = 2
},
new MessagePriority {
Name = "High",
DisplayText = "High",
Value = 3
},
};
foreach (var priority in priorities) {
_priorityRepository.Create(priority);
}
return priorities;
}
public void DeletePriority(MessagePriority priority) {
priority.Archived = true;
priority.ArchivedUtc = _clock.UtcNow;
}
public IEnumerable<MessageQueue> GetIdleQueues() {
return _queueRepository.Table.Where(x => x.Status == MessageQueueStatus.Idle).Select(x => ActivateQueue(x));
}
public void EnterProcessingStatus(MessageQueue queue) {
if(queue == null) throw new ArgumentNullException("queue");
if (queue.Status == MessageQueueStatus.Paused) throw new InvalidOperationException("Cannot process a paused queue. Think about it.");
if (queue.Status == MessageQueueStatus.Processing) throw new InvalidOperationException("Cannot process an already processing queue. What's the point?");
queue.Status = MessageQueueStatus.Processing;
queue.StartedUtc = _clock.UtcNow;
}
public void ExitProcessingStatus(MessageQueue queue) {
if(queue == null) throw new ArgumentNullException("queue");
if(queue.Status == MessageQueueStatus.Paused) throw new InvalidOperationException("Cannot stop a paused queue.");
if(queue.Status == MessageQueueStatus.Idle) throw new InvalidOperationException("Only processing queues can be stopped.");
queue.Status = MessageQueueStatus.Idle;
queue.EndedUtc = _clock.UtcNow;
}
public IEnumerable<MessageQueue> GetQueues() {
return _queueRepository.Table.Select(ActivateQueue);
}
public int CountMessages(int queueId, QueuedMessageStatus? status = null) {
return GetMessagesQuery(queueId, status).Count();
}
public IQueryable<QueuedMessage> GetMessages(int queueId, QueuedMessageStatus? status = null, int startIndex = 0, int pageSize = 10) {
return GetMessagesQuery(queueId, status).Skip(startIndex).Take(pageSize).Select(ActivateMessage).AsQueryable();
}
public QueuedMessage GetMessage(int id) {
return ActivateMessage(_messageRepository.Get(id));
}
public MessageQueue CreateQueue() {
var record = new MessageQueueRecord {
Status = MessageQueueStatus.Idle
};
_queueRepository.Create(record);
return ActivateQueue(record);
}
MessageQueue IMessageQueueManager.CreateDefaultQueue() {
var queue = CreateQueue();
queue.Name = "Default";
return queue;
}
public IQueryable<QueuedMessageRecord> GetMessagesQuery(int queueId, QueuedMessageStatus? status = null) {
var query = _messageRepository.Table.Where(x => x.QueueId == queueId);
if (status != null)
query = query.Where(x => x.Status == status.Value);
query = query.OrderByDescending(x => x.CreatedUtc);
return query;
}
public IEnumerable<QueuedMessage> GetPendingMessages(int queueId, int pageSize = 10) {
return _messageRepository.Table
.Where(x => x.Status == QueuedMessageStatus.Pending && x.QueueId == queueId)
.OrderBy(x => x.Priority.Value)
.ThenBy(x => x.CreatedUtc)
.Take(pageSize)
.Select(ActivateMessage)
.ToList();
}
private QueuedMessage ActivateMessage(QueuedMessageRecord record) {
return new QueuedMessage(record) {
QueueField = new Lazy<MessageQueue>(() => GetQueue(record.QueueId)),
RecipientsField = new Lazy<IEnumerable<MessageRecipient>>(() => ParseRecipients(record.Recipients)),
ChannelField = new Lazy<IMessageChannel>(() => GetChannel(record.ChannelName))
};
}
private MessageQueue ActivateQueue(MessageQueueRecord record) {
return new MessageQueue(record);
}
private static IEnumerable<MessageRecipient> ParseRecipients(string data) {
return String.IsNullOrWhiteSpace(data) ? Enumerable.Empty<MessageRecipient>() : JsonConvert.DeserializeObject<List<MessageRecipient>>(data);
}
private MessageQueueRecord CreateDefaultQueue() {
var queue = new MessageQueueRecord {
Name = "Default",
Status = MessageQueueStatus.Idle,
};
_queueRepository.Create(queue);
return queue;
}
private static string ToJson(object value) {
return value != null ? JsonConvert.SerializeObject(value) : null;
}
}
}

View File

@ -1,65 +1,62 @@
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 interface IMessageQueueProcessor : IDependency {
void ProcessQueues();
}
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 class MessageQueueProcessor : Component, IMessageQueueProcessor {
private readonly IMessageQueueManager _manager;
private readonly IClock _clock;
public MessageQueueProcessor(IMessageQueueManager manager, IClock clock) {
_manager = manager;
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 void ProcessQueues() {
var queuesToProcess = GetQueuesToProcess();
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;
foreach (var queue in queuesToProcess.AsParallel()) {
ProcessQueue(queue);
}
}
private IEnumerable<MessageQueue> GetQueuesToProcess() {
return _manager.GetIdleQueues().ToList();
}
private void ProcessQueue(MessageQueue queue) {
_manager.EnterProcessingStatus(queue);
var messages = _manager.GetPendingMessages(queue.Id);
while (messages.Any()) {
foreach (var message in messages.AsParallel()) {
ProcessMessage(message);
while ((messages = _messageQueueService.Value.GetMessages(QueuedMessageStatus.Pending, 0, 10).ToArray()).Any()) {
foreach (var message in messages.AsParallel()) {
ProcessMessage(message);
}
}
}
finally {
_rwl.ExitWriteLock();
}
messages = _manager.GetPendingMessages(queue.Id);
}
_manager.ExitProcessingStatus(queue);
}
private void ProcessMessage(QueuedMessage message) {
var channel = message.Channel;
private void ProcessMessage(QueuedMessageRecord message) {
message.StartedUtc = _clock.UtcNow;
message.StartedUtc = _clock.Value.UtcNow;
message.Status = QueuedMessageStatus.Sending;
Logger.Debug("Sending message ID {0}.", message.Id);
if (!message.Recipients.Any()) {
message.Status = QueuedMessageStatus.Faulted;
message.Result = String.Format("Cannot send message {0} because at least on recipient is required.", message.Id);
Logger.Error(message.Result);
return;
}
try {
channel.Send(message);
_messageService.Value.Send(message.Type, message.Payload);
message.Status = QueuedMessageStatus.Sent;
Logger.Debug("Sent message ID {0}.", message.Id);
}
@ -69,7 +66,7 @@ namespace Orchard.Messaging.Services {
Logger.Error(e, "An unexpected error while sending message {0}. Error message: {1}.", message.Id, e);
}
finally {
message.CompletedUtc = _clock.UtcNow;
message.CompletedUtc = _clock.Value.UtcNow;
}
}
}

View File

@ -0,0 +1,80 @@
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;
}
}
}

View File

@ -0,0 +1,10 @@
namespace Orchard.Messaging.Services {
/// <summary>
/// Default empty implementation of <see cref="IMessageChannelSelector"/>
/// </summary>
public class NullMessageChannelSelector : IMessageChannelSelector {
public MessageChannelSelectorResult GetChannel(string messageType, object payload) {
return null;
}
}
}

View File

@ -1,7 +1,7 @@
.navicon-messaging {
.navicon-message-queue {
background-image: url(./images/menu.messaging.png) !important;
}
.navicon-messaging:hover {
.navicon-message-queue:hover {
background-position: 0 -30px !important;
}

View File

@ -10,59 +10,49 @@ using Orchard.Tests;
namespace Orchard.Messaging.Tests {
[TestFixture]
public class MessageQueueProcessorTests : DatabaseEnabledTestsBase {
private List<QueuedMessage> _messages;
private List<QueuedMessageRecord> _messages;
protected override IEnumerable<Type> DatabaseTypes {
get {
yield return typeof(MessageQueueRecord);
yield return typeof(MessagePriority);
yield return typeof(QueuedMessageRecord);
}
}
public override void Register(ContainerBuilder builder) {
var messageManagerMock = new Mock<IMessageQueueManager>();
var queue = new MessageQueue(new MessageQueueRecord {
Name = "Default"
});
var messageManagerMock = new Mock<IMessageQueueService>();
builder.RegisterInstance(messageManagerMock.Object);
builder.RegisterType<MessageQueueProcessor>().As<IMessageQueueProcessor>();
builder.RegisterType<StubMessageChannel>().As<IMessageChannel>();
var queues = new List<MessageQueue> {queue};
_messages = new List<QueuedMessage> {
_messages = new List<QueuedMessageRecord> {
CreateMessage("Message 1"),
CreateMessage("Message 2")
};
messageManagerMock
.Setup(x => x.Send(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessagePriority>(), null))
.Setup(x => x.Enqueue(It.IsAny<string>(), It.IsAny<string>(), 0))
.Callback(() => _clock.Advance(TimeSpan.FromSeconds(1)))
.Returns(new QueuedMessage(new QueuedMessageRecord ()));
messageManagerMock.Setup(x => x.GetIdleQueues()).Returns(queues);
messageManagerMock.Setup(x => x.EnterProcessingStatus(queue)).Callback(() => {
queue.Record.Status = MessageQueueStatus.Processing;
queue.Record.StartedUtc = _clock.UtcNow;
});
.Returns(new QueuedMessageRecord ());
//messageManagerMock.Setup(x => x.EnterProcessingStatus()).Callback(() => {
// queue.Record.Status = MessageQueueStatus.Processing;
// queue.Record.StartedUtc = _clock.UtcNow;
//});
}
[Test]
public void ProcessingQueueWithEnoughTimeSendsAllMessages() {
var processor = _container.Resolve<IMessageQueueProcessor>();
processor.ProcessQueues();
processor.ProcessQueue();
foreach (var message in _messages) {
Assert.That(message.Status, Is.EqualTo(QueuedMessageStatus.Sent));
}
}
private QueuedMessage CreateMessage(string subject) {
return new QueuedMessage(new QueuedMessageRecord {Id = 1, Payload = "some payload data"}) {
ChannelField = new Lazy<IMessageChannel>(() => _container.Resolve<IMessageChannel>(new NamedParameter("simulatedProcessingTime", TimeSpan.FromSeconds(1)), new NamedParameter("clock", _clock))),
RecipientsField = new Lazy<IEnumerable<MessageRecipient>>(() => new[]{ new MessageRecipient("recipient@domain.com") })
};
private QueuedMessageRecord CreateMessage(string subject) {
return new QueuedMessageRecord {Id = 1, Type = "Email", Payload = "some payload data"};
}
}
}

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Autofac;
using NUnit.Framework;
using Orchard.Messaging.Models;
@ -12,51 +11,13 @@ namespace Orchard.Messaging.Tests {
public class MessageQueueTests : DatabaseEnabledTestsBase {
protected override IEnumerable<Type> DatabaseTypes {
get {
yield return typeof(MessageQueueRecord);
yield return typeof(MessagePriority);
yield return typeof(QueuedMessageRecord);
}
}
public override void Register(ContainerBuilder builder) {
builder.RegisterType<MessageQueueManager>().As<IMessageQueueManager>();
builder.RegisterType<MessageQueueService>().As<IMessageQueueService>();
}
[Test]
public void SendingWithoutSpecifyingQueueUsesDefaultQueue() {
var manager = _container.Resolve<IMessageQueueManager>();
var createdMessage = manager.Send("john.doe@live.com", "email", "This is a test subject");
var defaultQueue = manager.GetDefaultQueue();
Assert.That(createdMessage.Queue.Id, Is.EqualTo(defaultQueue.Id));
}
[Test]
public void SendingWithoutExistingPrioritiesCreatesDefaultPriorities() {
var manager = _container.Resolve<IMessageQueueManager>();
var createdMessage = manager.Send("john.doe@live.com", "email", "This is a test subject");
var low = manager.GetPriority("Low");
var normal = manager.GetPriority("Normal");
var high = manager.GetPriority("High");
Assert.That(low, Is.Not.Null);
Assert.That(normal, Is.Not.Null);
Assert.That(high, Is.Not.Null);
}
[Test]
public void SendingWithoutExplicitPriorityUsesDefaultPriority() {
var manager = _container.Resolve<IMessageQueueManager>();
var createdMessage = manager.Send("john.doe@live.com", "email", "This is a test subject");
var defaultPriority = manager.GetDefaultPriority();
Assert.That(createdMessage.Priority.Id, Is.EqualTo(defaultPriority.Id));
}
[Test]
public void DefaultPriorityIsLowestPriority() {
var manager = _container.Resolve<IMessageQueueManager>();
var priorities = manager.CreateDefaultPriorities().ToList();
var expectedPriority = priorities.OrderByDescending(x => x.Value).First();
var actualPriority = manager.GetDefaultPriority();
Assert.That(expectedPriority.Id, Is.EqualTo(actualPriority.Id));
}
}
}

View File

@ -1,5 +1,4 @@
using System;
using Orchard.Messaging.Models;
using Orchard.Messaging.Services;
using Orchard.Tests.Stubs;
@ -17,9 +16,7 @@ namespace Orchard.Messaging.Tests {
public void Dispose() {
}
public string Name { get { return ChannelName; } }
public void Send(QueuedMessage message) {
public void Process(string payload) {
_clock.Advance(_simulatedProcessingTime);
}
}

View File

@ -1,14 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Orchard.Messaging.ViewModels {
public class MessagePriorityViewModel {
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string DisplayText { get; set; }
public int Value { get; set; }
}
}

View File

@ -1,12 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace Orchard.Messaging.ViewModels {
public class MessageQueueViewModel {
public int Id { get; set; }
[Required]
public string Name { get; set; }
public string ReturnUrl { get; set; }
}
}

View File

@ -1,9 +1,7 @@
using System.Collections.Generic;
using Orchard.Messaging.Models;
using Orchard.Messaging.Models;
namespace Orchard.Messaging.ViewModels {
public class MessageSettingsPartViewModel {
public MessageSettingsPart MessageSettings { get; set; }
public IEnumerable<string> ChannelServices { get; set; }
}
}

View File

@ -1,4 +0,0 @@
<div>
<div class="envelope"></div>
</div>

View File

@ -3,7 +3,7 @@
@{
Style.Include("admin-messaging.css", "admin-messaging.min.css");
Layout.Title = T("Message Details");
var message = (QueuedMessage)Model.Message;
var message = (QueuedMessageRecord)Model.Message;
var returnUrl = (string)Model.ReturnUrl;
}
<fieldset class="summary">
@ -22,25 +22,17 @@
<label>@T("Priority")</label>
<span>@message.Priority</span>
</li>
<li>
<label>@T("Recipients")</label>
<span>@String.Join(", ", message.Recipients)</span>
</li>
<li>
<label>@T("Created")</label>
<span>@message.CreatedUtc</span>
</li>
<li>
<label>@T("Started")</label>
<span>@message.StartedUtc</span>
</li>
<li>
<label>@T("Completed")</label>
<span>@message.CompletedUtc</span>
</li>
<li>
<label>@T("Payload")</label>
<span>@message.Record.Payload</span>
<span>@message.Payload</span>
</li>
</ul>
</fieldset>

View File

@ -1,17 +1,19 @@
@using Orchard.Messaging.Models
@using Orchard.Localization
@using Orchard.Messaging.Models
@using Orchard.Messaging.ViewModels
@using Orchard.Utility.Extensions
@{
var queue = (MessageQueue) Model.Queue;
var messages = (IEnumerable<QueuedMessage>) Model.Messages;
var filter = (MessagesFilter) Model.Filter;
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}", (queue.Status).ToString().HtmlClassify())">@T("Status: {0}", queue.Status)</span>
@if (queue.Status == MessageQueueStatus.Paused) {
<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 {
@ -44,26 +46,37 @@ else {
<tr>
<th>@T("Status")</th>
<th>@T("Priority")</th>
<th>@T("Recipients")</th>
<th>@T("Payload")</th>
<th>@T("Type")</th>
<th>@T("Created")</th>
<th>@T("Started")</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>@message.Priority.Name</td>
<td>@String.Join(", ", message.Recipients).Ellipsize(50)</td>
<td>@message.Record.Payload.Ellipsize(100, "...")</td>
<td>@message.CreatedUtc</td>
<td>@message.StartedUtc</td>
<td>@message.CompletedUtc</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", "AdminMessage", new { message.Id, returnUrl = Request.Url.PathAndQuery })">@T("Details")</a>
<a href="@Url.Action("Details", "Admin", new { message.Id, returnUrl = Request.Url.PathAndQuery })">@T("Details")</a>
</td>
</tr>
}

View File

@ -1,10 +0,0 @@
@model Orchard.Messaging.ViewModels.MessagePriorityViewModel
@{
Layout.Title = T("Create Priority");
}
@Html.ValidationSummary()
@using (Html.BeginFormAntiForgeryPost()) {
@Html.EditorForModel()
<button type="submit">@T("Create")</button>
<a href="@Url.Action("Index")">@T("Cancel")</a>
}

View File

@ -1,9 +0,0 @@
@model Orchard.Messaging.ViewModels.MessagePriorityViewModel
@{
Layout.Title = T("Edit Priority");
}
@using (Html.BeginFormAntiForgeryPost()) {
@Html.EditorForModel()
<button type="submit">@T("Save")</button>
<a class="button" href="@Url.Action("Index")">@T("Back")</a>
}

View File

@ -1,40 +0,0 @@
@{
var priorities = (IEnumerable<dynamic>)Model.Priorities;
Script.Require("jQuery").AtFoot();
Script.Include("submit.js", "submit.min.js").AtFoot();
Style.Include("admin-messaging.css", "admin-messaging.min.css");
Layout.Title = T("Manage Priorities");
}
<div class="manage">
@Html.ActionLink(T("Create Priority").ToString(), "Create", new { Area = "Orchard.Messaging" }, new { @class = "button primaryAction" })
</div>
@if (!priorities.Any()) {
<div class="message info">@T("There are no priorities. <a href=\"{0}\">Create a new Priority</a>.", Url.Action("Create", "AdminPriority"))</div>
}
else {
using (Html.BeginFormAntiForgeryPost()) {
<table class="items priorities">
<thead>
<tr>
<th>@T("Name")</th>
<th>@T("Display Text")</th>
<th>@T("Value")</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var priority in priorities) {
<tr>
<td>@priority.Name</td>
<td>@priority.DisplayText</td>
<td>@priority.Value</td>
<td>
<a href="@Url.Action("Edit", "AdminPriority", new { priority.Id })">@T("Edit")</a>@T(" | ")
<a href="@Url.Action("Delete", "AdminPriority", new { priority.Id })" itemprop="RemoveUrl UnsafeUrl">@T("Delete")</a>
</td>
</tr>
}
</tbody>
</table>
}
}

View File

@ -1,9 +0,0 @@
@model Orchard.Messaging.ViewModels.MessageQueueViewModel
@{
Layout.Title = T("Create Queue");
}
@using (Html.BeginFormAntiForgeryPost()) {
@Html.EditorForModel()
<button type="submit">@T("Create")</button>
<a href="@Url.Action("Index")">@T("Cancel")</a>
}

View File

@ -1,10 +0,0 @@
@model Orchard.Messaging.ViewModels.MessageQueueViewModel
@{
Layout.Title = T("Edit Queue");
var backUrl = !String.IsNullOrWhiteSpace(Model.ReturnUrl) ? Model.ReturnUrl : Url.Action("Index");
}
@using (Html.BeginFormAntiForgeryPost()) {
@Html.EditorForModel()
<button type="submit">@T("Save")</button>
<a class="button" href="@backUrl">@T("Back")</a>
}

View File

@ -1,62 +0,0 @@
@using Orchard.Messaging.Models
@using Orchard.Utility.Extensions
@{
var queues = (IEnumerable<dynamic>) Model.Queues;
Script.Require("jQuery").AtFoot();
Script.Include("submit.js", "submit.min.js").AtFoot();
Style.Include("admin-messaging.css", "admin-messaging.min.css");
Layout.Title = T("Manage Queues");
}
<div class="manage">
@Html.ActionLink(T("Create Queue").ToString(), "Create", new { Area = "Orchard.Messaging" }, new { @class = "button primaryAction" })
</div>
@if (!queues.Any()) {
<div class="message info">@T("There are no queues. <a href=\"{0}\">Create a new Queue</a>.", Url.Action("Create", "AdminQueue"))</div>
}
else {
<table class="items queues">
<thead>
<tr>
<th>@T("Name")</th>
<th>@T("Status")</th>
<th>@T("Time Slice")</th>
<th>@T("Update Interval")</th>
<th>@T("Pending")</th>
<th>@T("Sent")</th>
<th>@T("Faulted")</th>
<th>@T("Last Run")</th>
<th>@T("Next Run")</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var queue in queues) {
<tr>
<td><a href="@Url.Action("List", "AdminQueue", new { queue.Id })">@queue.Name</a></td>
<td><span class="queue-status @String.Format("{0}", ((MessageQueueStatus)queue.Status).ToString().HtmlClassify())">@queue.Status</span></td>
<td>@queue.TimeSlice</td>
<td>@queue.UpdateFrequency</td>
<td>@queue.Pending</td>
<td>@queue.Sent</td>
<td>@queue.Faulted</td>
<td>@queue.EndedUtc</td>
<td>@queue.NextRun</td>
<td>
<ul class="horizontal">
<li>
@if (queue.Status == MessageQueueStatus.Paused) {
using (Html.BeginFormAntiForgeryPost(Url.Action("Resume", new { id = queue.Id }))) { <a href="#" class="queue-control play submit-form">@T("Resume")</a> }
}
@if (queue.Status == MessageQueueStatus.Idle || queue.Status == MessageQueueStatus.Processing) {
using (Html.BeginFormAntiForgeryPost(Url.Action("Pause", new { id = queue.Id }))) { <a href="#" class="queue-control pause submit-form">@T("Pause")</a> }
}
</li>
<li>&nbsp;|&nbsp;<a href="@Url.Action("Edit", "AdminQueue", new { queue.Id })">@T("Properties")</a></li>
</ul>
<div class="clear"></div>
</td>
</tr>
}
</tbody>
</table>
}

View File

@ -1,17 +0,0 @@
@model Orchard.Messaging.ViewModels.MessagePriorityViewModel
<fieldset>
<div>
@Html.HiddenFor(m => m.Id)
@Html.LabelFor(m => m.Name)
@Html.TextBoxFor(m => m.Name, new { @class = "text medium" })
</div>
<div>
@Html.LabelFor(m => m.DisplayText)
@Html.TextBoxFor(m => m.DisplayText, new { @class = "text medium" })
</div>
<div>
@Html.LabelFor(m => m.Value)
@Html.TextBoxFor(m => m.Value, new { @class = "text small" })
<span class="hint">@T("The higher the value, the higher the priority.")</span>
</div>
</fieldset>

View File

@ -1,21 +1,9 @@
@model MessageSettingsPartViewModel
@using Orchard.Messaging.Models;
@using Orchard.Messaging.ViewModels;
<fieldset>
<legend>@T("Messaging")</legend>
<div>
<label for="@Html.FieldIdFor(m => m.MessageSettings.DefaultChannelService)">@T("Default channel service for messages")</label>
@if ( Model.ChannelServices.Any() ) {
<select id="@Html.FieldIdFor(m => m.MessageSettings.DefaultChannelService)" name="@Html.FieldNameFor(m => m.MessageSettings.DefaultChannelService)">
@foreach ( var service in Model.ChannelServices ) {
<option @if(Model.MessageSettings.DefaultChannelService == service) { <text>selected="selected"</text> } value="@service">@service</option>
}
</select>
}
else {
<span class="hint">@T("You must enable a messaging channel (e.g., Orchard.Email) before being able to send messages.")</span>
}
</select>
</div>
</fieldset>
@*
<fieldset>
<legend>@T("Messaging")</legend>
<div>
</div>
</fieldset>
*@

View File

@ -11,29 +11,29 @@ namespace Orchard.Users.Drivers {
public class UserPartDriver : ContentPartDriver<UserPart> {
protected override void Importing(UserPart part, ContentManagement.Handlers.ImportContentContext context) {
part.Record.Email = context.Attribute(part.PartDefinition.Name, "Email");
part.Record.EmailChallengeToken = context.Attribute(part.PartDefinition.Name, "EmailChallengeToken");
part.Record.EmailStatus = (UserStatus)Enum.Parse(typeof(UserStatus), context.Attribute(part.PartDefinition.Name, "EmailStatus"));
part.Record.HashAlgorithm = context.Attribute(part.PartDefinition.Name, "HashAlgorithm");
part.Record.NormalizedUserName = context.Attribute(part.PartDefinition.Name, "NormalizedUserName");
part.Record.Password = context.Attribute(part.PartDefinition.Name, "Password");
part.Record.PasswordFormat = (MembershipPasswordFormat)Enum.Parse(typeof(MembershipPasswordFormat), context.Attribute(part.PartDefinition.Name, "PasswordFormat"));
part.Record.PasswordSalt = context.Attribute(part.PartDefinition.Name, "PasswordSalt");
part.Record.RegistrationStatus = (UserStatus)Enum.Parse(typeof(UserStatus), context.Attribute(part.PartDefinition.Name, "RegistrationStatus"));
part.Record.UserName = context.Attribute(part.PartDefinition.Name, "UserName");
part.Email = context.Attribute(part.PartDefinition.Name, "Email");
part.EmailChallengeToken = context.Attribute(part.PartDefinition.Name, "EmailChallengeToken");
part.EmailStatus = (UserStatus)Enum.Parse(typeof(UserStatus), context.Attribute(part.PartDefinition.Name, "EmailStatus"));
part.HashAlgorithm = context.Attribute(part.PartDefinition.Name, "HashAlgorithm");
part.NormalizedUserName = context.Attribute(part.PartDefinition.Name, "NormalizedUserName");
part.Password = context.Attribute(part.PartDefinition.Name, "Password");
part.PasswordFormat = (MembershipPasswordFormat)Enum.Parse(typeof(MembershipPasswordFormat), context.Attribute(part.PartDefinition.Name, "PasswordFormat"));
part.PasswordSalt = context.Attribute(part.PartDefinition.Name, "PasswordSalt");
part.RegistrationStatus = (UserStatus)Enum.Parse(typeof(UserStatus), context.Attribute(part.PartDefinition.Name, "RegistrationStatus"));
part.UserName = context.Attribute(part.PartDefinition.Name, "UserName");
}
protected override void Exporting(UserPart part, ContentManagement.Handlers.ExportContentContext context) {
context.Element(part.PartDefinition.Name).SetAttributeValue("Email", part.Record.Email);
context.Element(part.PartDefinition.Name).SetAttributeValue("EmailChallengeToken", part.Record.EmailChallengeToken);
context.Element(part.PartDefinition.Name).SetAttributeValue("EmailStatus", part.Record.EmailStatus);
context.Element(part.PartDefinition.Name).SetAttributeValue("HashAlgorithm", part.Record.HashAlgorithm);
context.Element(part.PartDefinition.Name).SetAttributeValue("NormalizedUserName", part.Record.NormalizedUserName);
context.Element(part.PartDefinition.Name).SetAttributeValue("Password", part.Record.Password);
context.Element(part.PartDefinition.Name).SetAttributeValue("PasswordFormat", part.Record.PasswordFormat);
context.Element(part.PartDefinition.Name).SetAttributeValue("PasswordSalt", part.Record.PasswordSalt);
context.Element(part.PartDefinition.Name).SetAttributeValue("RegistrationStatus", part.Record.RegistrationStatus);
context.Element(part.PartDefinition.Name).SetAttributeValue("UserName", part.Record.UserName);
context.Element(part.PartDefinition.Name).SetAttributeValue("Email", part.Email);
context.Element(part.PartDefinition.Name).SetAttributeValue("EmailChallengeToken", part.EmailChallengeToken);
context.Element(part.PartDefinition.Name).SetAttributeValue("EmailStatus", part.EmailStatus);
context.Element(part.PartDefinition.Name).SetAttributeValue("HashAlgorithm", part.HashAlgorithm);
context.Element(part.PartDefinition.Name).SetAttributeValue("NormalizedUserName", part.NormalizedUserName);
context.Element(part.PartDefinition.Name).SetAttributeValue("Password", part.Password);
context.Element(part.PartDefinition.Name).SetAttributeValue("PasswordFormat", part.PasswordFormat);
context.Element(part.PartDefinition.Name).SetAttributeValue("PasswordSalt", part.PasswordSalt);
context.Element(part.PartDefinition.Name).SetAttributeValue("RegistrationStatus", part.RegistrationStatus);
context.Element(part.PartDefinition.Name).SetAttributeValue("UserName", part.UserName);
}
}
}

View File

@ -1,4 +1,5 @@
using Orchard.ContentManagement;
using System.Web.Security;
using Orchard.ContentManagement;
using Orchard.Security;
namespace Orchard.Users.Models {
@ -12,6 +13,31 @@ namespace Orchard.Users.Models {
set { Store(x => x.UserName, value); }
}
public string EmailChallengeToken {
get { return Retrieve(x => x.EmailChallengeToken); }
set { Store(x => x.EmailChallengeToken, value); }
}
public string HashAlgorithm {
get { return Retrieve(x => x.HashAlgorithm); }
set { Store(x => x.HashAlgorithm, value); }
}
public string Password {
get { return Retrieve(x => x.Password); }
set { Store(x => x.Password, value); }
}
public MembershipPasswordFormat PasswordFormat {
get { return Retrieve(x => x.PasswordFormat); }
set { Store(x => x.PasswordFormat, value); }
}
public string PasswordSalt {
get { return Retrieve(x => x.PasswordSalt); }
set { Store(x => x.PasswordSalt, value); }
}
public string Email {
get { return Retrieve(x => x.Email); }
set { Store(x => x.Email, value); }

View File

@ -6,14 +6,14 @@ namespace Orchard.Users.ViewModels {
public class UserEditViewModel {
[Required]
public string UserName {
get { return User.As<UserPart>().Record.UserName; }
set { User.As<UserPart>().Record.UserName = value; }
get { return User.As<UserPart>().UserName; }
set { User.As<UserPart>().UserName = value; }
}
[Required]
public string Email {
get { return User.As<UserPart>().Record.Email; }
set { User.As<UserPart>().Record.Email = value; }
get { return User.As<UserPart>().Email; }
set { User.As<UserPart>().Email = value; }
}
public IContent User { get; set; }

View File

@ -14,7 +14,8 @@ namespace Upgrade {
builder
.AddImageSet("upgrade")
.Add(T("Upgrade to 1.8"), "0", menu => menu.Action("Index", "Route", new { area = "Upgrade" })
.Add(T("Infoset (1.8)"), "1", item => item.Action("Index", "Infoset", new { area = "Upgrade" }).LocalNav().Permission(StandardPermissions.SiteOwner))
.Add(T("Infoset (1.8)"), "1.0", item => item.Action("Index", "Infoset", new { area = "Upgrade" }).LocalNav().Permission(StandardPermissions.SiteOwner))
.Add(T("Messaging (1.8)"), "1.1", item => item.Action("Index", "Messaging", new { area = "Upgrade" }).LocalNav().Permission(StandardPermissions.SiteOwner))
.Add(T("Media (1.7)"), "2", item => item.Action("Index", "Media", new { area = "Upgrade" }).LocalNav().Permission(StandardPermissions.SiteOwner))
.Add(T("Taxonomies (1.7)"), "3", item => item.Action("Index", "Taxonomy", new { area = "Upgrade" }).LocalNav().Permission(StandardPermissions.SiteOwner))
.Add(T("Content Picker (1.7)"), "4", item => item.Action("Index", "ContentPicker", new { area = "Upgrade" }).LocalNav().Permission(StandardPermissions.SiteOwner))

View File

@ -211,6 +211,8 @@ namespace Upgrade.Controllers {
_upgradeService.ExecuteReader("DROP TABLE " + _upgradeService.GetPrefixedTableName("Orchard_Warmup_WarmupSettingsPartRecord"), null);
#endregion
// todo: user records
_orchardServices.Notifier.Information(T("Site Settings migrated successfully"));
return View();

View File

@ -0,0 +1,99 @@
using System.Web.Mvc;
using Newtonsoft.Json;
using Orchard;
using Orchard.Data;
using Orchard.Email.Models;
using Orchard.Localization;
using Orchard.Security;
using Orchard.UI.Admin;
using Orchard.UI.Notify;
using Orchard.Workflows.Models;
using Upgrade.Services;
namespace Upgrade.Controllers {
[Admin]
public class MessagingController : Controller {
private readonly IUpgradeService _upgradeService;
private readonly IOrchardServices _orchardServices;
private readonly IRepository<ActivityRecord> _repository;
public MessagingController(
IUpgradeService upgradeService,
IOrchardServices orchardServices,
IRepository<ActivityRecord> repository) {
_upgradeService = upgradeService;
_orchardServices = orchardServices;
_repository = repository;
}
public Localizer T { get; set; }
public ActionResult Index() {
var found = false;
_upgradeService.ExecuteReader("SELECT * FROM " + _upgradeService.GetPrefixedTableName("Orchard_Workflows_ActivityRecord") + " WHERE Name = 'SendEmail'",
(reader, connection) => {
found = true;
});
if (!found) {
_orchardServices.Notifier.Warning(T("This step is unnecessary as no Send Email activities were found."));
}
return View();
}
[HttpPost, ActionName("Index")]
public ActionResult IndexPOST() {
if (!_orchardServices.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to upgrade.")))
return new HttpUnauthorizedResult();
_upgradeService.ExecuteReader("SELECT * FROM " + _upgradeService.GetPrefixedTableName("Orchard_Workflows_ActivityRecord") + " WHERE Name = 'SendEmail'",
(reader, connection) => {
var record = _repository.Get((int) reader["Id"]);
if (record == null) {
return;
}
var state = JsonConvert.DeserializeAnonymousType(record.State, new {
Body = "",
Subject = "",
Recipient = "",
RecipientOther = "",
});
var newState = new EmailMessage {
Body = state.Body,
Subject = state.Subject
};
if (!newState.Body.StartsWith("<p ")) {
newState.Body =
"<p style=\"font-family:Arial, Helvetica; font-size:10pt;\">"
+ newState.Body
+ System.Environment.NewLine
+ "</p>";
}
if (state.Recipient == "owner") {
newState.Recipients = new [] {"{User.Current.Email}"};
}
else if (state.Recipient == "author") {
newState.Recipients = new[] { "{Content.Author.Email}" };
}
else if (state.Recipient == "admin") {
newState.Recipients = new[] { "{Site.SuperUser.Email}" };
}
else if (state.Recipient == "other") {
newState.Recipients = state.RecipientOther.Split(',');
}
record.State = JsonConvert.SerializeObject(newState);
});
_orchardServices.Notifier.Information(T("Email activities updated successfully"));
return RedirectToAction("Index");
}
}
}

View File

@ -44,6 +44,10 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<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="NHibernate, Version=3.3.1.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\lib\nhibernate\NHibernate.dll</HintPath>
@ -74,6 +78,7 @@
<Content Include="Styles\Web.config" />
<Compile Include="Controllers\ContentPickerController.cs" />
<Compile Include="Controllers\InfosetController.cs" />
<Compile Include="Controllers\MessagingController.cs" />
<Compile Include="Controllers\TaxonomyController.cs" />
<Compile Include="Controllers\MediaController.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
@ -96,6 +101,10 @@
<Project>{66FCCD76-2761-47E3-8D11-B45D0001DDAA}</Project>
<Name>Orchard.Autoroute</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Email\Orchard.Email.csproj">
<Project>{05660F47-D649-48BD-9DED-DF4E01E7CFF9}</Project>
<Name>Orchard.Email</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.MediaLibrary\Orchard.MediaLibrary.csproj">
<Project>{73a7688a-5bd3-4f7e-adfa-ce36c5a10e3b}</Project>
<Name>Orchard.MediaLibrary</Name>
@ -104,6 +113,10 @@
<Project>{194D3CCC-1153-474D-8176-FDE8D7D0D0BD}</Project>
<Name>Orchard.Widgets</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Workflows\Orchard.Workflows.csproj">
<Project>{7059493c-8251-4764-9c1e-2368b8b485bc}</Project>
<Name>Orchard.Workflows</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="AdminMenu.cs" />
@ -136,6 +149,9 @@
<ItemGroup>
<Content Include="Views\Infoset\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Messaging\Index.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@ -0,0 +1,14 @@
@using Orchard.Utility.Extensions
@{ Layout.Title = T("Migrate Messaging").ToString(); }
@using (Html.BeginFormAntiForgeryPost()) {
Html.ValidationSummary();
<fieldset>
<legend>@T("Migrating Email Activities:")</legend>
<span class="hint">@T("This migration step will update the existing Send Email activities to use the new Message Queueing functionality.")</span>
</fieldset>
<fieldset>
<button type="submit">@T("Migrate")</button>
</fieldset>
}

View File

@ -1,7 +1,9 @@
using Orchard.Events;
using System;
using Orchard.Events;
using Orchard.Messaging.Models;
namespace Orchard.Messaging.Events {
[Obsolete]
public interface IMessageEventHandler : IEventHandler {
void Sending(MessageContext context);
void Sent(MessageContext context);

View File

@ -5,6 +5,7 @@ using System.Net.Mail;
using Orchard.ContentManagement.Records;
namespace Orchard.Messaging.Models {
[Obsolete]
public class MessageContext {
public MailMessage MailMessage { get; private set; }
public string Type { get; set; }

View File

@ -7,6 +7,7 @@ using Orchard.Messaging.Models;
using Orchard.ContentManagement.Records;
namespace Orchard.Messaging.Services {
[Obsolete]
public class DefaultMessageManager : IMessageManager {
private readonly IMessageEventHandler _messageEventHandler;
private readonly IEnumerable<IMessagingChannel> _channels;

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using Orchard.ContentManagement.Records;
namespace Orchard.Messaging.Services {
[Obsolete]
public interface IMessageManager : IDependency {
/// <summary>
/// Sends a message to a channel using a content item as the recipient

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using Orchard.Messaging.Models;
namespace Orchard.Messaging.Services {
[Obsolete("Use the new IMessageChannel interface instead.")]
[Obsolete]
public interface IMessagingChannel : IDependency {
/// <summary>
/// Actually sends the message though this channel