Merge branch '7.0.2' of https://github.com/umbraco/Umbraco-CMS into 7.0.2

This commit is contained in:
perploug
2014-01-13 10:17:27 +01:00
54 changed files with 1817 additions and 560 deletions
@@ -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
{
-1
View File
@@ -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
{
@@ -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;
}
}
@@ -81,9 +81,16 @@ namespace Umbraco.Core.Services
/// <summary>
/// Gets a <see cref="ILanguage"/> by its culture code
/// </summary>
/// <param name="culture">Culture Code</param>
/// <param name="cultureName">Culture Code - also refered to as the Friendly name</param>
/// <returns><see cref="ILanguage"/></returns>
ILanguage GetLanguageByCultureCode(string culture);
ILanguage GetLanguageByCultureCode(string cultureName);
/// <summary>
/// Gets a <see cref="Language"/> by its iso code
/// </summary>
/// <param name="isoCode">Iso Code of the language (ie. en-US)</param>
/// <returns><see cref="Language"/></returns>
ILanguage GetLanguageByIsoCode(string isoCode);
/// <summary>
/// Gets all available languages
@@ -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.
/// </summary>
/// <param name="entity"></param>
/// <param name="user"></param>
/// <param name="operatingUser"></param>
/// <param name="action"></param>
void SendNotifications(IEntity entity, IUser user, IAction action);
/// <param name="actionName"></param>
/// <param name="http"></param>
/// <param name="createSubject"></param>
/// <param name="createBody"></param>
void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http,
Func<IUser, string[], string> createSubject,
Func<IUser, string[], string> createBody);
/// <summary>
/// Gets the notifications for the user
@@ -28,6 +36,17 @@ namespace Umbraco.Core.Services
/// <returns></returns>
IEnumerable<Notification> GetUserNotifications(IUser user);
/// <summary>
/// Gets the notifications for the user based on the specified node path
/// </summary>
/// <param name="user"></param>
/// <param name="path"></param>
/// <returns></returns>
/// <remarks>
/// Notifications are inherited from the parent so any child node will also have notifications assigned based on it's parent (ancestors)
/// </remarks>
IEnumerable<Notification> GetUserNotifications(IUser user, string path);
/// <summary>
/// Returns the notifications for an entity
/// </summary>
@@ -44,6 +44,22 @@ namespace Umbraco.Core.Services
/// <returns>An enumrable list of generated DataTypeDefinitions</returns>
IEnumerable<IDataTypeDefinition> ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Imports and saves the 'DictionaryItems' part of the package xml as a list of <see cref="IDictionaryItem"/>
/// </summary>
/// <param name="dictionaryItemElementList">Xml to import</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumerable list of dictionary items</returns>
IEnumerable<IDictionaryItem> ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true);
/// <summary>
/// Imports and saves the 'Languages' part of a package xml as a list of <see cref="ILanguage"/>
/// </summary>
/// <param name="languageElementList">Xml to import</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumerable list of generated languages</returns>
IEnumerable<ILanguage> ImportLanguages(XElement languageElementList, bool raiseEvents = true);
/// <summary>
/// Imports and saves package xml as <see cref="ITemplate"/>
/// </summary>
@@ -78,5 +94,55 @@ namespace Umbraco.Core.Services
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Media object</returns>
XElement Export(IMedia media, bool deep = false, bool raiseEvents = true);
/// <summary>
/// Exports a list of <see cref="ILanguage"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="languages">List of Languages to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Language object</returns>
XElement Export(IEnumerable<ILanguage> languages, bool raiseEvents = true);
/// <summary>
/// Exports a single <see cref="ILanguage"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="language">Language to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Language object</returns>
XElement Export(ILanguage language, bool raiseEvents = true);
/// <summary>
/// Exports a list of <see cref="IDictionaryItem"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">List of dictionary items to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem objects</returns>
XElement Export(IEnumerable<IDictionaryItem> dictionaryItem, bool includeChildren = true, bool raiseEvents = true);
/// <summary>
/// Exports a single <see cref="IDictionaryItem"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">Dictionary Item to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem object</returns>
XElement Export(IDictionaryItem dictionaryItem, bool includeChildren, bool raiseEvents = true);
/// <summary>
/// Exports a list of Data Types
/// </summary>
/// <param name="dataTypeDefinitions">List of data types to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition objects</returns>
XElement Export(IEnumerable<IDataTypeDefinition> dataTypeDefinitions, bool raiseEvents = true);
/// <summary>
/// Exports a single Data Type
/// </summary>
/// <param name="dataTypeDefinition">Data type to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition object</returns>
XElement Export(IDataTypeDefinition dataTypeDefinition, bool raiseEvents = true);
}
}
@@ -146,10 +146,10 @@ namespace Umbraco.Core.Services
{
repository.AddOrUpdate(dictionaryItem);
uow.Commit();
SavedDictionaryItem.RaiseEvent(new SaveEventArgs<IDictionaryItem>(dictionaryItem, false), this);
}
SavedDictionaryItem.RaiseEvent(new SaveEventArgs<IDictionaryItem>(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<IDictionaryItem>(dictionaryItem, false), this);
}
DeletedDictionaryItem.RaiseEvent(new DeleteEventArgs<IDictionaryItem>(dictionaryItem, false), this);
Audit.Add(AuditTypes.Delete, "Delete DictionaryItem performed by user", userId, dictionaryItem.Id);
}
@@ -193,13 +193,29 @@ namespace Umbraco.Core.Services
/// <summary>
/// Gets a <see cref="Language"/> by its culture code
/// </summary>
/// <param name="culture">Culture Code</param>
/// <param name="cultureName">Culture Name - also refered to as the Friendly name</param>
/// <returns><see cref="Language"/></returns>
public ILanguage GetLanguageByCultureCode(string culture)
public ILanguage GetLanguageByCultureCode(string cultureName)
{
using (var repository = _repositoryFactory.CreateLanguageRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<ILanguage>.Builder.Where(x => x.CultureName == culture);
var query = Query<ILanguage>.Builder.Where(x => x.CultureName == cultureName);
var items = repository.GetByQuery(query);
return items.FirstOrDefault();
}
}
/// <summary>
/// Gets a <see cref="Language"/> by its iso code
/// </summary>
/// <param name="isoCode">Iso Code of the language (ie. en-US)</param>
/// <returns><see cref="Language"/></returns>
public ILanguage GetLanguageByIsoCode(string isoCode)
{
using (var repository = _repositoryFactory.CreateLanguageRepository(_uowProvider.GetUnitOfWork()))
{
var query = Query<ILanguage>.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<ILanguage>(language, false), this);
}
SavedLanguage.RaiseEvent(new SaveEventArgs<ILanguage>(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<ILanguage>(language, false), this);
}
DeletedLanguage.RaiseEvent(new DeleteEventArgs<ILanguage>(language, false), this);
Audit.Add(AuditTypes.Delete, "Delete Language performed by user", userId, language.Id);
}
@@ -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;
}
/// <summary>
/// Sends the notifications for the specified user regarding the specified node and action.
/// </summary>
/// <param name="entity"></param>
/// <param name="user"></param>
/// <param name="operatingUser"></param>
/// <param name="action"></param>
public void SendNotifications(IEntity entity, IUser user, IAction action)
/// <param name="actionName"></param>
/// <param name="http"></param>
/// <param name="createSubject"></param>
/// <param name="createBody"></param>
/// <remarks>
/// Currently this will only work for Content entities!
/// </remarks>
public void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http,
Func<IUser, string[], string> createSubject,
Func<IUser, string[], string> 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<NotificationService>(string.Format("Notification type: {0} sent to {1} ({2})", action, u.Name, u.Email));
}
catch (Exception ex)
{
LogHelper.Error<NotificationService>("An error occurred sending notification", ex);
}
}
}
}
/// <summary>
@@ -41,6 +97,23 @@ namespace Umbraco.Core.Services
return repository.GetUserNotifications(user);
}
/// <summary>
/// Gets the notifications for the user based on the specified node path
/// </summary>
/// <param name="user"></param>
/// <param name="path"></param>
/// <returns></returns>
/// <remarks>
/// Notifications are inherited from the parent so any child node will also have notifications assigned based on it's parent (ancestors)
/// </remarks>
public IEnumerable<Notification> 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;
}
/// <summary>
/// Deletes notifications by entity
/// </summary>
@@ -99,5 +172,254 @@ namespace Umbraco.Core.Services
var repository = new NotificationsRepository(uow);
return repository.CreateNotification(user, entity, action);
}
#region private methods
/// <summary>
/// Sends the notification
/// </summary>
/// <param name="performingUser"></param>
/// <param name="mailingUser"></param>
/// <param name="content"></param>
/// <param name="allVersions"></param>
/// <param name="actionName">The action readable name - currently an action is just a single letter, this is the name associated with the letter </param>
/// <param name="http"></param>
/// <param name="createSubject">Callback to create the mail subject</param>
/// <param name="createBody">Callback to create the mail body</param>
private void SendNotification(IUser performingUser, IUser mailingUser, IContent content, IContent[] allVersions, string actionName, HttpContextBase http,
Func<IUser, string[], string> createSubject,
Func<IUser, string[], string> 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("<tr>");
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> Note: </th>");
summary.Append(
"<td style='text-align: left; vertical-align: top;'> <span style='background-color:red;'>Red for deleted characters</span>&nbsp;<span style='background-color:yellow;'>Yellow for inserted characters</span></td>");
summary.Append("</tr>");
summary.Append("<tr>");
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> New " +
p.PropertyType.Name + "</th>");
summary.Append("<td style='text-align: left; vertical-align: top;'>" +
ReplaceLinks(CompareText(oldText, newText, true, false, "<span style='background-color:yellow;'>", string.Empty), http.Request) +
"</td>");
summary.Append("</tr>");
summary.Append("<tr>");
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> Old " +
p.PropertyType.Name + "</th>");
summary.Append("<td style='text-align: left; vertical-align: top;'>" +
ReplaceLinks(CompareText(newText, oldText, true, false, "<span style='background-color:red;'>", string.Empty), http.Request) +
"</td>");
summary.Append("</tr>");
}
else
{
summary.Append("<tr>");
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'>" +
p.PropertyType.Name + "</th>");
summary.Append("<td style='text-align: left; vertical-align: top;'>" + newText + "</td>");
summary.Append("</tr>");
}
summary.Append(
"<tr><td colspan=\"2\" style=\"border-bottom: 1px solid #CCC; font-size: 2px;\">&nbsp;</td></tr>");
}
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 =
@"<html><head>
</head>
<body style='font-family: Trebuchet MS, arial, sans-serif; font-color: black;'>
" + 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<NotificationService>("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;
}
/// <summary>
/// Replaces the HTML symbols with the character equivalent.
/// </summary>
/// <param name="oldString">The old string.</param>
private static void ReplaceHtmlSymbols(ref string oldString)
{
oldString = oldString.Replace("&nbsp;", " ");
oldString = oldString.Replace("&rsquo;", "'");
oldString = oldString.Replace("&amp;", "&");
oldString = oldString.Replace("&ldquo;", "“");
oldString = oldString.Replace("&rdquo;", "”");
oldString = oldString.Replace("&quot;", "\"");
}
/// <summary>
/// Compares the text.
/// </summary>
/// <param name="oldText">The old text.</param>
/// <param name="newText">The new text.</param>
/// <param name="displayInsertedText">if set to <c>true</c> [display inserted text].</param>
/// <param name="displayDeletedText">if set to <c>true</c> [display deleted text].</param>
/// <param name="insertedStyle">The inserted style.</param>
/// <param name="deletedStyle">The deleted style.</param>
/// <returns></returns>
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("</span>");
}
// 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("</span>");
} // if
} // while
// write rest of unchanged chars
while (pos < newText.Length)
{
sb.Append(newText[pos]);
pos++;
} // while
return sb.ToString();
}
#endregion
}
}
+300 -51
View File
@@ -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<string, IContentType>();
}
#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
/// <summary>
@@ -790,22 +750,35 @@ namespace Umbraco.Core.Services
#region DataTypes
/// <summary>
/// Export a list of data types
/// Exports a list of Data Types
/// </summary>
/// <param name="dataTypeDefinitions"></param>
/// <returns></returns>
internal XElement Export(IEnumerable<IDataTypeDefinition> dataTypeDefinitions)
/// <param name="dataTypeDefinitions">List of data types to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition objects</returns>
public XElement Export(IEnumerable<IDataTypeDefinition> 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)
/// <summary>
/// Exports a single Data Type
/// </summary>
/// <param name="dataTypeDefinition">Data type to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition object</returns>
public XElement Export(IDataTypeDefinition dataTypeDefinition, bool raiseEvents = true)
{
if (raiseEvents)
{
if (ExportingDataType.IsRaisedEventCancelled(new SaveEventArgs<IDataTypeDefinition>(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<XElement>(xml, false), this);
return xml;
}
@@ -837,7 +813,7 @@ namespace Umbraco.Core.Services
/// Imports and saves package xml as <see cref="IDataTypeDefinition"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId"></param>
/// <param name="userId">Optional id of the user</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated DataTypeDefinitions</returns>
public IEnumerable<IDataTypeDefinition> ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true)
@@ -946,12 +922,232 @@ namespace Umbraco.Core.Services
#endregion
#region Dictionary Items
/// <summary>
/// Exports a list of <see cref="IDictionaryItem"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">List of dictionary items to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem objects</returns>
public XElement Export(IEnumerable<IDictionaryItem> 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;
}
/// <summary>
/// Exports a single <see cref="IDictionaryItem"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">Dictionary Item to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem object</returns>
public XElement Export(IDictionaryItem dictionaryItem, bool includeChildren, bool raiseEvents = true)
{
if (raiseEvents)
{
if (ExportingDictionaryItem.IsRaisedEventCancelled(new SaveEventArgs<IDictionaryItem>(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<XElement>(xml, false), this);
return xml;
}
/// <summary>
/// Imports and saves the 'DictionaryItems' part of the package xml as a list of <see cref="IDictionaryItem"/>
/// </summary>
/// <param name="dictionaryItemElementList">Xml to import</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumerable list of dictionary items</returns>
public IEnumerable<IDictionaryItem> ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true)
{
if (raiseEvents)
{
if (ImportingDictionaryItem.IsRaisedEventCancelled(new SaveEventArgs<XElement>(dictionaryItemElementList), this))
return Enumerable.Empty<IDictionaryItem>();
}
var languages = _localizationService.GetAllLanguages().ToList();
return ImportDictionaryItems(dictionaryItemElementList, languages, raiseEvents);
}
private IEnumerable<IDictionaryItem> ImportDictionaryItems(XElement dictionaryItemElementList, List<ILanguage> languages, bool raiseEvents)
{
var items = new List<IDictionaryItem>();
foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem"))
items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages));
if (raiseEvents)
DictionaryItemImported.RaiseEvent(new SaveEventArgs<IDictionaryItem>(items, false), this);
return items;
}
private IEnumerable<IDictionaryItem> ImportDictionaryItem(XElement dictionaryItemElement, List<ILanguage> languages)
{
var items = new List<IDictionaryItem>();
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<ILanguage> 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<ILanguage> languages)
{
var dictionaryItem = new DictionaryItem(key);
var translations = new List<IDictionaryTranslation>();
foreach (var valueElement in dictionaryItemElement.Elements("Value"))
AddDictionaryTranslation(translations, valueElement, languages);
dictionaryItem.Translations = translations;
return dictionaryItem;
}
private static bool DictionaryValueIsNew(IEnumerable<IDictionaryTranslation> translations, XElement valueElement)
{
return translations.All(t =>
String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, StringComparison.InvariantCultureIgnoreCase) != 0
);
}
private static void AddDictionaryTranslation(ICollection<IDictionaryTranslation> translations, XElement valueElement, IEnumerable<ILanguage> 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
/// <summary>
/// Exports a list of <see cref="ILanguage"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="languages">List of Languages to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ILanguage objects</returns>
public XElement Export(IEnumerable<ILanguage> languages, bool raiseEvents = true)
{
var xml = new XElement("Languages");
foreach (var language in languages)
{
xml.Add(Export(language, raiseEvents));
}
return xml;
}
/// <summary>
/// Exports a single <see cref="ILanguage"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="language">Language to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ILanguage object</returns>
public XElement Export(ILanguage language, bool raiseEvents = true)
{
if (raiseEvents)
{
if (ExportingLanguage.IsRaisedEventCancelled(new SaveEventArgs<ILanguage>(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<XElement>(xml, false), this);
return xml;
}
/// <summary>
/// Imports and saves the 'Languages' part of a package xml as a list of <see cref="ILanguage"/>
/// </summary>
/// <param name="languageElementList">Xml to import</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumerable list of generated languages</returns>
public IEnumerable<ILanguage> ImportLanguages(XElement languageElementList, bool raiseEvents = true)
{
if (raiseEvents)
{
if (ImportingLanguage.IsRaisedEventCancelled(new SaveEventArgs<XElement>(languageElementList), this))
return Enumerable.Empty<ILanguage>();
}
var list = new List<ILanguage>();
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<ILanguage>(list, false), this);
return list;
}
#endregion
#region Macros
@@ -1222,6 +1418,9 @@ namespace Umbraco.Core.Services
#endregion
#region Stylesheets
#endregion
#region Event Handlers
/// <summary>
/// Occurs before Importing Content
@@ -1283,6 +1482,56 @@ namespace Umbraco.Core.Services
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<IDataTypeDefinition>> DataTypeImported;
/// <summary>
/// Occurs before Exporting DataType
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<IDataTypeDefinition>> ExportingDataType;
/// <summary>
/// Occurs after DataType is Exported to Xml
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<XElement>> DataTypeExported;
/// <summary>
/// Occurs before Importing DictionaryItem
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<XElement>> ImportingDictionaryItem;
/// <summary>
/// Occurs after DictionaryItem is Imported and Saved
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<IDictionaryItem>> DictionaryItemImported;
/// <summary>
/// Occurs before Exporting DictionaryItem
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<IDictionaryItem>> ExportingDictionaryItem;
/// <summary>
/// Occurs after DictionaryItem is Exported to Xml
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<XElement>> DictionaryItemExported;
/// <summary>
/// Occurs before Importing Language
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<XElement>> ImportingLanguage;
/// <summary>
/// Occurs after Language is Imported and Saved
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<ILanguage>> LanguageImported;
/// <summary>
/// Occurs before Exporting Language
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<ILanguage>> ExportingLanguage;
/// <summary>
/// Occurs after Language is Exported to Xml
/// </summary>
public static event TypedEventHandler<IPackagingService, SaveEventArgs<XElement>> LanguageExported;
/// <summary>
/// Occurs before Importing Template
/// </summary>
@@ -29,6 +29,7 @@ namespace Umbraco.Core.Services
private Lazy<ISectionService> _sectionService;
private Lazy<IMacroService> _macroService;
private Lazy<IMemberTypeService> _memberTypeService;
private Lazy<INotificationService> _notificationService;
/// <summary>
/// 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<INotificationService>(() => new NotificationService(provider, _userService.Value, _contentService.Value));
if (_serverRegistrationService == null)
_serverRegistrationService = new Lazy<ServerRegistrationService>(() => new ServerRegistrationService(provider, repositoryFactory.Value));
@@ -153,6 +157,14 @@ namespace Umbraco.Core.Services
_tagService = new Lazy<ITagService>(() => new TagService(provider, repositoryFactory.Value));
}
/// <summary>
/// Gets the <see cref="INotificationService"/>
/// </summary>
internal INotificationService NotificationService
{
get { return _notificationService.Value; }
}
/// <summary>
/// Gets the <see cref="ServerRegistrationService"/>
/// </summary>
+510
View File
@@ -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
{
/// <summary>
/// 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
/// </summary>
internal class Diff
{
/// <summary>Data on one input file being compared.
/// </summary>
internal class DiffData
{
/// <summary>Number of elements (lines).</summary>
internal int Length;
/// <summary>Buffer of numbers that will be compared.</summary>
internal int[] Data;
/// <summary>
/// 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.
/// </summary>
internal bool[] Modified;
/// <summary>
/// Initialize the Diff-Data buffer.
/// </summary>
/// <param name="initData">reference to the buffer</param>
internal DiffData(int[] initData)
{
Data = initData;
Length = initData.Length;
Modified = new bool[Length + 2];
} // DiffData
} // class DiffData
/// <summary>details of one difference.</summary>
public struct Item
{
/// <summary>Start Line number in Data A.</summary>
public int StartA;
/// <summary>Start Line number in Data B.</summary>
public int StartB;
/// <summary>Number of changes in Data A.</summary>
public int DeletedA;
/// <summary>Number of changes in Data B.</summary>
public int InsertedB;
} // Item
/// <summary>
/// Shortest Middle Snake Return Data
/// </summary>
private struct Smsrd
{
internal int X, Y;
// internal int u, v; // 2002.09.20: no need for 2 points
}
/// <summary>
/// Find the difference in 2 texts, comparing by textlines.
/// </summary>
/// <param name="textA">A-version of the text (usualy the old one)</param>
/// <param name="textB">B-version of the text (usualy the new one)</param>
/// <returns>Returns a array of Items that describe the differences.</returns>
public static Item[] DiffText(string textA, string textB)
{
return (DiffText(textA, textB, false, false, false));
} // DiffText
/// <summary>
/// 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
/// </summary>
/// <param name="textA">A-version of the text (usualy the old one)</param>
/// <param name="textB">B-version of the text (usualy the new one)</param>
/// <returns>Returns a array of Items that describe the differences.</returns>
public static Item[] DiffText1(string textA, string textB)
{
return DiffInt(DiffCharCodes(textA, false), DiffCharCodes(textB, false));
}
/// <summary>
/// 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.
/// </summary>
/// <param name="textA">A-version of the text (usualy the old one)</param>
/// <param name="textB">B-version of the text (usualy the new one)</param>
/// <param name="trimSpace">When set to true, all leading and trailing whitespace characters are stripped out before the comparation is done.</param>
/// <param name="ignoreSpace">When set to true, all whitespace characters are converted to a single space character before the comparation is done.</param>
/// <param name="ignoreCase">When set to true, all characters are converted to their lowercase equivivalence before the comparation is done.</param>
/// <returns>Returns a array of Items that describe the differences.</returns>
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
/// <summary>
/// Diffs the char codes.
/// </summary>
/// <param name="aText">A text.</param>
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
/// <returns></returns>
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
/// <summary>
/// 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.
/// </summary>
/// <param name="data">A Diff data buffer containing the identified changes.</param>
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
/// <summary>
/// Find the difference in 2 arrays of integers.
/// </summary>
/// <param name="arrayA">A-version of the numbers (usualy the old one)</param>
/// <param name="arrayB">B-version of the numbers (usualy the new one)</param>
/// <returns>Returns a array of Items that describe the differences.</returns>
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
/// <summary>
/// This function converts all textlines of the text into unique numbers for every unique textline
/// so further work can work only with simple numbers.
/// </summary>
/// <param name="aText">the input text</param>
/// <param name="h">This extern initialized hashtable is used for storing all ever used textlines.</param>
/// <param name="trimSpace">ignore leading and trailing space characters</param>
/// <param name="ignoreSpace"></param>
/// <param name="ignoreCase"></param>
/// <returns>a array of integers.</returns>
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
/// <summary>
/// This is the algorithm to find the Shortest Middle Snake (SMS).
/// </summary>
/// <param name="dataA">sequence A</param>
/// <param name="lowerA">lower bound of the actual range in DataA</param>
/// <param name="upperA">upper bound of the actual range in DataA (exclusive)</param>
/// <param name="dataB">sequence B</param>
/// <param name="lowerB">lower bound of the actual range in DataB</param>
/// <param name="upperB">upper bound of the actual range in DataB (exclusive)</param>
/// <param name="downVector">a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.</param>
/// <param name="upVector">a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.</param>
/// <returns>a MiddleSnakeData record containing x,y and u,v</returns>
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
/// <summary>
/// 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.
/// </summary>
/// <param name="dataA">sequence A</param>
/// <param name="lowerA">lower bound of the actual range in DataA</param>
/// <param name="upperA">upper bound of the actual range in DataA (exclusive)</param>
/// <param name="dataB">sequence B</param>
/// <param name="lowerB">lower bound of the actual range in DataB</param>
/// <param name="upperB">upper bound of the actual range in DataB (exclusive)</param>
/// <param name="downVector">a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons.</param>
/// <param name="upVector">a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons.</param>
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()
/// <summary>Scan the tables of which lines are inserted and deleted,
/// producing an edit script in forward order.
/// </summary>
/// 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
}
+1
View File
@@ -1011,6 +1011,7 @@
<Compile Include="Standalone\StandaloneCoreApplication.cs" />
<Compile Include="Standalone\StandaloneCoreBootManager.cs" />
<Compile Include="Strings\ContentBaseExtensions.cs" />
<Compile Include="Strings\Diff.cs" />
<Compile Include="TopologicalSorter.cs" />
<Compile Include="Strings\DefaultUrlSegmentProvider.cs" />
<Compile Include="Strings\IUrlSegmentProvider.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()
{
@@ -0,0 +1,36 @@
<?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</website>
</author>
<readme><![CDATA[]]></readme>
</info>
<DictionaryItems>
<DictionaryItem Key="Parent">
<Value LanguageId="2" LanguageCultureAlias="nb-NO"><![CDATA[ForelderVerdi]]></Value>
<Value LanguageId="3" LanguageCultureAlias="en-GB"><![CDATA[ParentValue]]></Value>
<DictionaryItem Key="Child">
<Value LanguageId="2" LanguageCultureAlias="nb-NO"><![CDATA[BarnVerdi]]></Value>
<Value LanguageId="3" LanguageCultureAlias="en-GB"><![CDATA[ChildValue]]></Value>
</DictionaryItem>
</DictionaryItem>
</DictionaryItems>
<Languages>
<Language Id="2" CultureAlias="nb-NO" FriendlyName="Norwegian" />
<Language Id="3" CultureAlias="en-GB" FriendlyName="English (United Kingdom)" />
</Languages>
</umbPackage>
@@ -88,6 +88,32 @@ namespace Umbraco.Tests.Services.Importing {
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
///&lt;umbPackage&gt;
/// &lt;files /&gt;
/// &lt;info&gt;
/// &lt;package&gt;
/// &lt;name&gt;Dictionary-Package&lt;/name&gt;
/// &lt;version&gt;1.0&lt;/version&gt;
/// &lt;license url=&quot;http://www.opensource.org/licenses/mit-license.php&quot;&gt;MIT license&lt;/license&gt;
/// &lt;url&gt;http://not.available&lt;/url&gt;
/// &lt;requirements&gt;
/// &lt;major&gt;3&lt;/major&gt;
/// &lt;minor&gt;0&lt;/minor&gt;
/// &lt;patch&gt;0&lt;/patch&gt;
/// &lt;/requirements&gt;
/// &lt;/package&gt;
/// &lt;author&gt;
/// &lt;name&gt;Test&lt;/name&gt;
/// &lt;website&gt;http://not.available&lt;/w [rest of string was truncated]&quot;;.
/// </summary>
internal static string Dictionary_Package {
get {
return ResourceManager.GetString("Dictionary_Package", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
///&lt;umbPackage&gt;
@@ -145,4 +145,7 @@
<data name="CheckboxList_Content_Package_LegacyIds" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>checkboxlist-content-package-legacyids.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="Dictionary_Package" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>dictionary-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
</root>
@@ -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<IDictionaryTranslation>
{
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<IDictionaryTranslation>
{
new DictionaryTranslation(englishLanguage, expectedEnglishParentValue),
new DictionaryTranslation(norwegianLanguage, expectedNorwegianParentValue),
}
}
);
}
}
}
@@ -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<IDictionaryTranslation>
{
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<IDictionaryTranslation>
{
new DictionaryTranslation(languageNbNo, "BarnVerdi"),
new DictionaryTranslation(languageEnGb, "ChildValue")
};
childItem.Translations = childTranslations;
ServiceContext.LocalizationService.Save(childItem);
}
}
}
+1
View File
@@ -638,6 +638,7 @@
<Content Include="Services\Importing\CheckboxList-Content-Package.xml">
<SubType>Designer</SubType>
</Content>
<Content Include="Services\Importing\Dictionary-Package.xml" />
<Content Include="UmbracoExamine\TestFiles\media.xml" />
<Content Include="Services\Importing\InheritedDocTypes-Package.xml" />
<Content Include="Services\Importing\SingleDocType.xml" />
+3 -3
View File
@@ -115,15 +115,15 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="ClientDependency.Core, Version=1.7.1.1, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ClientDependency.Core">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll</HintPath>
</Reference>
<Reference Include="ClientDependency.Core.Mvc, Version=1.7.0.4, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ClientDependency.Core.Mvc">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.52.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.52.2941\lib\Examine.dll</HintPath>
</Reference>
@@ -467,20 +467,21 @@
</area>
<area alias="notifications">
<key alias="editNotifications">Rediger dine notificeringer for %0%</key>
<key alias="mailBody"><![CDATA[
<key alias="mailBody">
<![CDATA[
Hej %0%
Dette er en automatisk mail for at fortælle at handlingen '%1%'
er blevet udført på siden '%2%'
af brugeren '%3%'
Gå til http://%4%/default.aspx?section=content&id=%5% for at redigere.
Gå til http://%4%/#/content/content/edit/%5% for at redigere.
Ha' en dejlig dag!
Mange hilsner fra umbraco robotten
]]></key>
<key alias="mailBodyHtml"><![CDATA[<p>Hej %0%</p> <p>Dette er en automatisk mail for at informere dig om at opgaven <strong>'%1%'</strong> er blevet udførtpå siden <a href="http://%4%/actions/preview.aspx?id=%5%"><strong>'%2%'</strong></a> af brugeren <strong>'%3%'</strong> </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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISÉr&nbsp;&nbsp;</a> &nbsp; <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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;SLET&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p> <h3>Opdateringssammendrag:</h3> <table style="width: 100%;"> %6% </table> </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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISÉR&nbsp;&nbsp;</a> &nbsp; <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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;SLET&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p>Hav en fortsat god dag!<br /><br /> De bedste hilsner fra umbraco robotten </p>]]></key>
<key alias="mailBodyHtml"><![CDATA[<p>Hej %0%</p> <p>Dette er en automatisk mail for at informere dig om at opgaven <strong>'%1%'</strong> er blevet udførtpå siden <a href="http://%4%/#/content/content/edit/%5%"><strong>'%2%'</strong></a> af brugeren <strong>'%3%'</strong> </p> <div style="margin: 8px 0; padding: 8px; display: block;"> <br /> <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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <br /> </div> <p> <h3>Opdateringssammendrag:</h3> <table style="width: 100%;"> %6% </table> </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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISÉR&nbsp;&nbsp;</a> &nbsp; <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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RET&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;SLET&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p>Hav en fortsat god dag!<br /><br /> De bedste hilsner fra umbraco robotten </p>]]></key>
<key alias="mailSubject">[%0%] Notificering om %1% udført på %2%</key>
<key alias="notifications">Notificeringer</key>
</area>
@@ -497,15 +497,15 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die
<area alias="notifications">
<key alias="editNotifications">Bearbeiten Sie Ihre Benachrichtigungseinstellungen für '%0%'</key>
<key alias="mailBody">
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
</key>
Einen schönen Tag wünscht
Ihr freundlicher Umbraco-Robot
</key>
<key alias="mailBodyHtml">
&lt;p&gt;Hallo %0%,&lt;/p&gt;
@@ -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
<key alias="mailBodyHtml"><![CDATA[<p>Hi %0%</p>
<p>This is an automated mail to inform you that the task <strong>'%1%'</strong>
has been performed on the page <a href="http://%4%/actions/preview.aspx?id=%5%"><strong>'%2%'</strong></a>
has been performed on the page <a href="http://%4%/#/content/content/edit/%5%"><strong>'%2%'</strong></a>
by the user <strong>'%3%'</strong>
</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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -612,9 +610,7 @@ To manage your website, simply open the umbraco back office and start adding con
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -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
</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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -612,9 +610,7 @@ To manage your website, simply open the umbraco back office and start adding con
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -433,8 +433,8 @@ However, Runway offers an easy foundation based on best practices to get you sta
</area>
<area alias="notifications">
<key alias="editNotifications">Edite su notificación para %0%</key>
<key alias="mailBody">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</key>
<key alias="mailBodyHtml"><![CDATA[<p>Hola %0%</p> <p>Esto es un e-mail generado automáticamente para informarle que la tarea <strong>'%1%'</strong> ha sido realizada sobre la página <a href="http://%4%/actions/preview.aspx?id=%5%"><strong>'%2%'</strong></a> por el usuario <strong>'%3%'</strong> </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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp; <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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p> <h3>Resumen de actualización:</h3> <table style="width: 100%;"> %6% </table> </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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp; <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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p>¡Espero que tenga un buen día!<br /><br /> Saludos del robot umbraco. </p>]]></key>
<key alias="mailBody">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</key>
<key alias="mailBodyHtml"><![CDATA[<p>Hola %0%</p> <p>Esto es un e-mail generado automáticamente para informarle que la tarea <strong>'%1%'</strong> ha sido realizada sobre la página <a href="http://%4%/#/content/content/edit/%5%"><strong>'%2%'</strong></a> por el usuario <strong>'%3%'</strong> </p> <div style="margin: 8px 0; padding: 8px; display: block;"> <br /> <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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <br /> </div> <p> <h3>Resumen de actualización:</h3> <table style="width: 100%;"> %6% </table> </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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp; <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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p>¡Espero que tenga un buen día!<br /><br /> Saludos del robot umbraco. </p>]]></key>
<key alias="mailSubject">[%0%] Notificación acerca de %1% realizado en %2%</key>
<key alias="notifications">Notificaciones</key>
</area>
@@ -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 <strong>'%3%'</strong>
</p>
<div class="buttons">
<br />
<a class="buttonPublish" href="http://%4%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLIER&nbsp;&nbsp;</a> &nbsp;
<a class="buttonEdit" href="http://%4%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDITER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<a class="buttonDelete" href="http://%4%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;SUPPRIMER&nbsp;&nbsp;&nbsp;&nbsp;</a>
<br />
<a class="buttonEdit" href="http://%4%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDITER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -564,9 +562,7 @@ Pour gerer votre site, accéder simplement au back office et commencez à contri
<div class="buttons">
<br />
<a class="buttonPublish" href="http://%4%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLIER&nbsp;&nbsp;</a> &nbsp;
<a class="buttonEdit" href="http://%4%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDITER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<a class="buttonDelete" href="http://%4%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;SUPPRIMER&nbsp;&nbsp;&nbsp;&nbsp;</a>
<a class="buttonEdit" href="http://%4%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDITER&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -509,9 +509,7 @@ To manage your website, simply open the umbraco back office and start adding con
</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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -523,9 +521,7 @@ To manage your website, simply open the umbraco back office and start adding con
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -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
</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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBBLICA&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MODIFICA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;ELIMINA&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MODIFICA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -487,9 +485,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -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をインストールして作られた新しいウェブサイトがど
<p>ユーザー <strong>'%3%'</strong> によりページ <a href="http://%4%/actions/preview.aspx?id=%5%"><strong>'%2%'</strong></a> 上のタスク <strong>'%1%'</strong> から自動的にメールします。</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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;公開&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;編集&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;削除&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;編集&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -521,9 +519,7 @@ Runwayをインストールして作られた新しいウェブサイトがど
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;公開&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;編集&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;削除&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;編集&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -475,7 +475,7 @@
사용자 '%3%' 가 작업 '%1%' 를 페이지 '%2%' 에서
진행했음을 알리는 자동 발송 메일입니다.
편집하시려면 http://%4%/actions/editContent.aspx?id=%5% 로 이동하세요
편집하시려면 http://%4%/#/content/content/edit/%5% 로 이동하세요
좋은 하루 되세요!
@@ -488,9 +488,7 @@
</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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;발행&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;편집&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;삭제&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;편집&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -502,9 +500,7 @@
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;발행&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;편집&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;삭제&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;편집&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -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 <strong>'%3%'</strong>
</p>
<div class="buttons">
<br />
<a class="buttonPublish" href="http://%4%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<a class="buttonEdit" href="http://%4%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<a class="buttonDelete" href="http://%4%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</a>
<br />
<a class="buttonEdit" href="http://%4%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -466,9 +464,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<div class="buttons">
<br />
<a class="buttonPublish" href="http://%4%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<a class="buttonEdit" href="http://%4%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<a class="buttonDelete" href="http://%4%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</a>
<a class="buttonEdit" href="http://%4%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -459,9 +459,7 @@ Vennlig hilsen umbraco roboten
&lt;/p&gt;
&lt;div style="margin: 8px 0; padding: 8px; display: block;"&gt;
&lt;br /&gt;
&lt;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%"&gt;&amp;nbsp;&amp;nbsp;PUBLISER&amp;nbsp;&amp;nbsp;&lt;/a&gt; &amp;nbsp;
&lt;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%"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;REDIGER&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/a&gt; &amp;nbsp;
&lt;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%"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SLETT&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/a&gt;
&lt;br /&gt;
&lt;/div&gt;
&lt;p&gt;
@@ -473,9 +471,7 @@ Vennlig hilsen umbraco roboten
&lt;div style="margin: 8px 0; padding: 8px; display: block;"&gt;
&lt;br /&gt;
&lt;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%"&gt;&amp;nbsp;&amp;nbsp;PUBLISER&amp;nbsp;&amp;nbsp;&lt;/a&gt; &amp;nbsp;
&lt;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%"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;REDIGER&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/a&gt; &amp;nbsp;
&lt;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%"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SLETT&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/a&gt;
&lt;br /&gt;
&lt;/div&gt;
@@ -426,10 +426,10 @@ Możesz dodać dodatkowe języki w menu "Języki" po lewej stronie.]]></key>
<key alias="editNotifications">Edytuj powiadomienie dla %0%</key>
<key alias="mailBody"><![CDATA[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%'.
Możesz dalej edytować pod adresem:
http://%4%/actions/editContent.aspx?id=%5%
http://%4%/#/content/content/edit/%5%
Miłego dnia!]]></key>
<key alias="mailBodyHtml"><![CDATA[<p>Witaj %0%</p> <p>To jest automatyczny e-mail, wysłany aby poinformować Cię, że polecenie <strong>'%1%'</strong> zostało wykonane na stronie <a href="http://%4%/actions/preview.aspx?id=%5%"><strong>'%2%'</strong></a> przez użytkownika <strong>'%3%'</strong> </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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLIKUJ&nbsp;&nbsp;</a> &nbsp; <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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDYTUJ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;USUŃ&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p> <h3>Podsumowanie zmian:</h3> <table style="width: 100%;"> %6% </table> </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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLIKUJ&nbsp;&nbsp;</a> &nbsp; <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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDYTUJ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;USUŃ&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p>Miłego dnia!<br /><br /> Pozdrowienia od robota Umbraco </p>]]></key>
<key alias="mailBodyHtml"><![CDATA[<p>Witaj %0%</p> <p>To jest automatyczny e-mail, wysłany aby poinformować Cię, że polecenie <strong>'%1%'</strong> zostało wykonane na stronie <a href="http://%4%/actions/preview.aspx?id=%5%"><strong>'%2%'</strong></a> przez użytkownika <strong>'%3%'</strong> </p> <div style="margin: 8px 0; padding: 8px; display: block;"> <br /><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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDYTUJ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <br /> </div> <p> <h3>Podsumowanie zmian:</h3> <table style="width: 100%;"> %6% </table> </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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLIKUJ&nbsp;&nbsp;</a> &nbsp; <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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDYTUJ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;USUŃ&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p>Miłego dnia!<br /><br /> Pozdrowienia od robota Umbraco </p>]]></key>
<key alias="mailSubject">[%0%] Powiadomienie o %1% wykonane na %2%</key>
<key alias="notifications">Powiadomienie</key>
</area>
@@ -467,7 +467,7 @@ Pressione <strong>"próximo"</strong> para iniciar o assistente.]]></key>
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.
</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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -494,9 +492,7 @@ Vá até http://%4%/actions/editContent.aspx?id=%5% para editar.
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISH&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;DELETE&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -515,7 +515,7 @@
Операция '%1%'
была произведена на странице '%2%' пользователем '%3%'.
Вы можете увидеть изменения и отредактировать, перейдя по ссылке http://%4%/actions/editContent.aspx?id=%5%.
Вы можете увидеть изменения и отредактировать, перейдя по ссылке http://%4%/#/content/content/edit/%5%.
Удачи!
@@ -529,9 +529,7 @@
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;ОПУБЛИКОВАТЬ&nbsp;&nbsp;</a>&nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ИЗМЕНИТЬ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;УДАЛИТЬ&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ИЗМЕНИТЬ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -544,9 +542,7 @@
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;ОПУБЛИКОВАТЬ&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ИЗМЕНИТЬ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;УДАЛИТЬ&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ИЗМЕНИТЬ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -496,7 +496,7 @@
<area alias="notifications">
<key alias="editNotifications">Inställningar för notifieringar gällande {0}</key>
<key alias="mailBody">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.</key>
<key alias="mailBodyHtml"><![CDATA[<p>Hej {0}</p> <p>Detta mail skickas till dig automatiskt för att meddela att <strong>'{1}'</strong> har utförts på sidan <a href="http://{4}/actions/preview.aspx?id={5}"><strong>'{2}'</strong></a> av användaren <strong>'{3}'</strong> </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}/actions/publish.aspx?id={5}">&nbsp;&nbsp;PUBLICERA&nbsp;&nbsp;</a> &nbsp; <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}/actions/editContent.aspx?id={5}">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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}/actions/delete.aspx?id={5}">&nbsp;&nbsp;&nbsp;&nbsp;TA BORT&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div> <p> <h3>Sammanfattning av uppdateringen:</h3> <table style="width: 100%;"> {6} </table> </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}/actions/publish.aspx?id={5}">&nbsp;&nbsp;PUBLICERA&nbsp;&nbsp;</a> &nbsp; <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}/actions/editContent.aspx?id={5}">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;REDIGERA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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}/actions/delete.aspx?id={5}">&nbsp;&nbsp;&nbsp;&nbsp;TA BORT&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div>]]></key>
<key alias="mailBodyHtml"><![CDATA[<p>Hej {0}</p> <p>Detta mail skickas till dig automatiskt för att meddela att <strong>'{1}'</strong> har utförts på sidan <a href="http://{4}/actions/preview.aspx?id={5}"><strong>'{2}'</strong></a> av användaren <strong>'{3}'</strong> </p> <div style="margin: 8px 0; padding: 8px; display: block;"> <br /> <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}/actions/editContent.aspx?id={5}">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;EDIT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <br /> </div> <p> <h3>Sammanfattning av uppdateringen:</h3> <table style="width: 100%;"> {6} </table> </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}/actions/publish.aspx?id={5}">&nbsp;&nbsp;PUBLICERA&nbsp;&nbsp;</a> &nbsp; <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}/actions/editContent.aspx?id={5}">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;REDIGERA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp; <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}/actions/delete.aspx?id={5}">&nbsp;&nbsp;&nbsp;&nbsp;TA BORT&nbsp;&nbsp;&nbsp;&nbsp;</a> <br /> </div>]]></key>
<key alias="mailSubject">[{0}] Meddelande för att informera om att {1} har utförts på {2}</key>
<key alias="notifications">Notifieringar</key>
</area>
@@ -530,7 +530,7 @@
您好!这是一封自动邮件,提醒您用户'%3%'执行'%1%'任务已经在完成'%2%'。
转到 http://%4%/actions/editContent.aspx?id=%5% 进行编辑
转到 http://%4%/#/content/content/edit/%5% 进行编辑
]]></key>
<key alias="mailBodyHtml"><![CDATA[<p>%0%</p>
@@ -538,9 +538,7 @@
<p>您好!这是一封自动发送的邮件,告诉您任务<strong>'%1%'</strong>已在<a href="http://%4%/actions/preview.aspx?id=%5%"><strong>'%2%'</strong></a>被用户<strong>'%3%'</strong>执行</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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;发布&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编辑&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;删除&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编辑&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
<p>
@@ -552,9 +550,7 @@
<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%/actions/publish.aspx?id=%5%">&nbsp;&nbsp;发布&nbsp;&nbsp;</a> &nbsp;
<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%/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编辑&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<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%/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;删除&nbsp;&nbsp;&nbsp;&nbsp;</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%/#/content/content/edit/%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编辑&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a> &nbsp;
<br />
</div>
@@ -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));
}
}
}
@@ -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
{
/// <summary>
/// Subscribes to the relavent events in order to send out notifications
/// </summary>
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));
}
}
}
+4 -2
View File
@@ -102,7 +102,7 @@
<Reference Include="AutoMapper.Net4">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
</Reference>
<Reference Include="ClientDependency.Core, Version=1.7.1.1, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ClientDependency.Core">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll</HintPath>
</Reference>
@@ -110,7 +110,7 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.52.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.52.2941\lib\Examine.dll</HintPath>
</Reference>
@@ -338,6 +338,7 @@
<Compile Include="Models\TagModel.cs" />
<Compile Include="Models\UpgradeCheckResponse.cs" />
<Compile Include="Models\PasswordChangedModel.cs" />
<Compile Include="NotificationServiceExtensions.cs" />
<Compile Include="PropertyEditors\ColorListPreValueEditor.cs" />
<Compile Include="PropertyEditors\EmailAddressPropertyEditor.cs" />
<Compile Include="PropertyEditors\ListViewPropertyEditor.cs" />
@@ -407,6 +408,7 @@
<Compile Include="Routing\UrlProviderExtensions.cs" />
<Compile Include="Strategies\Migrations\ClearCsrfCookiesAfterUpgrade.cs" />
<Compile Include="Strategies\Migrations\ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs" />
<Compile Include="Strategies\NotificationsHandler.cs" />
<Compile Include="TagQuery.cs" />
<Compile Include="Trees\CoreTreeAttribute.cs" />
<Compile Include="Trees\DataTypeTreeController.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);
}
}
@@ -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();
@@ -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<nodeSorter>("Could not update content sort order", ex);
}
}
}
[Serializable]
@@ -40,11 +40,11 @@
<Reference Include="AzureDirectory">
<HintPath>..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.52.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.52.2941\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="Examine.Azure, Version=0.1.51.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine.Azure">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.Azure.0.1.51.2941\lib\Examine.Azure.dll</HintPath>
</Reference>
@@ -40,11 +40,11 @@
<Reference Include="AzureDirectory">
<HintPath>..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.52.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.52.2941\lib\Examine.dll</HintPath>
</Reference>
<Reference Include="Examine.Azure, Version=0.1.51.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine.Azure">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.Azure.0.1.51.2941\lib\Examine.Azure.dll</HintPath>
</Reference>
@@ -46,7 +46,7 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Examine, Version=0.1.52.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.52.2941\lib\Examine.dll</HintPath>
</Reference>
+1 -1
View File
@@ -82,7 +82,7 @@
<AssemblyOriginatorKeyFile>..\Solution Items\TheFARM-Public.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="Examine, Version=0.1.52.2941, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Examine">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Examine.0.1.52.2941\lib\Examine.dll</HintPath>
</Reference>
@@ -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
@@ -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 {
/// <summary>
/// This Class implements the Difference Algorithm published in
@@ -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
/// </summary>
[Obsolete("This class will be removed from the codebase in the future")]
public class Diff
{
@@ -489,6 +489,7 @@ namespace umbraco.cms.businesslogic.workflow
/// <summary>Data on one input file being compared.
/// </summary>
[Obsolete("This class will be removed from the codebase in the future, logic has moved to the Core project")]
internal class DiffData
{
@@ -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("<tr>");
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> Note: </th>");
summary.Append(
"<td style='text-align: left; vertical-align: top;'> <span style='background-color:red;'>Red for deleted characters</span>&nbsp;<span style='background-color:yellow;'>Yellow for inserted characters</span></td>");
summary.Append("</tr>");
summary.Append("<tr>");
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> New " +
p.PropertyType.Name + "</th>");
summary.Append("<td style='text-align: left; vertical-align: top;'>" +
ReplaceLinks(CompareText(oldText, newText, true, false,
"<span style='background-color:yellow;'>", string.Empty)) +
"</td>");
summary.Append("</tr>");
summary.Append("<tr>");
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> Old " +
oldProperty.PropertyType.Name + "</th>");
summary.Append("<td style='text-align: left; vertical-align: top;'>" +
ReplaceLinks(CompareText(newText, oldText, true, false,
"<span style='background-color:red;'>", string.Empty)) +
"</td>");
summary.Append("</tr>");
}
else
{
summary.Append("<tr>");
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'>" +
p.PropertyType.Name + "</th>");
summary.Append("<td style='text-align: left; vertical-align: top;'>" + newText + "</td>");
summary.Append("</tr>");
}
summary.Append(
"<tr><td colspan=\"2\" style=\"border-bottom: 1px solid #CCC; font-size: 2px;\">&nbsp;</td></tr>");
}
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 =
@"<html><head>
</head>
<body style='font-family: Trebuchet MS, arial, sans-serif; font-color: black;'>
" +
ui.Text("notifications", "mailBodyHtml", bodyVars, mailingUser) + "</body></html>";
}
// 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));
}
/// <summary>
@@ -328,80 +209,5 @@ namespace umbraco.cms.businesslogic.workflow
MakeNew(user, node, c);
}
/// <summary>
/// Replaces the HTML symbols with the character equivalent.
/// </summary>
/// <param name="oldString">The old string.</param>
private static void ReplaceHtmlSymbols(ref string oldString)
{
oldString = oldString.Replace("&nbsp;", " ");
oldString = oldString.Replace("&rsquo;", "'");
oldString = oldString.Replace("&amp;", "&");
oldString = oldString.Replace("&ldquo;", "“");
oldString = oldString.Replace("&rdquo;", "”");
oldString = oldString.Replace("&quot;", "\"");
}
/// <summary>
/// Compares the text.
/// </summary>
/// <param name="oldText">The old text.</param>
/// <param name="newText">The new text.</param>
/// <param name="displayInsertedText">if set to <c>true</c> [display inserted text].</param>
/// <param name="displayDeletedText">if set to <c>true</c> [display deleted text].</param>
/// <param name="insertedStyle">The inserted style.</param>
/// <param name="deletedStyle">The deleted style.</param>
/// <returns></returns>
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("</span>");
}
// 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("</span>");
} // if
} // while
// write rest of unchanged chars
while (pos < newText.Length)
{
sb.Append(newText[pos]);
pos++;
} // while
return sb.ToString();
}
}
}
+1 -1
View File
@@ -106,7 +106,7 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="ClientDependency.Core, Version=1.7.1.1, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ClientDependency.Core">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll</HintPath>
</Reference>
+1 -1
View File
@@ -68,7 +68,7 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="ClientDependency.Core, Version=1.7.1.1, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ClientDependency.Core">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll</HintPath>
</Reference>
@@ -114,7 +114,7 @@
<Project>{651E1350-91B6-44B7-BD60-7207006D7003}</Project>
<Name>Umbraco.Web</Name>
</ProjectReference>
<Reference Include="ClientDependency.Core, Version=1.7.1.1, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="ClientDependency.Core">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll</HintPath>
</Reference>