diff --git a/src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs b/src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs
index b68aef3c3d..6b55a4af7e 100644
--- a/src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs
+++ b/src/Umbraco.Core/Composing/CompositionRoots/CoreMappingProfilesCompositionRoot.cs
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Composing.CompositionRoots
{
public void Compose(IServiceRegistry container)
{
- container.Register();
+ container.Register();
}
}
}
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
index 2dcbe06458..332de45734 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
internal class ContentElement : UmbracoConfigurationElement, IContentSection
{
- private const string DefaultPreviewBadge = @"In Preview Mode - click to end";
+ private const string DefaultPreviewBadge = @"In Preview Mode - click to end";
[ConfigurationProperty("imaging")]
internal ContentImagingElement Imaging => (ContentImagingElement) this["imaging"];
diff --git a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
index 75ed8238d0..302ccbcc54 100644
--- a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
+++ b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
@@ -33,6 +33,8 @@ namespace Umbraco.Core.Models.Identity
private string[] _allowedSections;
private int[] _startMediaIds;
private int[] _startContentIds;
+ private DateTime? _lastPasswordChangeDateUtc;
+
///
/// Used to construct a new instance without an identity
@@ -136,6 +138,15 @@ namespace Umbraco.Core.Models.Identity
set => _beingDirty.SetPropertyValueAndDetectChanges(value, ref _userName, Ps.Value.UserNameSelector);
}
+ ///
+ /// LastPasswordChangeDateUtc so we can track changes to it
+ ///
+ public override DateTime? LastPasswordChangeDateUtc
+ {
+ get { return _lastPasswordChangeDateUtc; }
+ set { _beingDirty.SetPropertyValueAndDetectChanges(value, ref _lastPasswordChangeDateUtc, Ps.Value.LastPasswordChangeDateUtcSelector); }
+ }
+
///
/// Override LastLoginDateUtc so we can track changes to it
///
@@ -419,6 +430,7 @@ namespace Umbraco.Core.Models.Identity
public readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo(x => x.Email);
public readonly PropertyInfo UserNameSelector = ExpressionHelper.GetPropertyInfo(x => x.UserName);
public readonly PropertyInfo LastLoginDateUtcSelector = ExpressionHelper.GetPropertyInfo(x => x.LastLoginDateUtc);
+ public readonly PropertyInfo LastPasswordChangeDateUtcSelector = ExpressionHelper.GetPropertyInfo(x => x.LastPasswordChangeDateUtc);
public readonly PropertyInfo EmailConfirmedSelector = ExpressionHelper.GetPropertyInfo(x => x.EmailConfirmed);
public readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
public readonly PropertyInfo AccessFailedCountSelector = ExpressionHelper.GetPropertyInfo(x => x.AccessFailedCount);
@@ -439,5 +451,6 @@ namespace Umbraco.Core.Models.Identity
groups => groups.GetHashCode());
}
+
}
}
diff --git a/src/Umbraco.Core/Models/Identity/IdentityProfile.cs b/src/Umbraco.Core/Models/Identity/IdentityMapperProfile.cs
similarity index 90%
rename from src/Umbraco.Core/Models/Identity/IdentityProfile.cs
rename to src/Umbraco.Core/Models/Identity/IdentityMapperProfile.cs
index f44003b62a..81069bd74c 100644
--- a/src/Umbraco.Core/Models/Identity/IdentityProfile.cs
+++ b/src/Umbraco.Core/Models/Identity/IdentityMapperProfile.cs
@@ -8,9 +8,9 @@ using Umbraco.Core.Services;
namespace Umbraco.Core.Models.Identity
{
- public class IdentityProfile : Profile
+ public class IdentityMapperProfile : Profile
{
- public IdentityProfile(ILocalizedTextService textService, IEntityService entityService, IGlobalSettings globalSettings)
+ public IdentityMapperProfile(ILocalizedTextService textService, IEntityService entityService, IGlobalSettings globalSettings)
{
CreateMap()
.BeforeMap((src, dest) =>
@@ -19,6 +19,7 @@ namespace Umbraco.Core.Models.Identity
})
.ConstructUsing(src => new BackOfficeIdentityUser(src.Id, src.Groups))
.ForMember(dest => dest.LastLoginDateUtc, opt => opt.MapFrom(src => src.LastLoginDate.ToUniversalTime()))
+ .ForMember(user => user.LastPasswordChangeDateUtc, expression => expression.MapFrom(user => user.LastPasswordChangeDate.ToUniversalTime()))
.ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email))
.ForMember(dest => dest.EmailConfirmed, opt => opt.MapFrom(src => src.EmailConfirmedDate.HasValue))
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
diff --git a/src/Umbraco.Core/Models/Identity/IdentityUser.cs b/src/Umbraco.Core/Models/Identity/IdentityUser.cs
index 69b9e26385..b8ed355c55 100644
--- a/src/Umbraco.Core/Models/Identity/IdentityUser.cs
+++ b/src/Umbraco.Core/Models/Identity/IdentityUser.cs
@@ -75,6 +75,12 @@ namespace Umbraco.Core.Models.Identity
///
public virtual DateTime? LockoutEndDateUtc { get; set; }
+ ///
+ /// DateTime in UTC when the password was last changed.
+ ///
+ ///
+ public virtual DateTime? LastPasswordChangeDateUtc { get; set; }
+
///
/// Is lockout enabled for this user
///
diff --git a/src/Umbraco.Core/Security/BackOfficeUserManager.cs b/src/Umbraco.Core/Security/BackOfficeUserManager.cs
index 8324ec11a0..c4406054e6 100644
--- a/src/Umbraco.Core/Security/BackOfficeUserManager.cs
+++ b/src/Umbraco.Core/Security/BackOfficeUserManager.cs
@@ -434,6 +434,7 @@ namespace Umbraco.Core.Security
///
protected override async Task UpdatePassword(IUserPasswordStore passwordStore, T user, string newPassword)
{
+ user.LastPasswordChangeDateUtc = DateTime.UtcNow;
var userAwarePasswordHasher = PasswordHasher as IUserAwarePasswordHasher;
if (userAwarePasswordHasher == null)
return await base.UpdatePassword(passwordStore, user, newPassword);
@@ -484,15 +485,22 @@ namespace Umbraco.Core.Security
#endregion
- public override Task SetLockoutEndDateAsync(int userId, DateTimeOffset lockoutEnd)
+ public override async Task SetLockoutEndDateAsync(int userId, DateTimeOffset lockoutEnd)
{
- var result = base.SetLockoutEndDateAsync(userId, lockoutEnd);
+ var result = await base.SetLockoutEndDateAsync(userId, lockoutEnd);
// The way we unlock is by setting the lockoutEnd date to the current datetime
- if (result.Result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow)
+ if (result.Succeeded && lockoutEnd >= DateTimeOffset.UtcNow)
+ {
RaiseAccountLockedEvent(userId);
+ }
else
+ {
RaiseAccountUnlockedEvent(userId);
+ //Resets the login attempt fails back to 0 when unlock is clicked
+ await ResetAccessFailedCountAsync(userId);
+
+ }
return result;
}
@@ -517,13 +525,36 @@ namespace Umbraco.Core.Security
-
- public override Task AccessFailedAsync(int userId)
+ ///
+ /// Overides the microsoft ASP.NET user managment method
+ ///
+ ///
+ ///
+ /// returns a Async Task
+ ///
+ ///
+ /// Doesnt set fail attempts back to 0
+ ///
+ public override async Task AccessFailedAsync(int userId)
{
- var result = base.AccessFailedAsync(userId);
+ var lockoutStore = (IUserLockoutStore)Store;
+ var user = await FindByIdAsync(userId);
+ if (user == null)
+ throw new InvalidOperationException("No user found by user id " + userId);
+
+ var count = await lockoutStore.IncrementAccessFailedCountAsync(user);
+
+ if (count >= MaxFailedAccessAttemptsBeforeLockout)
+ {
+ await lockoutStore.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(DefaultAccountLockoutTimeSpan));
+ //NOTE: in normal aspnet identity this would do set the number of failed attempts back to 0
+ //here we are persisting the value for the back office
+ }
+
+ var result = await UpdateAsync(user);
//Slightly confusing: this will return a Success if we successfully update the AccessFailed count
- if (result.Result.Succeeded)
+ if (result.Succeeded)
RaiseLoginFailedEvent(userId);
return result;
diff --git a/src/Umbraco.Core/Security/BackOfficeUserStore.cs b/src/Umbraco.Core/Security/BackOfficeUserStore.cs
index 2f70f32d89..63970aca8f 100644
--- a/src/Umbraco.Core/Security/BackOfficeUserStore.cs
+++ b/src/Umbraco.Core/Security/BackOfficeUserStore.cs
@@ -637,6 +637,13 @@ namespace Umbraco.Core.Security
anythingChanged = true;
user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime();
}
+ if (identityUser.IsPropertyDirty("LastPasswordChangeDateUtc")
+ || (user.LastPasswordChangeDate != default(DateTime) && identityUser.LastPasswordChangeDateUtc.HasValue == false)
+ || identityUser.LastPasswordChangeDateUtc.HasValue && user.LastPasswordChangeDate.ToUniversalTime() != identityUser.LastPasswordChangeDateUtc.Value)
+ {
+ anythingChanged = true;
+ user.LastPasswordChangeDate = identityUser.LastPasswordChangeDateUtc.Value.ToLocalTime();
+ }
if (identityUser.IsPropertyDirty("EmailConfirmed")
|| (user.EmailConfirmedDate.HasValue && user.EmailConfirmedDate.Value != default(DateTime) && identityUser.EmailConfirmed == false)
|| ((user.EmailConfirmedDate.HasValue == false || user.EmailConfirmedDate.Value == default(DateTime)) && identityUser.EmailConfirmed))
diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs
index a61faa10c7..d77b2080ee 100644
--- a/src/Umbraco.Core/Services/IFileService.cs
+++ b/src/Umbraco.Core/Services/IFileService.cs
@@ -358,5 +358,6 @@ namespace Umbraco.Core.Services
/// The name of the snippet
/// The content of the partial view.
string GetPartialViewSnippetContent(string snippetName);
+
}
}
diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs
index e953a6073e..30e76468a6 100644
--- a/src/Umbraco.Core/Services/Implement/ContentService.cs
+++ b/src/Umbraco.Core/Services/Implement/ContentService.cs
@@ -1841,7 +1841,7 @@ namespace Umbraco.Core.Services.Implement
scope.Events.Dispatch(TreeChanged, this, new TreeChange(copy, TreeChangeTypes.RefreshBranch).ToEventArgs());
foreach (var x in copies)
scope.Events.Dispatch(Copied, this, new CopyEventArgs(x.Item1, x.Item2, false, x.Item2.ParentId, relateToOriginal));
- Audit(AuditType.Copy, "Copy Content performed by user", content.WriterId, content.Id);
+ Audit(AuditType.Copy, "Copy Content performed by user", userId, content.Id);
scope.Complete();
}
diff --git a/src/Umbraco.Core/Services/Implement/FileService.cs b/src/Umbraco.Core/Services/Implement/FileService.cs
index 09bd096f8e..4b95a0c1c3 100644
--- a/src/Umbraco.Core/Services/Implement/FileService.cs
+++ b/src/Umbraco.Core/Services/Implement/FileService.cs
@@ -332,16 +332,23 @@ namespace Umbraco.Core.Services.Implement
var evtMsgs = EventMessagesFactory.Get();
- //NOTE: This isn't pretty but we need to maintain backwards compatibility so we cannot change
+ //fixme: This isn't pretty because we we're required to maintain backwards compatibility so we could not change
// the event args here. The other option is to create a different event with different event
- // args specifically for this method... which also isn't pretty. So for now, we'll use this
- // dictionary approach to store 'additional data' in.
+ // args specifically for this method... which also isn't pretty. So fix this in v8!
var additionalData = new Dictionary
{
{ "CreateTemplateForContentType", true },
{ "ContentTypeAlias", contentTypeAlias },
};
+ // check that the template hasn't been created on disk before creating the content type
+ // if it exists, set the new template content to the existing file content
+ string content = GetViewContent(contentTypeAlias);
+ if (content.IsNullOrWhiteSpace() == false)
+ {
+ template.Content = content;
+ }
+
using (var scope = ScopeProvider.CreateScope())
{
var saveEventArgs = new SaveEventArgs(template, true, evtMsgs, additionalData);
@@ -368,6 +375,15 @@ namespace Umbraco.Core.Services.Implement
{
Content = content
};
+
+ // check that the template hasn't been created on disk before creating the content type
+ // if it exists, set the new template content to the existing file content
+ string existingContent = GetViewContent(template.Alias);
+ if (existingContent.IsNullOrWhiteSpace() == false)
+ {
+ template.Content = content;
+ }
+
if (masterTemplate != null)
{
template.SetMasterTemplate(masterTemplate);
@@ -659,10 +675,26 @@ namespace Umbraco.Core.Services.Implement
return _templateRepository.GetFileSize(filepath);
}
}
+
+ private string GetViewContent(string fileName)
+ {
+ if (fileName.IsNullOrWhiteSpace())
+ throw new ArgumentNullException(nameof(fileName));
- #endregion
+ if (!fileName.EndsWith(".cshtml"))
+ fileName = string.Concat(fileName, ".cshtml");
- #region Partial Views
+ var fs = _templateRepository.GetFileContentStream(fileName);
+ if (fs == null) return string.Empty;
+ using (var view = new StreamReader(fs))
+ {
+ return view.ReadToEnd().Trim();
+ }
+ }
+
+#endregion
+
+#region Partial Views
public IEnumerable GetPartialViewSnippetNames(params string[] filterNames)
{
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 4c2dd2df3f..1689be9b85 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -355,6 +355,7 @@
+
@@ -663,7 +664,6 @@
-
diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs
index fe32a43cc9..f1ac463305 100644
--- a/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs
+++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/ContentElementTests.cs
@@ -16,28 +16,28 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
[Test]
public void EmailAddress()
{
- Assert.IsTrue(SettingsSection.Content.NotificationEmailAddress == "robot@umbraco.dk");
+ Assert.AreEqual(SettingsSection.Content.NotificationEmailAddress, "robot@umbraco.dk");
}
[Test]
public virtual void DisableHtmlEmail()
{
- Assert.IsTrue(SettingsSection.Content.DisableHtmlEmail == true);
+ Assert.IsTrue(SettingsSection.Content.DisableHtmlEmail);
}
[Test]
public virtual void Can_Set_Multiple()
{
- Assert.IsTrue(SettingsSection.Content.Error404Collection.Count() == 3);
- Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).Culture == "default");
- Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).ContentId == 1047);
+ Assert.AreEqual(SettingsSection.Content.Error404Collection.Count(), 3);
+ Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(0).Culture, "default");
+ Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(0).ContentId, 1047);
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentId);
Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(0).HasContentKey);
- Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(1).Culture == "en-US");
- Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(1).ContentXPath == "$site/error [@name = 'error']");
+ Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(1).Culture, "en-US");
+ Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(1).ContentXPath, "$site/error [@name = 'error']");
Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentId);
Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(1).HasContentKey);
- Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).Culture == "en-UK");
- Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).ContentKey == new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC"));
+ Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(2).Culture, "en-UK");
+ Assert.AreEqual(SettingsSection.Content.Error404Collection.ElementAt(2).ContentKey, new Guid("8560867F-B88F-4C74-A9A4-679D8E5B3BFC"));
Assert.IsTrue(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentKey);
Assert.IsFalse(SettingsSection.Content.Error404Collection.ElementAt(2).HasContentId);
}
@@ -45,7 +45,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
[Test]
public void ScriptFolderPath()
{
- Assert.IsTrue(SettingsSection.Content.ScriptFolderPath == "/scripts");
+ Assert.AreEqual(SettingsSection.Content.ScriptFolderPath, "/scripts");
}
[Test]
public void ScriptFileTypes()
@@ -55,7 +55,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
[Test]
public void DisableScriptEditor()
{
- Assert.IsTrue(SettingsSection.Content.ScriptEditorDisable == false);
+ Assert.AreEqual(SettingsSection.Content.ScriptEditorDisable, false);
}
[Test]
@@ -71,80 +71,79 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
[Test]
public virtual void ImageAutoFillProperties()
{
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.Count() == 2);
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).Alias == "umbracoFile");
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias == "umbracoWidth");
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias == "umbracoHeight");
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias == "umbracoBytes");
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias == "umbracoExtension");
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).Alias == "umbracoFile2");
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias == "umbracoWidth2");
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias == "umbracoHeight2");
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias == "umbracoBytes2");
- Assert.IsTrue(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias == "umbracoExtension2");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.Count(), 2);
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).Alias, "umbracoFile");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).WidthFieldAlias, "umbracoWidth");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).HeightFieldAlias, "umbracoHeight");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).LengthFieldAlias, "umbracoBytes");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(0).ExtensionFieldAlias, "umbracoExtension");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).Alias, "umbracoFile2");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).WidthFieldAlias, "umbracoWidth2");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).HeightFieldAlias, "umbracoHeight2");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).LengthFieldAlias, "umbracoBytes2");
+ Assert.AreEqual(SettingsSection.Content.ImageAutoFillProperties.ElementAt(1).ExtensionFieldAlias, "umbracoExtension2");
}
[Test]
public void UploadAllowDirectories()
{
- Assert.IsTrue(SettingsSection.Content.UploadAllowDirectories == true);
+ Assert.IsTrue(SettingsSection.Content.UploadAllowDirectories);
}
[Test]
public void DefaultDocumentTypeProperty()
{
- Assert.IsTrue(SettingsSection.Content.DefaultDocumentTypeProperty == "Textstring");
+ Assert.AreEqual(SettingsSection.Content.DefaultDocumentTypeProperty, "Textstring");
}
[Test]
public void GlobalPreviewStorageEnabled()
{
- Assert.IsTrue(SettingsSection.Content.GlobalPreviewStorageEnabled == false);
+ Assert.IsFalse(SettingsSection.Content.GlobalPreviewStorageEnabled);
}
[Test]
public void CloneXmlContent()
{
- Assert.IsTrue(SettingsSection.Content.CloneXmlContent == true);
+ Assert.IsTrue(SettingsSection.Content.CloneXmlContent);
}
[Test]
public void EnsureUniqueNaming()
{
- Assert.IsTrue(SettingsSection.Content.EnsureUniqueNaming == true);
+ Assert.IsTrue(SettingsSection.Content.EnsureUniqueNaming);
}
-
[Test]
public void ForceSafeAliases()
{
- Assert.IsTrue(SettingsSection.Content.ForceSafeAliases == true);
+ Assert.IsTrue(SettingsSection.Content.ForceSafeAliases);
}
[Test]
public void XmlCacheEnabled()
{
- Assert.IsTrue(SettingsSection.Content.XmlCacheEnabled == true);
+ Assert.IsTrue(SettingsSection.Content.XmlCacheEnabled);
}
[Test]
public void ContinouslyUpdateXmlDiskCache()
{
- Assert.IsTrue(SettingsSection.Content.ContinouslyUpdateXmlDiskCache == true);
+ Assert.IsTrue(SettingsSection.Content.ContinouslyUpdateXmlDiskCache);
}
[Test]
public virtual void XmlContentCheckForDiskChanges()
{
- Assert.IsTrue(SettingsSection.Content.XmlContentCheckForDiskChanges == true);
+ Assert.IsTrue(SettingsSection.Content.XmlContentCheckForDiskChanges);
}
[Test]
public void EnableSplashWhileLoading()
{
- Assert.IsTrue(SettingsSection.Content.EnableSplashWhileLoading == false);
+ Assert.IsFalse(SettingsSection.Content.EnableSplashWhileLoading);
}
[Test]
public void PropertyContextHelpOption()
{
- Assert.IsTrue(SettingsSection.Content.PropertyContextHelpOption == "text");
+ Assert.AreEqual(SettingsSection.Content.PropertyContextHelpOption, "text");
}
[Test]
public void PreviewBadge()
{
- Assert.IsTrue(SettingsSection.Content.PreviewBadge == @"In Preview Mode - click to end");
+ Assert.AreEqual(SettingsSection.Content.PreviewBadge, @"In Preview Mode - click to end");
}
[Test]
public void ResolveUrlsFromTextString()
@@ -154,7 +153,7 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
[Test]
public void MacroErrors()
{
- Assert.IsTrue(SettingsSection.Content.MacroErrorBehaviour == MacroErrorBehaviour.Inline);
+ Assert.AreEqual(SettingsSection.Content.MacroErrorBehaviour, MacroErrorBehaviour.Inline);
}
[Test]
diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config
index 5373afd1b5..a436dad9f5 100644
--- a/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config
+++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/umbracoSettings.config
@@ -75,7 +75,10 @@
true
- In Preview Mode - click to end]]>
+
+ In Preview Mode - click to end
+ ]]>
";
- //create an id class for this element so we can re-select it after inserting
- var uniqueId = "umb-macro-" + editor.dom.uniqueId();
- var macroDiv = editor.dom.create('div',
- {
- 'class': 'umb-macro-holder ' + macroObject.macroAlias + ' mceNonEditable ' + uniqueId
- },
- macroSyntaxComment + 'Macro alias: ' + macroObject.macroAlias + '');
-
- editor.selection.setNode(macroDiv);
-
- var $macroDiv = $(editor.dom.select("div.umb-macro-holder." + uniqueId));
-
- //async load the macro content
- this.loadMacroContent($macroDiv, macroObject, $scope);
-
- },
-
- /** loads in the macro content async from the server */
- loadMacroContent: function($macroDiv, macroData, $scope) {
-
- //if we don't have the macroData, then we'll need to parse it from the macro div
- if (!macroData) {
- var contents = $macroDiv.contents();
- var comment = _.find(contents, function (item) {
- return item.nodeType === 8;
- });
- if (!comment) {
- throw "Cannot parse the current macro, the syntax in the editor is invalid";
- }
- var syntax = comment.textContent.trim();
- var parsed = macroService.parseMacroSyntax(syntax);
- macroData = parsed;
- }
-
- var $ins = $macroDiv.find("ins");
-
- //show the throbber
- $macroDiv.addClass("loading");
-
- var contentId = $routeParams.id;
-
- //need to wrap in safe apply since this might be occuring outside of angular
- angularHelper.safeApply($scope, function() {
- macroResource.getMacroResultAsHtmlForEditor(macroData.macroAlias, contentId, macroData.macroParamsDictionary)
- .then(function (htmlResult) {
-
- $macroDiv.removeClass("loading");
- htmlResult = htmlResult.trim();
- if (htmlResult !== "") {
- $ins.html(htmlResult);
- }
- });
- });
-
- },
-
- createLinkPicker: function(editor, $scope, onClick) {
-
- function createLinkList(callback) {
- return function() {
- var linkList = editor.settings.link_list;
-
- if (typeof(linkList) === "string") {
- tinymce.util.XHR.send({
- url: linkList,
- success: function(text) {
- callback(tinymce.util.JSON.parse(text));
- }
- });
- } else {
- callback(linkList);
- }
- };
- }
-
- function showDialog(linkList) {
- var data = {}, selection = editor.selection, dom = editor.dom, selectedElm, anchorElm, initialText;
- var win, linkListCtrl, relListCtrl, targetListCtrl;
-
- function linkListChangeHandler(e) {
- var textCtrl = win.find('#text');
-
- if (!textCtrl.value() || (e.lastControl && textCtrl.value() === e.lastControl.text())) {
- textCtrl.value(e.control.text());
- }
-
- win.find('#href').value(e.control.value());
- }
-
- function buildLinkList() {
- var linkListItems = [{
- text: 'None',
- value: ''
+function tinyMceService($log, imageHelper, $http, $timeout, macroResource, macroService, $routeParams, umbRequestHelper, angularHelper, userService) {
+ return {
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.tinyMceService#configuration
+ * @methodOf umbraco.services.tinyMceService
+ *
+ * @description
+ * Returns a collection of plugins available to the tinyMCE editor
+ *
+ */
+ configuration: function () {
+ return umbRequestHelper.resourcePromise(
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "rteApiBaseUrl",
+ "GetConfiguration"), {
+ cache: true
+ }),
+ 'Failed to retrieve tinymce configuration');
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.tinyMceService#defaultPrevalues
+ * @methodOf umbraco.services.tinyMceService
+ *
+ * @description
+ * Returns a default configration to fallback on in case none is provided
+ *
+ */
+ defaultPrevalues: function () {
+ var cfg = {};
+ cfg.toolbar = ["code", "bold", "italic", "styleselect", "alignleft", "aligncenter", "alignright", "bullist", "numlist", "outdent", "indent", "link", "image", "umbmediapicker", "umbembeddialog", "umbmacro"];
+ cfg.stylesheets = [];
+ cfg.dimensions = {
+ height: 500
+ };
+ cfg.maxImageSize = 500;
+ return cfg;
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.tinyMceService#createInsertEmbeddedMedia
+ * @methodOf umbraco.services.tinyMceService
+ *
+ * @description
+ * Creates the umbrco insert embedded media tinymce plugin
+ *
+ * @param {Object} editor the TinyMCE editor instance
+ * @param {Object} $scope the current controller scope
+ */
+ createInsertEmbeddedMedia: function (editor, scope, callback) {
+ editor.addButton('umbembeddialog', {
+ icon: 'custom icon-tv',
+ tooltip: 'Embed',
+ onclick: function () {
+ if (callback) {
+ callback();
+ }
+ }
+ });
+ },
+
+ insertEmbeddedMediaInEditor: function (editor, preview) {
+ editor.insertContent(preview);
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.tinyMceService#createMediaPicker
+ * @methodOf umbraco.services.tinyMceService
+ *
+ * @description
+ * Creates the umbrco insert media tinymce plugin
+ *
+ * @param {Object} editor the TinyMCE editor instance
+ * @param {Object} $scope the current controller scope
+ */
+ createMediaPicker: function (editor, scope, callback) {
+ editor.addButton('umbmediapicker', {
+ icon: 'custom icon-picture',
+ tooltip: 'Media Picker',
+ stateSelector: 'img',
+ onclick: function () {
+
+ var selectedElm = editor.selection.getNode(),
+ currentTarget;
+
+
+ if (selectedElm.nodeName === 'IMG') {
+ var img = $(selectedElm);
+
+ var hasUdi = img.attr("data-udi") ? true : false;
+
+ currentTarget = {
+ altText: img.attr("alt"),
+ url: img.attr("src")
+ };
+
+ if (hasUdi) {
+ currentTarget["udi"] = img.attr("data-udi");
+ } else {
+ currentTarget["id"] = img.attr("rel");
+ }
+ }
+
+ userService.getCurrentUser().then(function (userData) {
+ if (callback) {
+ callback(currentTarget, userData);
+ }
+ });
+
+ }
+ });
+ },
+
+ insertMediaInEditor: function (editor, img) {
+ if (img) {
+
+ var hasUdi = img.udi ? true : false;
+
+ var data = {
+ alt: img.altText || "",
+ src: (img.url) ? img.url : "nothing.jpg",
+ id: '__mcenew'
+ };
+
+ if (hasUdi) {
+ data["data-udi"] = img.udi;
+ } else {
+ //Considering these fixed because UDI will now be used and thus
+ // we have no need for rel http://issues.umbraco.org/issue/U4-6228, http://issues.umbraco.org/issue/U4-6595
+ data["rel"] = img.id;
+ data["data-id"] = img.id;
+ }
+
+ editor.insertContent(editor.dom.createHTML('img', data));
+
+ $timeout(function () {
+ var imgElm = editor.dom.get('__mcenew');
+ var size = editor.dom.getSize(imgElm);
+
+ if (editor.settings.maxImageSize && editor.settings.maxImageSize !== 0) {
+ var newSize = imageHelper.scaleToMaxSize(editor.settings.maxImageSize, size.w, size.h);
+
+ var s = "width: " + newSize.width + "px; height:" + newSize.height + "px;";
+ editor.dom.setAttrib(imgElm, 'style', s);
+ editor.dom.setAttrib(imgElm, 'id', null);
+
+ if (img.url) {
+ var src = img.url + "?width=" + newSize.width + "&height=" + newSize.height;
+ editor.dom.setAttrib(imgElm, 'data-mce-src', src);
+ }
+ }
+ }, 500);
+ }
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.tinyMceService#createUmbracoMacro
+ * @methodOf umbraco.services.tinyMceService
+ *
+ * @description
+ * Creates the insert umbrco macro tinymce plugin
+ *
+ * @param {Object} editor the TinyMCE editor instance
+ * @param {Object} $scope the current controller scope
+ */
+ createInsertMacro: function (editor, $scope, callback) {
+
+ var createInsertMacroScope = this;
+
+ /** Adds custom rules for the macro plugin and custom serialization */
+ editor.on('preInit', function (args) {
+ //this is requires so that we tell the serializer that a 'div' is actually allowed in the root, otherwise the cleanup will strip it out
+ editor.serializer.addRules('div');
+
+ /** This checks if the div is a macro container, if so, checks if its wrapped in a p tag and then unwraps it (removes p tag) */
+ editor.serializer.addNodeFilter('div', function (nodes, name) {
+ for (var i = 0; i < nodes.length; i++) {
+ if (nodes[i].attr("class") === "umb-macro-holder" && nodes[i].parent && nodes[i].parent.name.toUpperCase() === "P") {
+ nodes[i].parent.unwrap();
+ }
+ }
+ });
+
+ });
+
+ /**
+ * Because the macro gets wrapped in a P tag because of the way 'enter' works, this
+ * method will return the macro element if not wrapped in a p, or the p if the macro
+ * element is the only one inside of it even if we are deep inside an element inside the macro
+ */
+ function getRealMacroElem(element) {
+ var e = $(element).closest(".umb-macro-holder");
+ if (e.length > 0) {
+ if (e.get(0).parentNode.nodeName === "P") {
+ //now check if we're the only element
+ if (element.parentNode.childNodes.length === 1) {
+ return e.get(0).parentNode;
+ }
+ }
+ return e.get(0);
+ }
+ return null;
+ }
+
+ /** Adds the button instance */
+ editor.addButton('umbmacro', {
+ icon: 'custom icon-settings-alt',
+ tooltip: 'Insert macro',
+ onPostRender: function () {
+
+ var ctrl = this;
+ var isOnMacroElement = false;
+
+ /**
+ if the selection comes from a different element that is not the macro's
+ we need to check if the selection includes part of the macro, if so we'll force the selection
+ to clear to the next element since if people can select part of the macro markup they can then modify it.
+ */
+ function handleSelectionChange() {
+
+ if (!editor.selection.isCollapsed()) {
+ var endSelection = tinymce.activeEditor.selection.getEnd();
+ var startSelection = tinymce.activeEditor.selection.getStart();
+ //don't proceed if it's an entire element selected
+ if (endSelection !== startSelection) {
+
+ //if the end selection is a macro then move the cursor
+ //NOTE: we don't have to handle when the selection comes from a previous parent because
+ // that is automatically taken care of with the normal onNodeChanged logic since the
+ // evt.element will be the macro once it becomes part of the selection.
+ var $testForMacro = $(endSelection).closest(".umb-macro-holder");
+ if ($testForMacro.length > 0) {
+
+ //it came from before so move after, if there is no after then select ourselves
+ var next = $testForMacro.next();
+ if (next.length > 0) {
+ editor.selection.setCursorLocation($testForMacro.next().get(0));
+ } else {
+ selectMacroElement($testForMacro.get(0));
+ }
+
+ }
+ }
+ }
+ }
+
+ /** helper method to select the macro element */
+ function selectMacroElement(macroElement) {
+
+ // move selection to top element to ensure we can't edit this
+ editor.selection.select(macroElement);
+
+ // check if the current selection *is* the element (ie bug)
+ var currentSelection = editor.selection.getStart();
+ if (tinymce.isIE) {
+ if (!editor.dom.hasClass(currentSelection, 'umb-macro-holder')) {
+ while (!editor.dom.hasClass(currentSelection, 'umb-macro-holder') && currentSelection.parentNode) {
+ currentSelection = currentSelection.parentNode;
+ }
+ editor.selection.select(currentSelection);
+ }
+ }
+ }
+
+ /**
+ * Add a node change handler, test if we're editing a macro and select the whole thing, then set our isOnMacroElement flag.
+ * If we change the selection inside this method, then we end up in an infinite loop, so we have to remove ourselves
+ * from the event listener before changing selection, however, it seems that putting a break point in this method
+ * will always cause an 'infinite' loop as the caret keeps changing.
+ */
+ function onNodeChanged(evt) {
+
+ //set our macro button active when on a node of class umb-macro-holder
+ var $macroElement = $(evt.element).closest(".umb-macro-holder");
+
+ handleSelectionChange();
+
+ //set the button active
+ ctrl.active($macroElement.length !== 0);
+
+ if ($macroElement.length > 0) {
+ var macroElement = $macroElement.get(0);
+
+ //remove the event listener before re-selecting
+ editor.off('NodeChange', onNodeChanged);
+
+ selectMacroElement(macroElement);
+
+ //set the flag
+ isOnMacroElement = true;
+
+ //re-add the event listener
+ editor.on('NodeChange', onNodeChanged);
+ } else {
+ isOnMacroElement = false;
+ }
+
+ }
+
+ /** when the contents load we need to find any macros declared and load in their content */
+ editor.on("LoadContent", function (o) {
+
+ //get all macro divs and load their content
+ $(editor.dom.select(".umb-macro-holder.mceNonEditable")).each(function () {
+ createInsertMacroScope.loadMacroContent($(this), null, $scope);
+ });
+
+ });
+
+ /** This prevents any other commands from executing when the current element is the macro so the content cannot be edited */
+ editor.on('BeforeExecCommand', function (o) {
+ if (isOnMacroElement) {
+ if (o.preventDefault) {
+ o.preventDefault();
+ }
+ if (o.stopImmediatePropagation) {
+ o.stopImmediatePropagation();
+ }
+ return;
+ }
+ });
+
+ /** This double checks and ensures you can't paste content into the rendered macro */
+ editor.on("Paste", function (o) {
+ if (isOnMacroElement) {
+ if (o.preventDefault) {
+ o.preventDefault();
+ }
+ if (o.stopImmediatePropagation) {
+ o.stopImmediatePropagation();
+ }
+ return;
+ }
+ });
+
+ //set onNodeChanged event listener
+ editor.on('NodeChange', onNodeChanged);
+
+ /**
+ * Listen for the keydown in the editor, we'll check if we are currently on a macro element, if so
+ * we'll check if the key down is a supported key which requires an action, otherwise we ignore the request
+ * so the macro cannot be edited.
+ */
+ editor.on('KeyDown', function (e) {
+ if (isOnMacroElement) {
+ var macroElement = editor.selection.getNode();
+
+ //get the 'real' element (either p or the real one)
+ macroElement = getRealMacroElem(macroElement);
+
+ //prevent editing
+ e.preventDefault();
+ e.stopPropagation();
+
+ var moveSibling = function (element, isNext) {
+ var $e = $(element);
+ var $sibling = isNext ? $e.next() : $e.prev();
+ if ($sibling.length > 0) {
+ editor.selection.select($sibling.get(0));
+ editor.selection.collapse(true);
+ } else {
+ //if we're moving previous and there is no sibling, then lets recurse and just select the next one
+ if (!isNext) {
+ moveSibling(element, true);
+ return;
+ }
+
+ //if there is no sibling we'll generate a new p at the end and select it
+ editor.setContent(editor.getContent() + "
@@ -80,11 +91,10 @@
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js
index c48099c5fe..ca65e3d107 100644
--- a/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/content/content.copy.controller.js
@@ -20,9 +20,10 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController",
}
$scope.treeModel = {
hideHeader: false
- }
+ }
+ $scope.toggle = toggleHandler;
userService.getCurrentUser().then(function (userData) {
- $scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1;
+ $scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1;
});
var node = dialogOptions.currentNode;
@@ -57,7 +58,27 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController",
if (args.node.metaData.isContainer) {
openMiniListView(args.node);
}
- }
+ }
+
+ function toggleHandler(type){
+ // If the relateToOriginal toggle is clicked
+ if(type === "relate"){
+ if($scope.relateToOriginal){
+ $scope.relateToOriginal = false;
+ return;
+ }
+ $scope.relateToOriginal = true;
+ }
+
+ // If the recurvise toggle is clicked
+ if(type === "recursive"){
+ if($scope.recursive){
+ $scope.recursive = false;
+ return;
+ }
+ $scope.recursive = true;
+ }
+ }
$scope.hideSearch = function () {
$scope.searchInfo.showSearch = false;
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/copy.html b/src/Umbraco.Web.UI.Client/src/views/content/copy.html
index 7501508a48..018c543d08 100644
--- a/src/Umbraco.Web.UI.Client/src/views/content/copy.html
+++ b/src/Umbraco.Web.UI.Client/src/views/content/copy.html
@@ -69,13 +69,13 @@
-
+
-
-
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
index 0543fc0c66..78ce1f29a5 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
@@ -88,7 +88,10 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
// sortable options
$scope.sortableOptions = {
+ axis: "y",
+ containment: "parent",
distance: 10,
+ opacity: 0.7,
tolerance: "pointer",
scroll: true,
zIndex: 6000
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js
index 99c3fd80ff..397438d5a0 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js
@@ -1,7 +1,7 @@
(function() {
"use strict";
- function GridRichTextEditorController($scope, tinyMceService, macroService) {
+ function GridRichTextEditorController($scope, tinyMceService, macroService, editorState) {
var vm = this;
@@ -11,9 +11,11 @@
vm.openEmbed = openEmbed;
function openLinkPicker(editor, currentTarget, anchorElement) {
+
vm.linkPickerOverlay = {
view: "linkpicker",
currentTarget: currentTarget,
+ anchors: tinyMceService.getAnchorNames(JSON.stringify(editorState.current.properties)),
show: true,
submit: function(model) {
tinyMceService.insertLinkInEditor(editor, model.target, anchorElement);
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js
index 54ec3031cb..a9d1cd1234 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js
@@ -1,6 +1,6 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.RTEController",
- function ($rootScope, $scope, $q, $locale, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService, angularHelper, stylesheetResource, macroService) {
+ function ($rootScope, $scope, $q, $locale, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService, angularHelper, stylesheetResource, macroService, editorState) {
$scope.isLoading = true;
@@ -273,11 +273,12 @@ angular.module("umbraco")
syncContent(editor);
});
-
+
tinyMceService.createLinkPicker(editor, $scope, function(currentTarget, anchorElement) {
$scope.linkPickerOverlay = {
view: "linkpicker",
currentTarget: currentTarget,
+ anchors: tinyMceService.getAnchorNames(JSON.stringify(editorState.current.properties)),
show: true,
submit: function(model) {
tinyMceService.insertLinkInEditor(editor, model.target, anchorElement);
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js
index e92dd171c6..35b5bef306 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js
@@ -5,15 +5,12 @@ angular.module("umbraco")
var $typeahead;
$scope.isLoading = true;
- $scope.tagToAdd = "";
-
- assetsService.loadJs("lib/typeahead.js/typeahead.bundle.min.js", $scope).then(function () {
-
- $scope.isLoading = false;
-
- //load current value
-
- if ($scope.model.value) {
+ $scope.tagToAdd = "";
+
+ function setModelValue(val) {
+
+ $scope.model.value = val || $scope.model.value;
+ if ($scope.model.value) {
if (!$scope.model.config.storageType || $scope.model.config.storageType !== "Json") {
//it is csv
if (!$scope.model.value) {
@@ -21,7 +18,14 @@ angular.module("umbraco")
}
else {
if($scope.model.value.length > 0) {
- $scope.model.value = $scope.model.value.split(",");
+ // split the csv string, and remove any duplicate values
+ var tempArray = $scope.model.value.split(',').map(function(v) {
+ return v.trim();
+ });
+
+ $scope.model.value = tempArray.filter(function(v, i, self) {
+ return self.indexOf(v) === i;
+ });
}
}
}
@@ -29,6 +33,14 @@ angular.module("umbraco")
else {
$scope.model.value = [];
}
+ }
+
+ assetsService.loadJs("lib/typeahead.js/typeahead.bundle.min.js", $scope).then(function () {
+
+ $scope.isLoading = false;
+
+ //load current value
+ setModelValue();
// Method required by the valPropertyValidator directive (returns true if the property editor has at least one tag selected)
$scope.validateMandatory = function () {
@@ -85,17 +97,7 @@ angular.module("umbraco")
//vice versa
$scope.model.onValueChanged = function (newVal, oldVal) {
//update the display val again if it has changed from the server
- $scope.model.value = newVal;
-
- if (!$scope.model.config.storageType || $scope.model.config.storageType !== "Json") {
- //it is csv
- if (!$scope.model.value) {
- $scope.model.value = [];
- }
- else {
- $scope.model.value = $scope.model.value.split(",");
- }
- }
+ setModelValue(newVal);
};
//configure the tags data source
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html
index 313cf99a49..de232c690c 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.html
@@ -8,7 +8,7 @@
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js
index 0f95f78adc..39ae1dd583 100644
--- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js
@@ -67,7 +67,7 @@
});
- vm.save = function () {
+ vm.save = function (suppressNotification) {
vm.page.saveButtonState = "busy";
vm.template.content = vm.editor.getValue();
@@ -83,11 +83,13 @@
rebindCallback: function (orignal, saved) {}
}).then(function (saved) {
+ if (!suppressNotification) {
localizationService.localizeMany(["speechBubbles_templateSavedHeader", "speechBubbles_templateSavedText"]).then(function(data){
var header = data[0];
var message = data[1];
notificationsService.success(header, message);
});
+ }
vm.page.saveButtonState = "success";
@@ -173,6 +175,21 @@
vm.page.loading = false;
vm.template = template;
+ // if this is a new template, bind to the blur event on the name
+ if ($routeParams.create) {
+ $timeout(function() {
+ var nameField = angular.element(document.querySelector('[data-element="editor-name-field"]'));
+ if (nameField) {
+ nameField.bind('blur', function(event) {
+ if (event.target.value) {
+ vm.save(true);
+ }
+ });
+ }
+ });
+ }
+
+
//sync state
editorState.set(vm.template);
navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) {
diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js
index 48ccef0e51..a3f87ffc93 100644
--- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js
@@ -329,8 +329,10 @@
vm.unlockUserButtonState = "busy";
usersResource.unlockUsers([vm.user.id]).then(function (data) {
vm.user.userState = 0;
+ vm.user.failedPasswordAttempts = 0;
setUserDisplayState();
vm.unlockUserButtonState = "success";
+
}, function (error) {
vm.unlockUserButtonState = "error";
});
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
index 382cb29d9f..ee18248997 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
@@ -344,6 +344,7 @@
Link titleLink
+ Anchor / querystringNameManage hostnamesClose this window
@@ -462,6 +463,7 @@
Enter your email...Enter a message...Your username is usually your email
+ #value or ?key=valueAllow at root
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
index 89f9c7df00..e61c406fae 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -352,6 +352,7 @@
Link titleLink
+ Anchor / querystringNameClose this windowAre you sure you want to delete
@@ -470,6 +471,7 @@
Enter your email...Enter a message...Your username is usually your email
+ #value or ?key=valueAllow at root
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml
index 6f8ef5292d..31d1f9392d 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml
@@ -5,18 +5,21 @@
http://our.umbraco.org/documentation/Extending-Umbraco/Language-Files
- Gérer les noms d'hôtes
+ Culture et noms d'hôteInformations d'auditParcourirChanger le type de documentCopierCréer
+ Exporter
+ Créer un groupeCréer un packageSupprimerDésactiverVider la corbeille
- Exporter un type
- Importer un type
+ Activer
+ Exporter le type de document
+ Importer un type de documentImporter un packageEditer dans CanvasDéconnexion
@@ -28,15 +31,48 @@
RafraîchirRepublier le site tout entierRécupérer
+ Spécifiez les permissions pour la page %0%
+ Choisissez où déplacer
+ dans l'arborescence ci-dessousPermissionsVersion antérieureEnvoyer pour publicationEnvoyer pour traduction
+ Spécifier le groupeTrierEnvoyer pour publicationTraduireMettre à jour
- Valeur par défaut
+ Spécifier les permissions
+ Débloquer
+ Créer un modèle de contenu
+ Envoyer à nouveau l'invitation
+
+
+ Contenu
+ Administration
+ Structure
+ Autre
+
+
+ Permettre d'attribuer la culture et des noms d'hôte
+ Permettre d'accéder au journal d'historique d'un noeud
+ Permettre d'accéder à un noeud
+ Permettre de modifier le type de document d'un noeud
+ Permettre de copier un noeud
+ Permettre de créer des noeuds
+ Permettre de supprimer des noeuds
+ Permettre de déplacer un noeud
+ Permettre de définir et modifier l'accès public à un noeud
+ Permettre de publier un noeud
+ Permettre de modifier les permissions pour un noeud
+ Permettre de revenir à une situation antérieure
+ Permettre d'envoyer un noeud pour approbation avant publication
+ Permettre d'envoyer un noeud à la traduction
+ Permettre de modifier l'ordonnancement des noeuds
+ Permettre de traduire un noeud
+ Permettre de sauvegarder un noeud
+ Permettre la création d'un Modèle de ContenuPermission refusée.
@@ -45,20 +81,24 @@
Noeud invalide.Domaine invalide.Domaine déjà assigné.
- DomaineLangue
+ DomaineNouveau domaine '%0%' crééDomaine '%0%' suppriméDomaine '%0%' déjà assigné
- Les domaines contenant un chemin d'un niveau sont autorisés, ex : "example.com/en". Pour autant, cela
- devrait être évité. Utilisez plutôt la gestion des noms d'hôte.]]>Domaine '%0%' mis à jourEditer les domaines actuels
+
+ Les domaines contenant un chemin d'un niveau sont autorisés, ex : "example.com/en". Pour autant, cela
+ devrait être évité. Utilisez plutôt la gestion de la culture et des noms d'hôte.]]>
+ HériterCulture
- ou hériter de la culture des noeuds parents. S'appliquera aussi
- au noeud courant, à moins qu'un domaine ci-dessous soit aussi d'application.]]>
+
+ ou hériter de la culture des noeuds parents. S'appliquera aussi
+ au noeud courant, à moins qu'un domaine ci-dessous soit aussi d'application.]]>
+ Domaines
@@ -85,10 +125,12 @@
Liste numériqueInsérer une macroInsérer une image
+ Editer les relationsRetourner à la listeEditer les relationsSauverSauver et publier
+ Sauver et planifierSauver et envoyer pour approbationSauver la mise en page de la listePrévisualiser
@@ -97,7 +139,30 @@
Afficher les stylesInsérer un tableauGénérer les modèles
- Sauver et générer les modèles
+ Sauver et générer les modèles
+ Défaire
+ Refaire
+
+
+ Aperçu pour
+ Suppressions de contenu réalisées par utilisateur
+ Dépublications réalisées par utilisateur
+ Sauvegardes et publications réalisées par utilisateur
+ Sauvegardes de contenu réalisées par utilisateur
+ Déplacements de contenu réalisés par utilisateur
+ Copies de contenu réalisées par utilisateur
+ Récupérations de contenu réalisées par utilisateur
+ Envois de contenu pour publication réalisés par utilisateur
+ Envois de contenu pour traduction réalisés par utilisateur
+ Copie
+ Publication
+ Déplacement
+ Sauvegarde
+ Suppression
+ Dépublication
+ Récupération
+ Envoi pour publication
+ Envoi pour traductionPour changer le type de document du contenu séléctionné, faites d'abord un choix dans la liste des types valides à cet endroit.
@@ -139,30 +204,38 @@
Dernière publicationIl n'y a aucun élément à afficherIl n'y a aucun élément à afficher dans cette liste.
+ Aucun contenu n'a encore été ajouté
+ Aucun membre n'a encore été ajoutéType de MédiaLien vers des média(s)Groupe de membresRôleType de membre
+ Aucune modification n'a été faiteAucune date choisieTitre de la page
+ Ce média n'a pas de lienPropriétésCe document est publié mais n'est pas visible car son parent '%0%' n'est pas publiéOups : ce document est publié mais n'est pas présent dans le cache (erreur interne Umbraco)Oups: impossible d'obtenir cet url (erreur interne - voir fichier log)Oups: ce document est publié mais son url entrerait en collision avec le contenu %0%Publier
+ Publié
+ Publié (changements en cours)Statut de publicationPublié leDépublié leSupprimer la date
- Ordre de tri mis à jour
+ Défininir la date
+ Ordre de tri mis à jourPour trier les noeuds, faites-les simplement glisser à l'aide de la souris ou cliquez sur les entêtes de colonne. Vous pouvez séléctionner plusieurs noeuds en gardant la touche "shift" ou "ctrl" enfoncée pendant votre séléction.StatistiquesTitre (optionnel)Texte alternatif (optionnel)TypeDépublier
+ DépubliéDernière éditionDate/heure à laquelle ce document a été éditéSupprimer le(s) fichier(s)
@@ -173,6 +246,22 @@
CibleCeci se traduit par l'heure suivante sur le serveur :Qu'est-ce que cela signifie?]]>
+ Etes-vous certain(e) de vouloir supprimer cet éléménent?
+ La propriété %0% utilise l'éditeur %1% qui n'est pas supporté par Nested Content.
+ Ajouter un autre champ texte
+ Enlever ce champ texte
+ Racine du contenu
+ Cette valeur est masquée. Si vous avez besoin de pouvoir accéder à cette valeur, veuillez prendre contact avec l'administrateur du site web.
+ Cette valeur est masquée.
+
+
+ Créer un nouveau Modèle de Contenu à partir de '%0%'
+ Vide
+ Sélectionner un Modèle de Contenu
+ Modèle de Contenu créé
+ Un modèle de Contenu a été créé à partir de '%0%'
+ Un autre Modèle de Contenu existe déjà avec le même nom
+ Un Modèle de Contenu est du contenu pré-défini qu'un éditeur peut sélectionner et utiliser comme base pour la création de nouveau contenuCliquez pour télécharger
@@ -180,9 +269,10 @@
Lien vers le médiaou cliquez ici pour choisir un fichierLes seuls types de fichiers autorisés sont
- Impossible de télécharger ce fichier, il n'a pas un type de fichier autorisé.
+ Ce fichier ne peut pas ête chargé, il n'est pas d'un type de fichier autorisé.La taille maximum de fichier est
-
+ Racine du média
+
Créer un nouveau membreTous les membres
@@ -190,12 +280,20 @@
Où voulez-vous créer le nouveau %0%Créer un élément sous
+ Sélectionnez le type de document pour lequel vous souhaitez créer un modèle de contenuChoisissez un type et un titre"Types de documents".]]>"Types de médias".]]>Type de document sans modèleNouveau répertoireNouveau type de données
+ Nouveau fichier javascript
+ Nouvelle vue partielle vide
+ Nouvelle macro pour vue partielle
+ Nouvelle vue partielle à partir d'un snippet
+ Nouvelle macro pour vue partielle vide
+ Nouvelle macro pour vue partielle à partir d'un snippet
+ Nouvelle macro pour vue partielle (sans macro)Parcourir votre site
@@ -211,7 +309,8 @@
Invalider les changementsVous avez des changements en coursEtes-vous certain(e) de vouloir quitter cette page? - vous avez des changements en cours
-
+ La dépublication va supprimer du site cette page ainsi que tous ses descendants.
+
Terminé
@@ -270,7 +369,10 @@
Cette macro ne contient aucune propriété éditableCollerEditer les permissions pour
- Les éléments dans la corbeille sont en cours de suppression. Ne fermez pas cette fenêtre avant que cette opération soit terminée.
+ Définir les permissions pour
+ Définir les permissions pour %0% pour le groupe d'utilisateurs %1%
+ Sélectionnez les groupes d'utilisateurs pour lesquels vous souhaitez définir les permissions
+ Les éléments dans la corbeille sont en cours de suppression. Ne fermez pas cette fenêtre avant que cette opération soit terminée.La corbeille est maintenant videLes éléments supprimés de la corbeille seront supprimés définitivementregexlib.com rencontre actuellement des problèmes sur lesquels nous n'avons aucun contrôle. Nous sommes sincèrement désolés pour le désagrément.]]>
@@ -282,28 +384,41 @@
Le cache du site va être mis à jour. Tous les contenus publiés seront mis à jour. Et tous les contenus dépubliés resteront invisibles.Nombre de colonnesNombre de lignes
- Définir un placeholder ID. En mettant un ID sur votre placeholder, vous pouvez injecter du contenu à cet endroit depuis les modèles enfants,
- en faisant référence à cet ID au sein d'un élément <asp:content />.]]>
- Séléctionnez un placeholder id dans la liste ci-dessous. Vous pouvez seulement
- choisir un ID se trouvant dans le parent du modèle actuel.]]>
+
+ Définir un placeholder ID. En mettant un ID sur votre placeholder, vous pouvez injecter du contenu à cet endroit depuis les modèles enfants,
+ en faisant référence à cet ID au sein d'un élément <asp:content />.]]>
+
+
+ Séléctionnez un placeholder ID dans la liste ci-dessous. Vous pouvez seulement
+ choisir un ID se trouvant dans le parent du modèle actuel.]]>
+ Cliquez sur l'image pour la voir en taille réelleSélectionner un élémentVoir l'élément de cacheCréer un répertoire...Lier à l'original
+ Inclure les descendantsLa communauté la plus amicaleLier à la pageOuvre le document lié dans une nouvelle fenêtre ou un nouvel ongletLier à un media
+ Lier à un fichier
+ Sélectionner le noeud de base du contenuSélectionner le mediaSélectionner l'icôneSélectionner l'élémentSélectionner le lienSélectionner la macroSélectionner le contenu
+ Sélectionner le noeud de base des mediaSélectionner le membreSélectionner le groupe de membres
+ Sélectionner le noeud
+ Sélectionner les sections
+ Sélectionner les utilisateurs
+ Aucune icone n'a été trouvéeIl n'y a pas de paramètres pour cette macro
+ Il n'y a pas de macro disponible à insérerFournisseurs externes d'identificationDétails de l'exceptionTrace d'exécution
@@ -312,12 +427,22 @@
Enlevez votrecompteSélectionner un éditeur
+ Selectionner un snippet
-
+ %0%' ci-dessous. Vous pouvez ajouter d'autres langues depuis le menu ci-dessous "Langues".
- ]]>
+ ]]>
+
Nom de Culture
+ Modifiez la clé de l'élément de dictionaire.
+
+
+
+ Aperçu du dictionaireVotre nom d'utilisateur
@@ -325,15 +450,17 @@
Confirmation de votre mot de passeNommer %0%...Entrez un nom...
+ Entrez un email...
+ Entrez un nom d'utilisateur...Libellé...Entrez une description...Rechercher...Filtrer...Ajouter des tags (appuyer sur enter entre chaque tag)...Entrez votre email
- Votre nom d'utilisateur est généralement votre adresse email
+ Entrez un message...
+ Votre nom d'utilisateur est généralement votre adresse email
-
Autoriser à la racineSeuls les Types de Contenu qui ont ceci coché peuvent être créés au niveau racine des arborescences de contenu et de media
@@ -352,6 +479,11 @@
Créer une liste personnaliséeSupprimer la liste personnalisée
+
+ Renommé
+ Entrez un nouveau nom de répertoire ici
+ %0% a été renommé en %1%
+
Ajouter une valeur de baseType de donnée en base de donées
@@ -364,6 +496,13 @@
CSS associéesAfficher le libelléLargeur et hauteur
+ Tous les types de propriétés & les données de propriétés
+ utilisant ce type de données seront supprimés définitivement, veuillez confirmer que vous voulez également les supprimer
+ Oui, supprimer
+ et tous les types de propriétés & les données de propriétés utilisant ce type de données
+ Sélectionnez le répertoire où déplacer
+ dans l'arborescence ci-dessous
+ a été déplacé sousVos données ont été sauvegardées, mais avant de pouvoir publier votre page, il y a des erreurs que vous devez corriger :
@@ -401,6 +540,7 @@
Il y a une erreur de configuration du type de données utilisé pour cette propriété, veuillez vérifier le type de données.
+ OptionsA proposActionActions
@@ -418,6 +558,7 @@
Fermer la fenêtreCommenterConfirmer
+ ConserverConserver les proportionsContinuerCopier
@@ -429,6 +570,7 @@
SuppriméSuppression...Design
+ DictionnaireDimensionsBasTélécharger
@@ -438,18 +580,26 @@
EmailErreurTrouver
+ Premier
+ Général
+ GroupesHauteurAide
+ Cacher
+ HistoriqueIcôneImporter
+ InfoMarge intérieureInsérerInstallerNon valideJustifier
- Libellé
+ LibelléLangue
+ DernierMise en page
+ LiensEn cours de chargementBloquéConnexion
@@ -457,6 +607,7 @@
DéconnexionMacroObligatoire
+ MessageDéplacerPlusNom
@@ -464,9 +615,12 @@
SuivantNonde
+ InactifOKOuvrir
+ Actifou
+ Trier parMot de passeCheminPlaceholder ID
@@ -475,20 +629,25 @@
PropriétésEmail de réception des données de formulaireCorbeille
- Votre corbeille est vide
+ Votre corbeille est videRestant
+ EnleverRenommerRenouvellerRequis
+ RetrouverRéessayerPermissions
+ Publication ProgramméeRechercherDésolé, nous ne pouvons pas trouver ce que vous recherchez
+ Aucun élément n'a été ajoutéServeurMontrerAfficher la page à l'envoiTailleTrier
+ StatutEnvoyerTypeRechercher...
@@ -515,16 +674,32 @@
Sauvegarde...actuelIntégrer
+ Retrouversélectionné
+
NoirVertJauneOrangeBleu
+ Bleu-gris
+ Gris
+ Brun
+ Bleu Clair
+ Cyan
+ Vert Clair
+ Limon
+ Ambre
+ Orange FoncéRouge
+ Rose
+ Mauve
+ Mauve Foncé
+ Indigo
+
Ajouter un ongletAjouter une propriété
@@ -542,6 +717,16 @@
Passer à la vue en listeBasculer vers l'autorisation comme racine
+
+ Commenter/Décommenter les lignes
+ Supprimer la ligne
+ Copier les lignes vers le haut
+ Copier les lignes vers le bas
+ Déplacer les lignes vers le haut
+ Déplacer les lignes vers le bas
+
+ Général
+ EditeurCouleur de fond
@@ -558,30 +743,38 @@
Impossible de sauvegarder le fichier web.config. Veuillez modifier la "connection string" manuellement.Votre base de données a été détectée et est identifiée comme étantConfiguration de la base de données
-
+ installer pour installer la base de données Umbraco %0%
- ]]>
+ ]]>
+
Suivant pour poursuivre.]]>
- Base de données non trouvée ! Veuillez vérifier les informations de la "connection string" dans le fichier web.config.
+
+ Base de données non trouvée ! Veuillez vérifier les informations de la "connection string" dans le fichier web.config.
Pour poursuivre, veuillez éditer le fichier "web.config" (avec Visual Studio ou votre éditeur de texte favori), scroller jusqu'en bas, ajouter le "connection string" pour votre base de données dans la ligne avec la clé "umbracoDbDSN" et sauvegarder le fichier.
]]>
-
+ Plus d'informations sur l'édition du fichier web.config ici.]]>
+
+
+
Veuillez contacter votre fournisseur de services internet si nécessaire.
- Si vous installez Umbraco sur un ordinateur ou un serveur local, vous aurez peut-être besoin de consulter votre administrateur système.]]>
-
+
+
+
Appuyez sur le bouton Upgrader pour mettre à jour votre base de données vers Umbraco %0%
N'ayez pas d'inquiétude : aucun contenu ne sera supprimé et tout continuera à fonctionner parfaitement par après !
- ]]>
+ ]]>
+
- Appuyez sur Suivant pour
+ Appuyez sur Suivant pour
poursuivre. ]]>
-
+
Suivant pour poursuivre la configuration]]>Le mot de passe par défaut doit être modifié !]]>L'utilisateur par défaut a été désactivé ou n'a pas accès à Umbraco!
Aucune autre action n'est requise. Cliquez sur Suivant pour poursuivre.]]>
@@ -593,43 +786,56 @@
Fichiers et dossiers concernésPlus d'informations sur la configuration des permissionsVous devez donner à ASP.NET les droits de modification sur les fichiers/dossiers suivants
- Vos configurations de permissions sont presque parfaites !
- Vous pouvez faire fonctionner Umbraco sans problèmes, mais vous ne serez pas en mesure d'installer des packages, ce qui est hautement recommandé pour tirer pleinement profit d'Umbraco.]]>
+
+ Vos configurations de permissions sont presque parfaites !
+ Vous pouvez faire fonctionner Umbraco sans problèmes, mais vous ne serez pas en mesure d'installer des packages, ce qui est hautement recommandé pour tirer pleinement profit d'Umbraco.]]>
+ Comment résoudreCliquez ici pour lire la version textetutoriel vidéo sur la définition des permissions des répertoires pour Umbraco, ou lisez la version texte.]]>
- Vos configurations de permissions pourraient poser problème !
+
+ Vos configurations de permissions pourraient poser problème !
- Vous pouvez faire fonctionner Umbraco sans problèmes, mais vous ne serez pas en mesure d'installer des packages, ce qui est hautement recommandé pour tirer pleinement profit d'Umbraco.]]>
- Vos configurations de permissions ne sont pas prêtes pour Umbraco !
+ Vous pouvez faire fonctionner Umbraco sans problèmes, mais vous ne serez pas en mesure d'installer des packages, ce qui est hautement recommandé pour tirer pleinement profit d'Umbraco.]]>
+
+
+ Vos configurations de permissions ne sont pas prêtes pour Umbraco !
- Pour faire fonctionner Umbraco, vous aurez besoin de mettre à jour les permissions sur les fichiers/dossiers.]]>
- Vos configurations de permissions sont parfaites !
- Vous êtes prêt(e) à faire fonctionner Umbraco et à installer des packages !]]>
+ Pour faire fonctionner Umbraco, vous aurez besoin de mettre à jour les permissions sur les fichiers/dossiers.]]>
+
+
+ Vos configurations de permissions sont parfaites !
+ Vous êtes prêt(e) à faire fonctionner Umbraco et à installer des packages !]]>
+ Résoudre un problème sur un dossierSuivez ce lien pour plus d'informations sur les problèmes avec ASP.NET et la création de dossiersDéfinir les permissions de dossier
-
+
+ ]]>
+
Je veux démarrer "from scratch"
- Apprenez comment)
Vous pouvez toujours choisir d'installer Runway plus tard. Pour cela, allez dans la section "Développeur" et sélectionnez "Packages".
- ]]>
+ ]]>
+
Vous venez de mettre en place une plateforme Umbraco toute nette. Que voulez-vous faire ensuite ?Runway est installé
-
+
Voici la liste des modules recommandés, cochez ceux que vous souhaitez installer, ou regardez la liste complète des modules
- ]]>
+ ]]>
+
Recommandé uniquement pour les utilisateurs expérimentésJe veux commencer avec un site simple
-
"Runway" est un site simple qui fournit des types de documents et des modèles de base. L'installateur peut mettre en place Runway automatiquement pour vous,
mais vous pouvez facilement l'éditer, l'enrichir, ou le supprimer par la suite. Il n'est pas nécessaire, et vous pouvez parfaitement vous en passer pour utiliser Umbraco. Cela étant dit,
@@ -640,7 +846,8 @@
Inclus avec Runway : Home page, Getting Started page, Installing Modules page. Modules optionnels : Top Navigation, Sitemap, Contact, Gallery.
- ]]>
+ ]]>
+
Qu'est-ce que RunwayEtape 1/5 : Accepter la licenceEtape 2/5 : Configuration de la base de données
@@ -648,24 +855,36 @@
Etape 4/5 : Sécurité UmbracoEtape 5/5 : Umbraco est prêtMerci d'avoir choisi Umbraco
- Parcourir votre nouveau site
-Vous avez installé Runway, alors pourquoi ne pas jeter un oeil au look de votre nouveau site ?]]>
- Aide et informations complémentaires
-Obtenez de l'aide de notre "award winning" communauté, parcourez la documentation ou regardez quelques vidéos gratuites sur la manière de construire un site simple, d'utiliser les packages ainsi qu'un guide rapide sur la terminologie Umbraco]]>
+
+ Parcourir votre nouveau site
+Vous avez installé Runway, alors pourquoi ne pas jeter un oeil au look de votre nouveau site ?]]>
+
+
+ Aide et informations complémentaires
+Obtenez de l'aide de notre communauté "award winning", parcourez la documentation ou regardez quelques vidéos gratuites sur la manière de construire un site simple, d'utiliser les packages ainsi qu'un guide rapide sur la terminologie Umbraco]]>
+ Umbraco %0% est installé et prêt à être utilisé
- fichier /web.config et mettre à jour le paramètre AppSetting umbracoConfigurationStatus situé en bas à la valeur '%0%'.]]>
- démarrer instantanément en cliquant sur le bouton "Lancer Umbraco" ci-dessous.
-Si vous débutez avec Umbraco, vous pouvez trouver une foule de ressources dans nos pages "Getting Started".]]>
- Lancer Umbraco
-Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à ajouter du contenu, à mettre à jour les modèles d'affichage et feuilles de styles ou à ajouter de nouvelles fonctionnalités]]>
+
+ fichier /web.config et mettre à jour le paramètre AppSetting umbracoConfigurationStatus situé en bas à la valeur '%0%'.]]>
+
+
+ démarrer instantanément en cliquant sur le bouton "Lancer Umbraco" ci-dessous.
+Si vous débutez avec Umbraco, vous pouvez trouver une foule de ressources dans nos pages "Getting Started".]]>
+
+
+ Lancer Umbraco
+Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à ajouter du contenu, à mettre à jour les modèles d'affichage et feuilles de styles ou à ajouter de nouvelles fonctionnalités]]>
+ La connexion à la base de données a échoué.Umbraco Version 3Umbraco Version 4Regarder
- Umbraco %0%, qu'il s'agisse d'une nouvelle installation ou d'une mise à jour à partir de la version 3.0
+
+ Umbraco %0%, qu'il s'agisse d'une nouvelle installation ou d'une mise à jour à partir de la version 3.0
- Appuyez sur "suivant" pour commencer l'assistant.]]>
+ Appuyez sur "suivant" pour commencer l'assistant.]]>
+ Code de la Culture
@@ -676,13 +895,13 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Renouvellez votre session maintenant pour sauvegarder votre travail
- Joyeux dimanche
- Joyeux lundi
- Joyeux mardi
- Joyeux mercredi
+ Joyeux dimanche détonnant
+ Joyeux lundi lumineux
+ Joyeux mardi magique
+ Joyeux mercredi merveilleuxJoyeux jeudiJoyeux vendredi
- Joyeux samedi
+ Joyeux chamediConnectez-vous ci-dessousIdentifiez-vous avecLa session a expiré
@@ -690,13 +909,95 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Mot de passe oublié?Un email contenant un lien pour ré-initialiser votre mot de passe sera envoyé à l'adresse spécifiéeUn email contenant les instructions de ré-initialisation de votre mot de passe sera envoyée à l'adresse spécifiée si elle correspond à nos informations.
+ Montrer le mot de passe
+ Cacher le mot de passeRevenir au formulaire de connexionVeuillez fournir un nouveau mot de passeVotre mot de passe a été mis à jourLe lien sur lequel vous avez cliqué est non valide ou a expiré.Umbraco: Ré-initialiser le mot de passe
- Votre nom d'utilisateur pour vous connecter au back-office Umbraco est : %0%.
Cliquez ici pour ré-initialiser votre mot de passe, ou recopiez cet URL dans votre navigateur :
%1%
]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Une réinitialisation de votre mot de passe a été demandée
+
+
+ Votre nom d'utilisateur pour vous connecter au back-office Umbraco est : %0%
+