diff --git a/src/Umbraco.Core/Models/DictionaryItem.cs b/src/Umbraco.Core/Models/DictionaryItem.cs
index db3f47ba13..c670a75108 100644
--- a/src/Umbraco.Core/Models/DictionaryItem.cs
+++ b/src/Umbraco.Core/Models/DictionaryItem.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
-using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Models
{
diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs
index 6986fae027..ef8ebbf931 100644
--- a/src/Umbraco.Core/Models/Language.cs
+++ b/src/Umbraco.Core/Models/Language.cs
@@ -3,7 +3,6 @@ using System.Globalization;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
-using Umbraco.Core.Persistence.Mappers;
namespace Umbraco.Core.Models
{
diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs
index 0b456cda91..eee020eee7 100644
--- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs
@@ -180,7 +180,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
- translation.Id = Convert.ToInt32(Database.Insert(dto));
+ translation.Id = Convert.ToInt32(Database.Insert(textDto));
translation.Key = entity.Key;
}
}
diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs
index 82a9319884..5c3bc965ec 100644
--- a/src/Umbraco.Core/Services/ILocalizationService.cs
+++ b/src/Umbraco.Core/Services/ILocalizationService.cs
@@ -81,9 +81,16 @@ namespace Umbraco.Core.Services
///
/// Gets a by its culture code
///
- /// Culture Code
+ /// Culture Code - also refered to as the Friendly name
///
- ILanguage GetLanguageByCultureCode(string culture);
+ ILanguage GetLanguageByCultureCode(string cultureName);
+
+ ///
+ /// Gets a by its iso code
+ ///
+ /// Iso Code of the language (ie. en-US)
+ ///
+ ILanguage GetLanguageByIsoCode(string isoCode);
///
/// Gets all available languages
diff --git a/src/Umbraco.Core/Services/INotificationService.cs b/src/Umbraco.Core/Services/INotificationService.cs
index 801424d3b0..4be6d17f0d 100644
--- a/src/Umbraco.Core/Services/INotificationService.cs
+++ b/src/Umbraco.Core/Services/INotificationService.cs
@@ -1,7 +1,9 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Web;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
@@ -17,9 +19,15 @@ namespace Umbraco.Core.Services
/// Sends the notifications for the specified user regarding the specified node and action.
///
///
- ///
+ ///
///
- void SendNotifications(IEntity entity, IUser user, IAction action);
+ ///
+ ///
+ ///
+ ///
+ void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http,
+ Func createSubject,
+ Func createBody);
///
/// Gets the notifications for the user
@@ -28,6 +36,17 @@ namespace Umbraco.Core.Services
///
IEnumerable GetUserNotifications(IUser user);
+ ///
+ /// Gets the notifications for the user based on the specified node path
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Notifications are inherited from the parent so any child node will also have notifications assigned based on it's parent (ancestors)
+ ///
+ IEnumerable GetUserNotifications(IUser user, string path);
+
///
/// Returns the notifications for an entity
///
diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs
index 9f318988d7..2f711eb8af 100644
--- a/src/Umbraco.Core/Services/IPackagingService.cs
+++ b/src/Umbraco.Core/Services/IPackagingService.cs
@@ -44,6 +44,22 @@ namespace Umbraco.Core.Services
/// An enumrable list of generated DataTypeDefinitions
IEnumerable ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true);
+ ///
+ /// Imports and saves the 'DictionaryItems' part of the package xml as a list of
+ ///
+ /// Xml to import
+ /// Optional parameter indicating whether or not to raise events
+ /// An enumerable list of dictionary items
+ IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true);
+
+ ///
+ /// Imports and saves the 'Languages' part of a package xml as a list of
+ ///
+ /// Xml to import
+ /// Optional parameter indicating whether or not to raise events
+ /// An enumerable list of generated languages
+ IEnumerable ImportLanguages(XElement languageElementList, bool raiseEvents = true);
+
///
/// Imports and saves package xml as
///
@@ -78,5 +94,55 @@ namespace Umbraco.Core.Services
/// Optional parameter indicating whether or not to raise events
/// containing the xml representation of the Media object
XElement Export(IMedia media, bool deep = false, bool raiseEvents = true);
+
+ ///
+ /// Exports a list of items to xml as an
+ ///
+ /// List of Languages to export
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the Language object
+ XElement Export(IEnumerable languages, bool raiseEvents = true);
+
+ ///
+ /// Exports a single item to xml as an
+ ///
+ /// Language to export
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the Language object
+ XElement Export(ILanguage language, bool raiseEvents = true);
+
+ ///
+ /// Exports a list of items to xml as an
+ ///
+ /// List of dictionary items to export
+ /// Optional boolean indicating whether or not to include children
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the IDictionaryItem objects
+ XElement Export(IEnumerable dictionaryItem, bool includeChildren = true, bool raiseEvents = true);
+
+ ///
+ /// Exports a single item to xml as an
+ ///
+ /// Dictionary Item to export
+ /// Optional boolean indicating whether or not to include children
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the IDictionaryItem object
+ XElement Export(IDictionaryItem dictionaryItem, bool includeChildren, bool raiseEvents = true);
+
+ ///
+ /// Exports a list of Data Types
+ ///
+ /// List of data types to export
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the IDataTypeDefinition objects
+ XElement Export(IEnumerable dataTypeDefinitions, bool raiseEvents = true);
+
+ ///
+ /// Exports a single Data Type
+ ///
+ /// Data type to export
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the IDataTypeDefinition object
+ XElement Export(IDataTypeDefinition dataTypeDefinition, bool raiseEvents = true);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/LocalizationService.cs b/src/Umbraco.Core/Services/LocalizationService.cs
index 47284eb1ad..b7c0cb3b55 100644
--- a/src/Umbraco.Core/Services/LocalizationService.cs
+++ b/src/Umbraco.Core/Services/LocalizationService.cs
@@ -146,10 +146,10 @@ namespace Umbraco.Core.Services
{
repository.AddOrUpdate(dictionaryItem);
uow.Commit();
-
- SavedDictionaryItem.RaiseEvent(new SaveEventArgs(dictionaryItem, false), this);
}
+ SavedDictionaryItem.RaiseEvent(new SaveEventArgs(dictionaryItem, false), this);
+
Audit.Add(AuditTypes.Save, "Save DictionaryItem performed by user", userId, dictionaryItem.Id);
}
@@ -170,10 +170,10 @@ namespace Umbraco.Core.Services
//NOTE: The recursive delete is done in the repository
repository.Delete(dictionaryItem);
uow.Commit();
-
- DeletedDictionaryItem.RaiseEvent(new DeleteEventArgs(dictionaryItem, false), this);
}
+ DeletedDictionaryItem.RaiseEvent(new DeleteEventArgs(dictionaryItem, false), this);
+
Audit.Add(AuditTypes.Delete, "Delete DictionaryItem performed by user", userId, dictionaryItem.Id);
}
@@ -193,13 +193,29 @@ namespace Umbraco.Core.Services
///
/// Gets a by its culture code
///
- /// Culture Code
+ /// Culture Name - also refered to as the Friendly name
///
- public ILanguage GetLanguageByCultureCode(string culture)
+ public ILanguage GetLanguageByCultureCode(string cultureName)
{
using (var repository = _repositoryFactory.CreateLanguageRepository(_uowProvider.GetUnitOfWork()))
{
- var query = Query.Builder.Where(x => x.CultureName == culture);
+ var query = Query.Builder.Where(x => x.CultureName == cultureName);
+ var items = repository.GetByQuery(query);
+
+ return items.FirstOrDefault();
+ }
+ }
+
+ ///
+ /// Gets a by its iso code
+ ///
+ /// Iso Code of the language (ie. en-US)
+ ///
+ public ILanguage GetLanguageByIsoCode(string isoCode)
+ {
+ using (var repository = _repositoryFactory.CreateLanguageRepository(_uowProvider.GetUnitOfWork()))
+ {
+ var query = Query.Builder.Where(x => x.IsoCode == isoCode);
var items = repository.GetByQuery(query);
return items.FirstOrDefault();
@@ -234,10 +250,10 @@ namespace Umbraco.Core.Services
{
repository.AddOrUpdate(language);
uow.Commit();
-
- SavedLanguage.RaiseEvent(new SaveEventArgs(language, false), this);
}
+ SavedLanguage.RaiseEvent(new SaveEventArgs(language, false), this);
+
Audit.Add(AuditTypes.Save, "Save Language performed by user", userId, language.Id);
}
@@ -257,10 +273,10 @@ namespace Umbraco.Core.Services
//NOTE: There isn't any constraints in the db, so possible references aren't deleted
repository.Delete(language);
uow.Commit();
-
- DeletedLanguage.RaiseEvent(new DeleteEventArgs(language, false), this);
}
+ DeletedLanguage.RaiseEvent(new DeleteEventArgs(language, false), this);
+
Audit.Add(AuditTypes.Delete, "Delete Language performed by user", userId, language.Id);
}
diff --git a/src/Umbraco.Core/Services/NotificationService.cs b/src/Umbraco.Core/Services/NotificationService.cs
index 31c534470b..a750cd77fc 100644
--- a/src/Umbraco.Core/Services/NotificationService.cs
+++ b/src/Umbraco.Core/Services/NotificationService.cs
@@ -1,10 +1,20 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Net.Mail;
+using System.Text;
+using System.Threading;
+using System.Web;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.IO;
+using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
+using Umbraco.Core.Strings;
using umbraco.interfaces;
namespace Umbraco.Core.Services
@@ -12,21 +22,67 @@ namespace Umbraco.Core.Services
internal class NotificationService : INotificationService
{
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
+ private readonly IUserService _userService;
+ private readonly IContentService _contentService;
- public NotificationService(IDatabaseUnitOfWorkProvider provider)
+ public NotificationService(IDatabaseUnitOfWorkProvider provider, IUserService userService, IContentService contentService)
{
_uowProvider = provider;
+ _userService = userService;
+ _contentService = contentService;
}
///
/// Sends the notifications for the specified user regarding the specified node and action.
///
///
- ///
+ ///
///
- public void SendNotifications(IEntity entity, IUser user, IAction action)
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Currently this will only work for Content entities!
+ ///
+ public void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http,
+ Func createSubject,
+ Func createBody)
{
- throw new NotImplementedException();
+ if ((entity is IContent) == false)
+ {
+ throw new NotSupportedException();
+ }
+ var content = (IContent) entity;
+ //we'll lazily get these if we need to send notifications
+ IContent[] allVersions = null;
+
+ int totalUsers;
+ var allUsers = _userService.GetAllMembers(0, int.MaxValue, out totalUsers);
+ foreach (var u in allUsers)
+ {
+ if (u.IsApproved == false) continue;
+ var userNotifications = GetUserNotifications(u, content.Path).ToArray();
+ var notificationForAction = userNotifications.FirstOrDefault(x => x.Action == action);
+ if (notificationForAction != null)
+ {
+ //lazy load versions if notifications are required
+ if (allVersions == null)
+ {
+ allVersions = _contentService.GetVersions(entity.Id).ToArray();
+ }
+
+ try
+ {
+ SendNotification(operatingUser, u, content, allVersions, actionName, http, createSubject, createBody);
+ LogHelper.Debug(string.Format("Notification type: {0} sent to {1} ({2})", action, u.Name, u.Email));
+ }
+ catch (Exception ex)
+ {
+ LogHelper.Error("An error occurred sending notification", ex);
+ }
+ }
+ }
}
///
@@ -41,6 +97,23 @@ namespace Umbraco.Core.Services
return repository.GetUserNotifications(user);
}
+ ///
+ /// Gets the notifications for the user based on the specified node path
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Notifications are inherited from the parent so any child node will also have notifications assigned based on it's parent (ancestors)
+ ///
+ public IEnumerable GetUserNotifications(IUser user, string path)
+ {
+ var userNotifications = GetUserNotifications(user).ToArray();
+ var pathParts = path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
+ var result = userNotifications.Where(r => pathParts.InvariantContains(r.EntityId.ToString(CultureInfo.InvariantCulture))).ToList();
+ return result;
+ }
+
///
/// Deletes notifications by entity
///
@@ -99,5 +172,254 @@ namespace Umbraco.Core.Services
var repository = new NotificationsRepository(uow);
return repository.CreateNotification(user, entity, action);
}
+
+ #region private methods
+
+ ///
+ /// Sends the notification
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The action readable name - currently an action is just a single letter, this is the name associated with the letter
+ ///
+ /// Callback to create the mail subject
+ /// Callback to create the mail body
+ private void SendNotification(IUser performingUser, IUser mailingUser, IContent content, IContent[] allVersions, string actionName, HttpContextBase http,
+ Func createSubject,
+ Func 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");
+
+ int versionCount = (allVersions.Length > 1) ? (allVersions.Length - 2) : (allVersions.Length - 1);
+ var oldDoc = _contentService.GetByVersion(allVersions[versionCount].Version);
+
+ // build summary
+ var summary = new StringBuilder();
+ var props = content.Properties.ToArray();
+ foreach (var p in props)
+ {
+ var newText = p.Value != null ? p.Value.ToString() : "";
+ var oldText = newText;
+
+ // check if something was changed and display the changes otherwise display the fields
+ if (oldDoc.Properties.Contains(p.PropertyType.Alias))
+ {
+ 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)
+ && string.CompareOrdinal(oldText, newText) != 0)
+ {
+ summary.Append("");
+ summary.Append("| Note: | ");
+ summary.Append(
+ " 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("
");
+ summary.Append("");
+ summary.Append("| Old " +
+ p.PropertyType.Name + " | ");
+ summary.Append("" +
+ ReplaceLinks(CompareText(newText, oldText, true, false, "", string.Empty), http.Request) +
+ " | ");
+ summary.Append("
");
+ }
+ else
+ {
+ summary.Append("");
+ summary.Append("| " +
+ p.PropertyType.Name + " | ");
+ summary.Append("" + newText + " | ");
+ summary.Append("
");
+ }
+ summary.Append(
+ "| |
");
+ }
+
+ string protocol = GlobalSettings.UseSSL ? "https" : "http";
+
+
+ string[] subjectVars = {
+ http.Request.ServerVariables["SERVER_NAME"] + ":" +
+ http.Request.Url.Port +
+ IOHelper.ResolveUrl(SystemDirectories.Umbraco),
+ actionName,
+ content.Name
+ };
+ string[] bodyVars = {
+ mailingUser.Name,
+ actionName,
+ content.Name,
+ performingUser.Name,
+ 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,
+ //TODO: RE-enable this so we can have a nice url
+ /*umbraco.library.NiceUrl(documentObject.Id))*/
+ content.Id + ".aspx",
+ protocol)
+
+ };
+
+ // create the mail message
+ var mail = new MailMessage(UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress, mailingUser.Email);
+
+ // populate the message
+ mail.Subject = createSubject(mailingUser, subjectVars);
+ if (UmbracoConfig.For.UmbracoSettings().Content.DisableHtmlEmail)
+ {
+ mail.IsBodyHtml = false;
+ mail.Body = createBody(mailingUser, bodyVars);
+ }
+ else
+ {
+ mail.IsBodyHtml = true;
+ mail.Body =
+ @"
+
+
+" + createBody(mailingUser, bodyVars);
+ }
+
+ // nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here
+ // adding the server name to make sure we don't replace external links
+ if (GlobalSettings.UseSSL && string.IsNullOrEmpty(mail.Body) == false)
+ {
+ string serverName = http.Request.ServerVariables["SERVER_NAME"];
+ mail.Body = mail.Body.Replace(
+ string.Format("http://{0}", serverName),
+ 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)
+ {
+ LogHelper.Error("An error occurred sending notification", ex);
+ }
+ });
+ }
+
+ private static string ReplaceLinks(string text, HttpRequestBase request)
+ {
+ string domain = GlobalSettings.UseSSL ? "https://" : "http://";
+ domain += request.ServerVariables["SERVER_NAME"] + ":" + request.Url.Port + "/";
+ text = text.Replace("href=\"/", "href=\"" + domain);
+ text = text.Replace("src=\"/", "src=\"" + domain);
+ return text;
+ }
+
+ ///
+ /// Replaces the HTML symbols with the character equivalent.
+ ///
+ /// The old string.
+ private static void ReplaceHtmlSymbols(ref string oldString)
+ {
+ oldString = oldString.Replace(" ", " ");
+ oldString = oldString.Replace("’", "'");
+ oldString = oldString.Replace("&", "&");
+ oldString = oldString.Replace("“", "“");
+ oldString = oldString.Replace("”", "”");
+ oldString = oldString.Replace(""", "\"");
+ }
+
+ ///
+ /// Compares the text.
+ ///
+ /// The old text.
+ /// The new text.
+ /// if set to true [display inserted text].
+ /// if set to true [display deleted text].
+ /// The inserted style.
+ /// The deleted style.
+ ///
+ private static string CompareText(string oldText, string newText, bool displayInsertedText,
+ bool displayDeletedText, string insertedStyle, string deletedStyle)
+ {
+ var sb = new StringBuilder();
+ var diffs = Diff.DiffText1(oldText, newText);
+
+ int pos = 0;
+ for (int n = 0; n < diffs.Length; n++)
+ {
+ Diff.Item it = diffs[n];
+
+ // write unchanged chars
+ while ((pos < it.StartB) && (pos < newText.Length))
+ {
+ sb.Append(newText[pos]);
+ pos++;
+ } // while
+
+ // write deleted chars
+ if (displayDeletedText && it.DeletedA > 0)
+ {
+ sb.Append(deletedStyle);
+ for (int m = 0; m < it.DeletedA; m++)
+ {
+ sb.Append(oldText[it.StartA + m]);
+ } // for
+ sb.Append("");
+ }
+
+ // write inserted chars
+ if (displayInsertedText && pos < it.StartB + it.InsertedB)
+ {
+ sb.Append(insertedStyle);
+ while (pos < it.StartB + it.InsertedB)
+ {
+ sb.Append(newText[pos]);
+ pos++;
+ } // while
+ sb.Append("");
+ } // if
+ } // while
+
+ // write rest of unchanged chars
+ while (pos < newText.Length)
+ {
+ sb.Append(newText[pos]);
+ pos++;
+ } // while
+
+ return sb.ToString();
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs
index 970a278c3b..3dc7d8b61e 100644
--- a/src/Umbraco.Core/Services/PackagingService.cs
+++ b/src/Umbraco.Core/Services/PackagingService.cs
@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
+using System.Xml;
+using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
@@ -54,49 +56,7 @@ namespace Umbraco.Core.Services
_importedContentTypes = new Dictionary();
}
-
- #region Generic export methods
-
- internal void ExportToFile(string absoluteFilePath, string nodeType, int id)
- {
- XElement xml = null;
-
- if (nodeType.Equals("content", StringComparison.InvariantCultureIgnoreCase))
- {
- var content = _contentService.GetById(id);
- xml = Export(content);
- }
-
- if (nodeType.Equals("media", StringComparison.InvariantCultureIgnoreCase))
- {
- var media = _mediaService.GetById(id);
- xml = Export(media);
- }
-
- if (nodeType.Equals("contenttype", StringComparison.InvariantCultureIgnoreCase))
- {
- var contentType = _contentTypeService.GetContentType(id);
- xml = Export(contentType);
- }
-
- if (nodeType.Equals("mediatype", StringComparison.InvariantCultureIgnoreCase))
- {
- var mediaType = _contentTypeService.GetMediaType(id);
- xml = Export(mediaType);
- }
-
- if (nodeType.Equals("datatype", StringComparison.InvariantCultureIgnoreCase))
- {
- var dataType = _dataTypeService.GetDataTypeDefinitionById(id);
- xml = Export(dataType);
- }
-
- if (xml != null)
- xml.Save(absoluteFilePath);
- }
-
- #endregion
-
+
#region Content
///
@@ -790,22 +750,35 @@ namespace Umbraco.Core.Services
#region DataTypes
///
- /// Export a list of data types
+ /// Exports a list of Data Types
///
- ///
- ///
- internal XElement Export(IEnumerable dataTypeDefinitions)
+ /// List of data types to export
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the IDataTypeDefinition objects
+ public XElement Export(IEnumerable dataTypeDefinitions, bool raiseEvents = true)
{
var container = new XElement("DataTypes");
- foreach (var d in dataTypeDefinitions)
+ foreach (var dataTypeDefinition in dataTypeDefinitions)
{
- container.Add(Export(d));
+ container.Add(Export(dataTypeDefinition, raiseEvents));
}
return container;
}
- internal XElement Export(IDataTypeDefinition dataTypeDefinition)
+ ///
+ /// Exports a single Data Type
+ ///
+ /// Data type to export
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the IDataTypeDefinition object
+ public XElement Export(IDataTypeDefinition dataTypeDefinition, bool raiseEvents = true)
{
+ if (raiseEvents)
+ {
+ if (ExportingDataType.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this))
+ return default(XElement);
+ }
+
var prevalues = new XElement("PreValues");
var prevalueList = _dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeDefinition.Id)
@@ -830,6 +803,9 @@ namespace Umbraco.Core.Services
xml.Add(new XAttribute("Definition", dataTypeDefinition.Key));
xml.Add(new XAttribute("DatabaseType", dataTypeDefinition.DatabaseType.ToString()));
+ if (raiseEvents)
+ DataTypeExported.RaiseEvent(new SaveEventArgs(xml, false), this);
+
return xml;
}
@@ -837,7 +813,7 @@ namespace Umbraco.Core.Services
/// Imports and saves package xml as
///
/// Xml to import
- ///
+ /// Optional id of the user
/// Optional parameter indicating whether or not to raise events
/// An enumrable list of generated DataTypeDefinitions
public IEnumerable ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true)
@@ -946,12 +922,232 @@ namespace Umbraco.Core.Services
#endregion
#region Dictionary Items
+
+ ///
+ /// Exports a list of items to xml as an
+ ///
+ /// List of dictionary items to export
+ /// Optional boolean indicating whether or not to include children
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the IDictionaryItem objects
+ public XElement Export(IEnumerable dictionaryItem, bool includeChildren = true, bool raiseEvents = true)
+ {
+ var xml = new XElement("DictionaryItems");
+ foreach (var item in dictionaryItem)
+ {
+ xml.Add(Export(item, includeChildren, raiseEvents));
+ }
+ return xml;
+ }
+
+ ///
+ /// Exports a single item to xml as an
+ ///
+ /// Dictionary Item to export
+ /// Optional boolean indicating whether or not to include children
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the IDictionaryItem object
+ public XElement Export(IDictionaryItem dictionaryItem, bool includeChildren, bool raiseEvents = true)
+ {
+ if (raiseEvents)
+ {
+ if (ExportingDictionaryItem.IsRaisedEventCancelled(new SaveEventArgs(dictionaryItem), this))
+ return default(XElement);
+ }
+
+ var xml = new XElement("DictionaryItem", new XAttribute("Key", dictionaryItem.ItemKey));
+ foreach (var translation in dictionaryItem.Translations)
+ {
+ xml.Add(new XElement("Value",
+ new XAttribute("LanguageId", translation.Language.Id),
+ new XAttribute("LanguageCultureAlias", translation.Language.IsoCode),
+ new XCData(translation.Value)));
+ }
+
+ if (includeChildren)
+ {
+ var children = _localizationService.GetDictionaryItemChildren(dictionaryItem.Key);
+ foreach (var child in children)
+ {
+ xml.Add(Export(child, true));
+ }
+ }
+
+ if (raiseEvents)
+ DictionaryItemExported.RaiseEvent(new SaveEventArgs(xml, false), this);
+
+ return xml;
+ }
+
+ ///
+ /// Imports and saves the 'DictionaryItems' part of the package xml as a list of
+ ///
+ /// Xml to import
+ /// Optional parameter indicating whether or not to raise events
+ /// An enumerable list of dictionary items
+ public IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true)
+ {
+ if (raiseEvents)
+ {
+ if (ImportingDictionaryItem.IsRaisedEventCancelled(new SaveEventArgs(dictionaryItemElementList), this))
+ return Enumerable.Empty();
+ }
+
+ var languages = _localizationService.GetAllLanguages().ToList();
+ return ImportDictionaryItems(dictionaryItemElementList, languages, raiseEvents);
+ }
+
+ private IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, List languages, bool raiseEvents)
+ {
+ var items = new List();
+ foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem"))
+ items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages));
+
+ if (raiseEvents)
+ DictionaryItemImported.RaiseEvent(new SaveEventArgs(items, false), this);
+
+ return items;
+ }
+
+ private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages)
+ {
+ var items = new List();
+
+ IDictionaryItem dictionaryItem;
+ var key = dictionaryItemElement.Attribute("Key").Value;
+ if (_localizationService.DictionaryItemExists(key))
+ dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages);
+ else
+ dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages);
+ _localizationService.Save(dictionaryItem);
+ items.Add(dictionaryItem);
+ items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, true));
+ return items;
+ }
+
+ private IDictionaryItem GetAndUpdateDictionaryItem(string key, XElement dictionaryItemElement, List languages)
+ {
+ var dictionaryItem = _localizationService.GetDictionaryItemByKey(key);
+ var translations = dictionaryItem.Translations.ToList();
+ foreach (var valueElement in dictionaryItemElement.Elements("Value").Where(v => DictionaryValueIsNew(translations, v)))
+ AddDictionaryTranslation(translations, valueElement, languages);
+ dictionaryItem.Translations = translations;
+ return dictionaryItem;
+ }
+
+ private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List languages)
+ {
+ var dictionaryItem = new DictionaryItem(key);
+ var translations = new List();
+
+ foreach (var valueElement in dictionaryItemElement.Elements("Value"))
+ AddDictionaryTranslation(translations, valueElement, languages);
+
+ dictionaryItem.Translations = translations;
+ return dictionaryItem;
+ }
+
+ private static bool DictionaryValueIsNew(IEnumerable translations, XElement valueElement)
+ {
+ return translations.All(t =>
+ String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, StringComparison.InvariantCultureIgnoreCase) != 0
+ );
+ }
+
+ private static void AddDictionaryTranslation(ICollection translations, XElement valueElement, IEnumerable languages)
+ {
+ var languageId = valueElement.Attribute("LanguageCultureAlias").Value;
+ var language = languages.SingleOrDefault(l => l.IsoCode == languageId);
+ if (language == null)
+ return;
+ var translation = new DictionaryTranslation(language, valueElement.Value);
+ translations.Add(translation);
+ }
+
#endregion
#region Files
#endregion
#region Languages
+
+ ///
+ /// Exports a list of items to xml as an
+ ///
+ /// List of Languages to export
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the ILanguage objects
+ public XElement Export(IEnumerable languages, bool raiseEvents = true)
+ {
+ var xml = new XElement("Languages");
+ foreach (var language in languages)
+ {
+ xml.Add(Export(language, raiseEvents));
+ }
+ return xml;
+ }
+
+ ///
+ /// Exports a single item to xml as an
+ ///
+ /// Language to export
+ /// Optional parameter indicating whether or not to raise events
+ /// containing the xml representation of the ILanguage object
+ public XElement Export(ILanguage language, bool raiseEvents = true)
+ {
+ if (raiseEvents)
+ {
+ if (ExportingLanguage.IsRaisedEventCancelled(new SaveEventArgs(language), this))
+ return default(XElement);
+ }
+
+ var xml = new XElement("Language",
+ new XAttribute("Id", language.Id),
+ new XAttribute("CultureAlias", language.IsoCode),
+ new XAttribute("FriendlyName", language.CultureName));
+
+ if (raiseEvents)
+ LanguageExported.RaiseEvent(new SaveEventArgs(xml, false), this);
+
+ return xml;
+ }
+
+ ///
+ /// Imports and saves the 'Languages' part of a package xml as a list of
+ ///
+ /// Xml to import
+ /// Optional parameter indicating whether or not to raise events
+ /// An enumerable list of generated languages
+ public IEnumerable ImportLanguages(XElement languageElementList, bool raiseEvents = true)
+ {
+ if (raiseEvents)
+ {
+ if (ImportingLanguage.IsRaisedEventCancelled(new SaveEventArgs(languageElementList), this))
+ return Enumerable.Empty();
+ }
+
+ var list = new List();
+ foreach (var languageElement in languageElementList.Elements("Language"))
+ {
+ var isoCode = languageElement.Attribute("CultureAlias").Value;
+ var existingLanguage = _localizationService.GetLanguageByIsoCode(isoCode);
+ if (existingLanguage == null)
+ {
+ var langauge = new Language(isoCode)
+ {
+ CultureName = languageElement.Attribute("FriendlyName").Value
+ };
+ _localizationService.Save(langauge);
+ list.Add(langauge);
+ }
+ }
+
+ if (raiseEvents)
+ LanguageImported.RaiseEvent(new SaveEventArgs(list, false), this);
+
+ return list;
+ }
+
#endregion
#region Macros
@@ -1222,6 +1418,9 @@ namespace Umbraco.Core.Services
#endregion
+ #region Stylesheets
+ #endregion
+
#region Event Handlers
///
/// Occurs before Importing Content
@@ -1283,6 +1482,56 @@ namespace Umbraco.Core.Services
///
public static event TypedEventHandler> DataTypeImported;
+ ///
+ /// Occurs before Exporting DataType
+ ///
+ public static event TypedEventHandler> ExportingDataType;
+
+ ///
+ /// Occurs after DataType is Exported to Xml
+ ///
+ public static event TypedEventHandler> DataTypeExported;
+
+ ///
+ /// Occurs before Importing DictionaryItem
+ ///
+ public static event TypedEventHandler> ImportingDictionaryItem;
+
+ ///
+ /// Occurs after DictionaryItem is Imported and Saved
+ ///
+ public static event TypedEventHandler> DictionaryItemImported;
+
+ ///
+ /// Occurs before Exporting DictionaryItem
+ ///
+ public static event TypedEventHandler> ExportingDictionaryItem;
+
+ ///
+ /// Occurs after DictionaryItem is Exported to Xml
+ ///
+ public static event TypedEventHandler> DictionaryItemExported;
+
+ ///
+ /// Occurs before Importing Language
+ ///
+ public static event TypedEventHandler> ImportingLanguage;
+
+ ///
+ /// Occurs after Language is Imported and Saved
+ ///
+ public static event TypedEventHandler> LanguageImported;
+
+ ///
+ /// Occurs before Exporting Language
+ ///
+ public static event TypedEventHandler> ExportingLanguage;
+
+ ///
+ /// Occurs after Language is Exported to Xml
+ ///
+ public static event TypedEventHandler> LanguageExported;
+
///
/// Occurs before Importing Template
///
diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs
index 4bd2f433ba..ac2b8b24be 100644
--- a/src/Umbraco.Core/Services/ServiceContext.cs
+++ b/src/Umbraco.Core/Services/ServiceContext.cs
@@ -29,6 +29,7 @@ namespace Umbraco.Core.Services
private Lazy _sectionService;
private Lazy _macroService;
private Lazy _memberTypeService;
+ private Lazy _notificationService;
///
/// public ctor - will generally just be used for unit testing
@@ -101,6 +102,9 @@ namespace Umbraco.Core.Services
var provider = dbUnitOfWorkProvider;
var fileProvider = fileUnitOfWorkProvider;
+ if (_notificationService == null)
+ _notificationService = new Lazy(() => new NotificationService(provider, _userService.Value, _contentService.Value));
+
if (_serverRegistrationService == null)
_serverRegistrationService = new Lazy(() => new ServerRegistrationService(provider, repositoryFactory.Value));
@@ -153,6 +157,14 @@ namespace Umbraco.Core.Services
_tagService = new Lazy(() => new TagService(provider, repositoryFactory.Value));
}
+ ///
+ /// Gets the
+ ///
+ internal INotificationService NotificationService
+ {
+ get { return _notificationService.Value; }
+ }
+
///
/// Gets the
///
diff --git a/src/Umbraco.Core/Strings/Diff.cs b/src/Umbraco.Core/Strings/Diff.cs
new file mode 100644
index 0000000000..ed381d4f6f
--- /dev/null
+++ b/src/Umbraco.Core/Strings/Diff.cs
@@ -0,0 +1,510 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Umbraco.Core.Strings
+{
+ ///
+ /// This Class implements the Difference Algorithm published in
+ /// "An O(ND) Difference Algorithm and its Variations" by Eugene Myers
+ /// Algorithmica Vol. 1 No. 2, 1986, p 251.
+ ///
+ /// The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
+ /// each line is converted into a (hash) number. See DiffText().
+ ///
+ /// diff.cs: A port of the algorithm to C#
+ /// Copyright (c) by Matthias Hertel, http://www.mathertel.de
+ /// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
+ ///
+ internal class Diff
+ {
+ /// Data on one input file being compared.
+ ///
+ internal class DiffData
+ {
+
+ /// Number of elements (lines).
+ internal int Length;
+
+ /// Buffer of numbers that will be compared.
+ internal int[] Data;
+
+ ///
+ /// Array of booleans that flag for modified data.
+ /// This is the result of the diff.
+ /// This means deletedA in the first Data or inserted in the second Data.
+ ///
+ internal bool[] Modified;
+
+ ///
+ /// Initialize the Diff-Data buffer.
+ ///
+ /// reference to the buffer
+ internal DiffData(int[] initData)
+ {
+ Data = initData;
+ Length = initData.Length;
+ Modified = new bool[Length + 2];
+ } // DiffData
+
+ } // class DiffData
+
+ /// details of one difference.
+ public struct Item
+ {
+ /// Start Line number in Data A.
+ public int StartA;
+ /// Start Line number in Data B.
+ public int StartB;
+
+ /// Number of changes in Data A.
+ public int DeletedA;
+ /// Number of changes in Data B.
+ public int InsertedB;
+ } // Item
+
+ ///
+ /// Shortest Middle Snake Return Data
+ ///
+ private struct Smsrd
+ {
+ internal int X, Y;
+ // internal int u, v; // 2002.09.20: no need for 2 points
+ }
+
+ ///
+ /// Find the difference in 2 texts, comparing by textlines.
+ ///
+ /// A-version of the text (usualy the old one)
+ /// B-version of the text (usualy the new one)
+ /// Returns a array of Items that describe the differences.
+ public static Item[] DiffText(string textA, string textB)
+ {
+ return (DiffText(textA, textB, false, false, false));
+ } // DiffText
+
+ ///
+ /// Find the difference in 2 texts, comparing by textlines.
+ /// This method uses the DiffInt internally by 1st converting the string into char codes
+ /// then uses the diff int method
+ ///
+ /// A-version of the text (usualy the old one)
+ /// B-version of the text (usualy the new one)
+ /// Returns a array of Items that describe the differences.
+ public static Item[] DiffText1(string textA, string textB)
+ {
+ return DiffInt(DiffCharCodes(textA, false), DiffCharCodes(textB, false));
+ }
+
+
+ ///
+ /// Find the difference in 2 text documents, comparing by textlines.
+ /// The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents
+ /// each line is converted into a (hash) number. This hash-value is computed by storing all
+ /// textlines into a common hashtable so i can find dublicates in there, and generating a
+ /// new number each time a new textline is inserted.
+ ///
+ /// A-version of the text (usualy the old one)
+ /// B-version of the text (usualy the new one)
+ /// When set to true, all leading and trailing whitespace characters are stripped out before the comparation is done.
+ /// When set to true, all whitespace characters are converted to a single space character before the comparation is done.
+ /// When set to true, all characters are converted to their lowercase equivivalence before the comparation is done.
+ /// Returns a array of Items that describe the differences.
+ public static Item[] DiffText(string textA, string textB, bool trimSpace, bool ignoreSpace, bool ignoreCase)
+ {
+ // prepare the input-text and convert to comparable numbers.
+ var h = new Hashtable(textA.Length + textB.Length);
+
+ // The A-Version of the data (original data) to be compared.
+ var dataA = new DiffData(DiffCodes(textA, h, trimSpace, ignoreSpace, ignoreCase));
+
+ // The B-Version of the data (modified data) to be compared.
+ var dataB = new DiffData(DiffCodes(textB, h, trimSpace, ignoreSpace, ignoreCase));
+
+ h = null; // free up hashtable memory (maybe)
+
+ var max = dataA.Length + dataB.Length + 1;
+ // vector for the (0,0) to (x,y) search
+ var downVector = new int[2 * max + 2];
+ // vector for the (u,v) to (N,M) search
+ var upVector = new int[2 * max + 2];
+
+ Lcs(dataA, 0, dataA.Length, dataB, 0, dataB.Length, downVector, upVector);
+
+ Optimize(dataA);
+ Optimize(dataB);
+ return CreateDiffs(dataA, dataB);
+ } // DiffText
+
+
+ ///
+ /// Diffs the char codes.
+ ///
+ /// A text.
+ /// if set to true [ignore case].
+ ///
+ private static int[] DiffCharCodes(string aText, bool ignoreCase)
+ {
+ if (ignoreCase)
+ aText = aText.ToUpperInvariant();
+
+ var codes = new int[aText.Length];
+
+ for (int n = 0; n < aText.Length; n++)
+ codes[n] = (int)aText[n];
+
+ return (codes);
+ } // DiffCharCodes
+
+ ///
+ /// If a sequence of modified lines starts with a line that contains the same content
+ /// as the line that appends the changes, the difference sequence is modified so that the
+ /// appended line and not the starting line is marked as modified.
+ /// This leads to more readable diff sequences when comparing text files.
+ ///
+ /// A Diff data buffer containing the identified changes.
+ private static void Optimize(DiffData data)
+ {
+ var startPos = 0;
+ while (startPos < data.Length)
+ {
+ while ((startPos < data.Length) && (data.Modified[startPos] == false))
+ startPos++;
+ int endPos = startPos;
+ while ((endPos < data.Length) && (data.Modified[endPos] == true))
+ endPos++;
+
+ if ((endPos < data.Length) && (data.Data[startPos] == data.Data[endPos]))
+ {
+ data.Modified[startPos] = false;
+ data.Modified[endPos] = true;
+ }
+ else
+ {
+ startPos = endPos;
+ } // if
+ } // while
+ } // Optimize
+
+
+ ///
+ /// Find the difference in 2 arrays of integers.
+ ///
+ /// A-version of the numbers (usualy the old one)
+ /// B-version of the numbers (usualy the new one)
+ /// Returns a array of Items that describe the differences.
+ public static Item[] DiffInt(int[] arrayA, int[] arrayB)
+ {
+ // The A-Version of the data (original data) to be compared.
+ var dataA = new DiffData(arrayA);
+
+ // The B-Version of the data (modified data) to be compared.
+ var dataB = new DiffData(arrayB);
+
+ var max = dataA.Length + dataB.Length + 1;
+ // vector for the (0,0) to (x,y) search
+ var downVector = new int[2 * max + 2];
+ // vector for the (u,v) to (N,M) search
+ var upVector = new int[2 * max + 2];
+
+ Lcs(dataA, 0, dataA.Length, dataB, 0, dataB.Length, downVector, upVector);
+ return CreateDiffs(dataA, dataB);
+ } // Diff
+
+
+ ///
+ /// This function converts all textlines of the text into unique numbers for every unique textline
+ /// so further work can work only with simple numbers.
+ ///
+ /// the input text
+ /// This extern initialized hashtable is used for storing all ever used textlines.
+ /// ignore leading and trailing space characters
+ ///
+ ///
+ /// a array of integers.
+ private static int[] DiffCodes(string aText, IDictionary h, bool trimSpace, bool ignoreSpace, bool ignoreCase)
+ {
+ // get all codes of the text
+ var lastUsedCode = h.Count;
+
+ // strip off all cr, only use lf as textline separator.
+ aText = aText.Replace("\r", "");
+ var lines = aText.Split('\n');
+
+ var codes = new int[lines.Length];
+
+ for (int i = 0; i < lines.Length; ++i)
+ {
+ string s = lines[i];
+ if (trimSpace)
+ s = s.Trim();
+
+ if (ignoreSpace)
+ {
+ s = Regex.Replace(s, "\\s+", " "); // TODO: optimization: faster blank removal.
+ }
+
+ if (ignoreCase)
+ s = s.ToLower();
+
+ object aCode = h[s];
+ if (aCode == null)
+ {
+ lastUsedCode++;
+ h[s] = lastUsedCode;
+ codes[i] = lastUsedCode;
+ }
+ else
+ {
+ codes[i] = (int)aCode;
+ } // if
+ } // for
+ return (codes);
+ } // DiffCodes
+
+
+ ///
+ /// This is the algorithm to find the Shortest Middle Snake (SMS).
+ ///
+ /// sequence A
+ /// lower bound of the actual range in DataA
+ /// upper bound of the actual range in DataA (exclusive)
+ /// sequence B
+ /// lower bound of the actual range in DataB
+ /// upper bound of the actual range in DataB (exclusive)
+ /// a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.
+ /// a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.
+ /// a MiddleSnakeData record containing x,y and u,v
+ private static Smsrd Sms(DiffData dataA, int lowerA, int upperA, DiffData dataB, int lowerB, int upperB, int[] downVector, int[] upVector)
+ {
+ int max = dataA.Length + dataB.Length + 1;
+
+ int downK = lowerA - lowerB; // the k-line to start the forward search
+ int upK = upperA - upperB; // the k-line to start the reverse search
+
+ int delta = (upperA - lowerA) - (upperB - lowerB);
+ bool oddDelta = (delta & 1) != 0;
+
+ // The vectors in the publication accepts negative indexes. the vectors implemented here are 0-based
+ // and are access using a specific offset: UpOffset UpVector and DownOffset for DownVektor
+ int downOffset = max - downK;
+ int upOffset = max - upK;
+
+ int maxD = ((upperA - lowerA + upperB - lowerB) / 2) + 1;
+
+ // Debug.Write(2, "SMS", String.Format("Search the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB));
+
+ // init vectors
+ downVector[downOffset + downK + 1] = lowerA;
+ upVector[upOffset + upK - 1] = upperA;
+
+ for (int d = 0; d <= maxD; d++)
+ {
+
+ // Extend the forward path.
+ Smsrd ret;
+ for (int k = downK - d; k <= downK + d; k += 2)
+ {
+ // Debug.Write(0, "SMS", "extend forward path " + k.ToString());
+
+ // find the only or better starting point
+ int x, y;
+ if (k == downK - d)
+ {
+ x = downVector[downOffset + k + 1]; // down
+ }
+ else
+ {
+ x = downVector[downOffset + k - 1] + 1; // a step to the right
+ if ((k < downK + d) && (downVector[downOffset + k + 1] >= x))
+ x = downVector[downOffset + k + 1]; // down
+ }
+ y = x - k;
+
+ // find the end of the furthest reaching forward D-path in diagonal k.
+ while ((x < upperA) && (y < upperB) && (dataA.Data[x] == dataB.Data[y]))
+ {
+ x++; y++;
+ }
+ downVector[downOffset + k] = x;
+
+ // overlap ?
+ if (oddDelta && (upK - d < k) && (k < upK + d))
+ {
+ if (upVector[upOffset + k] <= downVector[downOffset + k])
+ {
+ ret.X = downVector[downOffset + k];
+ ret.Y = downVector[downOffset + k] - k;
+ // ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points
+ // ret.v = UpVector[UpOffset + k] - k;
+ return (ret);
+ } // if
+ } // if
+
+ } // for k
+
+ // Extend the reverse path.
+ for (int k = upK - d; k <= upK + d; k += 2)
+ {
+ // Debug.Write(0, "SMS", "extend reverse path " + k.ToString());
+
+ // find the only or better starting point
+ int x, y;
+ if (k == upK + d)
+ {
+ x = upVector[upOffset + k - 1]; // up
+ }
+ else
+ {
+ x = upVector[upOffset + k + 1] - 1; // left
+ if ((k > upK - d) && (upVector[upOffset + k - 1] < x))
+ x = upVector[upOffset + k - 1]; // up
+ } // if
+ y = x - k;
+
+ while ((x > lowerA) && (y > lowerB) && (dataA.Data[x - 1] == dataB.Data[y - 1]))
+ {
+ x--; y--; // diagonal
+ }
+ upVector[upOffset + k] = x;
+
+ // overlap ?
+ if (!oddDelta && (downK - d <= k) && (k <= downK + d))
+ {
+ if (upVector[upOffset + k] <= downVector[downOffset + k])
+ {
+ ret.X = downVector[downOffset + k];
+ ret.Y = downVector[downOffset + k] - k;
+ // ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points
+ // ret.v = UpVector[UpOffset + k] - k;
+ return (ret);
+ } // if
+ } // if
+
+ } // for k
+
+ } // for D
+
+ throw new ApplicationException("the algorithm should never come here.");
+ } // SMS
+
+
+ ///
+ /// This is the divide-and-conquer implementation of the longes common-subsequence (LCS)
+ /// algorithm.
+ /// The published algorithm passes recursively parts of the A and B sequences.
+ /// To avoid copying these arrays the lower and upper bounds are passed while the sequences stay constant.
+ ///
+ /// sequence A
+ /// lower bound of the actual range in DataA
+ /// upper bound of the actual range in DataA (exclusive)
+ /// sequence B
+ /// lower bound of the actual range in DataB
+ /// upper bound of the actual range in DataB (exclusive)
+ /// a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.
+ /// a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.
+ private static void Lcs(DiffData dataA, int lowerA, int upperA, DiffData dataB, int lowerB, int upperB, int[] downVector, int[] upVector)
+ {
+ // Debug.Write(2, "LCS", String.Format("Analyse the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB));
+
+ // Fast walkthrough equal lines at the start
+ while (lowerA < upperA && lowerB < upperB && dataA.Data[lowerA] == dataB.Data[lowerB])
+ {
+ lowerA++; lowerB++;
+ }
+
+ // Fast walkthrough equal lines at the end
+ while (lowerA < upperA && lowerB < upperB && dataA.Data[upperA - 1] == dataB.Data[upperB - 1])
+ {
+ --upperA; --upperB;
+ }
+
+ if (lowerA == upperA)
+ {
+ // mark as inserted lines.
+ while (lowerB < upperB)
+ dataB.Modified[lowerB++] = true;
+
+ }
+ else if (lowerB == upperB)
+ {
+ // mark as deleted lines.
+ while (lowerA < upperA)
+ dataA.Modified[lowerA++] = true;
+
+ }
+ else
+ {
+ // Find the middle snakea and length of an optimal path for A and B
+ Smsrd smsrd = Sms(dataA, lowerA, upperA, dataB, lowerB, upperB, downVector, upVector);
+ // Debug.Write(2, "MiddleSnakeData", String.Format("{0},{1}", smsrd.x, smsrd.y));
+
+ // The path is from LowerX to (x,y) and (x,y) to UpperX
+ Lcs(dataA, lowerA, smsrd.X, dataB, lowerB, smsrd.Y, downVector, upVector);
+ Lcs(dataA, smsrd.X, upperA, dataB, smsrd.Y, upperB, downVector, upVector); // 2002.09.20: no need for 2 points
+ }
+ } // LCS()
+
+
+ /// Scan the tables of which lines are inserted and deleted,
+ /// producing an edit script in forward order.
+ ///
+ /// dynamic array
+ private static Item[] CreateDiffs(DiffData dataA, DiffData dataB)
+ {
+ ArrayList a = new ArrayList();
+ Item aItem;
+ Item[] result;
+
+ int lineA = 0;
+ int lineB = 0;
+ while (lineA < dataA.Length || lineB < dataB.Length)
+ {
+ if ((lineA < dataA.Length) && (!dataA.Modified[lineA])
+ && (lineB < dataB.Length) && (!dataB.Modified[lineB]))
+ {
+ // equal lines
+ lineA++;
+ lineB++;
+
+ }
+ else
+ {
+ // maybe deleted and/or inserted lines
+ int startA = lineA;
+ int startB = lineB;
+
+ while (lineA < dataA.Length && (lineB >= dataB.Length || dataA.Modified[lineA]))
+ // while (LineA < DataA.Length && DataA.modified[LineA])
+ lineA++;
+
+ while (lineB < dataB.Length && (lineA >= dataA.Length || dataB.Modified[lineB]))
+ // while (LineB < DataB.Length && DataB.modified[LineB])
+ lineB++;
+
+ if ((startA < lineA) || (startB < lineB))
+ {
+ // store a new difference-item
+ aItem = new Item();
+ aItem.StartA = startA;
+ aItem.StartB = startB;
+ aItem.DeletedA = lineA - startA;
+ aItem.InsertedB = lineB - startB;
+ a.Add(aItem);
+ } // if
+ } // if
+ } // while
+
+ result = new Item[a.Count];
+ a.CopyTo(result);
+
+ return (result);
+ }
+
+ } // class Diff
+
+
+}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index ad49b46b54..45170dfc51 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -1011,6 +1011,7 @@
+
diff --git a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs
index 554c0f7ec4..e8e25068db 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs
@@ -180,7 +180,6 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
- [NUnit.Framework.Ignore]
[Test]
public void Can_Perform_Update_On_DictionaryRepository()
{
@@ -209,6 +208,35 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
+ [Test]
+ public void Can_Perform_Update_WithNewTranslation_On_DictionaryRepository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+ var languageRepository = new LanguageRepository(unitOfWork);
+ var repository = new DictionaryRepository(unitOfWork, languageRepository);
+
+ var languageNo = new Language("nb-NO") { CultureName = "nb-NO" };
+ ServiceContext.LocalizationService.Save(languageNo);
+
+ // Act
+ var item = repository.Get(1);
+ var translations = item.Translations.ToList();
+ translations.Add(new DictionaryTranslation(languageNo, "Les mer"));
+ item.Translations = translations;
+
+ repository.AddOrUpdate(item);
+ unitOfWork.Commit();
+
+ var dictionaryItem = repository.Get(1);
+
+ // Assert
+ Assert.That(dictionaryItem, Is.Not.Null);
+ Assert.That(dictionaryItem.Translations.Count(), Is.EqualTo(3));
+ Assert.That(dictionaryItem.Translations.Single(t => t.Language.IsoCode == "nb-NO").Value, Is.EqualTo("Les mer"));
+ }
+
[Test]
public void Can_Perform_Delete_On_DictionaryRepository()
{
diff --git a/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml b/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml
new file mode 100644
index 0000000000..6fa5a3d15f
--- /dev/null
+++ b/src/Umbraco.Tests/Services/Importing/Dictionary-Package.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+ Dictionary-Package
+ 1.0
+ MIT license
+ http://not.available
+
+ 3
+ 0
+ 0
+
+
+
+ Test
+ http://not.available
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs b/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs
index 1d06f2c885..c840f08842 100644
--- a/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs
+++ b/src/Umbraco.Tests/Services/Importing/ImportResources.Designer.cs
@@ -88,6 +88,32 @@ namespace Umbraco.Tests.Services.Importing {
}
}
+ ///
+ /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?>
+ ///<umbPackage>
+ /// <files />
+ /// <info>
+ /// <package>
+ /// <name>Dictionary-Package</name>
+ /// <version>1.0</version>
+ /// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license>
+ /// <url>http://not.available</url>
+ /// <requirements>
+ /// <major>3</major>
+ /// <minor>0</minor>
+ /// <patch>0</patch>
+ /// </requirements>
+ /// </package>
+ /// <author>
+ /// <name>Test</name>
+ /// <website>http://not.available</w [rest of string was truncated]";.
+ ///
+ internal static string Dictionary_Package {
+ get {
+ return ResourceManager.GetString("Dictionary_Package", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?>
///<umbPackage>
diff --git a/src/Umbraco.Tests/Services/Importing/ImportResources.resx b/src/Umbraco.Tests/Services/Importing/ImportResources.resx
index 180b74be79..dd47151b73 100644
--- a/src/Umbraco.Tests/Services/Importing/ImportResources.resx
+++ b/src/Umbraco.Tests/Services/Importing/ImportResources.resx
@@ -145,4 +145,7 @@
checkboxlist-content-package-legacyids.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
+ dictionary-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
+
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs
index e56abb548f..a53edac3a4 100644
--- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs
+++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs
@@ -2,9 +2,9 @@
using System.Linq;
using System.Xml.Linq;
using NUnit.Framework;
+using Umbraco.Core.Models;
using Umbraco.Core;
using Umbraco.Core.Models.Rdbms;
-using umbraco.editorControls.MultiNodeTreePicker;
namespace Umbraco.Tests.Services.Importing
{
@@ -388,5 +388,148 @@ namespace Umbraco.Tests.Services.Importing
Assert.That(allTemplates.Count(), Is.EqualTo(numberOfTemplates));
Assert.That(allTemplates.First(x => x.Alias == "umbHomepage").Content, Contains.Substring("THIS HAS BEEN UPDATED!"));
}
+
+ [Test]
+ public void PackagingService_Can_Import_DictionaryItems()
+ {
+ // Arrange
+ const string expectedEnglishParentValue = "ParentValue";
+ const string expectedNorwegianParentValue = "ForelderVerdi";
+ const string expectedEnglishChildValue = "ChildValue";
+ const string expectedNorwegianChildValue = "BarnVerdi";
+
+ var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package);
+ var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First();
+
+ AddLanguages();
+
+ // Act
+ ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement);
+
+ // Assert
+ AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB");
+ AssertDictionaryItem("Parent", expectedNorwegianParentValue, "nb-NO");
+ AssertDictionaryItem("Child", expectedEnglishChildValue, "en-GB");
+ AssertDictionaryItem("Child", expectedNorwegianChildValue, "nb-NO");
+ }
+
+ [Test]
+ public void PackagingService_WhenExistingDictionaryKey_ImportsNewChildren()
+ {
+ // Arrange
+ const string expectedEnglishParentValue = "ExistingParentValue";
+ const string expectedNorwegianParentValue = "EksisterendeForelderVerdi";
+ const string expectedEnglishChildValue = "ChildValue";
+ const string expectedNorwegianChildValue = "BarnVerdi";
+
+ var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package);
+ var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First();
+
+ AddLanguages();
+ AddExistingEnglishAndNorwegianParentDictionaryItem(expectedEnglishParentValue, expectedNorwegianParentValue);
+
+ // Act
+ ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement);
+
+ // Assert
+ AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB");
+ AssertDictionaryItem("Parent", expectedNorwegianParentValue, "nb-NO");
+ AssertDictionaryItem("Child", expectedEnglishChildValue, "en-GB");
+ AssertDictionaryItem("Child", expectedNorwegianChildValue, "nb-NO");
+ }
+
+ [Test]
+ public void PackagingService_WhenExistingDictionaryKey_OnlyAddsNewLanguages()
+ {
+ // Arrange
+ const string expectedEnglishParentValue = "ExistingParentValue";
+ const string expectedNorwegianParentValue = "ForelderVerdi";
+ const string expectedEnglishChildValue = "ChildValue";
+ const string expectedNorwegianChildValue = "BarnVerdi";
+
+ var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package);
+ var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First();
+
+ AddLanguages();
+ AddExistingEnglishParentDictionaryItem(expectedEnglishParentValue);
+
+ // Act
+ ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement);
+
+ // Assert
+ AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB");
+ AssertDictionaryItem("Parent", expectedNorwegianParentValue, "nb-NO");
+ AssertDictionaryItem("Child", expectedEnglishChildValue, "en-GB");
+ AssertDictionaryItem("Child", expectedNorwegianChildValue, "nb-NO");
+ }
+
+ [Test]
+ public void PackagingService_Can_Import_Languages()
+ {
+ // Arrange
+ var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package);
+ var LanguageItemsElement = newPackageXml.Elements("Languages").First();
+
+ // Act
+ var languages = ServiceContext.PackagingService.ImportLanguages(LanguageItemsElement);
+ var allLanguages = ServiceContext.LocalizationService.GetAllLanguages();
+
+ // Assert
+ Assert.That(languages.Any(x => x.HasIdentity == false), Is.False);
+ foreach (var language in languages)
+ {
+ Assert.That(allLanguages.Any(x => x.IsoCode == language.IsoCode), Is.True);
+ }
+ }
+
+ private void AddLanguages()
+ {
+ var norwegian = new Core.Models.Language("nb-NO");
+ var english = new Core.Models.Language("en-GB");
+ ServiceContext.LocalizationService.Save(norwegian, 0);
+ ServiceContext.LocalizationService.Save(english, 0);
+ }
+
+ private void AssertDictionaryItem(string key, string expectedValue, string cultureCode)
+ {
+ Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(key), "DictionaryItem key does not exist");
+ var dictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey(key);
+ var translation = dictionaryItem.Translations.SingleOrDefault(i => i.Language.IsoCode == cultureCode);
+ Assert.IsNotNull(translation, "Translation to {0} was not added", cultureCode);
+ var value = translation.Value;
+ Assert.That(value, Is.EqualTo(expectedValue), "Translation value was not set");
+ }
+
+ private void AddExistingEnglishParentDictionaryItem(string expectedEnglishParentValue)
+ {
+ var languages = ServiceContext.LocalizationService.GetAllLanguages().ToList();
+ var englishLanguage = languages.Single(l => l.IsoCode == "en-GB");
+ ServiceContext.LocalizationService.Save(
+ new DictionaryItem("Parent")
+ {
+ Translations = new List
+ {
+ new DictionaryTranslation(englishLanguage, expectedEnglishParentValue),
+ }
+ }
+ );
+ }
+
+ private void AddExistingEnglishAndNorwegianParentDictionaryItem(string expectedEnglishParentValue, string expectedNorwegianParentValue)
+ {
+ var languages = ServiceContext.LocalizationService.GetAllLanguages().ToList();
+ var englishLanguage = languages.Single(l => l.IsoCode == "en-GB");
+ var norwegianLanguage = languages.Single(l => l.IsoCode == "nb-NO");
+ ServiceContext.LocalizationService.Save(
+ new DictionaryItem("Parent")
+ {
+ Translations = new List
+ {
+ new DictionaryTranslation(englishLanguage, expectedEnglishParentValue),
+ new DictionaryTranslation(norwegianLanguage, expectedNorwegianParentValue),
+ }
+ }
+ );
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs
index 1ed7807403..0124b4ca1a 100644
--- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs
+++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs
@@ -1,41 +1,89 @@
-using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml.Linq;
using NUnit.Framework;
-using Umbraco.Core;
using Umbraco.Core.Models;
-using Umbraco.Tests.TestHelpers.Entities;
+using Umbraco.Tests.Services.Importing;
namespace Umbraco.Tests.Services
{
- //[TestFixture]
- //public class PackagingServiceTests : BaseServiceTest
- //{
- // [Test]
- // public void Export_Content()
- // {
- // var yesNo = DataTypesResolver.Current.GetById(new Guid(Constants.PropertyEditors.TrueFalse));
- // var txtField = DataTypesResolver.Current.GetById(new Guid(Constants.PropertyEditors.Textbox));
+ [TestFixture]
+ public class PackagingServiceTests : BaseServiceTest
+ {
+ [SetUp]
+ public override void Initialize()
+ {
+ base.Initialize();
+ }
- // var contentWithDataType = MockedContentTypes.CreateSimpleContentType(
- // "test",
- // "Test",
- // new PropertyTypeCollection(
- // new PropertyType[]
- // {
- // new PropertyType(new DataTypeDefinition(-1, txtField.Id)
- // {
- // Name = "Testing Textfield", DatabaseType = DataTypeDatabaseType.Ntext
- // }),
- // new PropertyType(new DataTypeDefinition(-1, yesNo.Id)
- // {
- // Name = "Testing intfield", DatabaseType = DataTypeDatabaseType.Integer
- // })
- // }));
+ [TearDown]
+ public override void TearDown()
+ {
+ base.TearDown();
+ }
- // var content = MockedContent.CreateSimpleContent(contentWithDataType);
- // content.Name = "Test";
+ [Test]
+ public void PackagingService_Can_Export_DictionaryItems()
+ {
+ // Arrange
+ CreateDictionaryData();
+ var dictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Parent");
- // var exported = ServiceContext.PackagingService.Export(content);
+ var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package);
+ var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First();
- // }
- //}
+ // Act
+ var xml = ServiceContext.PackagingService.Export(new []{dictionaryItem});
+
+ // Assert
+ Assert.That(xml.ToString(), Is.EqualTo(dictionaryItemsElement.ToString()));
+ }
+
+ [Test]
+ public void PackagingService_Can_Export_Languages()
+ {
+ // Arrange
+ var languageNbNo = new Language("nb-NO") { CultureName = "Norwegian" };
+ ServiceContext.LocalizationService.Save(languageNbNo);
+
+ var languageEnGb = new Language("en-GB") { CultureName = "English (United Kingdom)" };
+ ServiceContext.LocalizationService.Save(languageEnGb);
+
+ var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package);
+ var languageItemsElement = newPackageXml.Elements("Languages").First();
+
+ // Act
+ var xml = ServiceContext.PackagingService.Export(new[] { languageNbNo, languageEnGb });
+
+ // Assert
+ Assert.That(xml.ToString(), Is.EqualTo(languageItemsElement.ToString()));
+ }
+
+ private void CreateDictionaryData()
+ {
+ var languageNbNo = new Language("nb-NO") { CultureName = "nb-NO" };
+ ServiceContext.LocalizationService.Save(languageNbNo);
+
+ var languageEnGb = new Language("en-GB") { CultureName = "en-GB" };
+ ServiceContext.LocalizationService.Save(languageEnGb);
+
+ var parentItem = new DictionaryItem("Parent");
+ var parentTranslations = new List
+ {
+ new DictionaryTranslation(languageNbNo, "ForelderVerdi"),
+ new DictionaryTranslation(languageEnGb, "ParentValue")
+ };
+ parentItem.Translations = parentTranslations;
+ ServiceContext.LocalizationService.Save(parentItem);
+
+ var childItem = new DictionaryItem(parentItem.Key, "Child");
+ var childTranslations = new List
+ {
+ new DictionaryTranslation(languageNbNo, "BarnVerdi"),
+ new DictionaryTranslation(languageEnGb, "ChildValue")
+ };
+ childItem.Translations = childTranslations;
+ ServiceContext.LocalizationService.Save(childItem);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index c5eb46bb1e..955992e644 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -638,6 +638,7 @@
Designer
+
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index eaa279c342..f97ff2ca1e 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -115,15 +115,15 @@
False
..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll
-
+
False
..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll
-
+
False
..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll
-
+
False
..\packages\Examine.0.1.52.2941\lib\Examine.dll
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
index 1c9c21cdbe..c4b85332fa 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml
@@ -467,20 +467,21 @@
Rediger dine notificeringer for %0%
-
+
- Hej %0%
Dette er en automatisk mail for at informere dig om at opgaven '%1%' er blevet udførtpå siden '%2%' af brugeren '%3%'
Opdateringssammendrag:
Hav en fortsat god dag!
De bedste hilsner fra umbraco robotten
]]>
+ Hej %0% Dette er en automatisk mail for at informere dig om at opgaven '%1%' er blevet udførtpå siden '%2%' af brugeren '%3%'
Opdateringssammendrag:
Hav en fortsat god dag!
De bedste hilsner fra umbraco robotten
]]>
[%0%] Notificering om %1% udført på %2%
Notificeringer
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml
index 3721090de9..c2c16cccec 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml
@@ -497,15 +497,15 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die
Bearbeiten Sie Ihre Benachrichtigungseinstellungen für '%0%'
-Hallo %0%,
+ Hallo %0%,
-die Aufgabe '%1%' (von Benutzer '%3%') an der Seite '%2%' wurde ausgeführt.
+ die Aufgabe '%1%' (von Benutzer '%3%') an der Seite '%2%' wurde ausgeführt.
-Zum Bearbeiten verwenden Sie bitte diesen Link: http://%4%/umbraco/actions/editContent.aspx?id=%5%
+ Zum Bearbeiten verwenden Sie bitte diesen Link: http://%4%/#/content/content/edit/%5%
-Einen schönen Tag wünscht
-Ihr freundlicher Umbraco-Robot
-
+ Einen schönen Tag wünscht
+ Ihr freundlicher Umbraco-Robot
+
<p>Hallo %0%,</p>
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
index f417282ac2..1c04ba81e1 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
@@ -584,7 +584,7 @@ To manage your website, simply open the umbraco back office and start adding con
has been performed on the page '%2%'
by the user '%3%'
- Go to http://%4%/actions/editContent.aspx?id=%5% to edit.
+ Go to http://%4%/#/content/content/edit/%5% to edit.
Have a nice day!
@@ -593,14 +593,12 @@ To manage your website, simply open the umbraco back office and start adding con
Hi %0%
This is an automated mail to inform you that the task '%1%'
- has been performed on the page '%2%'
+ has been performed on the page '%2%'
by the user '%3%'
@@ -612,9 +610,7 @@ To manage your website, simply open the umbraco back office and start adding con
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
index b4266e8fd2..4311d1f04c 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
@@ -582,7 +582,7 @@ To manage your website, simply open the umbraco back office and start adding con
has been performed on the page '%2%'
by the user '%3%'
- Go to http://%4%/actions/editContent.aspx?id=%5% to edit.
+ Go to http://%4%/#/content/content/edit/%5% to edit.
Have a nice day!
@@ -598,9 +598,7 @@ To manage your website, simply open the umbraco back office and start adding con
@@ -612,9 +610,7 @@ To manage your website, simply open the umbraco back office and start adding con
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml
index 30bc95d93c..bc45128d7a 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml
@@ -433,8 +433,8 @@ However, Runway offers an easy foundation based on best practices to get you sta
Edite su notificación para %0%
- Hola %0% Esto es un e-mail automático para informarle que la tarea '%1%' ha sido realizada sobre la página '%2%' por el usuario '%3%' Vaya a http://%4%/actions/editContent.aspx?id=%5% para editarla. ¡Espero que tenga un buen día! Saludos del robot de umbraco
- Hola %0% Esto es un e-mail generado automáticamente para informarle que la tarea '%1%' ha sido realizada sobre la página '%2%' por el usuario '%3%'
Resumen de actualización:
¡Espero que tenga un buen día!
Saludos del robot umbraco.
]]>
+ Hola %0% Esto es un e-mail automático para informarle que la tarea '%1%' ha sido realizada sobre la página '%2%' por el usuario '%3%' Vaya a http://%4%/#/content/content/edit/%5% para editarla. ¡Espero que tenga un buen día! Saludos del robot de umbraco
+ Hola %0% Esto es un e-mail generado automáticamente para informarle que la tarea '%1%' ha sido realizada sobre la página '%2%' por el usuario '%3%'
Resumen de actualización:
¡Espero que tenga un buen día!
Saludos del robot umbraco.
]]>
[%0%] Notificación acerca de %1% realizado en %2%
Notificaciones
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
index 0f10085ab9..7524dd527c 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
@@ -534,7 +534,7 @@ Pour gerer votre site, accéder simplement au back office et commencez à contri
Ceci est un email automatique pour vous informer que la tâche '%1%' a été activée sur la page '%2%' par l'utilisateur '%3%'
- Allez à http://%4%/actions/editContent.aspx?id=%5% pour éditer.
+ Allez à http://%4%/#/content/content/edit/%5% pour éditer.
Bonne journée!
@@ -549,10 +549,8 @@ Pour gerer votre site, accéder simplement au back office et commencez à contri
par l'utilisateur '%3%'
@@ -564,9 +562,7 @@ Pour gerer votre site, accéder simplement au back office et commencez à contri
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml
index 201001ffbc..d22a0474e1 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml
@@ -509,9 +509,7 @@ To manage your website, simply open the umbraco back office and start adding con
@@ -523,9 +521,7 @@ To manage your website, simply open the umbraco back office and start adding con
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml
index d925460d57..f75a1d73be 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml
@@ -458,7 +458,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i
è stata eseguita sulla pagina '%2%'
dall'utente '%3%'
- Vai al link http://%4%/actions/editContent.aspx?id=%5% per effettuare le modifiche.
+ Vai al link http://%4%/#/content/content/edit/%5% per effettuare le modifiche.
Buona giornata!
@@ -473,9 +473,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i
@@ -487,9 +485,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml
index 2cea7d7838..bf60d41574 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml
@@ -496,7 +496,7 @@ Runwayをインストールして作られた新しいウェブサイトがど
ページ '%2%' 上のタスク'%1%'から
自動的にメールします。
- 編集はこちらから: http://%4%/actions/editContent.aspx?id=%5%
+ 編集はこちらから: http://%4%/#/content/content/edit/%5%
早々
@@ -507,9 +507,7 @@ Runwayをインストールして作られた新しいウェブサイトがど
ユーザー '%3%' によりページ '%2%' 上のタスク '%1%' から自動的にメールします。
@@ -521,9 +519,7 @@ Runwayをインストールして作られた新しいウェブサイトがど
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml
index 0a7ba87cff..2ec2088a01 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml
@@ -475,7 +475,7 @@
사용자 '%3%' 가 작업 '%1%' 를 페이지 '%2%' 에서
진행했음을 알리는 자동 발송 메일입니다.
- 편집하시려면 http://%4%/actions/editContent.aspx?id=%5% 로 이동하세요
+ 편집하시려면 http://%4%/#/content/content/edit/%5% 로 이동하세요
좋은 하루 되세요!
@@ -488,9 +488,7 @@
@@ -502,9 +500,7 @@
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml
index 39c7f53647..02f41b141f 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml
@@ -437,7 +437,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
is uitgevoerd op pagina '%2%'
door gebruiker '%3%'
- Ga naar http://%4%/actions/editContent.aspx?id=%5% om te bewerken.
+ Ga naar http://%4%/#/content/content/edit/%5% om te bewerken.
Een prettige dag!
@@ -451,10 +451,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
door gebruiker '%3%'
@@ -466,9 +464,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/no.xml b/src/Umbraco.Web.UI/umbraco/config/lang/no.xml
index 4e76541b2e..ed024af119 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/no.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/no.xml
@@ -459,9 +459,7 @@ Vennlig hilsen umbraco roboten
</p>
<div style="margin: 8px 0; padding: 8px; display: block;">
<br />
- <a style="color: white; font-weight: bold; background-color: #66cc66; text-decoration : none; margin-right: 20px; border: 8px solid #66cc66; width: 150px;" href="http://%4%/umbraco/actions/publish.aspx?id=%5%"> PUBLISER </a>
<a style="color: white; font-weight: bold; background-color: #5372c3; text-decoration : none; margin-right: 20px; border: 8px solid #5372c3; width: 150px;" href="http://%4%/umbraco/actions/editContent.aspx?id=%5%"> REDIGER </a>
- <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/umbraco/actions/delete.aspx?id=%5%"> SLETT </a>
<br />
</div>
<p>
@@ -473,9 +471,7 @@ Vennlig hilsen umbraco roboten
<div style="margin: 8px 0; padding: 8px; display: block;">
<br />
- <a style="color: white; font-weight: bold; background-color: #66cc66; text-decoration : none; margin-right: 20px; border: 8px solid #66cc66; width: 150px;" href="http://%4%/umbraco/actions/publish.aspx?id=%5%"> PUBLISER </a>
<a style="color: white; font-weight: bold; background-color: #5372c3; text-decoration : none; margin-right: 20px; border: 8px solid #5372c3; width: 150px;" href="http://%4%/umbraco/actions/editContent.aspx?id=%5%"> REDIGER </a>
- <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/umbraco/actions/delete.aspx?id=%5%"> SLETT </a>
<br />
</div>
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml
index 6db3d9e2a5..a9d419c9ab 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml
@@ -426,10 +426,10 @@ Możesz dodać dodatkowe języki w menu "Języki" po lewej stronie.]]>
Edytuj powiadomienie dla %0%
- Witaj %0% To jest automatyczny e-mail, wysłany aby poinformować Cię, że polecenie '%1%' zostało wykonane na stronie '%2%' przez użytkownika '%3%'
Podsumowanie zmian:
Miłego dnia!
Pozdrowienia od robota Umbraco
]]>
+ Witaj %0% To jest automatyczny e-mail, wysłany aby poinformować Cię, że polecenie '%1%' zostało wykonane na stronie '%2%' przez użytkownika '%3%'
Podsumowanie zmian:
Miłego dnia!
Pozdrowienia od robota Umbraco
]]>
[%0%] Powiadomienie o %1% wykonane na %2%
Powiadomienie
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml
index ec10a7bdce..bbb0e7e5e2 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml
@@ -467,7 +467,7 @@ Pressione "próximo" para iniciar o assistente.]]>
Esta é uma mensagem automatizada de email para informar que a tarefa '%1%' foi realizada na página '%2%' pelo usuário '%3%'
-Vá até http://%4%/actions/editContent.aspx?id=%5% para editar.
+Vá até http://%4%/#/content/content/edit/%5% para editar.
Tenha um bom dia!
@@ -480,9 +480,7 @@ Vá até http://%4%/actions/editContent.aspx?id=%5% para editar.
@@ -494,9 +492,7 @@ Vá até http://%4%/actions/editContent.aspx?id=%5% para editar.
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml
index d21847c9f8..667dcfe4a9 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml
@@ -515,7 +515,7 @@
Операция '%1%'
была произведена на странице '%2%' пользователем '%3%'.
- Вы можете увидеть изменения и отредактировать, перейдя по ссылке http://%4%/actions/editContent.aspx?id=%5%.
+ Вы можете увидеть изменения и отредактировать, перейдя по ссылке http://%4%/#/content/content/edit/%5%.
Удачи!
@@ -529,9 +529,7 @@
@@ -544,9 +542,7 @@
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml
index bb7f487072..3db191537b 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml
@@ -496,7 +496,7 @@
Inställningar för notifieringar gällande {0}
Hej {0} Detta mail skickas till dig automatiskt för att meddela att '{1}' har utförts på sidan '{2}' av användaren '{3}' Gå till http://{4}/actions/editContent.aspx?id={5} för att redigera.
- Hej {0} Detta mail skickas till dig automatiskt för att meddela att '{1}' har utförts på sidan '{2}' av användaren '{3}'
Sammanfattning av uppdateringen:
]]>
+ Hej {0} Detta mail skickas till dig automatiskt för att meddela att '{1}' har utförts på sidan '{2}' av användaren '{3}'
Sammanfattning av uppdateringen:
]]>
[{0}] Meddelande för att informera om att {1} har utförts på {2}
Notifieringar
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml
index 49a39399bd..e868feb5b5 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml
@@ -530,7 +530,7 @@
您好!这是一封自动邮件,提醒您用户'%3%'执行'%1%'任务已经在完成'%2%'。
- 转到 http://%4%/actions/editContent.aspx?id=%5% 进行编辑
+ 转到 http://%4%/#/content/content/edit/%5% 进行编辑
]]>
%0%:
@@ -538,9 +538,7 @@
您好!这是一封自动发送的邮件,告诉您任务'%1%'已在'%2%'被用户'%3%'执行
@@ -552,9 +550,7 @@
diff --git a/src/Umbraco.Web/NotificationServiceExtensions.cs b/src/Umbraco.Web/NotificationServiceExtensions.cs
new file mode 100644
index 0000000000..8404a99a0e
--- /dev/null
+++ b/src/Umbraco.Web/NotificationServiceExtensions.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Globalization;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Services;
+using umbraco;
+using umbraco.BusinessLogic.Actions;
+using umbraco.interfaces;
+
+namespace Umbraco.Web
+{
+ internal static class NotificationServiceExtensions
+ {
+ internal static void SendNotification(this INotificationService service, IUmbracoEntity entity, IAction action, ApplicationContext applicationContext)
+ {
+ if (global::Umbraco.Web.UmbracoContext.Current == null) return;
+ service.SendNotification(entity, action, global::Umbraco.Web.UmbracoContext.Current);
+ }
+
+ internal static void SendNotification(this INotificationService service, IUmbracoEntity entity, IAction action, UmbracoContext umbracoContext)
+ {
+ if (umbracoContext == null) return;
+ service.SendNotification(entity, action, umbracoContext, umbracoContext.Application);
+ }
+
+ internal static void SendNotification(this INotificationService service, IUmbracoEntity entity, IAction action, UmbracoContext umbracoContext, ApplicationContext applicationContext)
+ {
+ if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
+ if (applicationContext == null) throw new ArgumentNullException("applicationContext");
+
+ var user = umbracoContext.Security.CurrentUser;
+ applicationContext.Services.NotificationService.SendNotifications(
+ user,
+ entity,
+ action.Letter.ToString(CultureInfo.InvariantCulture),
+ ui.Text("actions", action.Alias),
+ umbracoContext.HttpContext,
+ (mailingUser, strings) => ui.Text("notifications", "mailSubject", strings, mailingUser),
+ (mailingUser, strings) => UmbracoConfig.For.UmbracoSettings().Content.DisableHtmlEmail
+ ? ui.Text("notifications", "mailBody", strings, mailingUser)
+ : ui.Text("notifications", "mailBodyHtml", strings, mailingUser));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Strategies/NotificationsHandler.cs b/src/Umbraco.Web/Strategies/NotificationsHandler.cs
new file mode 100644
index 0000000000..dc77a7b745
--- /dev/null
+++ b/src/Umbraco.Web/Strategies/NotificationsHandler.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Services;
+using umbraco;
+using umbraco.BusinessLogic.Actions;
+
+namespace Umbraco.Web.Strategies
+{
+ ///
+ /// Subscribes to the relavent events in order to send out notifications
+ ///
+ public sealed class NotificationsHandler : ApplicationEventHandler
+ {
+
+ protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
+ {
+ base.ApplicationStarted(umbracoApplication, applicationContext);
+
+ //Send notifications for the published action
+ ContentService.Published += (sender, args) =>
+ args.PublishedEntities.ForEach(
+ content =>
+ applicationContext.Services.NotificationService.SendNotification(
+ content, ActionPublish.Instance, applicationContext));
+
+ //Send notifications for the update and created actions
+ ContentService.Saved += (sender, args) =>
+ {
+ //need to determine if this is updating or if it is new
+ foreach (var entity in args.SavedEntities)
+ {
+ var dirty = (IRememberBeingDirty) entity;
+ if (dirty.WasPropertyDirty("Id"))
+ {
+ //it's new
+ applicationContext.Services.NotificationService.SendNotification(
+ entity, ActionNew.Instance, applicationContext);
+ }
+ else
+ {
+ //it's updating
+ applicationContext.Services.NotificationService.SendNotification(
+ entity, ActionUpdate.Instance, applicationContext);
+ }
+ }
+ };
+
+ //Send notifications for the delete action
+ ContentService.Deleted += (sender, args) =>
+ args.DeletedEntities.ForEach(
+ content =>
+ applicationContext.Services.NotificationService.SendNotification(
+ content, ActionDelete.Instance, applicationContext));
+
+ //Send notifications for the unpublish action
+ ContentService.UnPublished += (sender, args) =>
+ args.PublishedEntities.ForEach(
+ content =>
+ applicationContext.Services.NotificationService.SendNotification(
+ content, ActionUnPublish.Instance, applicationContext));
+
+ }
+
+ }
+}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 94e3ea315b..c0e1e010a6 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -102,7 +102,7 @@
..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll
-
+
False
..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll
@@ -110,7 +110,7 @@
False
..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll
-
+
False
..\packages\Examine.0.1.52.2941\lib\Examine.dll
@@ -338,6 +338,7 @@
+
@@ -407,6 +408,7 @@
+
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs
index 208b2a9780..78c4549a55 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs
@@ -8,6 +8,7 @@ using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
+using Umbraco.Web;
using umbraco.BusinessLogic;
using umbraco.BusinessLogic.Actions;
using umbraco.cms.businesslogic.web;
@@ -32,9 +33,9 @@ namespace umbraco.dialogs
int docId;
if (int.TryParse(Request.QueryString["id"], out docId))
{
-
- //TODO Send to publish!!
-
+ //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here
+ ApplicationContext.Services.NotificationService.SendNotification(
+ ApplicationContext.Services.ContentService.GetById(docId), ActionToPublish.Instance, ApplicationContext);
}
}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs
index 00d1cd76f4..2e5749c4f9 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs
@@ -1,9 +1,13 @@
using System;
using System.Data;
+using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
+using Umbraco.Core;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Models.EntityBase;
+using Umbraco.Web;
using umbraco.BasePages;
using umbraco.BusinessLogic;
using umbraco.BusinessLogic.Actions;
@@ -191,22 +195,15 @@ namespace umbraco.presentation.translation
if (t != null)
{
//user auth and content node validation
- if (t.Node.Id == int.Parse(taskNode.Attributes.GetNamedItem("id").Value) && (t.User.Id == base.getUser().Id || t.ParentUser.Id == base.getUser().Id))
+ if (t.Node.Id == int.Parse(taskNode.Attributes.GetNamedItem("id").Value) && (t.User.Id == UmbracoUser.Id || t.ParentUser.Id == UmbracoUser.Id))
{
// update node contents
var d = new Document(t.Node.Id);
- Document.Import(d.ParentId, getUser(), (XmlElement)taskNode);
+ Document.Import(d.ParentId, UmbracoUser, (XmlElement)taskNode);
- /* d.Text = taskNode.Attributes.GetNamedItem("nodeName").Value.Trim();
-
- // update data elements
- foreach (XmlNode data in taskNode.SelectNodes("data"))
- if (data.FirstChild != null)
- d.getProperty(data.Attributes.GetNamedItem("alias").Value).Value = data.FirstChild.Value;
- else
- d.getProperty(data.Attributes.GetNamedItem("alias").Value).Value = "";
- */
+ //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here
+ ApplicationContext.Services.NotificationService.SendNotification(d.Content, ActionTranslate.Instance, ApplicationContext);
t.Closed = true;
t.Save();
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs
index a6e2d7e402..1dfa3362da 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs
@@ -7,8 +7,12 @@ using System.Linq;
using System.Web.Script.Services;
using System.Web.Services;
using System.Xml;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
+using Umbraco.Web;
using Umbraco.Web.WebServices;
using umbraco.BasePages;
using umbraco.BusinessLogic;
@@ -160,12 +164,16 @@ namespace umbraco.presentation.webservices
if (parentNode != null)
content.SortNodes(ref parentNode);
+ //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here
+ ApplicationContext.Services.NotificationService.SendNotification(contentService.GetById(parentId), ActionSort.Instance, UmbracoContext, ApplicationContext);
+
}
catch (Exception ex)
{
LogHelper.Error("Could not update content sort order", ex);
}
}
+
}
[Serializable]
diff --git a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj
index db7b95bd7a..22792d6aa0 100644
--- a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj
+++ b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj
@@ -40,11 +40,11 @@
..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll
-
+
False
..\packages\Examine.0.1.52.2941\lib\Examine.dll
-
+
False
..\packages\Examine.Azure.0.1.51.2941\lib\Examine.Azure.dll
diff --git a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj
index d17a4c4921..203fc935dc 100644
--- a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj
+++ b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj
@@ -40,11 +40,11 @@
..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll
-
+
False
..\packages\Examine.0.1.52.2941\lib\Examine.dll
-
+
False
..\packages\Examine.Azure.0.1.51.2941\lib\Examine.Azure.dll
diff --git a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj
index 6bdc3d3666..aed13c7c13 100644
--- a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj
+++ b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj
@@ -46,7 +46,7 @@
false
-
+
False
..\packages\Examine.0.1.52.2941\lib\Examine.dll
diff --git a/src/UmbracoExamine/UmbracoExamine.csproj b/src/UmbracoExamine/UmbracoExamine.csproj
index c180189a64..7bcb270bce 100644
--- a/src/UmbracoExamine/UmbracoExamine.csproj
+++ b/src/UmbracoExamine/UmbracoExamine.csproj
@@ -82,7 +82,7 @@
..\Solution Items\TheFARM-Public.snk
-
+
False
..\packages\Examine.0.1.52.2941\lib\Examine.dll
diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs
index ef9b77e16b..10e6176877 100644
--- a/src/umbraco.cms/businesslogic/Packager/Installer.cs
+++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs
@@ -298,51 +298,27 @@ namespace umbraco.cms.businesslogic.packager
foreach (var dataTypeDefinition in dataTypeDefinitions)
{
insPack.Data.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture));
- //saveNeeded = true;
}
}
- /*foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//DataType"))
- {
- cms.businesslogic.datatype.DataTypeDefinition newDtd = cms.businesslogic.datatype.DataTypeDefinition.Import(n);
-
- if (newDtd != null)
- {
- insPack.Data.DataTypes.Add(newDtd.Id.ToString());
- saveNeeded = true;
- }
- }*/
-
- //if (saveNeeded) { insPack.Save(); saveNeeded = false; }
#endregion
#region Languages
- foreach (XmlNode n in Config.DocumentElement.SelectNodes("//Language"))
+ var languageItemsElement = rootElement.Descendants("Languages").FirstOrDefault();
+ if (languageItemsElement != null)
{
- language.Language newLang = language.Language.Import(n);
-
- if (newLang != null)
- {
- insPack.Data.Languages.Add(newLang.id.ToString(CultureInfo.InvariantCulture));
- //saveNeeded = true;
- }
+ var insertedLanguages = packagingService.ImportLanguages(languageItemsElement);
+ insPack.Data.Languages.AddRange(insertedLanguages.Select(l => l.Id.ToString()));
}
-
- //if (saveNeeded) { insPack.Save(); saveNeeded = false; }
+
#endregion
#region Dictionary items
- foreach (XmlNode n in Config.DocumentElement.SelectNodes("./DictionaryItems/DictionaryItem"))
+ var dictionaryItemsElement = rootElement.Descendants("DictionaryItems").FirstOrDefault();
+ if (dictionaryItemsElement != null)
{
- Dictionary.DictionaryItem newDi = Dictionary.DictionaryItem.Import(n);
-
- if (newDi != null)
- {
- insPack.Data.DictionaryItems.Add(newDi.id.ToString());
- //saveNeeded = true;
- }
+ var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement);
+ insPack.Data.DictionaryItems.AddRange(insertedDictionaryItems.Select(d => d.Id.ToString()));
}
-
- //if (saveNeeded) { insPack.Save(); saveNeeded = false; }
#endregion
#region Macros
@@ -368,56 +344,8 @@ namespace umbraco.cms.businesslogic.packager
foreach (var template in templates)
{
insPack.Data.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture));
- //saveNeeded = true;
}
}
-
- //if (saveNeeded) { insPack.Save(); saveNeeded = false; }
-
- /*foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template"))
- {
- var t = Template.Import(n, currentUser);
-
- insPack.Data.Templates.Add(t.Id.ToString());
-
- saveNeeded = true;
- }
-
- if (saveNeeded) { insPack.Save(); saveNeeded = false; }
-
-
- //NOTE: SD: I'm pretty sure the only thing the below script does is ensure that the Master template Id is set
- // in the database, but this is also duplicating the saving of the design content since the above Template.Import
- // already does this. I've left this for now because I'm not sure the reprocussions of removing it but seems there
- // is a lot of excess database calls happening here.
- foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template"))
- {
- string master = XmlHelper.GetNodeValue(n.SelectSingleNode("Master"));
- Template t = Template.GetByAlias(XmlHelper.GetNodeValue(n.SelectSingleNode("Alias")));
- if (master.Trim() != "")
- {
- var masterTemplate = Template.GetByAlias(master);
- if (masterTemplate != null)
- {
- t.MasterTemplate = Template.GetByAlias(master).Id;
- //SD: This appears to always just save an empty template because the design isn't set yet
- // this fixes an issue now that we have MVC because if there is an empty template and MVC is
- // the default, it will create a View not a master page and then the system will try to route via
- // MVC which means that the package will not work anymore.
- // The code below that imports the templates should suffice because it's actually importing
- // template data not just blank data.
-
- //if (UmbracoConfiguration.Current.UmbracoSettings.Templates.UseAspNetMasterPages)
- // t.SaveMasterPageFile(t.Design);
- }
- }
- // Master templates can only be generated when their master is known
- if (UmbracoConfiguration.Current.UmbracoSettings.Templates.UseAspNetMasterPages)
- {
- t.ImportDesign(XmlHelper.GetNodeValue(n.SelectSingleNode("Design")));
- t.SaveMasterPageFile(t.Design);
- }
- }*/
#endregion
#region DocumentTypes
@@ -436,44 +364,6 @@ namespace umbraco.cms.businesslogic.packager
//saveNeeded = true;
}
}
-
- //if (saveNeeded) { insPack.Save(); saveNeeded = false; }
-
- /*foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType"))
- {
- ImportDocumentType(n, currentUser, false);
- saveNeeded = true;
- }
-
- if (saveNeeded) { insPack.Save(); saveNeeded = false; }
-
-
- // Add documenttype structure
- foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType"))
- {
- DocumentType dt = DocumentType.GetByAlias(XmlHelper.GetNodeValue(n.SelectSingleNode("Info/Alias")));
- if (dt != null)
- {
- ArrayList allowed = new ArrayList();
- foreach (XmlNode structure in n.SelectNodes("Structure/DocumentType"))
- {
- DocumentType dtt = DocumentType.GetByAlias(XmlHelper.GetNodeValue(structure));
- if (dtt != null)
- allowed.Add(dtt.Id);
- }
-
- int[] adt = new int[allowed.Count];
- for (int i = 0; i < allowed.Count; i++)
- adt[i] = (int)allowed[i];
- dt.AllowedChildContentTypeIDs = adt;
- dt.Save();
- //PPH we log the document type install here.
- insPack.Data.Documenttypes.Add(dt.Id.ToString());
- saveNeeded = true;
- }
- }
-
- if (saveNeeded) { insPack.Save(); saveNeeded = false; }*/
#endregion
#region Stylesheets
@@ -496,10 +386,6 @@ namespace umbraco.cms.businesslogic.packager
var firstContentItem = content.First();
insPack.Data.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture);
}
- /*foreach (XmlElement n in _packageConfig.DocumentElement.SelectNodes("Documents/DocumentSet [@importMode = 'root']/*"))
- {
- insPack.Data.ContentNodeId = cms.businesslogic.web.Document.Import(-1, currentUser, n).ToString();
- }*/
#endregion
#region Package Actions
diff --git a/src/umbraco.cms/businesslogic/utilities/Diff.cs b/src/umbraco.cms/businesslogic/utilities/Diff.cs
index 62397f2495..3aa159de7e 100644
--- a/src/umbraco.cms/businesslogic/utilities/Diff.cs
+++ b/src/umbraco.cms/businesslogic/utilities/Diff.cs
@@ -3,6 +3,8 @@ using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
+//TODO: We've alraedy moved most of this logic to Core.Strings - need to review this as it has slightly more functionality but should be moved to core and obsoleted!
+
namespace umbraco.cms.businesslogic.utilities {
///
/// This Class implements the Difference Algorithm published in
diff --git a/src/umbraco.cms/businesslogic/workflow/Diff.cs b/src/umbraco.cms/businesslogic/workflow/Diff.cs
index e4e4d505a9..39cbc1d2c3 100644
--- a/src/umbraco.cms/businesslogic/workflow/Diff.cs
+++ b/src/umbraco.cms/businesslogic/workflow/Diff.cs
@@ -18,7 +18,7 @@ namespace umbraco.cms.businesslogic.workflow
/// Copyright (c) by Matthias Hertel, http://www.mathertel.de
/// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
///
-
+ [Obsolete("This class will be removed from the codebase in the future")]
public class Diff
{
@@ -489,6 +489,7 @@ namespace umbraco.cms.businesslogic.workflow
/// Data on one input file being compared.
///
+ [Obsolete("This class will be removed from the codebase in the future, logic has moved to the Core project")]
internal class DiffData
{
diff --git a/src/umbraco.cms/businesslogic/workflow/Notification.cs b/src/umbraco.cms/businesslogic/workflow/Notification.cs
index b272491362..4a0afbb38d 100644
--- a/src/umbraco.cms/businesslogic/workflow/Notification.cs
+++ b/src/umbraco.cms/businesslogic/workflow/Notification.cs
@@ -72,138 +72,19 @@ namespace umbraco.cms.businesslogic.workflow
}
}
- ///TODO: Include update with html mail notification and document contents
+ //TODO: Include update with html mail notification and document contents
private static void SendNotification(User performingUser, User mailingUser, Document documentObject, IAction action)
{
- // retrieve previous version of the document
- DocumentVersionList[] versions = documentObject.GetVersions();
- int versionCount = (versions.Length > 1) ? (versions.Length - 2) : (versions.Length - 1);
- var oldDoc = new Document(documentObject.Id, versions[versionCount].Version);
+ var nService = ApplicationContext.Current.Services.NotificationService;
+ var pUser = ApplicationContext.Current.Services.UserService.GetById(performingUser.Id);
- // build summary
- var summary = new StringBuilder();
- var props = documentObject.GenericProperties;
- foreach (Property p in props)
- {
- // check if something was changed and display the changes otherwise display the fields
- Property oldProperty = oldDoc.getProperty(p.PropertyType.Alias);
- string oldText = oldProperty.Value != null ? oldProperty.Value.ToString() : "";
- string newText = p.Value != null ? p.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 PPH: Had to change this, as a reference to the editorcontrols is not allowed, so a string comparison is the only way, this should be a DIFF or something instead..
- if (p.PropertyType.DataTypeDefinition.DataType.ToString() ==
- "umbraco.editorControls.tinymce.TinyMCEDataType" &&
- string.CompareOrdinal(oldText, newText) != 0)
- {
- summary.Append("");
- summary.Append("| Note: | ");
- summary.Append(
- " 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)) +
- " | ");
- summary.Append("
");
- summary.Append("");
- summary.Append("| Old " +
- oldProperty.PropertyType.Name + " | ");
- summary.Append("" +
- ReplaceLinks(CompareText(newText, oldText, true, false,
- "", string.Empty)) +
- " | ");
- summary.Append("
");
- }
- else
- {
- summary.Append("");
- summary.Append("| " +
- p.PropertyType.Name + " | ");
- summary.Append("" + newText + " | ");
- summary.Append("
");
- }
- summary.Append(
- "| |
");
- }
-
- string protocol = GlobalSettings.UseSSL ? "https" : "http";
-
-
- string[] subjectVars = {
- HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" +
- HttpContext.Current.Request.Url.Port +
- IOHelper.ResolveUrl(SystemDirectories.Umbraco), ui.Text(action.Alias)
- ,
- documentObject.Text
- };
- string[] bodyVars = {
- mailingUser.Name, ui.Text(action.Alias), documentObject.Text, performingUser.Name,
- HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" +
- HttpContext.Current.Request.Url.Port +
- IOHelper.ResolveUrl(SystemDirectories.Umbraco),
- documentObject.Id.ToString(), summary.ToString(),
- String.Format("{2}://{0}/{1}",
- HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" +
- HttpContext.Current.Request.Url.Port,
- /*umbraco.library.NiceUrl(documentObject.Id))*/
- documentObject.Id + ".aspx",
- protocol)
- //TODO: PPH removed the niceURL reference... cms.dll cannot reference the presentation project...
- //TODO: This should be moved somewhere else..
- };
-
- // create the mail message
- var mail = new MailMessage(UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress, mailingUser.Email);
-
- // populate the message
- mail.Subject = ui.Text("notifications", "mailSubject", subjectVars, mailingUser);
- if (UmbracoConfig.For.UmbracoSettings().Content.DisableHtmlEmail)
- {
- mail.IsBodyHtml = false;
- mail.Body = ui.Text("notifications", "mailBody", bodyVars, mailingUser);
- }
- else
- {
- mail.IsBodyHtml = true;
- mail.Body =
- @"
-
-
-" +
- ui.Text("notifications", "mailBodyHtml", bodyVars, mailingUser) + "";
- }
-
- // nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here
- // adding the server name to make sure we don't replace external links
- if (GlobalSettings.UseSSL && string.IsNullOrEmpty(mail.Body) == false)
- {
- string serverName = HttpContext.Current.Request.ServerVariables["SERVER_NAME"];
- mail.Body = mail.Body.Replace(
- string.Format("http://{0}", serverName),
- string.Format("https://{0}", serverName));
- }
-
- // send it
- var sender = new SmtpClient();
- sender.Send(mail);
- }
-
- private static string ReplaceLinks(string text)
- {
- string domain = GlobalSettings.UseSSL ? "https://" : "http://";
- domain += HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" +
- HttpContext.Current.Request.Url.Port + "/";
- text = text.Replace("href=\"/", "href=\"" + domain);
- text = text.Replace("src=\"/", "src=\"" + domain);
- return text;
+ nService.SendNotifications(
+ pUser, documentObject.Content, action.Letter.ToString(CultureInfo.InvariantCulture), ui.Text(action.Alias),
+ new HttpContextWrapper(HttpContext.Current),
+ (user, strings) => ui.Text("notifications", "mailSubject", strings, mailingUser),
+ (user, strings) => UmbracoSettings.NotificationDisableHtmlEmail
+ ? ui.Text("notifications", "mailBody", strings, mailingUser)
+ : ui.Text("notifications", "mailBodyHtml", strings, mailingUser));
}
///
@@ -328,80 +209,5 @@ namespace umbraco.cms.businesslogic.workflow
MakeNew(user, node, c);
}
- ///
- /// Replaces the HTML symbols with the character equivalent.
- ///
- /// The old string.
- private static void ReplaceHtmlSymbols(ref string oldString)
- {
- oldString = oldString.Replace(" ", " ");
- oldString = oldString.Replace("’", "'");
- oldString = oldString.Replace("&", "&");
- oldString = oldString.Replace("“", "");
- oldString = oldString.Replace("”", "");
- oldString = oldString.Replace(""", "\"");
- }
-
- ///
- /// Compares the text.
- ///
- /// The old text.
- /// The new text.
- /// if set to true [display inserted text].
- /// if set to true [display deleted text].
- /// The inserted style.
- /// The deleted style.
- ///
- private static string CompareText(string oldText, string newText, bool displayInsertedText,
- bool displayDeletedText, string insertedStyle, string deletedStyle)
- {
- var sb = new StringBuilder();
- Diff.Item[] diffs = Diff.DiffText1(oldText, newText);
-
- int pos = 0;
- for (int n = 0; n < diffs.Length; n++)
- {
- Diff.Item it = diffs[n];
-
- // write unchanged chars
- while ((pos < it.StartB) && (pos < newText.Length))
- {
- sb.Append(newText[pos]);
- pos++;
- } // while
-
- // write deleted chars
- if (displayDeletedText && it.deletedA > 0)
- {
- sb.Append(deletedStyle);
- for (int m = 0; m < it.deletedA; m++)
- {
- sb.Append(oldText[it.StartA + m]);
- } // for
- sb.Append("");
- }
-
- // write inserted chars
- if (displayInsertedText && pos < it.StartB + it.insertedB)
- {
- sb.Append(insertedStyle);
- while (pos < it.StartB + it.insertedB)
- {
- sb.Append(newText[pos]);
- pos++;
- } // while
- sb.Append("");
- } // if
- } // while
-
- // write rest of unchanged chars
- while (pos < newText.Length)
- {
- sb.Append(newText[pos]);
- pos++;
- } // while
-
- return sb.ToString();
- }
}
}
\ No newline at end of file
diff --git a/src/umbraco.cms/umbraco.cms.csproj b/src/umbraco.cms/umbraco.cms.csproj
index 595e07b40d..ea9437e8a4 100644
--- a/src/umbraco.cms/umbraco.cms.csproj
+++ b/src/umbraco.cms/umbraco.cms.csproj
@@ -106,7 +106,7 @@
false
-
+
False
..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll
diff --git a/src/umbraco.controls/umbraco.controls.csproj b/src/umbraco.controls/umbraco.controls.csproj
index 9c3c80c1ac..abd0f53dce 100644
--- a/src/umbraco.controls/umbraco.controls.csproj
+++ b/src/umbraco.controls/umbraco.controls.csproj
@@ -68,7 +68,7 @@
false
-
+
False
..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll
diff --git a/src/umbraco.editorControls/umbraco.editorControls.csproj b/src/umbraco.editorControls/umbraco.editorControls.csproj
index 50aabbe197..865b574024 100644
--- a/src/umbraco.editorControls/umbraco.editorControls.csproj
+++ b/src/umbraco.editorControls/umbraco.editorControls.csproj
@@ -114,7 +114,7 @@
{651E1350-91B6-44B7-BD60-7207006D7003}
Umbraco.Web
-
+
False
..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll