createBody)
{
if (performingUser == null) throw new ArgumentNullException("performingUser");
if (mailingUser == null) throw new ArgumentNullException("mailingUser");
if (content == null) throw new ArgumentNullException("content");
- if (allVersions == null) throw new ArgumentNullException("allVersions");
if (http == null) throw new ArgumentNullException("http");
if (createSubject == null) throw new ArgumentNullException("createSubject");
- if (createBody == null) throw new ArgumentNullException("createBody");
-
- //Ensure they are sorted: http://issues.umbraco.org/issue/U4-5180
- var allVersionsAsArray = allVersions.OrderBy(x => x.UpdateDate).ToArray();
-
- int versionCount = (allVersionsAsArray.Length > 1) ? (allVersionsAsArray.Length - 2) : (allVersionsAsArray.Length - 1);
- var oldDoc = _contentService.GetByVersion(allVersionsAsArray[versionCount].Version);
-
+ if (createBody == null) throw new ArgumentNullException("createBody");
+
// build summary
var summary = new StringBuilder();
var props = content.Properties.ToArray();
@@ -290,16 +347,16 @@ namespace Umbraco.Core.Services
{
var oldProperty = oldDoc.Properties[p.PropertyType.Alias];
oldText = oldProperty.Value != null ? oldProperty.Value.ToString() : "";
-
+
// replace html with char equivalent
ReplaceHtmlSymbols(ref oldText);
ReplaceHtmlSymbols(ref newText);
}
-
+
// make sure to only highlight changes done using TinyMCE editor... other changes will be displayed using default summary
// TODO: We should probably allow more than just tinymce??
- if ((p.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.TinyMCEAlias)
+ if ((p.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.TinyMCEAlias)
&& string.CompareOrdinal(oldText, newText) != 0)
{
summary.Append("");
@@ -308,26 +365,31 @@ namespace Umbraco.Core.Services
"| Red for deleted characters Yellow for inserted characters | ");
summary.Append("
");
summary.Append("");
- summary.Append("| New " +
- p.PropertyType.Name + " | ");
- summary.Append("" +
- ReplaceLinks(CompareText(oldText, newText, true, false, "", string.Empty), http.Request) +
- " | ");
+ summary.Append(" New ");
+ summary.Append(p.PropertyType.Name);
+ summary.Append(" | ");
+ summary.Append("");
+ summary.Append(ReplaceLinks(CompareText(oldText, newText, true, false, "", string.Empty), http.Request));
+ summary.Append(" | ");
summary.Append("
");
summary.Append("");
- summary.Append("| Old " +
- p.PropertyType.Name + " | ");
- summary.Append("" +
- ReplaceLinks(CompareText(newText, oldText, true, false, "", string.Empty), http.Request) +
- " | ");
+ summary.Append(" Old ");
+ summary.Append(p.PropertyType.Name);
+ summary.Append(" | ");
+ summary.Append("");
+ summary.Append(ReplaceLinks(CompareText(newText, oldText, true, false, "", string.Empty), http.Request));
+ summary.Append(" | ");
summary.Append("
");
}
else
{
summary.Append("");
- summary.Append("| " +
- p.PropertyType.Name + " | ");
- summary.Append("" + newText + " | ");
+ summary.Append("");
+ summary.Append(p.PropertyType.Name);
+ summary.Append(" | ");
+ summary.Append("");
+ summary.Append(newText);
+ summary.Append(" | ");
summary.Append("
");
}
summary.Append(
@@ -338,29 +400,27 @@ namespace Umbraco.Core.Services
string[] subjectVars = {
- http.Request.ServerVariables["SERVER_NAME"] + ":" +
- http.Request.Url.Port +
- IOHelper.ResolveUrl(SystemDirectories.Umbraco),
+ string.Concat(http.Request.ServerVariables["SERVER_NAME"], ":", http.Request.Url.Port, IOHelper.ResolveUrl(SystemDirectories.Umbraco)),
actionName,
content.Name
};
string[] bodyVars = {
- mailingUser.Name,
- actionName,
- content.Name,
+ mailingUser.Name,
+ actionName,
+ content.Name,
performingUser.Name,
- http.Request.ServerVariables["SERVER_NAME"] + ":" + http.Request.Url.Port + IOHelper.ResolveUrl(SystemDirectories.Umbraco),
+ string.Concat(http.Request.ServerVariables["SERVER_NAME"], ":", http.Request.Url.Port, IOHelper.ResolveUrl(SystemDirectories.Umbraco)),
content.Id.ToString(CultureInfo.InvariantCulture), summary.ToString(),
string.Format("{2}://{0}/{1}",
- http.Request.ServerVariables["SERVER_NAME"] + ":" + http.Request.Url.Port,
+ string.Concat(http.Request.ServerVariables["SERVER_NAME"], ":", http.Request.Url.Port),
//TODO: RE-enable this so we can have a nice url
/*umbraco.library.NiceUrl(documentObject.Id))*/
- content.Id + ".aspx",
+ string.Concat(content.Id, ".aspx"),
protocol)
-
+
};
- // create the mail message
+ // create the mail message
var mail = new MailMessage(UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress, mailingUser.Email);
// populate the message
@@ -374,10 +434,10 @@ namespace Umbraco.Core.Services
{
mail.IsBodyHtml = true;
mail.Body =
- @"
+ string.Concat(@"
-" + createBody(mailingUser, bodyVars);
+", createBody(mailingUser, bodyVars));
}
// nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here
@@ -390,32 +450,17 @@ namespace Umbraco.Core.Services
string.Format("https://{0}", serverName));
}
-
- // send it asynchronously, we don't want to got up all of the request time to send emails!
- ThreadPool.QueueUserWorkItem(state =>
- {
- try
- {
- using (mail)
- {
- using (var sender = new SmtpClient())
- {
- sender.Send(mail);
- }
- }
-
- }
- catch (Exception ex)
- {
- _logger.Error("An error occurred sending notification", ex);
- }
- });
+ return new NotificationRequest(mail, actionName, mailingUser.Name, mailingUser.Email);
}
private static string ReplaceLinks(string text, HttpRequestBase request)
{
- string domain = GlobalSettings.UseSSL ? "https://" : "http://";
- domain += request.ServerVariables["SERVER_NAME"] + ":" + request.Url.Port + "/";
+ var sb = new StringBuilder(GlobalSettings.UseSSL ? "https://" : "http://");
+ sb.Append(request.ServerVariables["SERVER_NAME"]);
+ sb.Append(":");
+ sb.Append(request.Url.Port);
+ sb.Append("/");
+ var domain = sb.ToString();
text = text.Replace("href=\"/", "href=\"" + domain);
text = text.Replace("src=\"/", "src=\"" + domain);
return text;
@@ -484,7 +529,7 @@ namespace Umbraco.Core.Services
pos++;
} // while
sb.Append("");
- } // if
+ } // if
} // while
// write rest of unchanged chars
@@ -495,8 +540,95 @@ namespace Umbraco.Core.Services
} // while
return sb.ToString();
+ }
+
+ // manage notifications
+ // ideally, would need to use IBackgroundTasks - but they are not part of Core!
+
+ private static readonly object Locker = new object();
+ private static readonly BlockingCollection Queue = new BlockingCollection();
+ private static volatile bool _running;
+
+ private void Enqueue(NotificationRequest notification)
+ {
+ Queue.Add(notification);
+ if (_running) return;
+ lock (Locker)
+ {
+ if (_running) return;
+ Process(Queue);
+ _running = true;
+ }
+ }
+
+ private class NotificationRequest
+ {
+ public NotificationRequest(MailMessage mail, string action, string userName, string email)
+ {
+ Mail = mail;
+ Action = action;
+ UserName = userName;
+ Email = email;
+ }
+
+ public MailMessage Mail { get; private set; }
+
+ public string Action { get; private set; }
+
+ public string UserName { get; private set; }
+
+ public string Email { get; private set; }
}
+ private void Process(BlockingCollection notificationRequests)
+ {
+ ThreadPool.QueueUserWorkItem(state =>
+ {
+ var s = new SmtpClient();
+ try
+ {
+ _logger.Debug("Begin processing notifications.");
+ while (true)
+ {
+ NotificationRequest request;
+ while (notificationRequests.TryTake(out request, 8 * 1000)) // stay on for 8s
+ {
+ try
+ {
+ if (Sendmail != null) Sendmail(s, request.Mail, _logger); else s.Send(request.Mail);
+ _logger.Debug(string.Format("Notification \"{0}\" sent to {1} ({2})", request.Action, request.UserName, request.Email));
+ }
+ catch (Exception ex)
+ {
+ _logger.Error("An error occurred sending notification", ex);
+ s.Dispose();
+ s = new SmtpClient();
+ }
+ finally
+ {
+ request.Mail.Dispose();
+ }
+ }
+ lock (Locker)
+ {
+ if (notificationRequests.Count > 0) continue; // last chance
+ _running = false; // going down
+ break;
+ }
+ }
+ }
+ finally
+ {
+ s.Dispose();
+ }
+ _logger.Debug("Done processing notifications.");
+ });
+ }
+
+ // for tests
+ internal static Action Sendmail;
+ //= (_, msg, logger) => logger.Debug("Email " + msg.To.ToString());
+
#endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs
index c7a63a884b..8e984d1e5d 100644
--- a/src/Umbraco.Core/Services/UserService.cs
+++ b/src/Umbraco.Core/Services/UserService.cs
@@ -9,6 +9,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Querying;
+using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Security;
@@ -506,6 +507,15 @@ namespace Umbraco.Core.Services
}
}
+ internal IEnumerable GetNextUsers(int id, int count)
+ {
+ var uow = UowProvider.GetUnitOfWork();
+ using (var repository = (UserRepository) RepositoryFactory.CreateUserRepository(uow))
+ {
+ return repository.GetNextUsers(id, count);
+ }
+ }
+
#endregion
#region Implementation of IUserService
diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs
index 87253dcb4c..ed4a59bab4 100644
--- a/src/Umbraco.Tests/Services/ContentServiceTests.cs
+++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs
@@ -68,6 +68,29 @@ namespace Umbraco.Tests.Services
Assert.IsTrue(contentService.PublishWithStatus(content).Success);
}
+ [Test]
+ public void Get_Top_Version_Ids()
+ {
+ // Arrange
+ var contentService = ServiceContext.ContentService;
+
+ // Act
+ var content = contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
+ for (int i = 0; i < 20; i++)
+ {
+ content.SetValue("bodyText", "hello world " + Guid.NewGuid());
+ contentService.SaveAndPublishWithStatus(content);
+ }
+
+
+ // Assert
+ var allVersions = contentService.GetVersionIds(content.Id, int.MaxValue);
+ Assert.AreEqual(21, allVersions.Count());
+
+ var topVersions = contentService.GetVersionIds(content.Id, 4);
+ Assert.AreEqual(4, topVersions.Count());
+ }
+
[Test]
public void Get_By_Ids_Sorted()
{