Merge branch '7.0.2' of https://github.com/umbraco/Umbraco-CMS into 7.0.2
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
@@ -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> <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;\"> </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(" ", " ");
|
||||
oldString = oldString.Replace("’", "'");
|
||||
oldString = oldString.Replace("&", "&");
|
||||
oldString = oldString.Replace("“", "“");
|
||||
oldString = oldString.Replace("”", "”");
|
||||
oldString = oldString.Replace(""", "\"");
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
}
|
||||
@@ -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 <?xml version="1.0" encoding="utf-8" ?>
|
||||
///<umbPackage>
|
||||
/// <files />
|
||||
/// <info>
|
||||
/// <package>
|
||||
/// <name>Dictionary-Package</name>
|
||||
/// <version>1.0</version>
|
||||
/// <license url="http://www.opensource.org/licenses/mit-license.php">MIT license</license>
|
||||
/// <url>http://not.available</url>
|
||||
/// <requirements>
|
||||
/// <major>3</major>
|
||||
/// <minor>0</minor>
|
||||
/// <patch>0</patch>
|
||||
/// </requirements>
|
||||
/// </package>
|
||||
/// <author>
|
||||
/// <name>Test</name>
|
||||
/// <website>http://not.available</w [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string Dictionary_Package {
|
||||
get {
|
||||
return ResourceManager.GetString("Dictionary_Package", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to <?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
///<umbPackage>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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%"> PUBLISÉr </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%/actions/editContent.aspx?id=%5%"> RET </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> SLET </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%"> PUBLISÉR </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%/actions/editContent.aspx?id=%5%"> RET </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> SLET </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%"> RET </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%"> PUBLISÉR </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%"> RET </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> SLET </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">
|
||||
<p>Hallo %0%,</p>
|
||||
|
||||
|
||||
@@ -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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </a>
|
||||
<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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </a>
|
||||
<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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </a>
|
||||
<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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </a>
|
||||
<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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </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%"> PUBLISH </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%"> EDIT </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> PUBLIER </a>
|
||||
<a class="buttonEdit" href="http://%4%/actions/editContent.aspx?id=%5%"> EDITER </a>
|
||||
<a class="buttonDelete" href="http://%4%/actions/delete.aspx?id=%5%"> SUPPRIMER </a>
|
||||
<br />
|
||||
<a class="buttonEdit" href="http://%4%/#/content/content/edit/%5%"> EDITER </a>
|
||||
<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%"> PUBLIER </a>
|
||||
<a class="buttonEdit" href="http://%4%/actions/editContent.aspx?id=%5%"> EDITER </a>
|
||||
<a class="buttonDelete" href="http://%4%/actions/delete.aspx?id=%5%"> SUPPRIMER </a>
|
||||
<a class="buttonEdit" href="http://%4%/#/content/content/edit/%5%"> EDITER </a>
|
||||
<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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </a>
|
||||
<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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </a>
|
||||
<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%"> PUBBLICA </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%/actions/editContent.aspx?id=%5%"> MODIFICA </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> ELIMINA </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%"> MODIFICA </a>
|
||||
<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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </a>
|
||||
<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%"> 公開 </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%/actions/editContent.aspx?id=%5%"> 編集 </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> 削除 </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%"> 編集 </a>
|
||||
<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%"> 公開 </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%/actions/editContent.aspx?id=%5%"> 編集 </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> 削除 </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%"> 編集 </a>
|
||||
<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%"> 발행 </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%/actions/editContent.aspx?id=%5%"> 편집 </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> 삭제 </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%"> 편집 </a>
|
||||
<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%"> 발행 </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%/actions/editContent.aspx?id=%5%"> 편집 </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> 삭제 </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%"> 편집 </a>
|
||||
<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%"> PUBLISH </a>
|
||||
<a class="buttonEdit" href="http://%4%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a class="buttonDelete" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </a>
|
||||
<br />
|
||||
<a class="buttonEdit" href="http://%4%/#/content/content/edit/%5%"> EDIT </a>
|
||||
<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%"> PUBLISH </a>
|
||||
<a class="buttonEdit" href="http://%4%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a class="buttonDelete" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </a>
|
||||
<a class="buttonEdit" href="http://%4%/#/content/content/edit/%5%"> EDIT </a>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -459,9 +459,7 @@ Vennlig hilsen umbraco roboten
|
||||
</p>
|
||||
<div style="margin: 8px 0; padding: 8px; display: block;">
|
||||
<br />
|
||||
<a style="color: white; font-weight: bold; background-color: #66cc66; text-decoration : none; margin-right: 20px; border: 8px solid #66cc66; width: 150px;" href="http://%4%/umbraco/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISER&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%/umbraco/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;REDIGER&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%/umbraco/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;SLETT&nbsp;&nbsp;&nbsp;&nbsp;</a>
|
||||
<br />
|
||||
</div>
|
||||
<p>
|
||||
@@ -473,9 +471,7 @@ Vennlig hilsen umbraco roboten
|
||||
|
||||
<div style="margin: 8px 0; padding: 8px; display: block;">
|
||||
<br />
|
||||
<a style="color: white; font-weight: bold; background-color: #66cc66; text-decoration : none; margin-right: 20px; border: 8px solid #66cc66; width: 150px;" href="http://%4%/umbraco/actions/publish.aspx?id=%5%">&nbsp;&nbsp;PUBLISER&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%/umbraco/actions/editContent.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;REDIGER&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%/umbraco/actions/delete.aspx?id=%5%">&nbsp;&nbsp;&nbsp;&nbsp;SLETT&nbsp;&nbsp;&nbsp;&nbsp;</a>
|
||||
<br />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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%"> PUBLIKUJ </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%/actions/editContent.aspx?id=%5%"> EDYTUJ </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> USUŃ </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%"> PUBLIKUJ </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%/actions/editContent.aspx?id=%5%"> EDYTUJ </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> USUŃ </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%"> EDYTUJ </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%"> PUBLIKUJ </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%"> EDYTUJ </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> USUŃ </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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </a>
|
||||
<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%"> PUBLISH </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%/actions/editContent.aspx?id=%5%"> EDIT </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> DELETE </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%"> EDIT </a>
|
||||
<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%"> ОПУБЛИКОВАТЬ </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%/actions/editContent.aspx?id=%5%"> ИЗМЕНИТЬ </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> УДАЛИТЬ </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%"> ИЗМЕНИТЬ </a>
|
||||
<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%"> ОПУБЛИКОВАТЬ </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%/actions/editContent.aspx?id=%5%"> ИЗМЕНИТЬ </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> УДАЛИТЬ </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%"> ИЗМЕНИТЬ </a>
|
||||
<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}"> PUBLICERA </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}/actions/editContent.aspx?id={5}"> EDIT </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://{4}/actions/delete.aspx?id={5}"> TA BORT </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}"> PUBLICERA </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}/actions/editContent.aspx?id={5}"> REDIGERA </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://{4}/actions/delete.aspx?id={5}"> TA BORT </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}"> EDIT </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}"> PUBLICERA </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}/actions/editContent.aspx?id={5}"> REDIGERA </a> <a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://{4}/actions/delete.aspx?id={5}"> TA BORT </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%"> 发布 </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%/actions/editContent.aspx?id=%5%"> 编辑 </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> 删除 </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%"> 编辑 </a>
|
||||
<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%"> 发布 </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%/actions/editContent.aspx?id=%5%"> 编辑 </a>
|
||||
<a style="color: white; font-weight: bold; background-color: #ca4a4a; text-decoration : none; margin-right: 20px; border: 8px solid #ca4a4a; width: 150px;" href="http://%4%/actions/delete.aspx?id=%5%"> 删除 </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%"> 编辑 </a>
|
||||
<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));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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> <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;\"> </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(" ", " ");
|
||||
oldString = oldString.Replace("’", "'");
|
||||
oldString = oldString.Replace("&", "&");
|
||||
oldString = oldString.Replace("“", "“");
|
||||
oldString = oldString.Replace("”", "”");
|
||||
oldString = oldString.Replace(""", "\"");
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user