diff --git a/README.md b/README.md index 91065214ad..d4a024ddbc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ Umbraco CMS =========== -##Note: Building version 7 from source## -If you're interested in using the source code and building version 7, make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself. +Umbraco is a free open source Content Management System built on the ASP.NET platform. + +## Building Umbraco from source ## +The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `grunt dev` in `src\Umbraco.Web.UI.Client`. + +If you're interested in making changes to Belle make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself. ## Watch a five minute introduction video ## diff --git a/build/Build.bat b/build/Build.bat index ead7df0abc..0c9b1f1ad2 100644 --- a/build/Build.bat +++ b/build/Build.bat @@ -4,11 +4,18 @@ SET comment= SET version=%release% IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%) +ECHO Building Umbraco %version% ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %release% +ECHO Installing the Microsoft.Bcl.Build package before anything else, otherwise you'd have to run build.cmd twice +SET nuGetFolder=%CD%\..\src\packages\ +..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder% + +ECHO Performing MSBuild and producing Umbraco binaries zip files %windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% +ECHO Adding dummy files to include in the NuGet package so that empty folders actually get created echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Code\dummy.txt echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Data\dummy.txt echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\App_Plugins\dummy.txt @@ -20,9 +27,11 @@ echo This file is only here so that the containing folder will be included in th echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\Views\Partials\dummy.txt echo This file is only here so that the containing folder will be included in the NuGet package, it is safe to delete. > .\_BuildOutput\WebApp\Views\MacroPartials\dummy.txt +ECHO Adding Web.config transform files to the NuGet package ren .\_BuildOutput\WebApp\MacroScripts\Web.config Web.config.transform ren .\_BuildOutput\WebApp\Views\Web.config Web.config.transform +ECHO Packing the NuGet release files ..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.Core.nuspec -Version %version% ..\src\.nuget\NuGet.exe pack NuSpecs\UmbracoCms.nuspec -Version %version% diff --git a/build/Build.proj b/build/Build.proj index 134bb668ed..a442c2bdd4 100644 --- a/build/Build.proj +++ b/build/Build.proj @@ -267,7 +267,7 @@ - + diff --git a/build/BuildBelle.bat b/build/BuildBelle.bat new file mode 100644 index 0000000000..bb9cfedb5b --- /dev/null +++ b/build/BuildBelle.bat @@ -0,0 +1,32 @@ +@ECHO OFF +ECHO Installing Npm NuGet Package + +SET nuGetFolder=%CD%\..\src\packages\ +ECHO Configured packages folder: %nuGetFolder% +ECHO Current folder: %CD% + +%CD%\..\src\.nuget\NuGet.exe install Npm.js -OutputDirectory %nuGetFolder% + +for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\" +for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\" + +ECHO Temporarily adding Npm and Node to path +SET oldPath=%PATH% + +path=%npmPath%;%nodePath%;%PATH% + +SET buildFolder=%CD% + +ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\ +CD %CD%\..\src\Umbraco.Web.UI.Client\ + +ECHO Do npm install and the grunt build of Belle +call npm install +call npm install -g grunt-cli +call grunt build + +ECHO Reset path to what it was before +path=%oldPath% + +ECHO Move back to the build folder +CD %buildFolder% \ No newline at end of file diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 65a19d9089..1c70c8b100 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -26,7 +26,7 @@ - + @@ -35,6 +35,7 @@ + diff --git a/src/.nuget/NuGet.exe b/src/.nuget/NuGet.exe index c296edf177..8f61340295 100644 Binary files a/src/.nuget/NuGet.exe and b/src/.nuget/NuGet.exe differ diff --git a/src/.nuget/NuGet.targets b/src/.nuget/NuGet.targets index 930ac95d01..6ff51f6e83 100644 --- a/src/.nuget/NuGet.targets +++ b/src/.nuget/NuGet.targets @@ -18,13 +18,13 @@ - + - + @@ -41,7 +41,7 @@ - $(NuGetToolsPath)\nuget.exe + $(NuGetToolsPath)\NuGet.exe @(PackageSource) "$(NuGetExePath)" @@ -50,9 +50,14 @@ $(TargetDir.Trim('\\')) -RequireConsent + -NonInteractive + + "$(SolutionDir) " + "$(SolutionDir)" + - $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(RequireConsentSwitch) -solutionDir "$(SolutionDir) " - $(NuGetCommand) pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols + $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) + $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols @@ -70,7 +75,6 @@ - )", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""'](\w+?)[""'].+?)(?:/>|>.*?)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""']([\w\.]+?)[""'].+?)(?:/>|>.*?)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); /// /// This formats the persisted string to something useful for the rte so that the macro renders properly since we @@ -39,8 +39,8 @@ namespace Umbraco.Core.Macros //
var alias = match.Groups[2].Value; var sb = new StringBuilder("
Xml representation of the passed in internal static XElement ToDeepXml(this IContent content) { - return ApplicationContext.Current.Services.PackagingService.Export(content, true); + return ApplicationContext.Current.Services.PackagingService.Export(content, true, raiseEvents: false); } /// @@ -628,7 +628,7 @@ namespace Umbraco.Core.Models /// Xml representation of the passed in public static XElement ToXml(this IContent content) { - return ApplicationContext.Current.Services.PackagingService.Export(content); + return ApplicationContext.Current.Services.PackagingService.Export(content, raiseEvents: false); } /// @@ -638,7 +638,7 @@ namespace Umbraco.Core.Models /// Xml representation of the passed in public static XElement ToXml(this IMedia media) { - return ApplicationContext.Current.Services.PackagingService.Export(media); + return ApplicationContext.Current.Services.PackagingService.Export(media, raiseEvents: false); } /// @@ -648,7 +648,7 @@ namespace Umbraco.Core.Models /// Xml representation of the passed in internal static XElement ToDeepXml(this IMedia media) { - return ApplicationContext.Current.Services.PackagingService.Export(media, true); + return ApplicationContext.Current.Services.PackagingService.Export(media, true, raiseEvents: false); } /// diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs index 914847aea2..d81f49fc4f 100644 --- a/src/Umbraco.Core/Models/Macro.cs +++ b/src/Umbraco.Core/Models/Macro.cs @@ -107,7 +107,7 @@ namespace Umbraco.Core.Models private bool _dontRender; private string _scriptFile; private string _scriptAssembly; - private string _python; + private string _scriptPath; private string _xslt; private readonly MacroPropertyCollection _properties; private readonly List _addedProperties; @@ -360,14 +360,14 @@ namespace Umbraco.Core.Models [DataMember] public string ScriptPath { - get { return _python; } + get { return _scriptPath; } set { SetPropertyValueAndDetectChanges(o => { - _python = value; - return _python; - }, _python, ScriptPathSelector); + _scriptPath = value; + return _scriptPath; + }, _scriptPath, ScriptPathSelector); } } diff --git a/src/Umbraco.Core/Models/MacroProperty.cs b/src/Umbraco.Core/Models/MacroProperty.cs index 243e5c0e91..ef0a024973 100644 --- a/src/Umbraco.Core/Models/MacroProperty.cs +++ b/src/Umbraco.Core/Models/MacroProperty.cs @@ -155,7 +155,6 @@ namespace Umbraco.Core.Models /// [DataMember] public string EditorAlias - { get { return _editorAlias; } set diff --git a/src/Umbraco.Core/Models/MemberType.cs b/src/Umbraco.Core/Models/MemberType.cs index c361891fa0..b2e3a4dc89 100644 --- a/src/Umbraco.Core/Models/MemberType.cs +++ b/src/Umbraco.Core/Models/MemberType.cs @@ -15,6 +15,7 @@ namespace Umbraco.Core.Models { //Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId private IDictionary> _memberTypePropertyTypes; + private string _alias; public MemberType(int parentId) : base(parentId) { @@ -27,6 +28,29 @@ namespace Umbraco.Core.Models } private static readonly PropertyInfo MemberTypePropertyTypesSelector = ExpressionHelper.GetPropertyInfo>>(x => x.MemberTypePropertyTypes); + private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo(x => x.Alias); + + /// + /// The Alias of the ContentType + /// + [DataMember] + public override string Alias + { + get { return _alias; } + set + { + //NOTE: WE are overriding this because we don't want to do a ToSafeAlias when the alias is the special case of + // "_umbracoSystemDefaultProtectType" which is used internally, currently there is an issue with the safe alias as it strips + // leading underscores which we don't want in this case. + // see : http://issues.umbraco.org/issue/U4-3968 + + SetPropertyValueAndDetectChanges(o => + { + _alias = value == "_umbracoSystemDefaultProtectType" ? value : value.ToSafeAlias(); + return _alias; + }, _alias, AliasSelector); + } + } /// /// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, PropertyTypeId) by the PropertyTypes' alias. diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs index 295cbdef66..c8bf96be8f 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs @@ -80,11 +80,8 @@ namespace Umbraco.Core.Models.PublishedContent private void InitializeConverters() { - var converters = PropertyValueConvertersResolver.Current.Converters.ToArray(); - - var defaultConverters = converters - .Where(x => x.GetType().GetCustomAttribute(false) != null) - .ToArray(); + var converters = PropertyValueConvertersResolver.Current.Converters.ToArray(); + var defaultConvertersWithAttributes = PropertyValueConvertersResolver.Current.DefaultConverters; _converter = null; @@ -98,8 +95,14 @@ namespace Umbraco.Core.Models.PublishedContent else if (foundConverters.Length > 1) { //more than one was found, we need to first figure out if one of these is an Umbraco default value type converter - var nonDefault = foundConverters.Except(defaultConverters).ToArray(); - if (nonDefault.Length > 1) + //get the non-default and see if we have one + var nonDefault = foundConverters.Except(defaultConvertersWithAttributes.Select(x => x.Item1)).ToArray(); + if (nonDefault.Length == 1) + { + //there's only 1 custom converter registered that so use it + _converter = nonDefault[0]; + } + else if (nonDefault.Length > 1) { //this is not allowed, there cannot be more than 1 custom converter throw new InvalidOperationException( @@ -109,9 +112,31 @@ namespace Umbraco.Core.Models.PublishedContent ContentType.Alias, PropertyTypeAlias, nonDefault[1].GetType().FullName, nonDefault[0].GetType().FullName)); } + else + { + //we need to remove any converters that have been shadowed by another converter + var foundDefaultConvertersWithAttributes = defaultConvertersWithAttributes.Where(x => foundConverters.Contains(x.Item1)); + var shadowedTypes = foundDefaultConvertersWithAttributes.SelectMany(x => x.Item2.DefaultConvertersToShadow); + var shadowedDefaultConverters = foundConverters.Where(x => shadowedTypes.Contains(x.GetType())); + var nonShadowedDefaultConverters = foundConverters.Except(shadowedDefaultConverters).ToArray(); - //there's only 1 custom converter registered that so use it - _converter = nonDefault[0]; + if (nonShadowedDefaultConverters.Length == 1) + { + //assign to the single default converter + _converter = nonShadowedDefaultConverters[0]; + } + else if (nonShadowedDefaultConverters.Length > 1) + { + //this is not allowed, there cannot be more than 1 custom converter + throw new InvalidOperationException( + string.Format("Type '{2}' cannot be an IPropertyValueConverter" + + " for property '{1}' of content type '{0}' because type '{3}' has already been detected as a converter" + + " for that property, and only one converter can exist for a property.", + ContentType.Alias, PropertyTypeAlias, + nonShadowedDefaultConverters[1].GetType().FullName, nonShadowedDefaultConverters[0].GetType().FullName)); + } + } + } // get the cache levels, quietely fixing the inconsistencies (no need to throw, really) diff --git a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs new file mode 100644 index 0000000000..edcc25ade8 --- /dev/null +++ b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.UI.WebControls; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core.Models +{ + internal static class UmbracoEntityExtensions + { + + public static object GetAdditionalDataValueIgnoreCase(this IUmbracoEntity entity, string key, object defaultVal) + { + if (entity.AdditionalData.ContainsKeyIgnoreCase(key) == false) return defaultVal; + return entity.AdditionalData.GetValueIgnoreCase(key, defaultVal); + } + + public static bool IsContainer(this IUmbracoEntity entity) + { + if (entity.AdditionalData.ContainsKeyIgnoreCase("IsContainer") == false) return false; + var val = entity.AdditionalData.GetValueIgnoreCase("IsContainer", null); + if (val is bool && (bool) val) + { + return true; + } + return false; + } + + } +} diff --git a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs index 60e176096f..273d605bef 100644 --- a/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Persistence/Caching/RuntimeCacheProvider.cs @@ -151,6 +151,21 @@ namespace Umbraco.Core.Persistence.Caching _keyTracker.Remove(key); } + public void Delete(Type type, int entityId) + { + var key = GetCompositeId(type, entityId); + if (_memoryCache != null) + { + _memoryCache.Remove(key); + } + else + { + HttpRuntime.Cache.Remove(key); + } + + _keyTracker.Remove(key); + } + /// /// Clear cache by type /// diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index 9a291194a9..24da2e9848 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -521,6 +521,10 @@ namespace Umbraco.Core.Persistence.Repositories totalRecords = Convert.ToInt32(pagedResult.TotalItems); //now that we have the member dto's we need to construct true members from the list. + if (totalRecords == 0) + { + return Enumerable.Empty(); + } var result = GetAll(pagedResult.Items.Select(x => x.NodeId).ToArray()); //now we need to ensure this result is also ordered by the same order by clause diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 4ccb3b73a4..9c6c742170 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -298,6 +298,11 @@ namespace Umbraco.Core.Persistence.Repositories totalRecords = Convert.ToInt32(pagedResult.TotalItems); //now that we have the user dto's we need to construct true members from the list. + if (totalRecords == 0) + { + return Enumerable.Empty(); + } + var result = GetAll(pagedResult.Items.Select(x => x.Id).ToArray()); //now we need to ensure this result is also ordered by the same order by clause diff --git a/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs index 4857e2bb07..10bdd053a8 100644 --- a/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/DefaultPropertyValueConverterAttribute.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; namespace Umbraco.Core.PropertyEditors { @@ -8,5 +10,27 @@ namespace Umbraco.Core.PropertyEditors [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] internal class DefaultPropertyValueConverterAttribute : Attribute { + public DefaultPropertyValueConverterAttribute() + { + DefaultConvertersToShadow = Enumerable.Empty(); + } + + public DefaultPropertyValueConverterAttribute(params Type[] convertersToShadow) + { + DefaultConvertersToShadow = convertersToShadow; + } + + /// + /// A DefaultPropertyValueConverter can 'shadow' other default property value converters so that + /// a DefaultPropertyValueConverter can be more specific than another one. + /// + /// + /// An example where this is useful is that both the RelatedLiksEditorValueConverter and the JsonValueConverter + /// will be returned as value converters for the Related Links Property editor, however the JsonValueConverter + /// is a very generic converter and the RelatedLiksEditorValueConverter is more specific than it, so the RelatedLiksEditorValueConverter + /// can specify that it 'shadows' the JsonValueConverter. + /// + public IEnumerable DefaultConvertersToShadow { get; private set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs b/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs index cc89e7671b..9e2fb5d10a 100644 --- a/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/LegacyParameterEditorAliasConverter.cs @@ -97,10 +97,7 @@ namespace Umbraco.Core.PropertyEditors CreateMap("mediaCurrent", Constants.PropertyEditors.MediaPickerAlias); - CreateMap("number", Constants.PropertyEditors.IntegerAlias); - - - + CreateMap("number", Constants.PropertyEditors.IntegerAlias); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs index 53fecdac9d..295fdb4b09 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConvertersResolver.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Threading; using Umbraco.Core.ObjectResolution; namespace Umbraco.Core.PropertyEditors @@ -36,5 +38,41 @@ namespace Umbraco.Core.PropertyEditors { get { return Values; } } + + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + private Tuple[] _defaults = null; + + /// + /// Caches and gets the default converters with their metadata + /// + internal Tuple[] DefaultConverters + { + get + { + using (var locker = new UpgradeableReadLock(_lock)) + { + if (_defaults == null) + { + locker.UpgradeToWriteLock(); + + var defaultConvertersWithAttributes = Converters + .Select(x => new + { + attribute = x.GetType().GetCustomAttribute(false), + converter = x + }) + .Where(x => x.attribute != null) + .ToArray(); + + _defaults = defaultConvertersWithAttributes + .Select( + x => new Tuple(x.converter, x.attribute)) + .ToArray(); + } + + return _defaults; + } + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Security/AuthenticationExtensions.cs b/src/Umbraco.Core/Security/AuthenticationExtensions.cs index b642796932..45c290daba 100644 --- a/src/Umbraco.Core/Security/AuthenticationExtensions.cs +++ b/src/Umbraco.Core/Security/AuthenticationExtensions.cs @@ -137,12 +137,19 @@ namespace Umbraco.Core.Security { if (response == null) throw new ArgumentNullException("response"); //remove the cookie - var cookie = new CookieHeaderValue(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "") + var authCookie = new CookieHeaderValue(UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName, "") { Expires = DateTime.Now.AddYears(-1), Path = "/" }; - response.Headers.AddCookies(new[] { cookie }); + //remove the preview cookie too + var prevCookie = new CookieHeaderValue(Constants.Web.PreviewCookieName, "") + { + Expires = DateTime.Now.AddYears(-1), + Path = "/" + }; + + response.Headers.AddCookies(new[] { authCookie, prevCookie }); } /// @@ -259,21 +266,27 @@ namespace Umbraco.Core.Security private static void Logout(this HttpContextBase http, string cookieName) { if (http == null) throw new ArgumentNullException("http"); - //remove from the request - http.Request.Cookies.Remove(cookieName); + //clear the preview cookie too + var cookies = new[] { cookieName, Constants.Web.PreviewCookieName }; + foreach (var c in cookies) + { + //remove from the request + http.Request.Cookies.Remove(c); + + //expire from the response + var formsCookie = http.Response.Cookies[c]; + if (formsCookie != null) + { + //this will expire immediately and be removed from the browser + formsCookie.Expires = DateTime.Now.AddYears(-1); + } + else + { + //ensure there's def an expired cookie + http.Response.Cookies.Add(new HttpCookie(c) { Expires = DateTime.Now.AddYears(-1) }); + } + } - //expire from the response - var formsCookie = http.Response.Cookies[cookieName]; - if (formsCookie != null) - { - //this will expire immediately and be removed from the browser - formsCookie.Expires = DateTime.Now.AddYears(-1); - } - else - { - //ensure there's def an expired cookie - http.Response.Cookies.Add(new HttpCookie(cookieName) { Expires = DateTime.Now.AddYears(-1) }); - } } private static FormsAuthenticationTicket GetAuthTicket(this HttpContextBase http, string cookieName) diff --git a/src/Umbraco.Core/Security/MembershipProviderExtensions.cs b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs new file mode 100644 index 0000000000..85db27ac97 --- /dev/null +++ b/src/Umbraco.Core/Security/MembershipProviderExtensions.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Security; +using Umbraco.Core.Security; + +namespace Umbraco.Core.Security +{ + internal static class MembershipProviderExtensions + { + /// + /// Returns true if the provider specified is a built-in Umbraco membership provider + /// + /// + /// + public static bool IsUmbracoMembershipProvider(this MembershipProvider membershipProvider) + { + return (membershipProvider is UmbracoMembershipProviderBase); + } + + public static UmbracoMembershipProviderBase AsUmbracoMembershipProvider(this MembershipProvider membershipProvider) + { + return (UmbracoMembershipProviderBase)membershipProvider; + } + + } +} diff --git a/src/Umbraco.Core/Services/INotificationService.cs b/src/Umbraco.Core/Services/INotificationService.cs index 801424d3b0..4be6d17f0d 100644 --- a/src/Umbraco.Core/Services/INotificationService.cs +++ b/src/Umbraco.Core/Services/INotificationService.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Web; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; @@ -17,9 +19,15 @@ namespace Umbraco.Core.Services /// Sends the notifications for the specified user regarding the specified node and action. /// /// - /// + /// /// - void SendNotifications(IEntity entity, IUser user, IAction action); + /// + /// + /// + /// + void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http, + Func createSubject, + Func createBody); /// /// Gets the notifications for the user @@ -28,6 +36,17 @@ namespace Umbraco.Core.Services /// IEnumerable GetUserNotifications(IUser user); + /// + /// Gets the notifications for the user based on the specified node path + /// + /// + /// + /// + /// + /// Notifications are inherited from the parent so any child node will also have notifications assigned based on it's parent (ancestors) + /// + IEnumerable GetUserNotifications(IUser user, string path); + /// /// Returns the notifications for an entity /// diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index 2f711eb8af..c7e68e6eca 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Services /// Imports and saves package xml as /// /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin). + /// Optional id of the User performing the operation. Default is zero (admin) /// Optional parameter indicating whether or not to raise events /// An enumrable list of generated ContentTypes IEnumerable ImportContentTypes(XElement element, int userId = 0, bool raiseEvents = true); @@ -30,7 +30,7 @@ namespace Umbraco.Core.Services /// /// Xml to import /// Boolean indicating whether or not to import the - /// Optional id of the User performing the operation. Default is zero (admin). + /// Optional id of the User performing the operation. Default is zero (admin) /// Optional parameter indicating whether or not to raise events /// An enumrable list of generated ContentTypes IEnumerable ImportContentTypes(XElement element, bool importStructure, int userId = 0, bool raiseEvents = true); @@ -39,7 +39,7 @@ namespace Umbraco.Core.Services /// Imports and saves package xml as /// /// Xml to import - /// + /// Optional id of the User performing the operation. Default is zero (admin). /// Optional parameter indicating whether or not to raise events /// An enumrable list of generated DataTypeDefinitions IEnumerable ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true); @@ -56,15 +56,25 @@ namespace Umbraco.Core.Services /// Imports and saves the 'Languages' part of a package xml as a list of /// /// Xml to import + /// Optional id of the User performing the operation. Default is zero (admin) /// Optional parameter indicating whether or not to raise events /// An enumerable list of generated languages - IEnumerable ImportLanguages(XElement languageElementList, bool raiseEvents = true); + IEnumerable ImportLanguages(XElement languageElementList, int userId = 0, bool raiseEvents = true); + + /// + /// Imports and saves the 'Macros' part of a package xml as a list of + /// + /// Xml to import + /// Optional id of the User performing the operation + /// Optional parameter indicating whether or not to raise events + /// + IEnumerable ImportMacros(XElement element, int userId = 0, bool raiseEvents = true); /// /// Imports and saves package xml as /// /// Xml to import - /// Optional user id + /// Optional id of the User performing the operation. Default is zero (admin) /// Optional parameter indicating whether or not to raise events /// An enumrable list of generated Templates IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); @@ -74,7 +84,7 @@ namespace Umbraco.Core.Services /// /// ContentType to export /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the ContentType item. + /// containing the xml representation of the ContentType item XElement Export(IContentType contentType, bool raiseEvents = true); /// @@ -144,5 +154,37 @@ namespace Umbraco.Core.Services /// Optional parameter indicating whether or not to raise events /// containing the xml representation of the IDataTypeDefinition object XElement Export(IDataTypeDefinition dataTypeDefinition, bool raiseEvents = true); + + /// + /// Exports a list of items to xml as an + /// + /// List of Templates to export + /// Optional parameter indicating whether or not to raise events + /// containing the xml representation of the ITemplate objects + XElement Export(IEnumerable templates, bool raiseEvents = true); + + /// + /// Exports a single item to xml as an + /// + /// Template to export + /// Optional parameter indicating whether or not to raise events + /// containing the xml representation of the ITemplate object + XElement Export(ITemplate template, bool raiseEvents = true); + + /// + /// Exports a list of items to xml as an + /// + /// Macros to export + /// Optional parameter indicating whether or not to raise events + /// containing the xml representation of the IMacro objects + XElement Export(IEnumerable macros, bool raiseEvents = true); + + /// + /// Exports a single item to xml as an + /// + /// Macro to export + /// Optional parameter indicating whether or not to raise events + /// containing the xml representation of the IMacro object + XElement Export(IMacro macro, bool raiseEvents = true); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index 3172f329fa..f37339c70d 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -191,7 +191,7 @@ namespace Umbraco.Core.Services using (var uow = _uowProvider.GetUnitOfWork()) { var repository = _repositoryFactory.CreateMemberRepository(uow); - //NOTE What about content that has the contenttype as part of its composition? + //TODO: What about content that has the contenttype as part of its composition? var query = Query.Builder.Where(x => x.ContentTypeId == memberTypeId); var members = repository.GetByQuery(query).ToArray(); @@ -635,7 +635,7 @@ namespace Umbraco.Core.Services var uow = _uowProvider.GetUnitOfWork(); using (var repository = _repositoryFactory.CreateMemberRepository(uow)) { - var query = Query.Builder.Where(x => x.Username == userName); + var query = Query.Builder.Where(x => x.Username.Equals(userName)); var member = repository.GetByQuery(query).FirstOrDefault(); return member; diff --git a/src/Umbraco.Core/Services/NotificationService.cs b/src/Umbraco.Core/Services/NotificationService.cs index 31c534470b..2e4ff7a9ec 100644 --- a/src/Umbraco.Core/Services/NotificationService.cs +++ b/src/Umbraco.Core/Services/NotificationService.cs @@ -1,10 +1,20 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Mail; +using System.Text; +using System.Threading; +using System.Web; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Strings; using umbraco.interfaces; namespace Umbraco.Core.Services @@ -12,21 +22,67 @@ namespace Umbraco.Core.Services internal class NotificationService : INotificationService { private readonly IDatabaseUnitOfWorkProvider _uowProvider; + private readonly IUserService _userService; + private readonly IContentService _contentService; - public NotificationService(IDatabaseUnitOfWorkProvider provider) + public NotificationService(IDatabaseUnitOfWorkProvider provider, IUserService userService, IContentService contentService) { _uowProvider = provider; + _userService = userService; + _contentService = contentService; } /// /// Sends the notifications for the specified user regarding the specified node and action. /// /// - /// + /// /// - public void SendNotifications(IEntity entity, IUser user, IAction action) + /// + /// + /// + /// + /// + /// Currently this will only work for Content entities! + /// + public void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http, + Func createSubject, + Func createBody) { - throw new NotImplementedException(); + if ((entity is IContent) == false) + { + throw new NotSupportedException(); + } + var content = (IContent) entity; + //we'll lazily get these if we need to send notifications + IContent[] allVersions = null; + + int totalUsers; + var allUsers = _userService.GetAllMembers(0, int.MaxValue, out totalUsers); + foreach (var u in allUsers) + { + if (u.IsApproved == false) continue; + var userNotifications = GetUserNotifications(u, content.Path).ToArray(); + var notificationForAction = userNotifications.FirstOrDefault(x => x.Action == action); + if (notificationForAction != null) + { + //lazy load versions if notifications are required + if (allVersions == null) + { + allVersions = _contentService.GetVersions(entity.Id).ToArray(); + } + + try + { + SendNotification(operatingUser, u, content, allVersions, actionName, http, createSubject, createBody); + LogHelper.Debug(string.Format("Notification type: {0} sent to {1} ({2})", action, u.Name, u.Email)); + } + catch (Exception ex) + { + LogHelper.Error("An error occurred sending notification", ex); + } + } + } } /// @@ -41,6 +97,23 @@ namespace Umbraco.Core.Services return repository.GetUserNotifications(user); } + /// + /// Gets the notifications for the user based on the specified node path + /// + /// + /// + /// + /// + /// Notifications are inherited from the parent so any child node will also have notifications assigned based on it's parent (ancestors) + /// + public IEnumerable GetUserNotifications(IUser user, string path) + { + var userNotifications = GetUserNotifications(user).ToArray(); + var pathParts = path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries); + var result = userNotifications.Where(r => pathParts.InvariantContains(r.EntityId.ToString(CultureInfo.InvariantCulture))).ToList(); + return result; + } + /// /// Deletes notifications by entity /// @@ -99,5 +172,255 @@ namespace Umbraco.Core.Services var repository = new NotificationsRepository(uow); return repository.CreateNotification(user, entity, action); } + + #region private methods + + /// + /// Sends the notification + /// + /// + /// + /// + /// + /// The action readable name - currently an action is just a single letter, this is the name associated with the letter + /// + /// Callback to create the mail subject + /// Callback to create the mail body + private void SendNotification(IUser performingUser, IUser mailingUser, IContent content, IContent[] allVersions, string actionName, HttpContextBase http, + Func createSubject, + Func createBody) + { + if (performingUser == null) throw new ArgumentNullException("performingUser"); + if (mailingUser == null) throw new ArgumentNullException("mailingUser"); + if (content == null) throw new ArgumentNullException("content"); + if (allVersions == null) throw new ArgumentNullException("allVersions"); + if (http == null) throw new ArgumentNullException("http"); + if (createSubject == null) throw new ArgumentNullException("createSubject"); + if (createBody == null) throw new ArgumentNullException("createBody"); + + int versionCount = (allVersions.Length > 1) ? (allVersions.Length - 2) : (allVersions.Length - 1); + var oldDoc = _contentService.GetByVersion(allVersions[versionCount].Version); + + // build summary + var summary = new StringBuilder(); + var props = content.Properties.ToArray(); + foreach (var p in props) + { + var newText = p.Value != null ? p.Value.ToString() : ""; + var oldText = newText; + + // check if something was changed and display the changes otherwise display the fields + if (oldDoc.Properties.Contains(p.PropertyType.Alias)) + { + var oldProperty = oldDoc.Properties[p.PropertyType.Alias]; + oldText = oldProperty.Value != null ? oldProperty.Value.ToString() : ""; + + // replace html with char equivalent + ReplaceHtmlSymbols(ref oldText); + ReplaceHtmlSymbols(ref newText); + } + + + // make sure to only highlight changes done using TinyMCE editor... other changes will be displayed using default summary + // TODO: We should probably allow more than just tinymce?? + if ((p.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.TinyMCEAlias) + && string.CompareOrdinal(oldText, newText) != 0) + { + summary.Append(""); + summary.Append(" Note: "); + summary.Append( + " Red for deleted characters Yellow for inserted characters"); + summary.Append(""); + summary.Append(""); + summary.Append(" New " + + p.PropertyType.Name + ""); + summary.Append("" + + ReplaceLinks(CompareText(oldText, newText, true, false, "", string.Empty), http.Request) + + ""); + summary.Append(""); + summary.Append(""); + summary.Append(" Old " + + p.PropertyType.Name + ""); + summary.Append("" + + ReplaceLinks(CompareText(newText, oldText, true, false, "", string.Empty), http.Request) + + ""); + summary.Append(""); + } + else + { + summary.Append(""); + summary.Append("" + + p.PropertyType.Name + ""); + summary.Append("" + newText + ""); + summary.Append(""); + } + summary.Append( + " "); + } + + string protocol = GlobalSettings.UseSSL ? "https" : "http"; + + + string[] subjectVars = { + http.Request.ServerVariables["SERVER_NAME"] + ":" + + http.Request.Url.Port + + IOHelper.ResolveUrl(SystemDirectories.Umbraco), + actionName, + content.Name + }; + string[] bodyVars = { + mailingUser.Name, + actionName, + content.Name, + performingUser.Name, + http.Request.ServerVariables["SERVER_NAME"] + ":" + http.Request.Url.Port + IOHelper.ResolveUrl(SystemDirectories.Umbraco), + content.Id.ToString(CultureInfo.InvariantCulture), summary.ToString(), + string.Format("{2}://{0}/{1}", + http.Request.ServerVariables["SERVER_NAME"] + ":" + http.Request.Url.Port, + //TODO: RE-enable this so we can have a nice url + /*umbraco.library.NiceUrl(documentObject.Id))*/ + content.Id + ".aspx", + protocol) + + }; + + // create the mail message + var mail = new MailMessage(UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress, mailingUser.Email); + + // populate the message + mail.Subject = createSubject(mailingUser, subjectVars); + if (UmbracoConfig.For.UmbracoSettings().Content.DisableHtmlEmail) + { + mail.IsBodyHtml = false; + mail.Body = createBody(mailingUser, bodyVars); + } + else + { + mail.IsBodyHtml = true; + mail.Body = + @" + + +" + createBody(mailingUser, bodyVars); + } + + // nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here + // adding the server name to make sure we don't replace external links + if (GlobalSettings.UseSSL && string.IsNullOrEmpty(mail.Body) == false) + { + string serverName = http.Request.ServerVariables["SERVER_NAME"]; + mail.Body = mail.Body.Replace( + string.Format("http://{0}", serverName), + string.Format("https://{0}", serverName)); + } + + + // send it asynchronously, we don't want to got up all of the request time to send emails! + ThreadPool.QueueUserWorkItem(state => + { + try + { + using (mail) + { + using (var sender = new SmtpClient()) + { + sender.Send(mail); + } + } + + } + catch (Exception ex) + { + LogHelper.Error("An error occurred sending notification", ex); + } + }); + } + + private static string ReplaceLinks(string text, HttpRequestBase request) + { + string domain = GlobalSettings.UseSSL ? "https://" : "http://"; + domain += request.ServerVariables["SERVER_NAME"] + ":" + request.Url.Port + "/"; + text = text.Replace("href=\"/", "href=\"" + domain); + text = text.Replace("src=\"/", "src=\"" + domain); + return text; + } + + /// + /// Replaces the HTML symbols with the character equivalent. + /// + /// The old string. + private static void ReplaceHtmlSymbols(ref string oldString) + { + oldString = oldString.Replace(" ", " "); + oldString = oldString.Replace("’", "'"); + oldString = oldString.Replace("&", "&"); + oldString = oldString.Replace("“", "“"); + oldString = oldString.Replace("”", "”"); + oldString = oldString.Replace(""", "\""); + } + + /// + /// Compares the text. + /// + /// The old text. + /// The new text. + /// if set to true [display inserted text]. + /// if set to true [display deleted text]. + /// The inserted style. + /// The deleted style. + /// + private static string CompareText(string oldText, string newText, bool displayInsertedText, + bool displayDeletedText, string insertedStyle, string deletedStyle) + { + var sb = new StringBuilder(); + var diffs = Diff.DiffText1(oldText, newText); + + int pos = 0; + for (int n = 0; n < diffs.Length; n++) + { + Diff.Item it = diffs[n]; + + // write unchanged chars + while ((pos < it.StartB) && (pos < newText.Length)) + { + sb.Append(newText[pos]); + pos++; + } // while + + // write deleted chars + if (displayDeletedText && it.DeletedA > 0) + { + sb.Append(deletedStyle); + for (int m = 0; m < it.DeletedA; m++) + { + sb.Append(oldText[it.StartA + m]); + } // for + sb.Append(""); + } + + // write inserted chars + if (displayInsertedText && pos < it.StartB + it.InsertedB) + { + sb.Append(insertedStyle); + while (pos < it.StartB + it.InsertedB) + { + sb.Append(newText[pos]); + pos++; + } // while + sb.Append(""); + } // if + } // while + + // write rest of unchanged chars + while (pos < newText.Length) + { + sb.Append(newText[pos]); + pos++; + } // while + + return sb.ToString(); + } + + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index 03e0d96a59..92b44e8d3e 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -4,6 +4,7 @@ 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; @@ -27,6 +28,7 @@ namespace Umbraco.Core.Services private readonly IContentService _contentService; private readonly IContentTypeService _contentTypeService; private readonly IMediaService _mediaService; + private readonly IMacroService _macroService; private readonly IDataTypeService _dataTypeService; private readonly IFileService _fileService; private readonly ILocalizationService _localizationService; @@ -38,6 +40,7 @@ namespace Umbraco.Core.Services public PackagingService(IContentService contentService, IContentTypeService contentTypeService, IMediaService mediaService, + IMacroService macroService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, @@ -47,6 +50,7 @@ namespace Umbraco.Core.Services _contentService = contentService; _contentTypeService = contentTypeService; _mediaService = mediaService; + _macroService = macroService; _dataTypeService = dataTypeService; _fileService = fileService; _localizationService = localizationService; @@ -67,15 +71,15 @@ namespace Umbraco.Core.Services /// containing the xml representation of the Content object public XElement Export(IContent content, bool deep = false, bool raiseEvents = true) { - if (raiseEvents) - { - if (ExportingContent.IsRaisedEventCancelled(new SaveEventArgs(content), this)) - return default(XElement); - } - //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); var nodeName = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema ? "node" : content.ContentType.Alias.ToSafeAliasWithForcingCheck(); + if (raiseEvents) + { + if (ExportingContent.IsRaisedEventCancelled(new ExportEventArgs(content, nodeName), this)) + return new XElement(nodeName); + } + var xml = Export(content, nodeName); xml.Add(new XAttribute("nodeType", content.ContentType.Id)); xml.Add(new XAttribute("creatorName", content.GetCreatorProfile().Name)); @@ -92,7 +96,7 @@ namespace Umbraco.Core.Services } if(raiseEvents) - ContentExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedContent.RaiseEvent(new ExportEventArgs(content, xml, false), this); return xml; } @@ -110,6 +114,7 @@ namespace Umbraco.Core.Services var xml = new XElement(nodeName, new XAttribute("id", contentBase.Id), + new XAttribute("key", contentBase.Key.ToString("D")), new XAttribute("parentID", contentBase.Level > 1 ? contentBase.ParentId : -1), new XAttribute("level", contentBase.Level), new XAttribute("creatorID", contentBase.CreatorId), @@ -163,7 +168,7 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (ImportingContent.IsRaisedEventCancelled(new SaveEventArgs(element), this)) + if (ImportingContent.IsRaisedEventCancelled(new ImportEventArgs(element), this)) return Enumerable.Empty(); } @@ -180,7 +185,7 @@ namespace Umbraco.Core.Services _contentService.Save(contents, userId); if(raiseEvents) - ContentImported.RaiseEvent(new SaveEventArgs(contents, false), this); + ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this); return contents; } @@ -194,7 +199,7 @@ namespace Umbraco.Core.Services _contentService.Save(contents, userId); if(raiseEvents) - ContentImported.RaiseEvent(new SaveEventArgs(contents, false), this); + ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this); return contents; } @@ -299,7 +304,7 @@ namespace Umbraco.Core.Services if (propertyType != null && propertyType.PropertyEditorAlias == Constants.PropertyEditors.CheckBoxListAlias) { var database = ApplicationContext.Current.DatabaseContext.Database; - var dtos = database.Fetch("WHERE datatypeNo" + "deId = @Id", new { Id = propertyType.DataTypeDefinitionId }); + var dtos = database.Fetch("WHERE datatypeNodeId = @Id", new { Id = propertyType.DataTypeDefinitionId }); var propertyValueList = new List(); foreach (var preValue in propertyValue.Split(',')) @@ -331,8 +336,8 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (ExportingContentType.IsRaisedEventCancelled(new SaveEventArgs(contentType), this)) - return default(XElement); + if (ExportingContentType.IsRaisedEventCancelled(new ExportEventArgs(contentType, "DocumentType"), this)) + return new XElement("DocumentType"); } var info = new XElement("Info", @@ -397,7 +402,7 @@ namespace Umbraco.Core.Services tabs); if (raiseEvents) - ContentTypeExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedContentType.RaiseEvent(new ExportEventArgs(contentType, xml, false), this); return xml; } @@ -426,7 +431,7 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (ImportingContentType.IsRaisedEventCancelled(new SaveEventArgs(element), this)) + if (ImportingContentType.IsRaisedEventCancelled(new ImportEventArgs(element), this)) return Enumerable.Empty(); } @@ -477,7 +482,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - ContentTypeImported.RaiseEvent(new SaveEventArgs(list, false), this); + ImportedContentType.RaiseEvent(new ImportEventArgs(list, element, false), this); return list; } @@ -722,6 +727,11 @@ namespace Umbraco.Core.Services return contentType; } + /// + /// Used during Content import to ensure that the ContentType of a content item exists + /// + /// + /// private IContentType FindContentTypeByAlias(string contentTypeAlias) { using (var repository = _repositoryFactory.CreateContentTypeRepository(_uowProvider.GetUnitOfWork())) @@ -774,12 +784,11 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (ExportingDataType.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) - return default(XElement); + if (ExportingDataType.IsRaisedEventCancelled(new ExportEventArgs(dataTypeDefinition, "DataType"), this)) + return new XElement("DataType"); } var prevalues = new XElement("PreValues"); - var prevalueList = _dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeDefinition.Id) .FormatAsDictionary(); @@ -803,7 +812,7 @@ namespace Umbraco.Core.Services xml.Add(new XAttribute("DatabaseType", dataTypeDefinition.DatabaseType.ToString())); if (raiseEvents) - DataTypeExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedDataType.RaiseEvent(new ExportEventArgs(dataTypeDefinition, xml, false), this); return xml; } @@ -819,7 +828,7 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (ImportingDataType.IsRaisedEventCancelled(new SaveEventArgs(element), this)) + if (ImportingDataType.IsRaisedEventCancelled(new ImportEventArgs(element), this)) return Enumerable.Empty(); } @@ -887,7 +896,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - DataTypeImported.RaiseEvent(new SaveEventArgs(list, false), this); + ImportedDataType.RaiseEvent(new ImportEventArgs(list, element, false), this); return list; } @@ -950,8 +959,8 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (ExportingDictionaryItem.IsRaisedEventCancelled(new SaveEventArgs(dictionaryItem), this)) - return default(XElement); + if (ExportingDictionaryItem.IsRaisedEventCancelled(new ExportEventArgs(dictionaryItem, "DictionaryItem"), this)) + return new XElement("DictionaryItem"); } var xml = new XElement("DictionaryItem", new XAttribute("Key", dictionaryItem.ItemKey)); @@ -973,7 +982,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - DictionaryItemExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedDictionaryItem.RaiseEvent(new ExportEventArgs(dictionaryItem, xml, false), this); return xml; } @@ -988,7 +997,7 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (ImportingDictionaryItem.IsRaisedEventCancelled(new SaveEventArgs(dictionaryItemElementList), this)) + if (ImportingDictionaryItem.IsRaisedEventCancelled(new ImportEventArgs(dictionaryItemElementList), this)) return Enumerable.Empty(); } @@ -1000,15 +1009,15 @@ namespace Umbraco.Core.Services { var items = new List(); foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem")) - items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages)); + items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, raiseEvents)); if (raiseEvents) - DictionaryItemImported.RaiseEvent(new SaveEventArgs(items, false), this); + ImportedDictionaryItem.RaiseEvent(new ImportEventArgs(items, dictionaryItemElementList, false), this); return items; } - private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages) + private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages, bool raiseEvents) { var items = new List(); @@ -1020,7 +1029,7 @@ namespace Umbraco.Core.Services dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages); _localizationService.Save(dictionaryItem); items.Add(dictionaryItem); - items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages)); + items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, raiseEvents)); return items; } @@ -1096,8 +1105,8 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (ExportingLanguage.IsRaisedEventCancelled(new SaveEventArgs(language), this)) - return default(XElement); + if (ExportingLanguage.IsRaisedEventCancelled(new ExportEventArgs(language, "Language"), this)) + return new XElement("Language"); } var xml = new XElement("Language", @@ -1106,7 +1115,7 @@ namespace Umbraco.Core.Services new XAttribute("FriendlyName", language.CultureName)); if (raiseEvents) - LanguageExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedLanguage.RaiseEvent(new ExportEventArgs(language, xml, false), this); return xml; } @@ -1115,13 +1124,14 @@ namespace Umbraco.Core.Services /// Imports and saves the 'Languages' part of a package xml as a list of /// /// Xml to import + /// Optional id of the User performing the operation /// Optional parameter indicating whether or not to raise events /// An enumerable list of generated languages - public IEnumerable ImportLanguages(XElement languageElementList, bool raiseEvents = true) + public IEnumerable ImportLanguages(XElement languageElementList, int userId = 0, bool raiseEvents = true) { if (raiseEvents) { - if (ImportingLanguage.IsRaisedEventCancelled(new SaveEventArgs(languageElementList), this)) + if (ImportingLanguage.IsRaisedEventCancelled(new ImportEventArgs(languageElementList), this)) return Enumerable.Empty(); } @@ -1142,7 +1152,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - LanguageImported.RaiseEvent(new SaveEventArgs(list, false), this); + ImportedLanguage.RaiseEvent(new ImportEventArgs(list, languageElementList, false), this); return list; } @@ -1150,6 +1160,171 @@ namespace Umbraco.Core.Services #endregion #region Macros + + /// + /// Imports and saves the 'Macros' part of a package xml as a list of + /// + /// Xml to import + /// Optional id of the User performing the operation + /// Optional parameter indicating whether or not to raise events + /// + public IEnumerable ImportMacros(XElement element, int userId = 0, bool raiseEvents = true) + { + if (raiseEvents) + { + if (ImportingMacro.IsRaisedEventCancelled(new ImportEventArgs(element), this)) + return Enumerable.Empty(); + } + + var name = element.Name.LocalName; + if (name.Equals("Macros") == false && name.Equals("macro") == false) + { + throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'Macros' for multiple imports or 'macro' for a single import."); + } + + var macroElements = name.Equals("Macros") + ? (from doc in element.Elements("macro") select doc).ToList() + : new List { element }; + + var macros = macroElements.Select(ParseMacroElement).ToList(); + + foreach (var macro in macros) + { + _macroService.Save(macro, userId); + } + + if (raiseEvents) + ImportedMacro.RaiseEvent(new ImportEventArgs(macros, element, false), this); + + return macros; + } + + private IMacro ParseMacroElement(XElement macroElement) + { + var macroName = macroElement.Element("name").Value; + var macroAlias = macroElement.Element("alias").Value; + var controlType = macroElement.Element("scriptType").Value; + var controlAssembly = macroElement.Element("scriptAssembly").Value; + var xsltPath = macroElement.Element("xslt").Value; + var scriptPath = macroElement.Element("scriptingFile").Value; + + //Following xml elements are treated as nullable properties + var useInEditorElement = macroElement.Element("useInEditor"); + var useInEditor = false; + if (useInEditorElement != null && string.IsNullOrEmpty(useInEditorElement.Value) == false) + { + useInEditor = bool.Parse(useInEditorElement.Value); + } + var cacheDurationElement = macroElement.Element("refreshRate"); + var cacheDuration = 0; + if (cacheDurationElement != null && string.IsNullOrEmpty(cacheDurationElement.Value) == false) + { + cacheDuration = int.Parse(cacheDurationElement.Value); + } + var cacheByMemberElement = macroElement.Element("cacheByMember"); + var cacheByMember = false; + if (cacheByMemberElement != null && string.IsNullOrEmpty(cacheByMemberElement.Value) == false) + { + cacheByMember = bool.Parse(cacheByMemberElement.Value); + } + var cacheByPageElement = macroElement.Element("cacheByPage"); + var cacheByPage = false; + if (cacheByPageElement != null && string.IsNullOrEmpty(cacheByPageElement.Value) == false) + { + cacheByPage = bool.Parse(cacheByPageElement.Value); + } + var dontRenderElement = macroElement.Element("dontRender"); + var dontRender = true; + if (dontRenderElement != null && string.IsNullOrEmpty(dontRenderElement.Value) == false) + { + dontRender = bool.Parse(dontRenderElement.Value); + } + + var macro = new Macro(macroAlias, macroName, controlType, controlAssembly, xsltPath, scriptPath, + cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); + + var properties = macroElement.Element("properties"); + if (properties != null) + { + int sortOrder = 0; + foreach (var property in properties.Elements()) + { + var propertyName = property.Attribute("name").Value; + var propertyAlias = property.Attribute("alias").Value; + var editorAlias = property.Attribute("propertyType").Value; + var sortOrderAttribute = property.Attribute("sortOrder"); + if (sortOrderAttribute != null) + { + sortOrder = int.Parse(sortOrderAttribute.Value); + } + + macro.Properties.Add(new MacroProperty(propertyAlias, propertyName, sortOrder, editorAlias)); + sortOrder++; + } + } + return macro; + } + + /// + /// Exports a list of items to xml as an + /// + /// Macros to export + /// Optional parameter indicating whether or not to raise events + /// containing the xml representation of the IMacro objects + public XElement Export(IEnumerable macros, bool raiseEvents = true) + { + var xml = new XElement("Macros"); + foreach (var item in macros) + { + xml.Add(Export(item, raiseEvents)); + } + return xml; + } + + /// + /// Exports a single item to xml as an + /// + /// Macro to export + /// Optional parameter indicating whether or not to raise events + /// containing the xml representation of the IMacro object + public XElement Export(IMacro macro, bool raiseEvents = true) + { + if (raiseEvents) + { + if (ExportingMacro.IsRaisedEventCancelled(new ExportEventArgs(macro, "macro"), this)) + return new XElement("macro"); + } + + var xml = new XElement("macro"); + xml.Add(new XElement("name", macro.Name)); + xml.Add(new XElement("alias", macro.Alias)); + xml.Add(new XElement("scriptType", macro.ControlType)); + xml.Add(new XElement("scriptAssembly", macro.ControlAssembly)); + xml.Add(new XElement("scriptingFile", macro.ScriptPath)); + xml.Add(new XElement("xslt", macro.XsltPath)); + xml.Add(new XElement("useInEditor", macro.UseInEditor.ToString())); + xml.Add(new XElement("dontRender", macro.DontRender.ToString())); + xml.Add(new XElement("refreshRate", macro.CacheDuration.ToString(CultureInfo.InvariantCulture))); + xml.Add(new XElement("cacheByMember", macro.CacheByMember.ToString())); + xml.Add(new XElement("cacheByPage", macro.CacheByPage.ToString())); + + var properties = new XElement("properties"); + foreach (var property in macro.Properties) + { + properties.Add(new XElement("property", + new XAttribute("name", property.Name), + new XAttribute("alias", property.Alias), + new XAttribute("sortOrder", property.SortOrder), + new XAttribute("propertyType", property.EditorAlias))); + } + xml.Add(properties); + + if (raiseEvents) + ExportedMacro.RaiseEvent(new ExportEventArgs(macro, xml, false), this); + + return xml; + } + #endregion #region Members @@ -1170,7 +1345,6 @@ namespace Umbraco.Core.Services xml.Add(new XAttribute("loginName", member.Username)); xml.Add(new XAttribute("email", member.Email)); - xml.Add(new XAttribute("key", member.Key)); return xml; } @@ -1188,15 +1362,15 @@ namespace Umbraco.Core.Services /// containing the xml representation of the Media object public XElement Export(IMedia media, bool deep = false, bool raiseEvents = true) { - if (raiseEvents) - { - if (ExportingMedia.IsRaisedEventCancelled(new SaveEventArgs(media), this)) - return default(XElement); - } - //nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias); var nodeName = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema ? "node" : media.ContentType.Alias.ToSafeAliasWithForcingCheck(); + if (raiseEvents) + { + if (ExportingMedia.IsRaisedEventCancelled(new ExportEventArgs(media, nodeName), this)) + return new XElement(nodeName); + } + var xml = Export(media, nodeName); xml.Add(new XAttribute("nodeType", media.ContentType.Id)); xml.Add(new XAttribute("writerName", media.GetCreatorProfile().Name)); @@ -1213,7 +1387,7 @@ namespace Umbraco.Core.Services } if(raiseEvents) - MediaExported.RaiseEvent(new SaveEventArgs(xml, false), this); + ExportedMedia.RaiseEvent(new ExportEventArgs(media, xml, false), this); return xml; } @@ -1321,7 +1495,7 @@ namespace Umbraco.Core.Services { if (raiseEvents) { - if (ImportingTemplate.IsRaisedEventCancelled(new SaveEventArgs(element), this)) + if (ImportingTemplate.IsRaisedEventCancelled(new ImportEventArgs(element), this)) return Enumerable.Empty(); } @@ -1394,7 +1568,7 @@ namespace Umbraco.Core.Services _fileService.SaveTemplate(templates, userId); if(raiseEvents) - TemplateImported.RaiseEvent(new SaveEventArgs(templates, false), this); + ImportedTemplate.RaiseEvent(new ImportEventArgs(templates, element, false), this); return templates; } @@ -1415,6 +1589,58 @@ namespace Umbraco.Core.Services return IOHelper.MapPath(SystemDirectories.Masterpages + "/" + alias.Replace(" ", "") + ".master"); } + /// + /// Exports a list of items to xml as an + /// + /// List of Templates to export + /// Optional parameter indicating whether or not to raise events + /// containing the xml representation of the ITemplate objects + public XElement Export(IEnumerable templates, bool raiseEvents = true) + { + var xml = new XElement("Templates"); + foreach (var item in templates) + { + xml.Add(Export(item, raiseEvents)); + } + return xml; + } + + /// + /// Exports a single item to xml as an + /// + /// Template to export + /// Optional parameter indicating whether or not to raise events + /// containing the xml representation of the ITemplate object + public XElement Export(ITemplate template, bool raiseEvents = true) + { + if (raiseEvents) + { + if (ExportingTemplate.IsRaisedEventCancelled(new ExportEventArgs(template, "Template"), this)) + return new XElement("Template"); + } + + var xml = new XElement("Template"); + xml.Add(new XElement("Name", template.Name)); + xml.Add(new XElement("Alias", template.Alias)); + xml.Add(new XElement("Design", new XCData(template.Content))); + + var concreteTemplate = template as Template; + if (concreteTemplate != null) + { + if (concreteTemplate.MasterTemplateId.IsValueCreated && + concreteTemplate.MasterTemplateId.Value != default(int)) + { + xml.Add(new XElement("Master", concreteTemplate.MasterTemplateId.ToString())); + xml.Add(new XElement("MasterAlias", concreteTemplate.MasterTemplateAlias)); + } + } + + if (raiseEvents) + ExportedTemplate.RaiseEvent(new ExportEventArgs(template, xml, false), this); + + return xml; + } + #endregion #region Stylesheets @@ -1424,122 +1650,152 @@ namespace Umbraco.Core.Services /// /// Occurs before Importing Content /// - public static event TypedEventHandler> ImportingContent; + public static event TypedEventHandler> ImportingContent; /// /// Occurs after Content is Imported and Saved /// - public static event TypedEventHandler> ContentImported; + public static event TypedEventHandler> ImportedContent; /// /// Occurs before Exporting Content /// - public static event TypedEventHandler> ExportingContent; + public static event TypedEventHandler> ExportingContent; /// /// Occurs after Content is Exported to Xml /// - public static event TypedEventHandler> ContentExported; + public static event TypedEventHandler> ExportedContent; /// /// Occurs before Exporting Media /// - public static event TypedEventHandler> ExportingMedia; + public static event TypedEventHandler> ExportingMedia; /// /// Occurs after Media is Exported to Xml /// - public static event TypedEventHandler> MediaExported; + public static event TypedEventHandler> ExportedMedia; /// /// Occurs before Importing ContentType /// - public static event TypedEventHandler> ImportingContentType; + public static event TypedEventHandler> ImportingContentType; /// /// Occurs after ContentType is Imported and Saved /// - public static event TypedEventHandler> ContentTypeImported; + public static event TypedEventHandler> ImportedContentType; /// /// Occurs before Exporting ContentType /// - public static event TypedEventHandler> ExportingContentType; + public static event TypedEventHandler> ExportingContentType; /// /// Occurs after ContentType is Exported to Xml /// - public static event TypedEventHandler> ContentTypeExported; + public static event TypedEventHandler> ExportedContentType; /// /// Occurs before Importing DataType /// - public static event TypedEventHandler> ImportingDataType; + public static event TypedEventHandler> ImportingDataType; /// /// Occurs after DataType is Imported and Saved /// - public static event TypedEventHandler> DataTypeImported; + public static event TypedEventHandler> ImportedDataType; /// /// Occurs before Exporting DataType /// - public static event TypedEventHandler> ExportingDataType; + public static event TypedEventHandler> ExportingDataType; /// /// Occurs after DataType is Exported to Xml /// - public static event TypedEventHandler> DataTypeExported; + public static event TypedEventHandler> ExportedDataType; /// /// Occurs before Importing DictionaryItem /// - public static event TypedEventHandler> ImportingDictionaryItem; + public static event TypedEventHandler> ImportingDictionaryItem; /// /// Occurs after DictionaryItem is Imported and Saved /// - public static event TypedEventHandler> DictionaryItemImported; + public static event TypedEventHandler> ImportedDictionaryItem; /// /// Occurs before Exporting DictionaryItem /// - public static event TypedEventHandler> ExportingDictionaryItem; + public static event TypedEventHandler> ExportingDictionaryItem; /// /// Occurs after DictionaryItem is Exported to Xml /// - public static event TypedEventHandler> DictionaryItemExported; + public static event TypedEventHandler> ExportedDictionaryItem; + + /// + /// Occurs before Importing Macro + /// + public static event TypedEventHandler> ImportingMacro; + + /// + /// Occurs after Macro is Imported and Saved + /// + public static event TypedEventHandler> ImportedMacro; + + /// + /// Occurs before Exporting Macro + /// + public static event TypedEventHandler> ExportingMacro; + + /// + /// Occurs after Macro is Exported to Xml + /// + public static event TypedEventHandler> ExportedMacro; /// /// Occurs before Importing Language /// - public static event TypedEventHandler> ImportingLanguage; + public static event TypedEventHandler> ImportingLanguage; /// /// Occurs after Language is Imported and Saved /// - public static event TypedEventHandler> LanguageImported; + public static event TypedEventHandler> ImportedLanguage; /// /// Occurs before Exporting Language /// - public static event TypedEventHandler> ExportingLanguage; + public static event TypedEventHandler> ExportingLanguage; /// /// Occurs after Language is Exported to Xml /// - public static event TypedEventHandler> LanguageExported; + public static event TypedEventHandler> ExportedLanguage; /// /// Occurs before Importing Template /// - public static event TypedEventHandler> ImportingTemplate; + public static event TypedEventHandler> ImportingTemplate; /// /// Occurs after Template is Imported and Saved /// - public static event TypedEventHandler> TemplateImported; + public static event TypedEventHandler> ImportedTemplate; + + /// + /// Occurs before Exporting Template + /// + public static event TypedEventHandler> ExportingTemplate; + + /// + /// Occurs after Template is Exported to Xml + /// + public static event TypedEventHandler> ExportedTemplate; #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/RelationService.cs b/src/Umbraco.Core/Services/RelationService.cs index 2f82af9fc9..8ae7d80745 100644 --- a/src/Umbraco.Core/Services/RelationService.cs +++ b/src/Umbraco.Core/Services/RelationService.cs @@ -337,7 +337,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationSaved.RaiseEvent(new SaveEventArgs(relation, false), this); + SavedRelation.RaiseEvent(new SaveEventArgs(relation, false), this); return relation; } @@ -365,7 +365,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationSaved.RaiseEvent(new SaveEventArgs(relation, false), this); + SavedRelation.RaiseEvent(new SaveEventArgs(relation, false), this); return relation; } @@ -428,7 +428,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationSaved.RaiseEvent(new SaveEventArgs(relation, false), this); + SavedRelation.RaiseEvent(new SaveEventArgs(relation, false), this); } /// @@ -447,7 +447,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationTypeSaved.RaiseEvent(new SaveEventArgs(relationType, false), this); + SavedRelationType.RaiseEvent(new SaveEventArgs(relationType, false), this); } /// @@ -466,7 +466,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationDeleted.RaiseEvent(new DeleteEventArgs(relation, false), this); + DeletedRelation.RaiseEvent(new DeleteEventArgs(relation, false), this); } /// @@ -485,7 +485,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationTypeDeleted.RaiseEvent(new DeleteEventArgs(relationType, false), this); + DeletedRelationType.RaiseEvent(new DeleteEventArgs(relationType, false), this); } /// @@ -508,7 +508,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RelationDeleted.RaiseEvent(new DeleteEventArgs(relations, false), this); + DeletedRelation.RaiseEvent(new DeleteEventArgs(relations, false), this); } #region Private Methods @@ -537,7 +537,7 @@ namespace Umbraco.Core.Services /// /// Occurs after a Relation is Deleted /// - public static event TypedEventHandler> RelationDeleted; + public static event TypedEventHandler> DeletedRelation; /// /// Occurs before Saving a Relation @@ -547,7 +547,7 @@ namespace Umbraco.Core.Services /// /// Occurs after a Relation is Saved /// - public static event TypedEventHandler> RelationSaved; + public static event TypedEventHandler> SavedRelation; /// /// Occurs before Deleting a RelationType @@ -557,7 +557,7 @@ namespace Umbraco.Core.Services /// /// Occurs after a RelationType is Deleted /// - public static event TypedEventHandler> RelationTypeDeleted; + public static event TypedEventHandler> DeletedRelationType; /// /// Occurs before Saving a RelationType @@ -567,7 +567,7 @@ namespace Umbraco.Core.Services /// /// Occurs after a RelationType is Saved /// - public static event TypedEventHandler> RelationTypeSaved; + public static event TypedEventHandler> SavedRelationType; #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index 4bd2f433ba..7b4a3317bb 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -29,6 +29,7 @@ namespace Umbraco.Core.Services private Lazy _sectionService; private Lazy _macroService; private Lazy _memberTypeService; + private Lazy _notificationService; /// /// public ctor - will generally just be used for unit testing @@ -101,6 +102,9 @@ namespace Umbraco.Core.Services var provider = dbUnitOfWorkProvider; var fileProvider = fileUnitOfWorkProvider; + if (_notificationService == null) + _notificationService = new Lazy(() => new NotificationService(provider, _userService.Value, _contentService.Value)); + if (_serverRegistrationService == null) _serverRegistrationService = new Lazy(() => new ServerRegistrationService(provider, repositoryFactory.Value)); @@ -129,7 +133,7 @@ namespace Umbraco.Core.Services _localizationService = new Lazy(() => new LocalizationService(provider, repositoryFactory.Value)); if (_packagingService == null) - _packagingService = new Lazy(() => new PackagingService(_contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value, _fileService.Value, _localizationService.Value, repositoryFactory.Value, provider)); + _packagingService = new Lazy(() => new PackagingService(_contentService.Value, _contentTypeService.Value, _mediaService.Value, _macroService.Value, _dataTypeService.Value, _fileService.Value, _localizationService.Value, repositoryFactory.Value, provider)); if (_entityService == null) _entityService = new Lazy(() => new EntityService(provider, repositoryFactory.Value, _contentService.Value, _contentTypeService.Value, _mediaService.Value, _dataTypeService.Value)); @@ -153,6 +157,14 @@ namespace Umbraco.Core.Services _tagService = new Lazy(() => new TagService(provider, repositoryFactory.Value)); } + /// + /// Gets the + /// + internal INotificationService NotificationService + { + get { return _notificationService.Value; } + } + /// /// Gets the /// diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 89f2f9f7de..efa4812055 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -152,7 +152,7 @@ namespace Umbraco.Core.Services { using (var repository = _repositoryFactory.CreateUserRepository(_uowProvider.GetUnitOfWork())) { - var query = Query.Builder.Where(x => x.Username == login); + var query = Query.Builder.Where(x => x.Username.Equals(login)); return repository.GetByQuery(query).FirstOrDefault(); } } diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 4847df3fc4..0b0c76f620 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -1084,7 +1084,7 @@ namespace Umbraco.Core var cases2 = cases.ToCleanStringType() & CleanStringType.CaseMask; return legacy != null ? legacy.LegacyConvertStringCase(phrase, cases2) - : helper.CleanString(phrase, CleanStringType.Ascii | CleanStringType.Alias | cases2); + : helper.CleanString(phrase, CleanStringType.Ascii | CleanStringType.ConvertCase | cases2); } // the new methods to clean a string (to alias, url segment...) diff --git a/src/Umbraco.Core/Strings/CleanStringType.cs b/src/Umbraco.Core/Strings/CleanStringType.cs index f681c42d4a..4c53be4cb8 100644 --- a/src/Umbraco.Core/Strings/CleanStringType.cs +++ b/src/Umbraco.Core/Strings/CleanStringType.cs @@ -14,25 +14,6 @@ namespace Umbraco.Core.Strings // note: you have 32 bits at your disposal // 0xffffffff - - // masks - - /// - /// Flag mask for casing. - /// - CaseMask = 0x3f, // 0xff - 8 possible values - - /// - /// Flag mask for encoding. - /// - CodeMask = 0x700, // 0xff00 - 8 possible values - - /// - /// Flag mask for role. - /// - RoleMask = 0x070000, // 0xff0000 - 8 possible values - - // no value /// @@ -43,6 +24,11 @@ namespace Umbraco.Core.Strings // casing values + /// + /// Flag mask for casing. + /// + CaseMask = PascalCase | CamelCase | Unchanged | LowerCase | UpperCase | UmbracoCase, + /// /// Pascal casing eg "PascalCase". /// @@ -78,6 +64,11 @@ namespace Umbraco.Core.Strings // encoding values + /// + /// Flag mask for encoding. + /// + CodeMask = Unicode | Utf8 | Ascii, + /// /// Unicode encoding. /// @@ -97,6 +88,11 @@ namespace Umbraco.Core.Strings // role values + /// + /// Flag mask for role. + /// + RoleMask = UrlSegment | Alias | FileName | ConvertCase, + /// /// Url role. /// @@ -110,6 +106,11 @@ namespace Umbraco.Core.Strings /// /// FileName role. /// - FileName = 0x040000 + FileName = 0x040000, + + /// + /// ConvertCase role. + /// + ConvertCase = 0x080000 } } diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index e153a436c0..92d82c9a6b 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -1,4 +1,8 @@ -using System; + +// debugging - define to write cleaning details & steps to console +#define WRTCONS + +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -142,7 +146,7 @@ namespace Umbraco.Core.Strings { PreFilter = ApplyUrlReplaceCharacters, IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', // letter, digit or underscore - StringType = CleanStringType.Utf8 | CleanStringType.LowerCase, + StringType = (UmbracoConfig.For.UmbracoSettings().RequestHandler.ConvertUrlsToAscii ? CleanStringType.Ascii : CleanStringType.Utf8) | CleanStringType.LowerCase, BreakTermsOnUpper = false, Separator = '-' }).WithConfig(CleanStringType.FileName, new Config @@ -160,6 +164,12 @@ namespace Umbraco.Core.Strings : (char.IsLetterOrDigit(c) || c == '_'), // letter, digit or underscore StringType = CleanStringType.Ascii | CleanStringType.UmbracoCase, BreakTermsOnUpper = false + }).WithConfig(CleanStringType.ConvertCase, new Config + { + PreFilter = null, + IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', // letter, digit or underscore + StringType = CleanStringType.Ascii, + BreakTermsOnUpper = true }); } @@ -487,6 +497,10 @@ function validateSafeAlias(id, value, immediate, callback) {{ if (culture == null) throw new ArgumentNullException("culture"); +#if WRTCONS + Console.WriteLine("STRING TYPE {0}", stringType); +#endif + // get config var config = GetConfig(stringType, culture); stringType = config.StringTypeExtend(stringType); @@ -554,6 +568,9 @@ function validateSafeAlias(id, value, immediate, callback) {{ var state = StateBreak; caseType &= CleanStringType.CaseMask; +#if WRTCONS + Console.WriteLine("CASE {0}", caseType); +#endif // if we apply global ToUpper or ToLower to text here // then we cannot break words on uppercase chars @@ -579,13 +596,13 @@ function validateSafeAlias(id, value, immediate, callback) {{ var isPair = char.IsSurrogate(c); if (isPair) throw new NotSupportedException("Surrogate pairs are not supported."); - - //Console.WriteLine("CHAR '{0}' {1} {2} - {3} - {4}/{5} {6}", - // c, - // isTerm ? "term" : "!term", isUpper ? "upper" : "!upper", - // state, - // i, ipos, leading ? "leading" : "!leading"); - +#if WRTCONS + Console.WriteLine("CHAR '{0}' {1} {2} - {3} - {4}/{5} {6}", + c, + isTerm ? "term" : "!term", isUpper ? "upper" : "!upper", + state, + i, ipos, leading ? "leading" : "!leading"); +#endif switch (state) { // within a break @@ -692,11 +709,12 @@ function validateSafeAlias(id, value, immediate, callback) {{ CleanStringType caseType, CultureInfo culture, bool isAcronym) { var term = input.Substring(ipos, len); - //Console.WriteLine("TERM \"{0}\" {1} {2}", - // term, - // isAcronym ? "acronym" : "word", - // caseType); - +#if WRTCONS + Console.WriteLine("TERM \"{0}\" {1} {2}", + term, + isAcronym ? "acronym" : "word", + caseType); +#endif if (isAcronym) { if ((caseType == CleanStringType.CamelCase && len <= 2 && opos > 0) || diff --git a/src/Umbraco.Core/Strings/Diff.cs b/src/Umbraco.Core/Strings/Diff.cs new file mode 100644 index 0000000000..ed381d4f6f --- /dev/null +++ b/src/Umbraco.Core/Strings/Diff.cs @@ -0,0 +1,510 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Umbraco.Core.Strings +{ + /// + /// This Class implements the Difference Algorithm published in + /// "An O(ND) Difference Algorithm and its Variations" by Eugene Myers + /// Algorithmica Vol. 1 No. 2, 1986, p 251. + /// + /// The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents + /// each line is converted into a (hash) number. See DiffText(). + /// + /// diff.cs: A port of the algorithm to C# + /// Copyright (c) by Matthias Hertel, http://www.mathertel.de + /// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx + /// + internal class Diff + { + /// Data on one input file being compared. + /// + internal class DiffData + { + + /// Number of elements (lines). + internal int Length; + + /// Buffer of numbers that will be compared. + internal int[] Data; + + /// + /// Array of booleans that flag for modified data. + /// This is the result of the diff. + /// This means deletedA in the first Data or inserted in the second Data. + /// + internal bool[] Modified; + + /// + /// Initialize the Diff-Data buffer. + /// + /// reference to the buffer + internal DiffData(int[] initData) + { + Data = initData; + Length = initData.Length; + Modified = new bool[Length + 2]; + } // DiffData + + } // class DiffData + + /// details of one difference. + public struct Item + { + /// Start Line number in Data A. + public int StartA; + /// Start Line number in Data B. + public int StartB; + + /// Number of changes in Data A. + public int DeletedA; + /// Number of changes in Data B. + public int InsertedB; + } // Item + + /// + /// Shortest Middle Snake Return Data + /// + private struct Smsrd + { + internal int X, Y; + // internal int u, v; // 2002.09.20: no need for 2 points + } + + /// + /// Find the difference in 2 texts, comparing by textlines. + /// + /// A-version of the text (usualy the old one) + /// B-version of the text (usualy the new one) + /// Returns a array of Items that describe the differences. + public static Item[] DiffText(string textA, string textB) + { + return (DiffText(textA, textB, false, false, false)); + } // DiffText + + /// + /// Find the difference in 2 texts, comparing by textlines. + /// This method uses the DiffInt internally by 1st converting the string into char codes + /// then uses the diff int method + /// + /// A-version of the text (usualy the old one) + /// B-version of the text (usualy the new one) + /// Returns a array of Items that describe the differences. + public static Item[] DiffText1(string textA, string textB) + { + return DiffInt(DiffCharCodes(textA, false), DiffCharCodes(textB, false)); + } + + + /// + /// Find the difference in 2 text documents, comparing by textlines. + /// The algorithm itself is comparing 2 arrays of numbers so when comparing 2 text documents + /// each line is converted into a (hash) number. This hash-value is computed by storing all + /// textlines into a common hashtable so i can find dublicates in there, and generating a + /// new number each time a new textline is inserted. + /// + /// A-version of the text (usualy the old one) + /// B-version of the text (usualy the new one) + /// When set to true, all leading and trailing whitespace characters are stripped out before the comparation is done. + /// When set to true, all whitespace characters are converted to a single space character before the comparation is done. + /// When set to true, all characters are converted to their lowercase equivivalence before the comparation is done. + /// Returns a array of Items that describe the differences. + public static Item[] DiffText(string textA, string textB, bool trimSpace, bool ignoreSpace, bool ignoreCase) + { + // prepare the input-text and convert to comparable numbers. + var h = new Hashtable(textA.Length + textB.Length); + + // The A-Version of the data (original data) to be compared. + var dataA = new DiffData(DiffCodes(textA, h, trimSpace, ignoreSpace, ignoreCase)); + + // The B-Version of the data (modified data) to be compared. + var dataB = new DiffData(DiffCodes(textB, h, trimSpace, ignoreSpace, ignoreCase)); + + h = null; // free up hashtable memory (maybe) + + var max = dataA.Length + dataB.Length + 1; + // vector for the (0,0) to (x,y) search + var downVector = new int[2 * max + 2]; + // vector for the (u,v) to (N,M) search + var upVector = new int[2 * max + 2]; + + Lcs(dataA, 0, dataA.Length, dataB, 0, dataB.Length, downVector, upVector); + + Optimize(dataA); + Optimize(dataB); + return CreateDiffs(dataA, dataB); + } // DiffText + + + /// + /// Diffs the char codes. + /// + /// A text. + /// if set to true [ignore case]. + /// + private static int[] DiffCharCodes(string aText, bool ignoreCase) + { + if (ignoreCase) + aText = aText.ToUpperInvariant(); + + var codes = new int[aText.Length]; + + for (int n = 0; n < aText.Length; n++) + codes[n] = (int)aText[n]; + + return (codes); + } // DiffCharCodes + + /// + /// If a sequence of modified lines starts with a line that contains the same content + /// as the line that appends the changes, the difference sequence is modified so that the + /// appended line and not the starting line is marked as modified. + /// This leads to more readable diff sequences when comparing text files. + /// + /// A Diff data buffer containing the identified changes. + private static void Optimize(DiffData data) + { + var startPos = 0; + while (startPos < data.Length) + { + while ((startPos < data.Length) && (data.Modified[startPos] == false)) + startPos++; + int endPos = startPos; + while ((endPos < data.Length) && (data.Modified[endPos] == true)) + endPos++; + + if ((endPos < data.Length) && (data.Data[startPos] == data.Data[endPos])) + { + data.Modified[startPos] = false; + data.Modified[endPos] = true; + } + else + { + startPos = endPos; + } // if + } // while + } // Optimize + + + /// + /// Find the difference in 2 arrays of integers. + /// + /// A-version of the numbers (usualy the old one) + /// B-version of the numbers (usualy the new one) + /// Returns a array of Items that describe the differences. + public static Item[] DiffInt(int[] arrayA, int[] arrayB) + { + // The A-Version of the data (original data) to be compared. + var dataA = new DiffData(arrayA); + + // The B-Version of the data (modified data) to be compared. + var dataB = new DiffData(arrayB); + + var max = dataA.Length + dataB.Length + 1; + // vector for the (0,0) to (x,y) search + var downVector = new int[2 * max + 2]; + // vector for the (u,v) to (N,M) search + var upVector = new int[2 * max + 2]; + + Lcs(dataA, 0, dataA.Length, dataB, 0, dataB.Length, downVector, upVector); + return CreateDiffs(dataA, dataB); + } // Diff + + + /// + /// This function converts all textlines of the text into unique numbers for every unique textline + /// so further work can work only with simple numbers. + /// + /// the input text + /// This extern initialized hashtable is used for storing all ever used textlines. + /// ignore leading and trailing space characters + /// + /// + /// a array of integers. + private static int[] DiffCodes(string aText, IDictionary h, bool trimSpace, bool ignoreSpace, bool ignoreCase) + { + // get all codes of the text + var lastUsedCode = h.Count; + + // strip off all cr, only use lf as textline separator. + aText = aText.Replace("\r", ""); + var lines = aText.Split('\n'); + + var codes = new int[lines.Length]; + + for (int i = 0; i < lines.Length; ++i) + { + string s = lines[i]; + if (trimSpace) + s = s.Trim(); + + if (ignoreSpace) + { + s = Regex.Replace(s, "\\s+", " "); // TODO: optimization: faster blank removal. + } + + if (ignoreCase) + s = s.ToLower(); + + object aCode = h[s]; + if (aCode == null) + { + lastUsedCode++; + h[s] = lastUsedCode; + codes[i] = lastUsedCode; + } + else + { + codes[i] = (int)aCode; + } // if + } // for + return (codes); + } // DiffCodes + + + /// + /// This is the algorithm to find the Shortest Middle Snake (SMS). + /// + /// sequence A + /// lower bound of the actual range in DataA + /// upper bound of the actual range in DataA (exclusive) + /// sequence B + /// lower bound of the actual range in DataB + /// upper bound of the actual range in DataB (exclusive) + /// a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons. + /// a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons. + /// a MiddleSnakeData record containing x,y and u,v + private static Smsrd Sms(DiffData dataA, int lowerA, int upperA, DiffData dataB, int lowerB, int upperB, int[] downVector, int[] upVector) + { + int max = dataA.Length + dataB.Length + 1; + + int downK = lowerA - lowerB; // the k-line to start the forward search + int upK = upperA - upperB; // the k-line to start the reverse search + + int delta = (upperA - lowerA) - (upperB - lowerB); + bool oddDelta = (delta & 1) != 0; + + // The vectors in the publication accepts negative indexes. the vectors implemented here are 0-based + // and are access using a specific offset: UpOffset UpVector and DownOffset for DownVektor + int downOffset = max - downK; + int upOffset = max - upK; + + int maxD = ((upperA - lowerA + upperB - lowerB) / 2) + 1; + + // Debug.Write(2, "SMS", String.Format("Search the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB)); + + // init vectors + downVector[downOffset + downK + 1] = lowerA; + upVector[upOffset + upK - 1] = upperA; + + for (int d = 0; d <= maxD; d++) + { + + // Extend the forward path. + Smsrd ret; + for (int k = downK - d; k <= downK + d; k += 2) + { + // Debug.Write(0, "SMS", "extend forward path " + k.ToString()); + + // find the only or better starting point + int x, y; + if (k == downK - d) + { + x = downVector[downOffset + k + 1]; // down + } + else + { + x = downVector[downOffset + k - 1] + 1; // a step to the right + if ((k < downK + d) && (downVector[downOffset + k + 1] >= x)) + x = downVector[downOffset + k + 1]; // down + } + y = x - k; + + // find the end of the furthest reaching forward D-path in diagonal k. + while ((x < upperA) && (y < upperB) && (dataA.Data[x] == dataB.Data[y])) + { + x++; y++; + } + downVector[downOffset + k] = x; + + // overlap ? + if (oddDelta && (upK - d < k) && (k < upK + d)) + { + if (upVector[upOffset + k] <= downVector[downOffset + k]) + { + ret.X = downVector[downOffset + k]; + ret.Y = downVector[downOffset + k] - k; + // ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points + // ret.v = UpVector[UpOffset + k] - k; + return (ret); + } // if + } // if + + } // for k + + // Extend the reverse path. + for (int k = upK - d; k <= upK + d; k += 2) + { + // Debug.Write(0, "SMS", "extend reverse path " + k.ToString()); + + // find the only or better starting point + int x, y; + if (k == upK + d) + { + x = upVector[upOffset + k - 1]; // up + } + else + { + x = upVector[upOffset + k + 1] - 1; // left + if ((k > upK - d) && (upVector[upOffset + k - 1] < x)) + x = upVector[upOffset + k - 1]; // up + } // if + y = x - k; + + while ((x > lowerA) && (y > lowerB) && (dataA.Data[x - 1] == dataB.Data[y - 1])) + { + x--; y--; // diagonal + } + upVector[upOffset + k] = x; + + // overlap ? + if (!oddDelta && (downK - d <= k) && (k <= downK + d)) + { + if (upVector[upOffset + k] <= downVector[downOffset + k]) + { + ret.X = downVector[downOffset + k]; + ret.Y = downVector[downOffset + k] - k; + // ret.u = UpVector[UpOffset + k]; // 2002.09.20: no need for 2 points + // ret.v = UpVector[UpOffset + k] - k; + return (ret); + } // if + } // if + + } // for k + + } // for D + + throw new ApplicationException("the algorithm should never come here."); + } // SMS + + + /// + /// This is the divide-and-conquer implementation of the longes common-subsequence (LCS) + /// algorithm. + /// The published algorithm passes recursively parts of the A and B sequences. + /// To avoid copying these arrays the lower and upper bounds are passed while the sequences stay constant. + /// + /// sequence A + /// lower bound of the actual range in DataA + /// upper bound of the actual range in DataA (exclusive) + /// sequence B + /// lower bound of the actual range in DataB + /// upper bound of the actual range in DataB (exclusive) + /// a vector for the (0,0) to (x,y) search. Passed as a parameter for speed reasons. + /// a vector for the (u,v) to (N,M) search. Passed as a parameter for speed reasons. + private static void Lcs(DiffData dataA, int lowerA, int upperA, DiffData dataB, int lowerB, int upperB, int[] downVector, int[] upVector) + { + // Debug.Write(2, "LCS", String.Format("Analyse the box: A[{0}-{1}] to B[{2}-{3}]", LowerA, UpperA, LowerB, UpperB)); + + // Fast walkthrough equal lines at the start + while (lowerA < upperA && lowerB < upperB && dataA.Data[lowerA] == dataB.Data[lowerB]) + { + lowerA++; lowerB++; + } + + // Fast walkthrough equal lines at the end + while (lowerA < upperA && lowerB < upperB && dataA.Data[upperA - 1] == dataB.Data[upperB - 1]) + { + --upperA; --upperB; + } + + if (lowerA == upperA) + { + // mark as inserted lines. + while (lowerB < upperB) + dataB.Modified[lowerB++] = true; + + } + else if (lowerB == upperB) + { + // mark as deleted lines. + while (lowerA < upperA) + dataA.Modified[lowerA++] = true; + + } + else + { + // Find the middle snakea and length of an optimal path for A and B + Smsrd smsrd = Sms(dataA, lowerA, upperA, dataB, lowerB, upperB, downVector, upVector); + // Debug.Write(2, "MiddleSnakeData", String.Format("{0},{1}", smsrd.x, smsrd.y)); + + // The path is from LowerX to (x,y) and (x,y) to UpperX + Lcs(dataA, lowerA, smsrd.X, dataB, lowerB, smsrd.Y, downVector, upVector); + Lcs(dataA, smsrd.X, upperA, dataB, smsrd.Y, upperB, downVector, upVector); // 2002.09.20: no need for 2 points + } + } // LCS() + + + /// Scan the tables of which lines are inserted and deleted, + /// producing an edit script in forward order. + /// + /// dynamic array + private static Item[] CreateDiffs(DiffData dataA, DiffData dataB) + { + ArrayList a = new ArrayList(); + Item aItem; + Item[] result; + + int lineA = 0; + int lineB = 0; + while (lineA < dataA.Length || lineB < dataB.Length) + { + if ((lineA < dataA.Length) && (!dataA.Modified[lineA]) + && (lineB < dataB.Length) && (!dataB.Modified[lineB])) + { + // equal lines + lineA++; + lineB++; + + } + else + { + // maybe deleted and/or inserted lines + int startA = lineA; + int startB = lineB; + + while (lineA < dataA.Length && (lineB >= dataB.Length || dataA.Modified[lineA])) + // while (LineA < DataA.Length && DataA.modified[LineA]) + lineA++; + + while (lineB < dataB.Length && (lineA >= dataA.Length || dataB.Modified[lineB])) + // while (LineB < DataB.Length && DataB.modified[LineB]) + lineB++; + + if ((startA < lineA) || (startB < lineB)) + { + // store a new difference-item + aItem = new Item(); + aItem.StartA = startA; + aItem.StartB = startB; + aItem.DeletedA = lineA - startA; + aItem.InsertedB = lineB - startB; + a.Add(aItem); + } // if + } // if + } // while + + result = new Item[a.Count]; + a.CopyTo(result); + + return (result); + } + + } // class Diff + + +} diff --git a/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs b/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs index 23ac4e3931..e08defdedd 100644 --- a/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs +++ b/src/Umbraco.Core/Strings/Utf8ToAsciiConverter.cs @@ -3316,6 +3316,243 @@ namespace Umbraco.Core.Strings output[opos++] = '~'; break; + // BEGIN CUSTOM TRANSLITERATION OF CYRILIC CHARS + + #region Cyrilic chars + + // russian uppercase "А Б В Г Д Е Ё Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я" + // russian lowercase "а б в г д е ё ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я" + + // notes + // read http://www.vesic.org/english/blog/c-sharp/transliteration-easy-way-microsoft-transliteration-utility/ + // should we look into MS Transliteration Utility (http://msdn.microsoft.com/en-US/goglobal/bb688104.aspx) + // also UnicodeSharpFork https://bitbucket.org/DimaStefantsov/unidecodesharpfork + // also Transliterator http://transliterator.codeplex.com/ + // + // in any case it would be good to generate all those "case" statements instead of writing them by hand + // time for a T4 template? + // also we should support extensibility so ppl can register more cases in external code + + // fixme + // transliterates Анастасия as Anastasiya, and not Anastasia + // Ольга --> Ol'ga, Татьяна --> Tat'yana -- that's bad (?) + // Note: should ä (german umlaut) become a or ae ? + + case '\u0410': // А + output[opos++] = 'A'; + break; + case '\u0430': // а + output[opos++] = 'a'; + break; + case '\u0411': // Б + output[opos++] = 'B'; + break; + case '\u0431': // б + output[opos++] = 'b'; + break; + case '\u0412': // В + output[opos++] = 'V'; + break; + case '\u0432': // в + output[opos++] = 'v'; + break; + case '\u0413': // Г + output[opos++] = 'G'; + break; + case '\u0433': // г + output[opos++] = 'g'; + break; + case '\u0414': // Д + output[opos++] = 'D'; + break; + case '\u0434': // д + output[opos++] = 'd'; + break; + case '\u0415': // Е + output[opos++] = 'E'; + break; + case '\u0435': // е + output[opos++] = 'e'; + break; + case '\u0401': // Ё + output[opos++] = 'E'; // alt. Yo + break; + case '\u0451': // ё + output[opos++] = 'e'; // alt. yo + break; + case '\u0416': // Ж + output[opos++] = 'Z'; + output[opos++] = 'h'; + break; + case '\u0436': // ж + output[opos++] = 'z'; + output[opos++] = 'h'; + break; + case '\u0417': // З + output[opos++] = 'Z'; + break; + case '\u0437': // з + output[opos++] = 'z'; + break; + case '\u0418': // И + output[opos++] = 'I'; + break; + case '\u0438': // и + output[opos++] = 'i'; + break; + case '\u0419': // Й + output[opos++] = 'I'; // alt. Y, J + break; + case '\u0439': // й + output[opos++] = 'i'; // alt. y, j + break; + case '\u041A': // К + output[opos++] = 'K'; + break; + case '\u043A': // к + output[opos++] = 'k'; + break; + case '\u041B': // Л + output[opos++] = 'L'; + break; + case '\u043B': // л + output[opos++] = 'l'; + break; + case '\u041C': // М + output[opos++] = 'M'; + break; + case '\u043C': // м + output[opos++] = 'm'; + break; + case '\u041D': // Н + output[opos++] = 'N'; + break; + case '\u043D': // н + output[opos++] = 'n'; + break; + case '\u041E': // О + output[opos++] = 'O'; + break; + case '\u043E': // о + output[opos++] = 'o'; + break; + case '\u041F': // П + output[opos++] = 'P'; + break; + case '\u043F': // п + output[opos++] = 'p'; + break; + case '\u0420': // Р + output[opos++] = 'R'; + break; + case '\u0440': // р + output[opos++] = 'r'; + break; + case '\u0421': // С + output[opos++] = 'S'; + break; + case '\u0441': // с + output[opos++] = 's'; + break; + case '\u0422': // Т + output[opos++] = 'T'; + break; + case '\u0442': // т + output[opos++] = 't'; + break; + case '\u0423': // У + output[opos++] = 'U'; + break; + case '\u0443': // у + output[opos++] = 'u'; + break; + case '\u0424': // Ф + output[opos++] = 'F'; + break; + case '\u0444': // ф + output[opos++] = 'f'; + break; + case '\u0425': // Х + output[opos++] = 'K'; // alt. X + output[opos++] = 'h'; + break; + case '\u0445': // х + output[opos++] = 'k'; // alt. x + output[opos++] = 'h'; + break; + case '\u0426': // Ц + output[opos++] = 'F'; + break; + case '\u0446': // ц + output[opos++] = 'f'; + break; + case '\u0427': // Ч + output[opos++] = 'C'; // alt. Ts, C + output[opos++] = 'h'; + break; + case '\u0447': // ч + output[opos++] = 'c'; // alt. ts, c + output[opos++] = 'h'; + break; + case '\u0428': // Ш + output[opos++] = 'S'; // alt. Ch, S + output[opos++] = 'h'; + break; + case '\u0448': // ш + output[opos++] = 's'; // alt. ch, s + output[opos++] = 'h'; + break; + case '\u0429': // Щ + output[opos++] = 'S'; // alt. Shch, Sc + output[opos++] = 'h'; + break; + case '\u0449': // щ + output[opos++] = 's'; // alt. shch, sc + output[opos++] = 'h'; + break; + case '\u042A': // Ъ + output[opos++] = '"'; // " + break; + case '\u044A': // ъ + output[opos++] = '"'; // " + break; + case '\u042B': // Ы + output[opos++] = 'Y'; + break; + case '\u044B': // ы + output[opos++] = 'y'; + break; + case '\u042C': // Ь + output[opos++] = '\''; // ' + break; + case '\u044C': // ь + output[opos++] = '\''; // ' + break; + case '\u042D': // Э + output[opos++] = 'E'; + break; + case '\u044D': // э + output[opos++] = 'e'; + break; + case '\u042E': // Ю + output[opos++] = 'Y'; // alt. Ju + output[opos++] = 'u'; + break; + case '\u044E': // ю + output[opos++] = 'y'; // alt. ju + output[opos++] = 'u'; + break; + case '\u042F': // Я + output[opos++] = 'Y'; // alt. Ja + output[opos++] = 'a'; + break; + case '\u044F': // я + output[opos++] = 'y'; // alt. ja + output[opos++] = 'a'; + break; + + #endregion + // BEGIN EXTRA /* case '£': diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ad49b46b54..18cd086cbe 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -287,6 +287,8 @@ + + @@ -339,6 +341,7 @@ + @@ -952,6 +955,7 @@ + @@ -1011,6 +1015,7 @@ + @@ -1059,6 +1064,9 @@ Constants.cs + + Constants.cs + diff --git a/src/Umbraco.Core/UriExtensions.cs b/src/Umbraco.Core/UriExtensions.cs index a3ce4d0c0f..426d161f58 100644 --- a/src/Umbraco.Core/UriExtensions.cs +++ b/src/Umbraco.Core/UriExtensions.cs @@ -14,24 +14,93 @@ namespace Umbraco.Core /// public static class UriExtensions { - /// /// Checks if the current uri is a back office request /// /// + /// + /// The current application path or VirtualPath + /// /// - internal static bool IsBackOfficeRequest(this Uri url) + /// + /// There are some special routes we need to check to properly determine this: + /// + /// If any route has an extension in the path like .aspx = back office + /// + /// These are def back office: + /// /Umbraco/RestServices = back office + /// /Umbraco/BackOffice = back office + /// If it's not any of the above, and there's no extension then we cannot determine if it's back office or front-end + /// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice + /// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute. + /// + /// These are def front-end: + /// /Umbraco/Surface = front-end + /// /Umbraco/Api = front-end + /// But if we've got this far we'll just have to assume it's front-end anyways. + /// + /// + internal static bool IsBackOfficeRequest(this Uri url, string applicationPath) { - - var authority = url.GetLeftPart(UriPartial.Authority); - var afterAuthority = url.GetLeftPart(UriPartial.Query) - .TrimStart(authority) - .TrimStart("/"); + applicationPath = applicationPath ?? string.Empty; + var fullUrlPath = url.AbsolutePath.TrimStart(new[] {'/'}); + var appPath = applicationPath.TrimStart(new[] {'/'}); + var urlPath = fullUrlPath.TrimStart(appPath).EnsureStartsWith('/'); - //check if this is in the umbraco back office - return afterAuthority.InvariantStartsWith(GlobalSettings.Path.TrimStart("/")); + var isUmbracoPath = urlPath.InvariantStartsWith(GlobalSettings.Path.EnsureStartsWith('/')); + //if not, then def not back office + if (isUmbracoPath == false) return false; + + //if its the normal /umbraco path + if (urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea) + || urlPath.InvariantEquals("/" + GlobalSettings.UmbracoMvcArea + "/")) + { + return true; + } + + //check for a file extension + var extension = Path.GetExtension(url.LocalPath); + //has an extension, def back office + if (extension.IsNullOrWhiteSpace() == false) return true; + //check for special case asp.net calls like: + // /umbraco/webservices/legacyAjaxCalls.asmx/js which will return a null file extension but are still considered extension'd requests + if (urlPath.InvariantContains(".asmx/") + || urlPath.InvariantContains(".aspx/") + || urlPath.InvariantContains(".ashx/") + || urlPath.InvariantContains(".axd/") + || urlPath.InvariantContains(".svc/")) + { + return true; + } + + //check for special back office paths + if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/BackOffice/") + || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/RestServices/")) + { + return true; + } + + //check for special front-end paths + if (urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/Surface/") + || urlPath.InvariantStartsWith("/" + GlobalSettings.UmbracoMvcArea + "/Api/")) + { + return false; + } + + //if its none of the above, we will have to try to detect if it's a PluginController route, we can detect this by + // checking how many parts the route has, for example, all PluginController routes will be routed like + // Umbraco/MYPLUGINAREA/MYCONTROLLERNAME/{action}/{id} + // so if the path contains at a minimum 3 parts: Umbraco + MYPLUGINAREA + MYCONTROLLERNAME then we will have to assume it is a + // plugin controller for the front-end. + if (urlPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries).Length >= 3) + { + return false; + } + + //if its anything else we can assume it's back office + return true; } /// diff --git a/src/Umbraco.Tests/CoreStrings/DefaultShortStringHelperTests.cs b/src/Umbraco.Tests/CoreStrings/DefaultShortStringHelperTests.cs index 5beac13d0e..64a432e815 100644 --- a/src/Umbraco.Tests/CoreStrings/DefaultShortStringHelperTests.cs +++ b/src/Umbraco.Tests/CoreStrings/DefaultShortStringHelperTests.cs @@ -6,8 +6,12 @@ using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; +using Moq; using NUnit.Framework; +using umbraco; using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Strings; using Umbraco.Core.ObjectResolution; using Umbraco.Tests.TestHelpers; @@ -65,6 +69,13 @@ namespace Umbraco.Tests.CoreStrings PreFilter = WhiteQuotes, IsTerm = (c, leading) => leading ? char.IsLetter(c) : char.IsLetterOrDigit(c), StringType = CleanStringType.UmbracoCase | CleanStringType.Ascii + }) + .WithConfig(CleanStringType.ConvertCase, new DefaultShortStringHelper.Config + { + PreFilter = null, + IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', // letter, digit or underscore + StringType = CleanStringType.Ascii, + BreakTermsOnUpper = true }); ShortStringHelperResolver.Reset(); @@ -98,6 +109,47 @@ namespace Umbraco.Tests.CoreStrings return s; } + [Test] + public void U4_4055_4056() + { + var settings = SettingsForTests.GenerateMockSettings(); + var contentMock = Mock.Get(settings.RequestHandler); + contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); + contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); + SettingsForTests.ConfigureSettings(settings); + + const string input = "publishedVersion"; + + Assert.AreEqual("PublishedVersion", input.ConvertCase(StringAliasCaseType.PascalCase)); // obsolete, use the one below + Assert.AreEqual("PublishedVersion", input.ToCleanString(CleanStringType.ConvertCase | CleanStringType.PascalCase | CleanStringType.Ascii)); // role, case and code + } + + [Test] + public void U4_4056() + { + var settings = SettingsForTests.GenerateMockSettings(); + var contentMock = Mock.Get(settings.RequestHandler); + contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); + contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); + SettingsForTests.ConfigureSettings(settings); + + const string input = "ÆØÅ and æøå and 中文测试 and אודות האתר and größer БбДдЖж page"; + + var helper = new DefaultShortStringHelper().WithDefaultConfig(); // unicode + var output = helper.CleanStringForUrlSegment(input); + Assert.AreEqual("æøå-and-æøå-and-中文测试-and-אודות-האתר-and-größer-ббдджж-page", output); + + helper = new DefaultShortStringHelper() + .WithConfig(CleanStringType.UrlSegment, new DefaultShortStringHelper.Config + { + IsTerm = (c, leading) => char.IsLetterOrDigit(c) || c == '_', + StringType = CleanStringType.LowerCase | CleanStringType.Ascii, // ascii + Separator = '-' + }); + output = helper.CleanStringForUrlSegment(input); + Assert.AreEqual("aeoa-and-aeoa-and-and-and-grosser-bbddzhzh-page", output); + } + [Test] public void CleanStringUnderscoreInTerm() { @@ -361,6 +413,12 @@ namespace Umbraco.Tests.CoreStrings [Test] public void CleanStringDefaultConfig() { + var settings = SettingsForTests.GenerateMockSettings(); + var contentMock = Mock.Get(settings.RequestHandler); + contentMock.Setup(x => x.CharCollection).Returns(Enumerable.Empty()); + contentMock.Setup(x => x.ConvertUrlsToAscii).Returns(false); + SettingsForTests.ConfigureSettings(settings); + var helper = new DefaultShortStringHelper().WithDefaultConfig(); const string input = "0123 中文测试 中文测试 léger ZÔRG (2) a?? *x"; diff --git a/src/Umbraco.Tests/Macros/MacroParserTests.cs b/src/Umbraco.Tests/Macros/MacroParserTests.cs index b4c17fec18..a8e171df3e 100644 --- a/src/Umbraco.Tests/Macros/MacroParserTests.cs +++ b/src/Umbraco.Tests/Macros/MacroParserTests.cs @@ -16,6 +16,29 @@ namespace Umbraco.Tests.Macros Assert.AreEqual(@"

hello world

", content); } + [Test] + public void Format_RTE_Data_For_Editor_With_Non_AlphaNumeric_Char_In_Alias() + { + var content = @"

asdfasdf

+

asdfsadf

+ +

asdfasdf

"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); + +// Assert.AreEqual(@"

asdfasdf

+//

asdfsadf

+//
+// +//Macro alias: Map
+//

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + Assert.AreEqual(@"

asdfasdf

+

asdfsadf

+
+ +Macro alias: My.Map.isCool
+

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + } + [Test] public void Format_RTE_Data_For_Editor() { @@ -25,9 +48,16 @@ namespace Umbraco.Tests.Macros

asdfasdf

"; var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary(){{"test1", "value1"},{"test2", "value2"}}); +// Assert.AreEqual(@"

asdfasdf

+//

asdfsadf

+//
+// +//Macro alias: Map
+//

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + Assert.AreEqual(@"

asdfasdf

asdfsadf

-
+
Macro alias: Map

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); @@ -42,9 +72,15 @@ namespace Umbraco.Tests.Macros

asdfasdf

"; var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); +// Assert.AreEqual(@"

asdfasdf

+//

asdfsadf

+//
+// +//Macro alias: Map
+//

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); Assert.AreEqual(@"

asdfasdf

asdfsadf

-
+
Macro alias: Map

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); @@ -59,9 +95,15 @@ namespace Umbraco.Tests.Macros

asdfasdf

"; var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); +// Assert.AreEqual(@"

asdfasdf

+//

asdfsadf

+//
+// +//Macro alias: Map
+//

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); Assert.AreEqual(@"

asdfasdf

asdfsadf

-
+
Macro alias: Map

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); @@ -76,9 +118,15 @@ namespace Umbraco.Tests.Macros

asdfasdf

"; var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); +// Assert.AreEqual(@"

asdfasdf

+//

asdfsadf

+//
+// +//Macro alias: Map
+//

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); Assert.AreEqual(@"

asdfasdf

asdfsadf

-
+
Macro alias: Map

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); @@ -93,9 +141,15 @@ namespace Umbraco.Tests.Macros

asdfasdf

"; var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary() { { "test1", "value1" }, { "test2", "value2" } }); +// Assert.AreEqual(@"

asdfasdf

+//

asdfsadf

+//
+// +//Macro alias: Map
+//

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); Assert.AreEqual(@"

asdfasdf

asdfsadf

-
+
Macro alias: Map

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); @@ -104,10 +158,26 @@ namespace Umbraco.Tests.Macros [Test] public void Format_RTE_Data_For_Persistence() { +// var content = @" +// +//

asdfasdf

+//
+// +//asdfasdf +//asdfas +//asdfasdfasdf +//

asdfasdf

+//
+//asdfdasf +//
+//asdfsdf +//
+// +//"; var content = @"

asdfasdf

-
+
asdfasdf asdfas @@ -170,7 +240,15 @@ asdfsdf [Test] public void Format_RTE_Data_For_Persistence_Custom_Single_Entry() { - var content = @"
+// var content = @"
+//
+//
+//

null

+//
+//
1089
+//
+//
"; + var content = @"

null

diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index f6d31a2414..07d6837502 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -38,6 +38,7 @@ namespace Umbraco.Tests new Mock().Object, new Mock().Object, new Mock().Object, + new Mock().Object, new Mock().Object, new Mock().Object, new Mock().Object, @@ -77,6 +78,7 @@ namespace Umbraco.Tests new Mock().Object, new Mock().Object, new Mock().Object, + new Mock().Object, new Mock().Object, new Mock().Object, new Mock().Object, diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs index a53edac3a4..17b092c16b 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs @@ -234,8 +234,7 @@ namespace Umbraco.Tests.Services.Importing Assert.That(contents.Any(), Is.True); Assert.That(contents.Count(), Is.EqualTo(numberOfDocs)); } - - + [Test] public void PackagingService_Can_Import_CheckboxList_Content_Package_Xml_With_Property_Editor_Aliases() { @@ -482,6 +481,51 @@ namespace Umbraco.Tests.Services.Importing } } + [Test] + public void PackagingService_Can_Import_Macros() + { + // Arrange + string strXml = ImportResources.uBlogsy_Package; + var xml = XElement.Parse(strXml); + var macrosElement = xml.Descendants("Macros").First(); + var packagingService = ServiceContext.PackagingService; + + // Act + var macros = packagingService.ImportMacros(macrosElement).ToList(); + + // Assert + Assert.That(macros.Any(), Is.True); + + var allMacros = ServiceContext.MacroService.GetAll().ToList(); + foreach (var macro in macros) + { + Assert.That(allMacros.Any(x => x.Alias == macro.Alias), Is.True); + } + } + + [Test] + public void PackagingService_Can_Import_Macros_With_Properties() + { + // Arrange + string strXml = ImportResources.XsltSearch_Package; + var xml = XElement.Parse(strXml); + var macrosElement = xml.Descendants("Macros").First(); + var packagingService = ServiceContext.PackagingService; + + // Act + var macros = packagingService.ImportMacros(macrosElement).ToList(); + + // Assert + Assert.That(macros.Any(), Is.True); + Assert.That(macros.First().Properties.Any(), Is.True); + + var allMacros = ServiceContext.MacroService.GetAll().ToList(); + foreach (var macro in macros) + { + Assert.That(allMacros.Any(x => x.Alias == macro.Alias), Is.True); + } + } + private void AddLanguages() { var norwegian = new Core.Models.Language("nb-NO"); diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs index 0124b4ca1a..f1da377d4d 100644 --- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs +++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using NUnit.Framework; @@ -22,6 +23,23 @@ namespace Umbraco.Tests.Services base.TearDown(); } + [Test] + public void PackagingService_Can_Export_Macro() + { + // Arrange + var macro = new Macro("test1", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml"); + ServiceContext.MacroService.Save(macro); + + // Act + var element = ServiceContext.PackagingService.Export(macro); + + // Assert + Assert.That(element, Is.Not.Null); + Assert.That(element.Element("name").Value, Is.EqualTo("Test")); + Assert.That(element.Element("alias").Value, Is.EqualTo("test1")); + Console.Write(element.ToString()); + } + [Test] public void PackagingService_Can_Export_DictionaryItems() { diff --git a/src/Umbraco.Tests/UriExtensionsTests.cs b/src/Umbraco.Tests/UriExtensionsTests.cs index 61ed49d65d..8e826fc428 100644 --- a/src/Umbraco.Tests/UriExtensionsTests.cs +++ b/src/Umbraco.Tests/UriExtensionsTests.cs @@ -10,20 +10,33 @@ namespace Umbraco.Tests [TestFixture] public class UriExtensionsTests { - [TestCase("http://www.domain.com/umbraco", true)] - [TestCase("http://www.domain.com/Umbraco/", true)] - [TestCase("http://www.domain.com/umbraco/default.aspx", true)] - [TestCase("http://www.domain.com/umbraco/test/test", true)] - [TestCase("http://www.domain.com/Umbraco/test/test.aspx", true)] - [TestCase("http://www.domain.com/umbraco/test/test.js", true)] - [TestCase("http://www.domain.com/umbrac", false)] - [TestCase("http://www.domain.com/test", false)] - [TestCase("http://www.domain.com/test/umbraco", false)] - [TestCase("http://www.domain.com/test/umbraco.aspx", false)] - public void Is_Back_Office_Request(string input, bool expected) + [TestCase("http://www.domain.com/umbraco", "", true)] + [TestCase("http://www.domain.com/Umbraco/", "", true)] + [TestCase("http://www.domain.com/umbraco/default.aspx", "", true)] + [TestCase("http://www.domain.com/umbraco/test/test", "", false)] + [TestCase("http://www.domain.com/umbraco/test/test/test", "", false)] + [TestCase("http://www.domain.com/Umbraco/test/test.aspx", "", true)] + [TestCase("http://www.domain.com/umbraco/test/test.js", "", true)] + [TestCase("http://www.domain.com/umbrac", "", false)] + [TestCase("http://www.domain.com/test", "", false)] + [TestCase("http://www.domain.com/test/umbraco", "", false)] + [TestCase("http://www.domain.com/test/umbraco.aspx", "", false)] + [TestCase("http://www.domain.com/Umbraco/restServices/blah", "", true)] + [TestCase("http://www.domain.com/Umbraco/Backoffice/blah", "", true)] + [TestCase("http://www.domain.com/Umbraco/anything", "", true)] + [TestCase("http://www.domain.com/Umbraco/anything/", "", true)] + [TestCase("http://www.domain.com/Umbraco/surface/blah", "", false)] + [TestCase("http://www.domain.com/umbraco/api/blah", "", false)] + [TestCase("http://www.domain.com/myvdir/umbraco/api/blah", "myvdir", false)] + [TestCase("http://www.domain.com/MyVdir/umbraco/api/blah", "/myvdir", false)] + [TestCase("http://www.domain.com/MyVdir/Umbraco/", "myvdir", true)] + [TestCase("http://www.domain.com/MyVdir/Umbraco/restServices/blah", "/myvdir", true)] + [TestCase("http://www.domain.com/umbraco/webservices/legacyAjaxCalls.asmx/js", "", true)] + [TestCase("http://www.domain.com/umbraco/test/legacyAjaxCalls.ashx?some=query&blah=js", "", true)] + public void Is_Back_Office_Request(string input, string virtualPath, bool expected) { - var source = new Uri(input); - Assert.AreEqual(expected, source.IsBackOfficeRequest()); + var source = new Uri(input); + Assert.AreEqual(expected, source.IsBackOfficeRequest(virtualPath)); } [TestCase("http://www.domain.com/install", true)] diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js index 912c624ce4..5db8fc074e 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js @@ -364,6 +364,12 @@ Umbraco.Sys.registerNamespace("Umbraco.Application"); getRootScope().$emit("app.closeDialogs", undefined); } }, + /* This is used for the package installer to call in order to reload all app assets so we don't have to reload the window */ + _packageInstalled: function() { + var injector = getRootInjector(); + var packageHelper = injector.get("packageHelper"); + packageHelper.packageInstalled(); + }, _debug: function(strMsg) { if (this._isDebug) { Sys.Debug.trace("UmbClientMgr: " + strMsg); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/editors/cropsy.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/editors/cropsy.directive.js deleted file mode 100644 index c0e8b6cc0f..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/directives/editors/cropsy.directive.js +++ /dev/null @@ -1,266 +0,0 @@ -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbCropsy -* @restrict E -* @function -* @description -* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form. -**/ -angular.module("umbraco.directives") - .directive('umbCropsy', function ($timeout, localizationService, $log) { - return { - restrict: 'E', - replace: true, - templateUrl: 'views/directives/umb-cropsy.html', - scope: { - src: '@', - width: '@', - height: '@', - presets: '@', - crop: "=" - }, - link: function(scope, element, attrs) { - - scope.scale = 100; - - //if image is over this, we re-calculate the editors global ratio - //this will not have an effect on the result, since that is returned in percentage - scope.maxHeight = 500; - scope.maxWidth = 600; - scope.width = 400; - scope.height = 320; - - scope.dimensions = { - image: {}, - cropper:{}, - viewport:{}, - margin: 20, - ratio: 1 - }; - - scope.style = function () { - return { - 'height': (parseInt(scope.height, 10) + 2 * scope.dimensions.margin) + 'px', - 'width': (parseInt(scope.width, 10) + 2 * scope.dimensions.margin) + 'px' - }; - }; - - //elements - var $viewport = element.find(".viewport"); - var $image = element.find("img"); - var $overlay = element.find(".overlay"); - var $container = element.find(".crop-container"); - - //default constraints for drag n drop - var constraints = {left: {max: 20, min: 20}, top: {max: 20, min: 20}, }; - - var setDimensions = function(){ - scope.dimensions.image.width = $image.width(); - scope.dimensions.image.height = $image.height(); - - scope.dimensions.viewport.width = $viewport.width(); - scope.dimensions.viewport.height = $viewport.height(); - - scope.dimensions.cropper.width = scope.dimensions.viewport.width - 2 * scope.dimensions.margin; - scope.dimensions.cropper.height = scope.dimensions.viewport.height - 2 * scope.dimensions.margin; - }; - - var setImageSize = function(width, height){ - $image.width(width); - $image.height(height); - scope.dimensions.image.width = width; - scope.dimensions.image.height = height; - }; - - //when loading an image without any crop info, we center and fit it - var fitImage = function(){ - fitToViewPort($image); - centerImage($image); - syncOverLay(); - setConstraints($image); - }; - - //utill for centering scaled image - var centerImage = function(img) { - var image_width = img.width(), - image_height = img.height(), - mask_width = $viewport.width(), - mask_height = $viewport.height(); - - img.css({ - 'position': 'absolute', - 'left': scope.dimensions.viewport.width / 2 - scope.dimensions.image.width / 2, - 'top': scope.dimensions.viewport.height / 2 - scope.dimensions.image.height / 2 - }); - }; - - //utill for scaling image to fit viewport - var fitToViewPort = function(img) { - //returns size fitting the cropper - var size = calculateAspectRatioFit( - scope.dimensions.image.width, - scope.dimensions.image.height, - scope.dimensions.cropper.width, - scope.dimensions.cropper.height, - true); - - //sets the image size and updates the scope - setImageSize(size.width, size.height); - - scope.minScale = size.ratio; - scope.maxScale = size.ratio * 3; - scope.currentScale = scope.minScale; - scope.scale = scope.currentScale; - }; - - var resizeImageToScale = function(img, ratio){ - //do stuff - var size = calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio); - - setImageSize(size.width, size.height); - centerImage(img); - scope.currentScale = scope.scale; - - syncOverLay(); - }; - - //set constaints for cropping drag and drop - var setConstraints = function(img){ - //do stuff - var w = img.width(), - h = img.height(), - crop_width = $viewport.width() - 2 * 20, - crop_height = $viewport.height() - 2 * 20; - - constraints.left.min = 20 + crop_width - w; - constraints.top.min = 20 + crop_height - h; - }; - - //utill for getting either min/max aspect ratio to scale image after - var calculateAspectRatioFit = function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) { - var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ]; - - if(maximize){ - ratio = Math.max(ratio[0], ratio[1]); - }else{ - ratio = Math.min(ratio[0], ratio[1]); - } - - return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; - }; - - //utill for scaling width / height given a ratio - var calculateSizeToRatio= function(srcWidth, srcHeight, ratio) { - return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; - }; - - var calculateCropBox = function(){ - scope.crop.left = Math.abs($image[0].offsetLeft - scope.dimensions.margin) / scope.dimensions.image.width; - scope.crop.top = Math.abs($image[0].offsetTop - scope.dimensions.margin) / scope.dimensions.image.height; - - scope.crop.right = 1 - Math.abs(scope.dimensions.cropper.width - (scope.dimensions.image.width - scope.crop.left)) / scope.dimensions.image.width; - scope.crop.bottom = 1 - Math.abs(scope.dimensions.cropper.height - (scope.dimensions.image.height - scope.crop.top)) / scope.dimensions.image.height; - }; - - var calculatePosition = function(crop){ - - var left = (crop.left * scope.dimensions.image.originalWidth); - var top = (crop.top * scope.dimensions.image.originalHeight); - - var cropped_width = scope.dimensions.image.originalWidth - left; - var ratio = cropped_width / scope.dimensions.image.originalWidth; - - scope.scale = ratio; - resizeImageToScale($image, ratio); - - $image.css({ - "top": -top, - "left": -left - }); - - syncOverLay(); - }; - - - - var syncOverLay = function(){ - $overlay.height($image.height()); - $overlay.width($image.width()); - - $overlay.css({ - "top": $image[0].offsetTop, - "left": $image[0].offsetLeft - }); - - calculateCropBox(); - }; - - //Drag and drop positioning, using jquery ui draggable - var onStartDragPosition, top, left; - $overlay.draggable({ - start: function(event, ui) { - syncOverLay(); - }, - drag: function(event, ui) { - - - if(ui.position.left <= constraints.left.max && ui.position.left >= constraints.left.min){ - $image.css({ - 'left': ui.position.left - }); - } - - if(ui.position.top <= constraints.top.max && ui.position.top >= constraints.top.min){ - $image.css({ - 'top': ui.position.top - }); - } - }, - stop: function() { - syncOverLay(); - } - }); - - - - - - //// INIT ///// - $image.load(function(){ - $timeout(function(){ - $image.width("auto"); - $image.height("auto"); - - scope.dimensions.image.originalWidth = $image.width(); - scope.dimensions.image.originalHeight = $image.height(); - - setDimensions(); - - if(scope.crop && scope.crop.top){ - calculatePosition(scope.crop); - }else{ - fitImage(); - } - - scope.loaded = true; - }); - }); - - - /// WATCHERS //// - scope.$watch("scale", function(){ - if(scope.loaded && scope.scale !== scope.currentScale){ - resizeImageToScale($image, scope.scale); - setConstraints($image); - } - }); - - /// WATCHERS //// - scope.$watch("crop", function(newVal, oldVal){ - if(scope.loaded && newVal !== oldVal){ - calculatePosition(scope.crop); - } - }); - } - }; - }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js index 142bea84f5..244893fdc1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/html/umbphotofolder.directive.js @@ -34,15 +34,14 @@ angular.module("umbraco.directives.html") var fixedRowWidth = Math.max(element.width(), minWidth); scope.containerStyle = { width: fixedRowWidth + "px" }; - scope.rows = umbPhotoFolderHelper.buildGrid(photos, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin); if (attrs.filterBy) { scope.$watch(attrs.filterBy, function(newVal, oldVal) { - if (newVal !== oldVal) { + if (newVal && newVal !== oldVal) { var p = $filter('filter')(photos, newVal, false); scope.baseline = 0; - var m = umbPhotoFolderHelper.buildGrid(p, fixedRowWidth, 400); + var m = umbPhotoFolderHelper.buildGrid(p, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin); scope.rows = m; } }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js index 349bf0a496..b6b59991b5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js @@ -65,6 +65,11 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se scope.currentSection = args.value; } }); + + eventsService.on("app.reInitialize", function (e, args) { + //re-load the sections if we're re-initializing (i.e. package installed) + loadSections(); + }); //on page resize window.onresize = calculateHeight; diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js index 523f9c51f2..1984d4d009 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js @@ -59,8 +59,8 @@ angular.module('umbraco.mocks'). id: 1, active: true, properties: [ - { alias: "list", label: "List", view: "listview", value: "", hideLabel: true }, - { alias: "media", label: "Media picker", view: "mediapicker", value: "" } + { alias: "list", label: "List", view: "listview", value: "", hideLabel: true, config:{entityType: "content"} }, + { alias: "media", label: "Media picker", view: "mediapicker", value: "" ,config:{} } ] }, { @@ -72,7 +72,7 @@ angular.module('umbraco.mocks'). { alias: "textarea", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, { alias: "map", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, - { alias: "content", label: "Content picker", view: "contentpicker", value: "1234,23242,23232,23231" } + { alias: "media", label: "Media picker", view: "mediapicker", value: "1234,23242,23232,23231", config: {multiPicker: 1} } ] }, { diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js index 73504fd19c..2578cdf877 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/media.mocks.js @@ -7,6 +7,17 @@ angular.module('umbraco.mocks'). return [200, nodes, null]; } + function returnNodebyIds(status, data, headers) { + var ids = mocksUtils.getParameterByName(data, "ids") || "1234,1234,4234"; + var items = []; + + _.each(ids, function(id){ + items.push(_getNode( parseInt( id, 10 )) ); + }); + + return [200, items, null]; + } + function returnNodebyId(status, data, headers) { if (!mocksUtils.checkAuth()) { @@ -16,86 +27,47 @@ angular.module('umbraco.mocks'). var id = mocksUtils.getParameterByName(data, "id") || 1234; id = parseInt(id, 10); - var node = { - name: "My content with id: " + id, - updateDate: new Date(), - publishDate: new Date(), - id: id, - parentId: 1234, - icon: "icon-file-alt", - owner: {name: "Administrator", id: 0}, - updater: {name: "Per Ploug Krogslund", id: 1}, - path: "-1,1234,2455", - tabs: [ - { - label: "Child documents", - alias: "tab00", - id: 0, - active: true, - properties: [ - { - alias: "list", label: "List", view: "listview", value: "", hideLabel: true } - ] - }, - { - label: "Content", - alias: "tab01", - id: 1, - properties: [ - { alias: "bodyText", label: "Body Text", description:"Here you enter the primary article contents", view: "rte", value: "

askjdkasj lasjd

" }, - { alias: "textarea", label: "textarea", view: "textarea", value: "ajsdka sdjkds", config: { rows: 4 } }, - { alias: "map", label: "Map", view: "googlemaps", value: "37.4419,-122.1419", config: { mapType: "ROADMAP", zoom: 4 } }, - { alias: "media", label: "Media picker", view: "mediapicker", value: "" }, - { alias: "content", label: "Content picker", view: "contentpicker", value: "" } - ] - }, - { - label: "Sample Editor", - alias: "tab02", - id: 2, - properties: [ - { alias: "datepicker", label: "Datepicker", view: "datepicker", config: { rows: 7 } }, - { alias: "tags", label: "Tags", view: "tags", value: ""} - ] - }, - { - label: "Grid", - alias: "tab03", - id: 3, - properties: [ - { alias: "grid", label: "Grid", view: "grid", value: "test", hideLabel: true } - ] - },{ - label: "WIP", - alias: "tab04", - id: 4, - properties: [ - { alias: "tes", label: "Stuff", view: "test", value: "", - - config: { - fields: [ - { alias: "embedded", label: "Embbeded", view: "textstring", value: ""}, - { alias: "embedded2", label: "Embbeded 2", view: "contentpicker", value: ""}, - { alias: "embedded3", label: "Embbeded 3", view: "textarea", value: ""}, - { alias: "embedded4", label: "Embbeded 4", view: "datepicker", value: ""} - ] - } - } - ] - } - ] - }; - return [200, node, null]; + + + return [200, _getNode(id), null]; } + function _getNode(id){ + var node = { + name: "My media with id: " + id, + updateDate: new Date(), + publishDate: new Date(), + id: id, + parentId: 1234, + icon: "icon-file-alt", + owner: {name: "Administrator", id: 0}, + updater: {name: "Per Ploug Krogslund", id: 1}, + path: "-1,1234,2455", + tabs: [ + { + label: "Media", + alias: "tab0", + id: 0, + properties: [ + { alias: "umbracoFile", label: "File", description:"Some file", view: "rte", value: "/media/1234/random.jpg" } + ] + } + ] + }; + return node; + } return { register: function() { $httpBackend - .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Media/GetById')) + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Media/GetById?')) .respond(returnNodebyId); + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Media/GetByIds?')) + .respond(returnNodebyIds); + $httpBackend .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Media/GetChildren')) .respond(returnNodeCollection); diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/services/mediahelperService.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/services/mediahelperService.mocks.js new file mode 100644 index 0000000000..2ab633d9ef --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/services/mediahelperService.mocks.js @@ -0,0 +1,20 @@ +angular.module('umbraco.mocks'). + factory('imageHelperMocks', ['$httpBackend', 'mocksUtils', function ($httpBackend, mocksUtils) { + 'use strict'; + + function returnEntitybyIds(){ + return "hello.jpg"; + } + + + return { + register: function () { + + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Images/GetBigThumbnail')) + .respond(returnEntitybyIds); + + + } + }; + }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.httpbackend.js b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.httpbackend.js index 220d55a996..2cd9545e6a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.httpbackend.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.httpbackend.js @@ -1,7 +1,7 @@ var umbracoAppDev = angular.module('umbraco.httpbackend', ['umbraco', 'ngMockE2E', 'umbraco.mocks']); -function initBackEnd($httpBackend, contentMocks, mediaMocks, treeMocks, userMocks, contentTypeMocks, sectionMocks, entityMocks, dataTypeMocks, dashboardMocks, macroMocks, utilMocks, localizationMocks, prevaluesMocks) { +function initBackEnd($httpBackend, contentMocks, mediaMocks, treeMocks, userMocks, contentTypeMocks, sectionMocks, entityMocks, dataTypeMocks, dashboardMocks, macroMocks, utilMocks, localizationMocks, prevaluesMocks, imageHelperMocks) { console.log("httpBackend inited"); @@ -19,6 +19,7 @@ function initBackEnd($httpBackend, contentMocks, mediaMocks, treeMocks, userMock localizationMocks.register(); prevaluesMocks.register(); + imageHelperMocks.register(); entityMocks.register(); $httpBackend.whenGET(/^views\//).passThrough(); diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js index a4c31df259..e0586487fd 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js @@ -15,6 +15,8 @@ Umbraco.Sys.ServerVariables = { "authenticationApiBaseUrl": "/umbraco/UmbracoApi/Authentication/", //For this we'll just provide a file that exists during the mock session since we don't really have legay js tree stuff "legacyTreeJs": "/belle/lib/yepnope/empty.js", + "serverVarsJs": "/belle/lib/yepnope/empty.js", + "imagesApiBaseUrl": "/umbraco/UmbracoApi/Images/", "entityApiBaseUrl": "/umbraco/UmbracoApi/Entity/", "dashboardApiBaseUrl": "/umbraco/UmbracoApi/Dashboard/", "updateCheckApiBaseUrl": "/umbraco/Api/UpdateCheck/", diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 182b487f11..75d2f47a28 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -99,16 +99,6 @@ function entityResource($q, $http, umbRequestHelper) { 'Failed to retreive entity data for id ' + id); }, - getByQuery: function (query, rootNodeId, type) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "entityApiBaseUrl", - "GetByQuery", - [{query: query},{ rootNodeId: rootNodeId}, {type: type }])), - 'Failed to retreive entity data for query ' + query); - }, - /** * @ngdoc method * @name umbraco.resources.entityResource#getByIds diff --git a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js index ef534fd7b1..e3f86a4b14 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/assets.service.js @@ -41,7 +41,7 @@ * */ angular.module('umbraco.services') -.factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope) { +.factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope, $http) { var initAssetsLoaded = false; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js index 560b743a29..d050b62961 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js @@ -65,7 +65,7 @@ function eventsService($q, $rootScope) { return $rootScope.$on(name, callback); }, - /** pass in the result of subscribe to this method, or just call the method returned from subscribe to unsubscribe */ + /** pass in the result of 'on' to this method, or just call the method returned from 'on' to unsubscribe */ unsubscribe: function(handle) { if (angular.isFunction(handle)) { handle(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js index 0c240a294c..0b1f73e696 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js @@ -13,7 +13,7 @@ function macroService() { /** parses the special macro syntax like and returns an object with the macro alias and it's parameters */ parseMacroSyntax: function (syntax) { - var expression = /(<\?UMBRACO_MACRO macroAlias=["'](\w+?)["'].+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/im; + var expression = /(<\?UMBRACO_MACRO macroAlias=["']([\w\.]+?)["'].+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/im; var match = expression.exec(syntax); if (!match || match.length < 3) { return null; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index 5abc1116d6..0b416a2753 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -1,5 +1,26 @@ /*Contains multiple services for various helper tasks */ +function packageHelper(assetsService, treeService, eventsService) { + + return { + + /** Called when a package is installed, this resets a bunch of data and ensures the new package assets are loaded in */ + packageInstalled: function () { + //assetsService._reloadApplicationAssets().then(function() { + // treeService.clearCache(); + // //send event + // //eventsService.emit("app.reInitialize"); + + // //TODO: This doesn't work and will end in an infinite browser load loop, we can't really + // // re-bootstrap anyways since that would be the same as loading the whole browser window. + // //angular.bootstrap(document, ['umbraco']); + //}); + } + + }; +} +angular.module('umbraco.services').factory('packageHelper', packageHelper); + function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, umbRequestHelper) { return { /** sets the image's url - will check if it is a folder or a real image */ diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 0490f3e5e5..6ffde599a6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -184,51 +184,6 @@ ul.color-picker li a { } -// -// Cropper -// ------------------------------------------------- - -.umb-cropper .crop-container .viewport img { - position: absolute; - top: 0; - left: 0; -} - - -.umb-cropper .overlay { - top: 0; - left: 0; - cursor: move; - z-index: 6001; - border: 1px solid red; -} - -.umb-cropper .viewport { - overflow: hidden; - position: relative; - width: 600px; - height: 400px; - border:1px solid #ccc; - background-image: url('images/viewport_background.gif'); -} - -.umb-cropper .viewport:after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 5999; - -moz-opacity: .75; - opacity: .75; - filter: alpha(opacity=7); - -webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); - -moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); - box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2); -} - - // // folder-browser // -------------------------------------------------- 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 3b31b79b09..c9d911825c 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 @@ -1,59 +1,62 @@ angular.module("umbraco") .controller("Umbraco.Editors.Content.CopyController", function ($scope, eventsService, contentResource, navigationService, appState, treeService) { - var dialogOptions = $scope.$parent.dialogOptions; - - $scope.dialogTreeEventHandler = $({}); - var node = dialogOptions.currentNode; - $scope.dialogTreeEventHandler.bind("treeNodeSelect", function(ev, args){ - args.event.preventDefault(); - args.event.stopPropagation(); + var dialogOptions = $scope.$parent.dialogOptions; - eventsService.emit("editors.content.copyController.select", args); - - var c = $(args.event.target.parentElement); - if ($scope.selectedEl) { - $scope.selectedEl.find(".temporary").remove(); - $scope.selectedEl.find("i.umb-tree-icon").show(); - } + $scope.relateToOriginal = false; + $scope.dialogTreeEventHandler = $({}); - var temp = ""; - var icon = c.find("i.umb-tree-icon"); - if (icon.length > 0) { - icon.hide().after(temp); - } else { - c.prepend(temp); - } + var node = dialogOptions.currentNode; - $scope.target = args.node; - $scope.selectedEl = c; + $scope.dialogTreeEventHandler.bind("treeNodeSelect", function (ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); - }); + eventsService.emit("editors.content.copyController.select", args); - $scope.copy = function(){ - contentResource.copy({parentId: $scope.target.id, id: node.id, relateToOriginal: $scope.relate}) - .then(function(path){ - $scope.error = false; - $scope.success = true; + var c = $(args.event.target.parentElement); + if ($scope.selectedEl) { + $scope.selectedEl.find(".temporary").remove(); + $scope.selectedEl.find("i.umb-tree-icon").show(); + } - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the copied content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was copied!!) + var temp = ""; + var icon = c.find("i.umb-tree-icon"); + if (icon.length > 0) { + icon.hide().after(temp); + } else { + c.prepend(temp); + } - navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function(args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true }); - } - }); + $scope.target = args.node; + $scope.selectedEl = c; - },function(err){ - $scope.success = false; - $scope.error = err; - }); - }; -}); \ No newline at end of file + }); + + $scope.copy = function () { + contentResource.copy({ parentId: $scope.target.id, id: node.id, relateToOriginal: $scope.relateToOriginal }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the copied content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was copied!!) + + navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + }); + }; + }); \ No newline at end of file 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 10f9c5c21e..cbdff09b29 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/copy.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/copy.html @@ -1,49 +1,52 @@
-
-
+
+
+ + +

+ Choose where to copy {{currentNode.name}} to in the tree struture below +

-

- Choose where to copy {{currentNode.name}} to in the tree struture below -

+
+

{{error.errorMsg}}

+

{{error.data.Message}}

+
-
-

{{error.errorMsg}}

-

{{error.data.Message}}

-
+
+

+ {{currentNode.name}} was copied to + {{target.name}} +

-
-

{{currentNode.name}} was copied to - {{target.name}}

+ +
- -
- -
- - +
+ + - - - - - -
-
-
+ + + + + +
+ +
+
- +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/umb-cropsy.html b/src/Umbraco.Web.UI.Client/src/views/directives/umb-cropsy.html deleted file mode 100644 index 82f83d56d6..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/directives/umb-cropsy.html +++ /dev/null @@ -1,32 +0,0 @@ -
-
-
- -
-
-
- - - -
-		{{dimensions | json}}
-
- -
-		{{box | json}}
-
- - -
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js index 87fa2c8724..5850225e68 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.controller.js @@ -5,6 +5,15 @@ angular.module('umbraco') function($scope, dialogService, entityResource, $log, iconHelper){ + if (!$scope.model) { + $scope.model = {}; + } + if (!$scope.model.value) { + $scope.model.value = { + type: "content" + }; + } + if($scope.model.value.id && $scope.model.value.type !== "member"){ var ent = "Document"; if($scope.model.value.type === "media"){ diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html index 080be5cc58..c9fdd8674d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treesource.html @@ -13,6 +13,7 @@
  • + {{node.name}}
  • @@ -23,19 +24,6 @@ Choose... - - - -
  • - - - Global - Current - ...Or pick a node
  • 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 70de53bf24..70a98843b6 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 @@ -3,7 +3,7 @@ angular.module('umbraco') .controller("Umbraco.PropertyEditors.ContentPickerController", - function($scope, dialogService, entityResource, editorState, $log, iconHelper){ + function($scope, dialogService, entityResource, $log, iconHelper){ $scope.renderModel = []; $scope.ids = $scope.model.value ? $scope.model.value.split(',') : []; @@ -11,8 +11,6 @@ angular.module('umbraco') $scope.cfg = { multiPicker: "0", entityType: "Document", - filterCssClass: "not-allowed not-published", - startNode:{ type: "content", id: -1 @@ -33,23 +31,11 @@ angular.module('umbraco') $scope.cfg.entityType = "Media"; } - //if we have a query for the startnode, we will use that. - if($scope.cfg.startNode.query){ - var rootId = -1; - if($scope.cfg.startNode.scope === "current"){ - rootId = editorState.current.id; - } - - entityResource.getByQuery($scope.cfg.startNode.query, rootId, "Document").then(function(ent){ - $scope.cfg.startNodeId = ent.id; - }); - }else{ - $scope.cfg.startNodeId = $scope.cfg.startNode.id; - } - $scope.cfg.callback = populate; $scope.cfg.treeAlias = $scope.cfg.startNode.type; - $scope.cfg.section = $scope.cfg.startNode.type; + $scope.cfg.section = $scope.cfg.startNode.type; + $scope.cfg.startNodeId = $scope.cfg.startNode.id; + $scope.cfg.filterCssClass = "not-allowed not-published"; //load current data entityResource.getByIds($scope.ids, $scope.cfg.entityType).then(function(data){ @@ -68,39 +54,36 @@ angular.module('umbraco') $scope.remove =function(index){ $scope.renderModel.splice(index, 1); - $scope.ids.splice(index, 1); - $scope.model.value = trim($scope.ids.join(), ","); }; $scope.add =function(item){ if($scope.ids.indexOf(item.id) < 0){ item.icon = iconHelper.convertFromLegacyIcon(item.icon); - - $scope.ids.push(item.id); $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); - $scope.model.value = trim($scope.ids.join(), ","); } }; $scope.clear = function() { - $scope.model.value = ""; $scope.renderModel = []; - $scope.ids = []; - }; - - $scope.sortableOptions = { - update: function(e, ui) { - var r = []; - angular.forEach($scope.renderModel, function(value, key){ - r.push(value.id); - }); - - $scope.ids = r; - $scope.model.value = trim($scope.ids.join(), ","); - } }; + //We need to watch our renderModel so that we can update the underlying $scope.model.value properly, this is required + // because the ui-sortable doesn't dispatch an event after the digest of the sort operation. Any of the events for UI sortable + // occur after the DOM has updated but BEFORE the digest has occured so the model has NOT changed yet - it even states so in the docs. + // In their source code there is no event so we need to just subscribe to our model changes here. + //This also makes it easier to manage models, we update one and the rest will just work. + $scope.$watch(function() { + //return the joined Ids as a string to watch + return _.map($scope.renderModel, function(i) { + return i.id; + }).join(); + }, function (newVal) { + $scope.ids = _.map($scope.renderModel, function (i) { + return i.id; + }); + $scope.model.value = trim($scope.ids.join(), ","); + }); $scope.$on("formSubmitting", function (ev, args) { $scope.model.value = trim($scope.ids.join(), ","); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index e22d4cf22b..ecdd82e15a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -1,7 +1,7 @@
    • @@ -15,12 +15,13 @@
    - + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index af5779b9a7..39edcc9590 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -32,14 +32,6 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl $scope.sync(); }; - $scope.edit = function(image){ - $scope.currentImage = image; - $scope.currentImage.crop ={}; - if(!$scope.currentImage.crop){ - //$scope.currentImage.crop = { "left": 0.31731772342645215, "top": 0.17420325244997603, "right": 0.36246473116627076, "bottom": 0.30226197981593617 }; - } - } - $scope.add = function() { dialogService.mediaPicker({ multiPicker: multiPicker, @@ -65,7 +57,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl $scope.sortableOptions = { update: function(e, ui) { - var r = []; + var r = []; + //TODO: Instead of doing this with a half second delay would be better to use a watch like we do in the + // content picker. THen we don't have to worry about setting ids, render models, models, we just set one and let the + // watch do all the rest. $timeout(function(){ angular.forEach($scope.images, function(value, key){ r.push(value.id); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js index afbb248e5d..21afc87c2b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js @@ -33,52 +33,45 @@ angular.module('umbraco') $scope.remove =function(index){ $scope.renderModel.splice(index, 1); - $scope.ids.splice(index, 1); - $scope.model.value = trim($scope.ids.join(), ","); }; $scope.add =function(item){ if($scope.ids.indexOf(item.id) < 0){ item.icon = iconHelper.convertFromLegacyIcon(item.icon); - - $scope.ids.push(item.id); - $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); - $scope.model.value = trim($scope.ids.join(), ","); + $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); } }; $scope.clear = function() { - $scope.model.value = ""; $scope.renderModel = []; - $scope.ids = []; }; - - $scope.sortableOptions = { - update: function(e, ui) { - var r = []; - angular.forEach($scope.renderModel, function(value, key){ - r.push(value.id); - }); - - $scope.ids = r; - $scope.model.value = trim($scope.ids.join(), ","); - } - }; - + //We need to watch our renderModel so that we can update the underlying $scope.model.value properly, this is required + // because the ui-sortable doesn't dispatch an event after the digest of the sort operation. Any of the events for UI sortable + // occur after the DOM has updated but BEFORE the digest has occured so the model has NOT changed yet - it even states so in the docs. + // In their source code there is no event so we need to just subscribe to our model changes here. + //This also makes it easier to manage models, we update one and the rest will just work. + $scope.$watch(function () { + //return the joined Ids as a string to watch + return _.map($scope.renderModel, function (i) { + return i.id; + }).join(); + }, function (newVal) { + $scope.ids = _.map($scope.renderModel, function (i) { + return i.id; + }); + $scope.model.value = trim($scope.ids.join(), ","); + }); $scope.$on("formSubmitting", function (ev, args) { $scope.model.value = trim($scope.ids.join(), ","); }); - - function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); return str.replace(rgxtrim, ''); } - function populate(data){ if(angular.isArray(data)){ _.each(data, function (item, i) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html index 473c5694e8..168b6cdd6d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html @@ -2,7 +2,7 @@
    • diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js index f275cdc25b..3e13e0014b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js @@ -43,7 +43,7 @@ $scope.model.value.splice(idx, 1); }; - $scope.add = function () { + $scope.add = function ($event) { if ($scope.newCaption == "") { $scope.hasError = true; } else { @@ -79,6 +79,7 @@ $scope.newInternalName = ''; } + $event.preventDefault(); }; $scope.switch = function ($event) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html index 5513d96a58..afd61a4e88 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.html @@ -51,7 +51,6 @@ -
      @@ -68,10 +67,9 @@
      - +
      - diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js index 3b453a7930..64a21b670e 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/media/edit-media-controller.spec.js @@ -47,7 +47,7 @@ describe('edit media controller tests', function () { }); it('it should have a tabs collection', function () { - expect(scope.content.tabs.length).toBe(5); + expect(scope.content.tabs.length).toBe(1); }); it('it should have a properties collection on each tab', function () { diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js index 2ffd204d24..b42a861e8a 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js @@ -60,7 +60,7 @@ describe('Content picker controller tests', function () { it("Removing an item should update renderModel, ids and model.value", function(){ scope.remove(1); - + scope.$apply(); expect(scope.renderModel.length).toBe(2); expect(scope.ids.length).toBe(2); expect(scope.model.value).toBe("1233,23121"); @@ -69,7 +69,7 @@ describe('Content picker controller tests', function () { it("Adding an item should update renderModel, ids and model.value", function(){ scope.add({name: "meh", id: 666, icon: "woop"}); - + scope.$apply(); expect(scope.renderModel.length).toBe(4); expect(scope.ids.length).toBe(4); expect(scope.model.value).toBe("1233,1231,23121,666"); @@ -77,12 +77,14 @@ describe('Content picker controller tests', function () { it("Adding a dublicate item should note update renderModel, ids and model.value", function(){ - scope.add({name: "meh", id: 666, icon: "woop"}); + scope.add({ name: "meh", id: 666, icon: "woop" }); + scope.$apply(); expect(scope.renderModel.length).toBe(4); expect(scope.ids.length).toBe(4); expect(scope.model.value).toBe("1233,1231,23121,666"); - scope.add({name: "meh 2", id: 666, icon: "woop 2"}); + scope.add({ name: "meh 2", id: 666, icon: "woop 2" }); + scope.$apply(); expect(scope.renderModel.length).toBe(4); expect(scope.ids.length).toBe(4); expect(scope.model.value).toBe("1233,1231,23121,666"); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js index 470b7b59f8..381ae22713 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js @@ -21,6 +21,20 @@ describe('macro service tests', function () { expect(result.marcoParamsDictionary.test2).toBe("hello"); + }); + + it('can parse syntax for macros with aliases containing dots', function () { + + var result = macroService.parseMacroSyntax(""); + + expect(result).not.toBeNull(); + expect(result.macroAlias).toBe("Map.Test"); + expect(result.marcoParamsDictionary.test1).not.toBeUndefined(); + expect(result.marcoParamsDictionary.test1).toBe("asdf"); + expect(result.marcoParamsDictionary.test2).not.toBeUndefined(); + expect(result.marcoParamsDictionary.test2).toBe("hello"); + + }); it('can parse syntax for macros with body', function () { diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index eaa279c342..ab7f865b06 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -115,15 +115,15 @@ False ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll - + False ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll - + False ..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll - + False ..\packages\Examine.0.1.52.2941\lib\Examine.dll @@ -616,6 +616,7 @@ treeInit.aspx + @@ -2135,13 +2136,13 @@ - + - + - + Code @@ -2161,7 +2162,7 @@ - + @@ -2667,11 +2668,17 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\" - + + + + + + + diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty (ForUseWithCustomViews).cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty (ForUseWithCustomViews).cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty.cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/EmptyTemplate.cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Empty.cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml index f9e913d9c4..062e5c7171 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/MultinodeTree-picker.cshtml @@ -10,10 +10,10 @@ @* Lists each selected value from the picker as a link *@
        - @foreach(var id in CurrentPage.PropertyWithPicker){ + @foreach(var id in CurrentPage.PropertyWithPicker.Split(',')){ @*For each link, get the node, and display its name and url*@ - var content = Umbraco.Content(id.InnerText); + var content = Umbraco.Content(id);
      • @content.Name
      • } diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty (ForUseWithCustomViews).cshtml similarity index 100% rename from src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate (ForUseWithCustomViews).cshtml rename to src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty (ForUseWithCustomViews).cshtml diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml new file mode 100644 index 0000000000..2363dcc14c --- /dev/null +++ b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/Empty.cshtml @@ -0,0 +1 @@ +@inherits Umbraco.Web.Mvc.UmbracoTemplatePage \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/EmptyTemplate.cshtml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml index 39efceb361..b3e292230d 100644 --- a/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViews/Templates/MultinodeTree-picker.cshtml @@ -10,10 +10,10 @@ @* Lists each selected value from the picker as a link *@
          - @foreach(var id in CurrentPage.PropertyWithPicker){ + @foreach(var id in CurrentPage.PropertyWithPicker.Split(',')){ @*For each link, get the node, and display its name and url*@ - var content = Umbraco.Content(id.InnerText); + var content = Umbraco.Content(id);
        • @content.Name
        • } diff --git a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx index bf10210713..b34560a19e 100644 --- a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx +++ b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx @@ -22,5 +22,5 @@ <%=umbraco.ui.Text("cancel")%> - + diff --git a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs index 5fcd310656..6ba27b5fe7 100644 --- a/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs +++ b/src/Umbraco.Web.UI/Umbraco/create/PartialView.ascx.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Web.UI; using System.Web.UI.WebControls; @@ -24,16 +24,16 @@ namespace Umbraco.Web.UI.Umbraco.Create var path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/"); list.Items.Clear(); - // always add the options of empty templates - list.Items.Add(new ListItem("Empty Template", "EmptyTemplate.cshtml")); - list.Items.Add(new ListItem("Empty Template (For Use With Custom Views)", "EmptyTemplate (ForUseWithCustomViews).cshtml")); + // always add the options of empty snippets + list.Items.Add(new ListItem("Empty", "Empty.cshtml")); + list.Items.Add(new ListItem("Empty (For Use With Custom Views)", "Empty (ForUseWithCustomViews).cshtml")); if (System.IO.Directory.Exists(path)) { const string extension = ".cshtml"; - //Already adding Empty Template as the first item, so don't add it again - foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("EmptyTemplate") == false)) + //Already adding Empty as the first item, so don't add it again + foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("Empty") == false)) { var filename = System.IO.Path.GetFileName(fileInfo.FullName); diff --git a/src/Umbraco.Web.UI/config/splashes/booting.aspx b/src/Umbraco.Web.UI/config/splashes/booting.aspx index 267e36b369..1bea61f89d 100644 --- a/src/Umbraco.Web.UI/config/splashes/booting.aspx +++ b/src/Umbraco.Web.UI/config/splashes/booting.aspx @@ -2,7 +2,7 @@ <% // NH: Adds this inline check to avoid a simple codebehind file in the legacy project! - if (!umbraco.cms.helpers.url.ValidateProxyUrl(Request["url"], Request.Url.AbsoluteUri)) + if (Request["url"].ToLower().Contains("booting.aspx") || !umbraco.cms.helpers.url.ValidateProxyUrl(Request["url"], Request.Url.AbsoluteUri)) { throw new ArgumentException("Can't redirect to the requested url - it's not local or an approved proxy url", "url"); diff --git a/src/Umbraco.Web.UI/install/InstallerRestService.aspx.cs b/src/Umbraco.Web.UI/install/InstallerRestService.aspx.cs index 248e6c17d2..8b197d75f0 100644 --- a/src/Umbraco.Web.UI/install/InstallerRestService.aspx.cs +++ b/src/Umbraco.Web.UI/install/InstallerRestService.aspx.cs @@ -1,6 +1,7 @@ using System; using System.Configuration; using System.Security.Authentication; +using System.Web; using System.Web.Script.Serialization; using System.Web.Script.Services; using System.Web.Services; @@ -97,6 +98,11 @@ namespace Umbraco.Web.UI.Install HandleConnectionStrings(); + //After upgrading we must restart the app pool - the reason is because PetaPoco caches a lot of the mapping logic + // and after we upgrade a db, some of the mapping needs to be updated so we restart the app pool to clear it's cache or + // else we can end up with YSODs + ApplicationContext.Current.RestartApplicationPool(new HttpContextWrapper(HttpContext.Current)); + var js = new JavaScriptSerializer(); var jsonResult = js.Serialize(result); return jsonResult; diff --git a/src/Umbraco.Web.UI/install/steps/Database.ascx.cs b/src/Umbraco.Web.UI/install/steps/Database.ascx.cs index ede7132da0..45eab19d45 100644 --- a/src/Umbraco.Web.UI/install/steps/Database.ascx.cs +++ b/src/Umbraco.Web.UI/install/steps/Database.ascx.cs @@ -161,10 +161,10 @@ namespace Umbraco.Web.UI.Install.Steps DatabasePassword.Text = GetConnectionStringValue(connectionStringBuilder, "password"); if (string.IsNullOrEmpty(DatabasePassword.Text)) DatabasePassword.Text = GetConnectionStringValue(connectionStringBuilder, "pwd"); - toggleVisible(DatabaseServerItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabaseUsernameItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabasePasswordItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabaseNameItem, !ManualConnectionString && !IsEmbeddedDatabase); + ToggleVisible(DatabaseServerItem, !ManualConnectionString && !IsEmbeddedDatabase); + ToggleVisible(DatabaseUsernameItem, !ManualConnectionString && !IsEmbeddedDatabase); + ToggleVisible(DatabasePasswordItem, !ManualConnectionString && !IsEmbeddedDatabase); + ToggleVisible(DatabaseNameItem, !ManualConnectionString && !IsEmbeddedDatabase); if (IsNewInstall || IsEmbeddedDatabase) dbinit.Text = "$('#databaseOptionEmbedded').click();$('#databaseOptionEmbedded').change();"; @@ -243,15 +243,15 @@ namespace Umbraco.Web.UI.Install.Steps /// The event arguments. protected void DatabaseType_SelectedIndexChanged(object sender, EventArgs e) { - toggleVisible(DatabaseServerItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabaseUsernameItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabasePasswordItem, !ManualConnectionString && !IsEmbeddedDatabase); - toggleVisible(DatabaseNameItem, !ManualConnectionString && !IsEmbeddedDatabase); + ToggleVisible(DatabaseServerItem, !ManualConnectionString && !IsEmbeddedDatabase); + ToggleVisible(DatabaseUsernameItem, !ManualConnectionString && !IsEmbeddedDatabase); + ToggleVisible(DatabasePasswordItem, !ManualConnectionString && !IsEmbeddedDatabase); + ToggleVisible(DatabaseNameItem, !ManualConnectionString && !IsEmbeddedDatabase); //toggleVisible(DatabaseConnectionString, ManualConnectionString); } - private static void toggleVisible(HtmlGenericControl div, bool visible) + private static void ToggleVisible(HtmlGenericControl div, bool visible) { if (!visible) div.Attributes["style"] = "display: none;"; diff --git a/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.cs b/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.cs index 4053f0c782..141bc56958 100644 --- a/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.cs +++ b/src/Umbraco.Web.UI/install/steps/TheEnd.ascx.cs @@ -1,5 +1,9 @@ using System; +using System.Web; +using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Web.Security; namespace Umbraco.Web.UI.Install.Steps { @@ -12,15 +16,20 @@ namespace Umbraco.Web.UI.Install.Steps { GlobalSettings.ConfigurationStatus = UmbracoVersion.Current.ToString(3); } - catch (Exception) + catch (Exception ex) { - //errorLiteral.Text = ex.ToString(); + LogHelper.Error("An error occurred updating the config status", ex); } // Update ClientDependency version var clientDependencyConfig = new ClientDependencyConfiguration(); var clientDependencyUpdated = clientDependencyConfig.IncreaseVersionNumber(); + //Clear the auth cookie - this is required so that the login screen is displayed after upgrade and so the + // csrf anti-forgery tokens are created, otherwise there will just be JS errors if the user has an old + // login token from a previous version when we didn't have csrf tokens in place + var security = new WebSecurity(new HttpContextWrapper(Context), ApplicationContext.Current); + security.ClearCurrentLogin(); } } diff --git a/src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx b/src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx new file mode 100644 index 0000000000..fd9c5182f2 --- /dev/null +++ b/src/Umbraco.Web.UI/umbraco/ClientRedirect.aspx @@ -0,0 +1,23 @@ +<%@ Page Language="C#" AutoEventWireup="true" Inherits="System.Web.UI.Page" %> +<%-- + This page is required because we cannot reload the angular app with a changed Hash since it just detects the hash and doesn't reload. + So this is used purely for a full reload of an angular app with a changed hash. +--%> + + + + Redirecting... + + + + Redirecting... + + diff --git a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml index 742b1f36c4..d05908e2cf 100644 --- a/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml +++ b/src/Umbraco.Web.UI/umbraco/config/create/UI.Release.xml @@ -61,7 +61,7 @@
          User
          - /create/simple.ascx + /create/user.ascx diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 1c9c21cdbe..c4b85332fa 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -467,20 +467,21 @@ Rediger dine notificeringer for %0% - + - Hej %0%

          Dette er en automatisk mail for at informere dig om at opgaven '%1%' er blevet udførtpå siden '%2%' af brugeren '%3%'

          Opdateringssammendrag:

          %6%

          Hav en fortsat god dag!

          De bedste hilsner fra umbraco robotten

          ]]>
          + Hej %0%

          Dette er en automatisk mail for at informere dig om at opgaven '%1%' er blevet udførtpå siden '%2%' af brugeren '%3%'

          Opdateringssammendrag:

          %6%

          Hav en fortsat god dag!

          De bedste hilsner fra umbraco robotten

          ]]>
          [%0%] Notificering om %1% udført på %2% Notificeringer diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml index 3721090de9..c2c16cccec 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml @@ -497,15 +497,15 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die Bearbeiten Sie Ihre Benachrichtigungseinstellungen für '%0%' -Hallo %0%, + Hallo %0%, -die Aufgabe '%1%' (von Benutzer '%3%') an der Seite '%2%' wurde ausgeführt. + die Aufgabe '%1%' (von Benutzer '%3%') an der Seite '%2%' wurde ausgeführt. -Zum Bearbeiten verwenden Sie bitte diesen Link: http://%4%/umbraco/actions/editContent.aspx?id=%5% + Zum Bearbeiten verwenden Sie bitte diesen Link: http://%4%/#/content/content/edit/%5% -Einen schönen Tag wünscht -Ihr freundlicher Umbraco-Robot - + Einen schönen Tag wünscht + Ihr freundlicher Umbraco-Robot + <p>Hallo %0%,</p> diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index f417282ac2..1c04ba81e1 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -584,7 +584,7 @@ To manage your website, simply open the umbraco back office and start adding con has been performed on the page '%2%' by the user '%3%' - Go to http://%4%/actions/editContent.aspx?id=%5% to edit. + Go to http://%4%/#/content/content/edit/%5% to edit. Have a nice day! @@ -593,14 +593,12 @@ To manage your website, simply open the umbraco back office and start adding con Hi %0%

          This is an automated mail to inform you that the task '%1%' - has been performed on the page '%2%' + has been performed on the page '%2%' by the user '%3%'

          @@ -612,9 +610,7 @@ To manage your website, simply open the umbraco back office and start adding con

          diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index b4266e8fd2..4311d1f04c 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -582,7 +582,7 @@ To manage your website, simply open the umbraco back office and start adding con has been performed on the page '%2%' by the user '%3%' - Go to http://%4%/actions/editContent.aspx?id=%5% to edit. + Go to http://%4%/#/content/content/edit/%5% to edit. Have a nice day! @@ -598,9 +598,7 @@ To manage your website, simply open the umbraco back office and start adding con

          @@ -612,9 +610,7 @@ To manage your website, simply open the umbraco back office and start adding con

          diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml index 30bc95d93c..bc45128d7a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml @@ -433,8 +433,8 @@ However, Runway offers an easy foundation based on best practices to get you sta Edite su notificación para %0% - Hola %0% Esto es un e-mail automático para informarle que la tarea '%1%' ha sido realizada sobre la página '%2%' por el usuario '%3%' Vaya a http://%4%/actions/editContent.aspx?id=%5% para editarla. ¡Espero que tenga un buen día! Saludos del robot de umbraco - Hola %0%

          Esto es un e-mail generado automáticamente para informarle que la tarea '%1%' ha sido realizada sobre la página '%2%' por el usuario '%3%'

          Resumen de actualización:

          %6%

          ¡Espero que tenga un buen día!

          Saludos del robot umbraco.

          ]]>
          + Hola %0% Esto es un e-mail automático para informarle que la tarea '%1%' ha sido realizada sobre la página '%2%' por el usuario '%3%' Vaya a http://%4%/#/content/content/edit/%5% para editarla. ¡Espero que tenga un buen día! Saludos del robot de umbraco + Hola %0%

          Esto es un e-mail generado automáticamente para informarle que la tarea '%1%' ha sido realizada sobre la página '%2%' por el usuario '%3%'

          Resumen de actualización:

          %6%

          ¡Espero que tenga un buen día!

          Saludos del robot umbraco.

          ]]>
          [%0%] Notificación acerca de %1% realizado en %2% Notificaciones diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml index 0f10085ab9..7524dd527c 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml @@ -534,7 +534,7 @@ Pour gerer votre site, accéder simplement au back office et commencez à contri Ceci est un email automatique pour vous informer que la tâche '%1%' a été activée sur la page '%2%' par l'utilisateur '%3%' - Allez à http://%4%/actions/editContent.aspx?id=%5% pour éditer. + Allez à http://%4%/#/content/content/edit/%5% pour éditer. Bonne journée! @@ -549,10 +549,8 @@ Pour gerer votre site, accéder simplement au back office et commencez à contri par l'utilisateur '%3%'

          @@ -564,9 +562,7 @@ Pour gerer votre site, accéder simplement au back office et commencez à contri

          diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml index 201001ffbc..d22a0474e1 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml @@ -509,9 +509,7 @@ To manage your website, simply open the umbraco back office and start adding con

          @@ -523,9 +521,7 @@ To manage your website, simply open the umbraco back office and start adding con

          diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml index d925460d57..f75a1d73be 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml @@ -458,7 +458,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i è stata eseguita sulla pagina '%2%' dall'utente '%3%' - Vai al link http://%4%/actions/editContent.aspx?id=%5% per effettuare le modifiche. + Vai al link http://%4%/#/content/content/edit/%5% per effettuare le modifiche. Buona giornata! @@ -473,9 +473,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i

          @@ -487,9 +485,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i

          diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml index 2cea7d7838..bf60d41574 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml @@ -496,7 +496,7 @@ Runwayをインストールして作られた新しいウェブサイトがど ページ '%2%' 上のタスク'%1%'から 自動的にメールします。 - 編集はこちらから: http://%4%/actions/editContent.aspx?id=%5% + 編集はこちらから: http://%4%/#/content/content/edit/%5% 早々 @@ -507,9 +507,7 @@ Runwayをインストールして作られた新しいウェブサイトがど

          ユーザー '%3%' によりページ '%2%' 上のタスク '%1%' から自動的にメールします。

          @@ -521,9 +519,7 @@ Runwayをインストールして作られた新しいウェブサイトがど

          diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml index 0a7ba87cff..2ec2088a01 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml @@ -475,7 +475,7 @@ 사용자 '%3%' 가 작업 '%1%' 를 페이지 '%2%' 에서 진행했음을 알리는 자동 발송 메일입니다. - 편집하시려면 http://%4%/actions/editContent.aspx?id=%5% 로 이동하세요 + 편집하시려면 http://%4%/#/content/content/edit/%5% 로 이동하세요 좋은 하루 되세요! @@ -488,9 +488,7 @@

          @@ -502,9 +500,7 @@

          diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index 39c7f53647..02f41b141f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -437,7 +437,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je is uitgevoerd op pagina '%2%' door gebruiker '%3%' - Ga naar http://%4%/actions/editContent.aspx?id=%5% om te bewerken. + Ga naar http://%4%/#/content/content/edit/%5% om te bewerken. Een prettige dag! @@ -451,10 +451,8 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je door gebruiker '%3%'

          @@ -466,9 +464,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je

          diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/no.xml b/src/Umbraco.Web.UI/umbraco/config/lang/no.xml index 4e76541b2e..ed024af119 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/no.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/no.xml @@ -459,9 +459,7 @@ Vennlig hilsen umbraco roboten </p> <div style="margin: 8px 0; padding: 8px; display: block;"> <br /> - <a style="color: white; font-weight: bold; background-color: #66cc66; text-decoration : none; margin-right: 20px; border: 8px solid #66cc66; width: 150px;" href="http://%4%/umbraco/actions/publish.aspx?id=%5%">&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> diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml index 6db3d9e2a5..a9d419c9ab 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml @@ -426,10 +426,10 @@ Możesz dodać dodatkowe języki w menu "Języki" po lewej stronie.]]>
          Edytuj powiadomienie dla %0% - Witaj %0%

          To jest automatyczny e-mail, wysłany aby poinformować Cię, że polecenie '%1%' zostało wykonane na stronie '%2%' przez użytkownika '%3%'

          Podsumowanie zmian:

          %6%

          Miłego dnia!

          Pozdrowienia od robota Umbraco

          ]]>
          + Witaj %0%

          To jest automatyczny e-mail, wysłany aby poinformować Cię, że polecenie '%1%' zostało wykonane na stronie '%2%' przez użytkownika '%3%'

          Podsumowanie zmian:

          %6%

          Miłego dnia!

          Pozdrowienia od robota Umbraco

          ]]>
          [%0%] Powiadomienie o %1% wykonane na %2% Powiadomienie diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml index ec10a7bdce..bbb0e7e5e2 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml @@ -467,7 +467,7 @@ Pressione "próximo" para iniciar o assistente.]]>
          Esta é uma mensagem automatizada de email para informar que a tarefa '%1%' foi realizada na página '%2%' pelo usuário '%3%' -Vá até http://%4%/actions/editContent.aspx?id=%5% para editar. +Vá até http://%4%/#/content/content/edit/%5% para editar. Tenha um bom dia! @@ -480,9 +480,7 @@ Vá até http://%4%/actions/editContent.aspx?id=%5% para editar.

          @@ -494,9 +492,7 @@ Vá até http://%4%/actions/editContent.aspx?id=%5% para editar.

          diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index d21847c9f8..667dcfe4a9 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -515,7 +515,7 @@ Операция '%1%' была произведена на странице '%2%' пользователем '%3%'. - Вы можете увидеть изменения и отредактировать, перейдя по ссылке http://%4%/actions/editContent.aspx?id=%5%. + Вы можете увидеть изменения и отредактировать, перейдя по ссылке http://%4%/#/content/content/edit/%5%. Удачи! @@ -529,9 +529,7 @@ @@ -544,9 +542,7 @@ diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml index bb7f487072..3db191537b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml @@ -496,7 +496,7 @@ Inställningar för notifieringar gällande {0} Hej {0} Detta mail skickas till dig automatiskt för att meddela att '{1}' har utförts på sidan '{2}' av användaren '{3}' Gå till http://{4}/actions/editContent.aspx?id={5} för att redigera. - Hej {0}

          Detta mail skickas till dig automatiskt för att meddela att '{1}' har utförts på sidan '{2}' av användaren '{3}'

          Sammanfattning av uppdateringen:

          {6}

          ]]>
          + Hej {0}

          Detta mail skickas till dig automatiskt för att meddela att '{1}' har utförts på sidan '{2}' av användaren '{3}'

          Sammanfattning av uppdateringen:

          {6}

          ]]>
          [{0}] Meddelande för att informera om att {1} har utförts på {2} Notifieringar diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml index 49a39399bd..e868feb5b5 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml @@ -530,7 +530,7 @@ 您好!这是一封自动邮件,提醒您用户'%3%'执行'%1%'任务已经在完成'%2%'。 - 转到 http://%4%/actions/editContent.aspx?id=%5% 进行编辑 + 转到 http://%4%/#/content/content/edit/%5% 进行编辑 ]]> %0%:

          @@ -538,9 +538,7 @@

          您好!这是一封自动发送的邮件,告诉您任务'%1%'已在'%2%'被用户'%3%'执行

          @@ -552,9 +550,7 @@

          diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs index 8ed79da349..6e07894b9f 100644 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs @@ -24,16 +24,16 @@ namespace Umbraco.Web.UI.Umbraco.Create var path = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViewMacros/Templates/"); list.Items.Clear(); - // always add the options of empty templates - list.Items.Add(new ListItem("Empty Template", "EmptyTemplate.cshtml")); - list.Items.Add(new ListItem("Empty Template (For Use With Custom Views)", "EmptyTemplate (ForUseWithCustomViews).cshtml")); + // always add the options of empty snippets + list.Items.Add(new ListItem("Empty", "Empty.cshtml")); + list.Items.Add(new ListItem("Empty (For Use With Custom Views)", "Empty (ForUseWithCustomViews).cshtml")); if (System.IO.Directory.Exists(path)) { const string extension = ".cshtml"; - //Already adding Empty Template as the first item, so don't add it again - foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("EmptyTemplate") == false)) + //Already adding Empty as the first item, so don't add it again + foreach (var fileInfo in new System.IO.DirectoryInfo(path).GetFiles("*" + extension).Where(f => f.Name.StartsWith("Empty") == false)) { var filename = System.IO.Path.GetFileName(fileInfo.FullName); diff --git a/src/Umbraco.Web.UI/umbraco/create/language.ascx b/src/Umbraco.Web.UI/umbraco/create/language.ascx index 272b618d1d..686cbbdb96 100644 --- a/src/Umbraco.Web.UI/umbraco/create/language.ascx +++ b/src/Umbraco.Web.UI/umbraco/create/language.ascx @@ -1,11 +1,18 @@ -<%@ Control Language="c#" AutoEventWireup="True" Codebehind="language.ascx.cs" Inherits="umbraco.cms.presentation.create.controls.language" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> - +<%@ Control Language="c#" AutoEventWireup="True" Inherits="umbraco.cms.presentation.create.controls.language" TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%> +<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> -
          <%=umbraco.ui.Text("choose")%> <%=umbraco.ui.Text("language")%>:
          - -
          + -
          -<%= umbraco.ui.Text("or") %>  <%=umbraco.ui.Text("cancel")%> -
          + + + + * + + + + + <%=umbraco.ui.Text("cancel")%> + + + + diff --git a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx b/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx index 481a030bcc..0aae25a974 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx +++ b/src/Umbraco.Web.UI/umbraco/developer/Packages/installer.aspx @@ -1,6 +1,7 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" CodeBehind="installer.aspx.cs" +<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" AutoEventWireup="True" Inherits="umbraco.presentation.developer.packages.Installer" Trace="false" ValidateRequest="false" %> +<%@ Import Namespace="umbraco" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> @@ -260,6 +261,7 @@ +

          All items in the package has been installed

          @@ -274,6 +276,36 @@ + + + +

          + Please wait while the browser is reloaded... +
          + + +
          +
          diff --git a/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx b/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx index e833a9ac99..b19dc8374d 100644 --- a/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx +++ b/src/Umbraco.Web.UI/umbraco/dialogs/protectPage.aspx @@ -1,7 +1,6 @@ -<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" Codebehind="protectPage.aspx.cs" AutoEventWireup="True" Inherits="umbraco.presentation.umbraco.dialogs.protectPage" %> +<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" AutoEventWireup="True" Inherits="umbraco.presentation.umbraco.dialogs.protectPage" %> <%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - - - -
          -

          - Only install packages from sources you know and trust!

          -

          - When installing an Umbraco package you should use the same caution as when you install - an application on your computer.

          -

          - A malicious package could damage your Umbraco installation just like a malicious - application can damage your computer. -

          -

          - It is recommended to install from the official Umbraco package - repository or a custom repository whenever it's possible. -

          -

          - - -

          -
          -
          - -

          - -
          - - <%= umbraco.ui.Text("packager", "chooseLocalPackageText") %> - -

          -
          - - - - -
          - - -
          -

          - This repository requires authentication before you can download any packages from - it.
          - Please enter email and password to login. -

          -
          -
          - - - - - - -
          - -
          -
          -

          - Please note: Installing a package containing several items and - files can take some time. Do not refresh the page or navigate away before, the installer - notifies you the install is completed. -

          -
          - - - - - - - - - - - - - - - -
          -

          - Binary files in the package!

          -

          - Read more... -

          - -
          -
          - -
          -

          - Macro Conflicts in the package!

          -

          - Read more... -

          - -
          -
          - -
          -

          - Template Conflicts in the package!

          -

          - Read more... -

          - -
          -
          - -
          -

          - Stylesheet Conflicts in the package!

          -

          - Read more... -

          - -
          -
          - -
          - - -
          -
          - -
          - - - - - - - - - -

          - All items in the package has been installed

          -

          - Overview of what was installed can found under "installed package" in the developer - section.

          -

          - Uninstall is available at the same location.

          -

          - - -

          - -
          -
          - -
          -
          diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs index e18d46c625..a429f3018f 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Drawing; +using System.Globalization; using System.Threading; using System.Web; using System.Web.SessionState; @@ -36,6 +37,8 @@ namespace umbraco.presentation.developer.packages private readonly cms.businesslogic.packager.Installer _installer = new cms.businesslogic.packager.Installer(); private string _tempFileName = ""; + protected string RefreshQueryString { get; set; } + protected void Page_Load(object sender, EventArgs e) { var ex = new Exception(); @@ -53,9 +56,9 @@ namespace umbraco.presentation.developer.packages } //if we are actually in the middle of installing something... meaning we keep redirecting back to this page with - // custom query strings - // TODO: SD: This process needs to be fixed/changed/etc... to use the InstallPackageController - // http://issues.umbraco.org/issue/U4-1047 + // custom query strings + // TODO: SD: This process needs to be fixed/changed/etc... to use the InstallPackageController + // http://issues.umbraco.org/issue/U4-1047 if (!string.IsNullOrEmpty(Request.GetItemAsString("installing"))) { HideAllPanes(); @@ -63,22 +66,22 @@ namespace umbraco.presentation.developer.packages ProcessInstall(Request.GetItemAsString("installing")); //process the current step } - else if (tempFile.Value.IsNullOrWhiteSpace() //if we haven't downloaded the .umb temp file yet - && (!Request.GetItemAsString("guid").IsNullOrWhiteSpace() && !Request.GetItemAsString("repoGuid").IsNullOrWhiteSpace())) + else if (tempFile.Value.IsNullOrWhiteSpace() //if we haven't downloaded the .umb temp file yet + && (!Request.GetItemAsString("guid").IsNullOrWhiteSpace() && !Request.GetItemAsString("repoGuid").IsNullOrWhiteSpace())) { //we'll fetch the local information we have about our repo, to find out what webservice to query. - _repo = cms.businesslogic.packager.repositories.Repository.getByGuid(Request.GetItemAsString("repoGuid")); - + _repo = cms.businesslogic.packager.repositories.Repository.getByGuid(Request.GetItemAsString("repoGuid")); + if (_repo != null && _repo.HasConnection()) { //from the webservice we'll fetch some info about the package. - cms.businesslogic.packager.repositories.Package pack = _repo.Webservice.PackageByGuid(Request.GetItemAsString("guid")); + cms.businesslogic.packager.repositories.Package pack = _repo.Webservice.PackageByGuid(Request.GetItemAsString("guid")); //if the package is protected we will ask for the users credentials. (this happens every time they try to fetch anything) if (!pack.Protected) { //if it isn't then go straigt to the accept licens screen - tempFile.Value = _installer.Import(_repo.fetch(Request.GetItemAsString("guid"))); + tempFile.Value = _installer.Import(_repo.fetch(Request.GetItemAsString("guid"))); UpdateSettings(); } @@ -94,7 +97,7 @@ namespace umbraco.presentation.developer.packages else { fb.Style.Add("margin-top", "7px"); - fb.type = global::umbraco.uicontrols.Feedback.feedbacktype.error; + fb.type = uicontrols.Feedback.feedbacktype.error; fb.Text = "No connection to repository. Runway could not be installed as there was no connection to: '" + _repo.RepositoryUrl + "'"; pane_upload.Visible = false; } @@ -107,7 +110,7 @@ namespace umbraco.presentation.developer.packages acceptCheckbox.Attributes.Add("onmouseup", "document.getElementById('" + ButtonInstall.ClientID + "').disabled = false;"); } - protected void uploadFile(object sender, System.EventArgs e) + protected void uploadFile(object sender, EventArgs e) { try { @@ -131,7 +134,7 @@ namespace umbraco.presentation.developer.packages string memberGuid = _repo.Webservice.authenticate(tb_email.Text, library.md5(tb_password.Text)); //if we auth correctly and get a valid key back, we will fetch the file from the repo webservice. - if (!string.IsNullOrEmpty(memberGuid)) + if (string.IsNullOrEmpty(memberGuid) == false) { tempFile.Value = _installer.Import(_repo.fetch(helper.Request("guid"), memberGuid)); UpdateSettings(); @@ -212,77 +215,80 @@ namespace umbraco.presentation.developer.packages private void ProcessInstall(string currentStep) { - var dir = Request.GetItemAsString("dir"); + var dir = Request.GetItemAsString("dir"); var packageId = 0; - int.TryParse(Request.GetItemAsString("pId"), out packageId); + int.TryParse(Request.GetItemAsString("pId"), out packageId); switch (currentStep) { case "businesslogic": - //first load in the config from the temporary directory - //this will ensure that the installer have access to all the new files and the package manifest - _installer.LoadConfig(dir); + //first load in the config from the temporary directory + //this will ensure that the installer have access to all the new files and the package manifest + _installer.LoadConfig(dir); _installer.InstallBusinessLogic(packageId, dir); //making sure that publishing actions performed from the cms layer gets pushed to the presentation library.RefreshContent(); - - if (!string.IsNullOrEmpty(_installer.Control)) + + if (string.IsNullOrEmpty(_installer.Control) == false) { - Response.Redirect("installer.aspx?installing=customInstaller&dir=" + dir + "&pId=" + packageId.ToString() + "&customControl=" + Server.UrlEncode(_installer.Control) + "&customUrl=" + Server.UrlEncode(_installer.Url)); + Response.Redirect("installer.aspx?installing=refresh&dir=" + dir + "&pId=" + packageId.ToString() + "&customControl=" + Server.UrlEncode(_installer.Control) + "&customUrl=" + Server.UrlEncode(_installer.Url)); } else { - Response.Redirect("installer.aspx?installing=finished&dir=" + dir + "&pId=" + packageId.ToString() + "&customUrl=" + Server.UrlEncode(_installer.Url)); + Response.Redirect("installer.aspx?installing=refresh&dir=" + dir + "&pId=" + packageId.ToString() + "&customUrl=" + Server.UrlEncode(_installer.Url)); } break; case "customInstaller": - var customControl = Request.GetItemAsString("customControl"); + var customControl = Request.GetItemAsString("customControl"); - if (!customControl.IsNullOrWhiteSpace()) + if (customControl.IsNullOrWhiteSpace() == false) { HideAllPanes(); - _configControl = new System.Web.UI.UserControl().LoadControl(SystemDirectories.Root + customControl); + _configControl = LoadControl(SystemDirectories.Root + customControl); _configControl.ID = "packagerConfigControl"; pane_optional.Controls.Add(_configControl); pane_optional.Visible = true; - if (!IsPostBack) - { - //We still need to clean everything up which is normally done in the Finished Action - PerformPostInstallCleanup(packageId, dir); - } - + if (!IsPostBack) + { + //We still need to clean everything up which is normally done in the Finished Action + PerformPostInstallCleanup(packageId, dir); + } + } else { //if the custom installer control is empty here (though it should never be because we've already checked for it previously) - //then we should run the normal FinishedAction - PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); + //then we should run the normal FinishedAction + PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); } break; + case "refresh": + PerformRefreshAction(packageId, dir, Request.GetItemAsString("customUrl"), Request.GetItemAsString("customControl")); + break; case "finished": - PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); + PerformFinishedAction(packageId, dir, Request.GetItemAsString("customUrl")); break; default: break; } } - /// - /// Perform the 'Finished' action of the installer - /// - /// - /// - /// - private void PerformFinishedAction(int packageId, string dir, string url) - { - HideAllPanes(); - //string url = _installer.Url; - string packageViewUrl = "installedPackage.aspx?id=" + packageId.ToString(); + /// + /// Perform the 'Finished' action of the installer + /// + /// + /// + /// + private void PerformFinishedAction(int packageId, string dir, string url) + { + HideAllPanes(); + //string url = _installer.Url; + string packageViewUrl = "installedPackage.aspx?id=" + packageId.ToString(CultureInfo.InvariantCulture); bt_viewInstalledPackage.OnClientClick = "document.location = '" + packageViewUrl + "'; return false;"; @@ -292,23 +298,65 @@ namespace umbraco.presentation.developer.packages pane_success.Visible = true; - PerformPostInstallCleanup(packageId, dir); - } + PerformPostInstallCleanup(packageId, dir); + } - /// - /// Runs Post install actions such as clearning any necessary cache, reloading the correct tree nodes, etc... - /// - /// - /// - private void PerformPostInstallCleanup(int packageId, string dir) - { - BasePage.Current.ClientTools.ReloadActionNode(true, true); - _installer.InstallCleanUp(packageId, dir); - //clear the tree cache - ClientTools.ClearClientTreeCache().RefreshTree("packager"); - TreeDefinitionCollection.Instance.ReRegisterTrees(); - BizLogicAction.ReRegisterActionsAndHandlers(); - } + /// + /// Perform the 'Refresh' action of the installer + /// + /// + /// + /// + /// + private void PerformRefreshAction(int packageId, string dir, string url, string customControl) + { + HideAllPanes(); + + //create the URL to refresh to + // /umbraco/developer/packages/installer.aspx?installing=finished + // &dir=X:\Projects\Umbraco\Umbraco_7.0\src\Umbraco.Web.UI\App_Data\aef8c41f-63a0-494b-a1e2-10d761647033 + // &pId=3 + // &customUrl=http:%2f%2four.umbraco.org%2fprojects%2fwebsite-utilities%2fmerchello + + if (customControl.IsNullOrWhiteSpace()) + { + RefreshQueryString = Server.UrlEncode(string.Format( + "installing=finished&dir={0}&pId={1}&customUrl={2}", + dir, packageId, url)); + } + else + { + RefreshQueryString = Server.UrlEncode(string.Format( + "installing=customInstaller&dir={0}&pId={1}&customUrl={2}&customControl={3}", + dir, packageId, url, customControl)); + } + + pane_refresh.Visible = true; + + PerformPostInstallCleanup(packageId, dir); + } + + /// + /// Runs Post refresh actions such reloading the correct tree nodes, etc... + /// + private void PerformPostRefreshAction() + { + BasePage.Current.ClientTools.ReloadActionNode(true, true); + } + + /// + /// Runs Post install actions such as clearning any necessary cache, reloading the correct tree nodes, etc... + /// + /// + /// + private void PerformPostInstallCleanup(int packageId, string dir) + { + _installer.InstallCleanUp(packageId, dir); + //clear the tree cache - we'll do this here even though the browser will reload, but just in case it doesn't can't hurt. + ClientTools.ClearClientTreeCache().RefreshTree("packager"); + TreeDefinitionCollection.Instance.ReRegisterTrees(); + BizLogicAction.ReRegisterActionsAndHandlers(); + } //this accepts the package, creates the manifest and then installs the files. protected void startInstall(object sender, System.EventArgs e) @@ -323,11 +371,11 @@ namespace umbraco.presentation.developer.packages //and then copy over the files. This will take some time if it contains .dlls that will reboot the system.. _installer.InstallFiles(pId, tempFile.Value); - //TODO: This is a total hack, we need to refactor the installer to be just like the package installer during the - // install process and use AJAX to ensure that app pool restarts and restarts PROPERLY before installing the business - // logic. Until then, we are going to put a thread sleep here for 2 seconds in hopes that we always fluke out and the app - // pool will be restarted after redirect. - Thread.Sleep(2000); + //TODO: This is a total hack, we need to refactor the installer to be just like the package installer during the + // install process and use AJAX to ensure that app pool restarts and restarts PROPERLY before installing the business + // logic. Until then, we are going to put a thread sleep here for 2 seconds in hopes that we always fluke out and the app + // pool will be restarted after redirect. + Thread.Sleep(2000); Response.Redirect("installer.aspx?installing=businesslogic&dir=" + tempFile.Value + "&pId=" + pId.ToString()); } @@ -339,7 +387,428 @@ namespace umbraco.presentation.developer.packages pane_installing.Visible = false; pane_optional.Visible = false; pane_success.Visible = false; + pane_refresh.Visible = false; pane_upload.Visible = false; } + + /// + /// Panel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.UmbracoPanel Panel1; + + /// + /// fb control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Feedback fb; + + /// + /// pane_upload control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_upload; + + /// + /// PropertyPanel9 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel9; + + /// + /// file1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputFile file1; + + /// + /// ButtonLoadPackage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button ButtonLoadPackage; + + /// + /// progbar1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.ProgressBar progbar1; + + /// + /// pane_authenticate control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_authenticate; + + /// + /// tb_email control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox tb_email; + + /// + /// PropertyPanel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; + + /// + /// tb_password control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox tb_password; + + /// + /// PropertyPanel2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; + + /// + /// Button1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button Button1; + + /// + /// pane_acceptLicense control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel pane_acceptLicense; + + /// + /// pane_acceptLicenseInner control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_acceptLicenseInner; + + /// + /// PropertyPanel3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; + + /// + /// LabelName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelName; + + /// + /// PropertyPanel5 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel5; + + /// + /// LabelAuthor control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelAuthor; + + /// + /// PropertyPanel4 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel4; + + /// + /// LabelMore control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelMore; + + /// + /// PropertyPanel6 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel6; + + /// + /// LabelLicense control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label LabelLicense; + + /// + /// PropertyPanel7 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel7; + + /// + /// acceptCheckbox control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CheckBox acceptCheckbox; + + /// + /// PropertyPanel8 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel8; + + /// + /// readme control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal readme; + + /// + /// pp_unsecureFiles control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_unsecureFiles; + + /// + /// lt_files control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lt_files; + + /// + /// pp_macroConflicts control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_macroConflicts; + + /// + /// ltrMacroAlias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal ltrMacroAlias; + + /// + /// pp_templateConflicts control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_templateConflicts; + + protected global::umbraco.uicontrols.PropertyPanel BinaryFileErrorsPanel; + protected global::umbraco.uicontrols.PropertyPanel LegacyPropertyEditorPanel; + protected global::System.Web.UI.WebControls.Literal BinaryFileErrorReport; + + /// + /// ltrTemplateAlias control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal ltrTemplateAlias; + + /// + /// pp_stylesheetConflicts control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_stylesheetConflicts; + + /// + /// ltrStylesheetNames control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal ltrStylesheetNames; + + /// + /// _progbar1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.ProgressBar _progbar1; + + /// + /// ButtonInstall control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button ButtonInstall; + + /// + /// pane_installing control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_installing; + + /// + /// progBar2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.ProgressBar progBar2; + + /// + /// lit_installStatus control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lit_installStatus; + + /// + /// pane_optional control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_optional; + + /// + /// pane_success control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_success; + + protected global::umbraco.uicontrols.Pane pane_refresh; + + /// + /// bt_viewInstalledPackage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_viewInstalledPackage; + + /// + /// lit_authorUrl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal lit_authorUrl; + + /// + /// tempFile control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; + + /// + /// processState control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden processState; } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs deleted file mode 100644 index abe1f67506..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/installer.aspx.designer.cs +++ /dev/null @@ -1,433 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.developer.packages { - - - public partial class Installer { - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.UmbracoPanel Panel1; - - /// - /// fb control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Feedback fb; - - /// - /// pane_upload control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_upload; - - /// - /// PropertyPanel9 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel9; - - /// - /// file1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputFile file1; - - /// - /// ButtonLoadPackage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button ButtonLoadPackage; - - /// - /// progbar1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar progbar1; - - /// - /// pane_authenticate control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_authenticate; - - /// - /// tb_email control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_email; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; - - /// - /// tb_password control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_password; - - /// - /// PropertyPanel2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; - - /// - /// Button1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button Button1; - - /// - /// pane_acceptLicense control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel pane_acceptLicense; - - /// - /// pane_acceptLicenseInner control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_acceptLicenseInner; - - /// - /// PropertyPanel3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; - - /// - /// LabelName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelName; - - /// - /// PropertyPanel5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel5; - - /// - /// LabelAuthor control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelAuthor; - - /// - /// PropertyPanel4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel4; - - /// - /// LabelMore control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelMore; - - /// - /// PropertyPanel6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel6; - - /// - /// LabelLicense control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label LabelLicense; - - /// - /// PropertyPanel7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel7; - - /// - /// acceptCheckbox control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox acceptCheckbox; - - /// - /// PropertyPanel8 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel8; - - /// - /// readme control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal readme; - - /// - /// pp_unsecureFiles control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_unsecureFiles; - - /// - /// lt_files control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lt_files; - - /// - /// pp_macroConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_macroConflicts; - - /// - /// ltrMacroAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrMacroAlias; - - /// - /// pp_templateConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_templateConflicts; - - protected global::umbraco.uicontrols.PropertyPanel BinaryFileErrorsPanel; - protected global::umbraco.uicontrols.PropertyPanel LegacyPropertyEditorPanel; - protected global::System.Web.UI.WebControls.Literal BinaryFileErrorReport; - - /// - /// ltrTemplateAlias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrTemplateAlias; - - /// - /// pp_stylesheetConflicts control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_stylesheetConflicts; - - /// - /// ltrStylesheetNames control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal ltrStylesheetNames; - - /// - /// _progbar1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar _progbar1; - - /// - /// ButtonInstall control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button ButtonInstall; - - /// - /// pane_installing control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_installing; - - /// - /// progBar2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.ProgressBar progBar2; - - /// - /// lit_installStatus control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lit_installStatus; - - /// - /// pane_optional control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_optional; - - /// - /// pane_success control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_success; - - /// - /// bt_viewInstalledPackage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_viewInstalledPackage; - - /// - /// lit_authorUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal lit_authorUrl; - - /// - /// tempFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; - - /// - /// processState control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden processState; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs index 208b2a9780..78c4549a55 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs @@ -8,6 +8,7 @@ using System.Web.SessionState; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; +using Umbraco.Web; using umbraco.BusinessLogic; using umbraco.BusinessLogic.Actions; using umbraco.cms.businesslogic.web; @@ -32,9 +33,9 @@ namespace umbraco.dialogs int docId; if (int.TryParse(Request.QueryString["id"], out docId)) { - - //TODO Send to publish!! - + //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here + ApplicationContext.Services.NotificationService.SendNotification( + ApplicationContext.Services.ContentService.GetById(docId), ActionToPublish.Instance, ApplicationContext); } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx deleted file mode 100644 index 19edd39dfc..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx +++ /dev/null @@ -1,170 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" Codebehind="protectPage.aspx.cs" AutoEventWireup="True" Inherits="umbraco.presentation.umbraco.dialogs.protectPage" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> - - - - - - - - - - - - - - - - - -
          -

          <%= umbraco.ui.Text("publicAccess", "paSimple", base.getUser())%>

          -

          <%= umbraco.ui.Text("publicAccess", "paSimpleHelp", base.getUser())%>

          -
          -
          - - -
          -

          <%= umbraco.ui.Text("publicAccess", "paAdvanced", base.getUser())%>

          -

          <%= umbraco.ui.Text("publicAccess", "paAdvancedHelp", base.getUser())%>

          - - -

          - <%= umbraco.ui.Text("publicAccess", "paAdvancedNoGroups", base.getUser())%> -

          -
          - -
          -
          -

          -   <%= umbraco.ui.Text("or") %>  <%=umbraco.ui.Text("cancel")%> -

          -
          - - - - -

          <%= umbraco.ui.Text("publicAccess", "paSetLogin", base.getUser())%>

          -
          - - - - - - -
          - - - -

          <%= umbraco.ui.Text("publicAccess", "paSelectRoles", base.getUser())%>

          -
          - - - -
          - - - - - -
          - - <%=umbraco.ui.Text("paLoginPageHelp")%> - -

          -
          - - - -
          - - <%=umbraco.ui.Text("paErrorPageHelp")%> - -
          -
          - -
          -

          - - -   <%= umbraco.ui.Text("or") %>  <%=umbraco.ui.Text("cancel")%> -

          -
          - - -
          - - - - - - - \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs index b2116c7cf4..c914b9350b 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs @@ -1,13 +1,17 @@ -using System; +using System; +using System.Globalization; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Windows.Forms.VisualStyles; +using Umbraco.Core; +using Umbraco.Core.Logging; using umbraco.cms.businesslogic.member; using umbraco.cms.businesslogic.web; using umbraco.controls; using umbraco.cms.helpers; using umbraco.BasePages; +using Umbraco.Core.Security; namespace umbraco.presentation.umbraco.dialogs { @@ -22,11 +26,16 @@ namespace umbraco.presentation.umbraco.dialogs } - protected System.Web.UI.WebControls.Literal jsShowWindow; + protected Literal jsShowWindow; protected DualSelectbox _memberGroups = new DualSelectbox(); protected ContentPicker loginPagePicker = new ContentPicker(); protected ContentPicker errorPagePicker = new ContentPicker(); + override protected void OnInit(EventArgs e) + { + base.OnInit(e); + } + protected void selectMode(object sender, EventArgs e) { p_mode.Visible = false; @@ -46,11 +55,11 @@ namespace umbraco.presentation.umbraco.dialogs } } - protected void Page_Load(object sender, System.EventArgs e) + protected void Page_Load(object sender, EventArgs e) { // Check for editing int documentId = int.Parse(helper.Request("nodeId")); - cms.businesslogic.web.Document documentObject = new cms.businesslogic.web.Document(documentId); + var documentObject = new Document(documentId); jsShowWindow.Text = ""; ph_errorpage.Controls.Add(errorPagePicker); @@ -61,12 +70,12 @@ namespace umbraco.presentation.umbraco.dialogs pp_loginPage.Text = ui.Text("paLoginPage"); pp_errorPage.Text = ui.Text("paErrorPage"); - pane_chooseMode.Text = ui.Text("publicAccess", "paHowWould", base.getUser()); - pane_pages.Text = ui.Text("publicAccess", "paSelectPages", base.getUser()); - pane_simple.Text = ui.Text("publicAccess", "paSimple", base.getUser()); - pane_advanced.Text = ui.Text("publicAccess", "paAdvanced", base.getUser()); + pane_chooseMode.Text = ui.Text("publicAccess", "paHowWould", UmbracoUser); + pane_pages.Text = ui.Text("publicAccess", "paSelectPages", UmbracoUser); + pane_simple.Text = ui.Text("publicAccess", "paSimple", UmbracoUser); + pane_advanced.Text = ui.Text("publicAccess", "paAdvanced", UmbracoUser); - if (!IsPostBack) + if (IsPostBack == false) { if (Access.IsProtected(documentId, documentObject.Path) && Access.GetProtectionType(documentId) != ProtectionType.NotProtected) { @@ -78,23 +87,27 @@ namespace umbraco.presentation.umbraco.dialogs int loginPage = Access.GetLoginPage(documentObject.Path); try { - Document loginPageObj = new Document(loginPage); + var loginPageObj = new Document(loginPage); if (loginPageObj != null) { - loginPagePicker.Value = loginPage.ToString(); + loginPagePicker.Value = loginPage.ToString(CultureInfo.InvariantCulture); } - Document errorPageObj = new Document(errorPage); - errorPagePicker.Value = errorPage.ToString(); + var errorPageObj = new Document(errorPage); + errorPagePicker.Value = errorPage.ToString(CultureInfo.InvariantCulture); } - catch + catch (Exception ex) { + LogHelper.Error("An error occurred initializing the protect page editor", ex); } if (Access.GetProtectionType(documentId) == ProtectionType.Simple) { - MembershipUser m = Access.GetAccessingMembershipUser(documentId); - simpleLogin.Text = m.UserName; + MembershipUser m = Access.GetAccessingMembershipUser(documentId); pane_simple.Visible = true; + pp_pass.Visible = false; + simpleLogin.Visible = false; + SimpleLoginLabel.Visible = true; + SimpleLoginLabel.Text = m.UserName; pane_advanced.Visible = false; bt_protect.CommandName = "simple"; @@ -114,17 +127,17 @@ namespace umbraco.presentation.umbraco.dialogs // Load up membergrouops _memberGroups.ID = "Membergroups"; _memberGroups.Width = 175; - string selectedGroups = ""; - string[] _roles = Roles.GetAllRoles(); + var selectedGroups = ""; + var roles = Roles.GetAllRoles(); - if (_roles.Length > 0) + if (roles.Length > 0) { - foreach (string role in _roles) + foreach (string role in roles) { ListItem li = new ListItem(role, role); - if (!IsPostBack) + if (IsPostBack == false) { - if (cms.businesslogic.web.Access.IsProtectedByMembershipRole(int.Parse(helper.Request("nodeid")), role)) + if (Access.IsProtectedByMembershipRole(int.Parse(helper.Request("nodeid")), role)) selectedGroups += role + ","; } _memberGroups.Items.Add(li); @@ -145,26 +158,14 @@ namespace umbraco.presentation.umbraco.dialogs // Put user code to initialize the page here } - #region Web Form Designer generated code - override protected void OnInit(EventArgs e) + protected void ChangeOnClick(object sender, EventArgs e) { - // - // CODEGEN: This call is required by the ASP.NET Web Form Designer. - // - InitializeComponent(); - base.OnInit(e); + SimpleLoginNameValidator.IsValid = true; + SimpleLoginLabel.Visible = false; + simpleLogin.Visible = true; + pp_pass.Visible = true; } - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - - } - #endregion - protected void protect_Click(object sender, CommandEventArgs e) { if (string.IsNullOrEmpty(errorPagePicker.Value)) @@ -173,63 +174,64 @@ namespace umbraco.presentation.umbraco.dialogs if (string.IsNullOrEmpty(loginPagePicker.Value)) cv_loginPage.IsValid = false; + //reset + SimpleLoginNameValidator.IsValid = true; if (Page.IsValid) { int pageId = int.Parse(helper.Request("nodeId")); - p_buttons.Visible = false; - pane_advanced.Visible = false; - pane_simple.Visible = false; - + if (e.CommandName == "simple") { - MembershipUser member = Membership.GetUser(simpleLogin.Text); + var memberLogin = simpleLogin.Visible ? simpleLogin.Text : SimpleLoginLabel.Text; + + var member = Membership.GetUser(memberLogin); if (member == null) { + var tempEmail = "u" + Guid.NewGuid().ToString("N") + "@example.com"; + // this needs to work differently depending on umbraco members or external membership provider - if (!cms.businesslogic.member.Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider() == false) { - member = Membership.CreateUser(simpleLogin.Text, simplePassword.Text); + member = Membership.CreateUser(memberLogin, simplePassword.Text, tempEmail); } else { - try + //if it's the umbraco membership provider, then we need to tell it what member type to create it with + if (MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType) == null) { - if ( - cms.businesslogic.member.MemberType.GetByAlias("_umbracoSystemDefaultProtectType") == null) - { - cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), "_umbracoSystemDefaultProtectType"); - } + MemberType.MakeNew(BusinessLogic.User.GetUser(0), Constants.Conventions.MemberTypes.SystemDefaultProtectType); } - catch + var provider = Membership.Provider.AsUmbracoMembershipProvider(); + MembershipCreateStatus status; + member = provider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType, + memberLogin, simplePassword.Text, tempEmail, null, null, true, null, out status); + if (status != MembershipCreateStatus.Success) { - cms.businesslogic.member.MemberType.MakeNew(BusinessLogic.User.GetUser(0), "_umbracoSystemDefaultProtectType"); + SimpleLoginNameValidator.IsValid = false; + SimpleLoginNameValidator.ErrorMessage = "Could not create user: " + status; + SimpleLoginNameValidator.Text = "Could not create user: " + status; + return; } - - // create member - Member mem = cms.businesslogic.member.Member.MakeNew(simpleLogin.Text, "", cms.businesslogic.member.MemberType.GetByAlias("_umbracoSystemDefaultProtectType"), base.getUser()); - // working around empty password restriction for Umbraco Member Mode - mem.Password = simplePassword.Text; - member = Membership.GetUser(simpleLogin.Text); } } - else + else if (pp_pass.Visible) { - // change password if it's not empty - if (string.IsNullOrWhiteSpace(simplePassword.Text) == false) - { - var mem = Member.GetMemberFromLoginName(simpleLogin.Text); - mem.Password = simplePassword.Text; - } + SimpleLoginNameValidator.IsValid = false; + SimpleLoginLabel.Visible = true; + SimpleLoginLabel.Text = memberLogin; + simpleLogin.Visible = false; + pp_pass.Visible = false; + return; } // Create or find a memberGroup - string simpleRoleName = "__umbracoRole_" + simpleLogin.Text; - if (!Roles.RoleExists(simpleRoleName)) + var simpleRoleName = "__umbracoRole_" + member.UserName; + if (Roles.RoleExists(simpleRoleName) == false) { Roles.CreateRole(simpleRoleName); } - if (!Roles.IsUserInRole(member.UserName, simpleRoleName)) + if (Roles.IsUserInRole(member.UserName, simpleRoleName) == false) { Roles.AddUserToRole(member.UserName, simpleRoleName); } @@ -240,17 +242,21 @@ namespace umbraco.presentation.umbraco.dialogs } else if (e.CommandName == "advanced") { - cms.businesslogic.web.Access.ProtectPage(false, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value)); + Access.ProtectPage(false, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value)); foreach (ListItem li in _memberGroups.Items) - if (("," + _memberGroups.Value + ",").IndexOf("," + li.Value + ",") > -1) - cms.businesslogic.web.Access.AddMembershipRoleToDocument(pageId, li.Value); + if (("," + _memberGroups.Value + ",").IndexOf("," + li.Value + ",", StringComparison.Ordinal) > -1) + Access.AddMembershipRoleToDocument(pageId, li.Value); else - cms.businesslogic.web.Access.RemoveMembershipRoleFromDocument(pageId, li.Value); + Access.RemoveMembershipRoleFromDocument(pageId, li.Value); } feedback.Text = ui.Text("publicAccess", "paIsProtected", new cms.businesslogic.CMSNode(pageId).Text) + "

          " + ui.Text("closeThisWindow") + ""; + p_buttons.Visible = false; + pane_advanced.Visible = false; + pane_simple.Visible = false; + ClientTools.ReloadActionNode(true, false); feedback.type = global::umbraco.uicontrols.Feedback.feedbacktype.success; @@ -273,5 +279,289 @@ namespace umbraco.presentation.umbraco.dialogs feedback.type = global::umbraco.uicontrols.Feedback.feedbacktype.success; } + + protected CustomValidator SimpleLoginNameValidator; + protected Label SimpleLoginLabel; + + ///

          + /// tempFile control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; + + /// + /// feedback control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Feedback feedback; + + /// + /// p_mode control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel p_mode; + + /// + /// pane_chooseMode control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_chooseMode; + + /// + /// rb_simple control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RadioButton rb_simple; + + /// + /// rb_advanced control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RadioButton rb_advanced; + + /// + /// p_noGroupsFound control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel p_noGroupsFound; + + /// + /// bt_selectMode control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_selectMode; + + /// + /// pane_simple control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_simple; + + /// + /// PropertyPanel1 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; + + /// + /// pp_login control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_login; + + /// + /// simpleLogin control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox simpleLogin; + + /// + /// pp_pass control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_pass; + + /// + /// simplePassword control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox simplePassword; + + /// + /// pane_advanced control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_advanced; + + /// + /// PropertyPanel3 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; + + /// + /// PropertyPanel2 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; + + /// + /// groupsSelector control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder groupsSelector; + + /// + /// p_buttons control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel p_buttons; + + /// + /// pane_pages control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.Pane pane_pages; + + /// + /// pp_loginPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_loginPage; + + /// + /// ph_loginpage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder ph_loginpage; + + /// + /// cv_loginPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CustomValidator cv_loginPage; + + /// + /// pp_errorPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::umbraco.uicontrols.PropertyPanel pp_errorPage; + + /// + /// ph_errorpage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder ph_errorpage; + + /// + /// cv_errorPage control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.CustomValidator cv_errorPage; + + /// + /// bt_protect control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_protect; + + /// + /// bt_buttonRemoveProtection control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button bt_buttonRemoveProtection; + + /// + /// errorId control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden errorId; + + /// + /// loginId control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputHidden loginId; + + /// + /// js control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder js; + + } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs deleted file mode 100644 index 6ed66eea24..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.designer.cs +++ /dev/null @@ -1,295 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:2.0.50727.4200 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.umbraco.dialogs { - - - public partial class protectPage { - - /// - /// tempFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; - - /// - /// feedback control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Feedback feedback; - - /// - /// p_mode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_mode; - - /// - /// pane_chooseMode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_chooseMode; - - /// - /// rb_simple control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButton rb_simple; - - /// - /// rb_advanced control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButton rb_advanced; - - /// - /// p_noGroupsFound control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_noGroupsFound; - - /// - /// bt_selectMode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_selectMode; - - /// - /// pane_simple control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_simple; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1; - - /// - /// pp_login control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_login; - - /// - /// simpleLogin control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox simpleLogin; - - /// - /// pp_pass control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_pass; - - /// - /// simplePassword control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox simplePassword; - - /// - /// pane_advanced control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_advanced; - - /// - /// PropertyPanel3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel3; - - /// - /// PropertyPanel2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel PropertyPanel2; - - /// - /// groupsSelector control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder groupsSelector; - - /// - /// p_buttons control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_buttons; - - /// - /// pane_pages control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane pane_pages; - - /// - /// pp_loginPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_loginPage; - - /// - /// ph_loginpage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_loginpage; - - /// - /// cv_loginPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator cv_loginPage; - - /// - /// pp_errorPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_errorPage; - - /// - /// ph_errorpage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_errorpage; - - /// - /// cv_errorPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator cv_errorPage; - - /// - /// bt_protect control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_protect; - - /// - /// bt_buttonRemoveProtection control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_buttonRemoveProtection; - - /// - /// errorId control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden errorId; - - /// - /// loginId control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden loginId; - - /// - /// js control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder js; - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs index c165310b3c..bc95326f49 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/MemberSearch.ascx.cs @@ -6,6 +6,7 @@ using Examine.LuceneEngine.SearchCriteria; using Examine.SearchCriteria; using umbraco.cms.businesslogic.member; using System.Web.Security; +using Umbraco.Core.Security; namespace umbraco.presentation.umbraco.members { @@ -13,7 +14,7 @@ namespace umbraco.presentation.umbraco.members { protected void Page_Load(object sender, EventArgs e) { - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) ButtonSearch.Text = ui.Text("search"); } @@ -22,7 +23,7 @@ namespace umbraco.presentation.umbraco.members { resultsPane.Visible = true; - if (Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { var query = searchQuery.Text.ToLower(); var internalSearcher = UmbracoContext.Current.InternalMemberSearchProvider; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs index 271f71e8b9..e26847b1e7 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/members/ViewMembers.aspx.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Web; +using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; +using Umbraco.Core.Security; namespace umbraco.presentation.members { public partial class ViewMembers : BasePages.UmbracoEnsuredPage { @@ -20,7 +22,7 @@ namespace umbraco.presentation.members { private void bindRp() { string _letter = Request.QueryString["letter"]; if (!string.IsNullOrEmpty(_letter)) { - if (cms.businesslogic.member.Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { if (_letter == "#") { @@ -41,7 +43,7 @@ namespace umbraco.presentation.members { public void bindMember(object sender, RepeaterItemEventArgs e) { if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { - if (cms.businesslogic.member.Member.InUmbracoMemberMode()) + if (Membership.Provider.IsUmbracoMembershipProvider()) { cms.businesslogic.member.Member mem = (cms.businesslogic.member.Member)e.Item.DataItem; Literal _name = (Literal)e.Item.FindControl("lt_name"); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs index 00d1cd76f4..2e5749c4f9 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs @@ -1,9 +1,13 @@ using System; using System.Data; +using System.Globalization; using System.IO; using System.Text; using System.Xml; +using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Web; using umbraco.BasePages; using umbraco.BusinessLogic; using umbraco.BusinessLogic.Actions; @@ -191,22 +195,15 @@ namespace umbraco.presentation.translation if (t != null) { //user auth and content node validation - if (t.Node.Id == int.Parse(taskNode.Attributes.GetNamedItem("id").Value) && (t.User.Id == base.getUser().Id || t.ParentUser.Id == base.getUser().Id)) + if (t.Node.Id == int.Parse(taskNode.Attributes.GetNamedItem("id").Value) && (t.User.Id == UmbracoUser.Id || t.ParentUser.Id == UmbracoUser.Id)) { // update node contents var d = new Document(t.Node.Id); - Document.Import(d.ParentId, getUser(), (XmlElement)taskNode); + Document.Import(d.ParentId, UmbracoUser, (XmlElement)taskNode); - /* d.Text = taskNode.Attributes.GetNamedItem("nodeName").Value.Trim(); - - // update data elements - foreach (XmlNode data in taskNode.SelectNodes("data")) - if (data.FirstChild != null) - d.getProperty(data.Attributes.GetNamedItem("alias").Value).Value = data.FirstChild.Value; - else - d.getProperty(data.Attributes.GetNamedItem("alias").Value).Value = ""; - */ + //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here + ApplicationContext.Services.NotificationService.SendNotification(d.Content, ActionTranslate.Instance, ApplicationContext); t.Closed = true; t.Save(); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs index a6e2d7e402..1dfa3362da 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs @@ -7,8 +7,12 @@ using System.Linq; using System.Web.Script.Services; using System.Web.Services; using System.Xml; +using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Web; using Umbraco.Web.WebServices; using umbraco.BasePages; using umbraco.BusinessLogic; @@ -160,12 +164,16 @@ namespace umbraco.presentation.webservices if (parentNode != null) content.SortNodes(ref parentNode); + //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here + ApplicationContext.Services.NotificationService.SendNotification(contentService.GetById(parentId), ActionSort.Instance, UmbracoContext, ApplicationContext); + } catch (Exception ex) { LogHelper.Error("Could not update content sort order", ex); } } + } [Serializable] diff --git a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj index db7b95bd7a..22792d6aa0 100644 --- a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj +++ b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj @@ -40,11 +40,11 @@ ..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll - + False ..\packages\Examine.0.1.52.2941\lib\Examine.dll - + False ..\packages\Examine.Azure.0.1.51.2941\lib\Examine.Azure.dll diff --git a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj index d17a4c4921..203fc935dc 100644 --- a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj +++ b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj @@ -40,11 +40,11 @@ ..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll - + False ..\packages\Examine.0.1.52.2941\lib\Examine.dll - + False ..\packages\Examine.Azure.0.1.51.2941\lib\Examine.Azure.dll diff --git a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj index 6bdc3d3666..aed13c7c13 100644 --- a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj +++ b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj @@ -46,7 +46,7 @@ false - + False ..\packages\Examine.0.1.52.2941\lib\Examine.dll diff --git a/src/UmbracoExamine/UmbracoExamine.csproj b/src/UmbracoExamine/UmbracoExamine.csproj index c180189a64..7bcb270bce 100644 --- a/src/UmbracoExamine/UmbracoExamine.csproj +++ b/src/UmbracoExamine/UmbracoExamine.csproj @@ -82,7 +82,7 @@ ..\Solution Items\TheFARM-Public.snk - + False ..\packages\Examine.0.1.52.2941\lib\Examine.dll diff --git a/src/umbraco.businesslogic/StateHelper.cs b/src/umbraco.businesslogic/StateHelper.cs index 10e81dd614..e84d3aa129 100644 --- a/src/umbraco.businesslogic/StateHelper.cs +++ b/src/umbraco.businesslogic/StateHelper.cs @@ -349,7 +349,7 @@ namespace umbraco.BusinessLogic * we currently reproduce this by configuring each cookie with a 30d expires, but does * that actually make sense? shouldn't some cookie have _no_ expires? */ - static readonly Cookie _preview = new Cookie(Constants.Web.PreviewCookieName, 30d); // was "PreviewSet" + static readonly Cookie _preview = new Cookie(Constants.Web.PreviewCookieName, TimeSpan.Zero); // was "PreviewSet" static readonly Cookie _userContext = new Cookie(Constants.Web.AuthCookieName, 30d); // was "UserContext" static readonly Cookie _member = new Cookie("UMB_MEMBER", 30d); // was "umbracoMember" @@ -431,7 +431,7 @@ namespace umbraco.BusinessLogic } public void SetValue(string value) { - SetValueWithDate(value, DateTime.Now + _expires); + SetValueWithDate(value, _expires == TimeSpan.Zero ? DateTime.MinValue : DateTime.Now + _expires); } public void SetValue(string value, double days) @@ -441,7 +441,7 @@ namespace umbraco.BusinessLogic public void SetValue(string value, TimeSpan expires) { - SetValue(value, DateTime.Now + expires); + SetValue(value, expires == TimeSpan.Zero ? DateTime.MinValue : DateTime.Now + expires); } public void SetValue(string value, DateTime expires) @@ -451,7 +451,7 @@ namespace umbraco.BusinessLogic private void SetValueWithDate(string value, DateTime expires) { - HttpCookie cookie = new HttpCookie(_key, value); + var cookie = new HttpCookie(_key, value); if (GlobalSettings.UseSSL) cookie.Secure = true; @@ -459,7 +459,12 @@ namespace umbraco.BusinessLogic //ensure http only, this should only be able to be accessed via the server cookie.HttpOnly = true; - cookie.Expires = expires; + //set an expiry date if not min value, otherwise leave it as a session cookie. + if (expires != DateTime.MinValue) + { + cookie.Expires = expires; + } + ResponseCookie = cookie; // original Umbraco code also does this @@ -473,7 +478,7 @@ namespace umbraco.BusinessLogic { if (RequestCookie != null || ResponseCookie != null) { - HttpCookie cookie = new HttpCookie(_key); + var cookie = new HttpCookie(_key); cookie.Expires = DateTime.Now.AddDays(-1); ResponseCookie = cookie; } diff --git a/src/umbraco.cms/businesslogic/ContentType.cs b/src/umbraco.cms/businesslogic/ContentType.cs index 71d676f54d..1f14459c40 100644 --- a/src/umbraco.cms/businesslogic/ContentType.cs +++ b/src/umbraco.cms/businesslogic/ContentType.cs @@ -199,10 +199,17 @@ namespace umbraco.cms.businesslogic /// The Alias of the ContentType /// The Iconurl of Contents of this ContentType protected static void Create(int NodeId, string Alias, string IconUrl) + { + Create(NodeId, Alias, IconUrl, true); + } + + internal static void Create(int nodeId, string alias, string iconUrl, bool formatAlias) { SqlHelper.ExecuteNonQuery( - "Insert into cmsContentType (nodeId,alias,icon) values (" + NodeId + ",'" + helpers.Casing.SafeAliasWithForcingCheck(Alias) + - "','" + IconUrl + "')"); + "Insert into cmsContentType (nodeId,alias,icon) values (" + + nodeId + ",'" + + (formatAlias ? helpers.Casing.SafeAliasWithForcingCheck(alias) : alias) + + "','" + iconUrl + "')"); } /// diff --git a/src/umbraco.cms/businesslogic/member/Member.cs b/src/umbraco.cms/businesslogic/member/Member.cs index 66f7114dd8..2ae976a45a 100644 --- a/src/umbraco.cms/businesslogic/member/Member.cs +++ b/src/umbraco.cms/businesslogic/member/Member.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Data; using System.Diagnostics; +using System.Globalization; using System.Runtime.CompilerServices; using System.Web; using System.Xml; @@ -12,6 +13,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Querying; using umbraco.cms.businesslogic.cache; using umbraco.BusinessLogic; using umbraco.DataLayer; @@ -19,6 +21,7 @@ using System.Web.Security; using System.Text; using System.Security.Cryptography; using System.Linq; +using Umbraco.Core.Security; namespace umbraco.cms.businesslogic.member { @@ -64,6 +67,12 @@ namespace umbraco.cms.businesslogic.member #region Constructors + internal Member(IMember member) + : base(member) + { + SetupNode(member); + } + /// /// Initializes a new instance of the Member class. /// @@ -104,20 +113,10 @@ namespace umbraco.cms.businesslogic.member public static IEnumerable GetAllAsList() { - var tmp = new List(); - using (var dr = SqlHelper.ExecuteReader( - string.Format(_sQLOptimizedMany.Trim(), "1=1", "umbracoNode.text"), - SqlHelper.CreateParameter("@nodeObjectType", Member._objectType))) - { - while (dr.Read()) - { - var m = new Member(dr.GetInt("id"), true); - m.PopulateMemberFromReader(dr); - tmp.Add(m); - } - } - - return tmp.ToArray(); + int totalRecs; + return ApplicationContext.Current.Services.MemberService.GetAllMembers(0, int.MaxValue, out totalRecs) + .Select(x => new Member(x)) + .ToArray(); } /// @@ -150,7 +149,11 @@ namespace umbraco.cms.businesslogic.member /// public static Member[] getMemberFromFirstLetter(char letter) { - return GetMemberByName(letter.ToString(), true); + int totalRecs; + return ApplicationContext.Current.Services.MemberService.FindMembersByUsername( + letter.ToString(CultureInfo.InvariantCulture), 0, int.MaxValue, out totalRecs, StringPropertyMatchType.StartsWith) + .Select(x => new Member(x)) + .ToArray(); } public static Member[] GetMemberByName(string usernameToMatch, bool matchByNameInsteadOfLogin) @@ -211,11 +214,12 @@ namespace umbraco.cms.businesslogic.member /// The umbraco usercontext /// The email of the user /// The new member + [MethodImpl(MethodImplOptions.Synchronized)] public static Member MakeNew(string Name, string LoginName, string Email, MemberType mbt, User u) { if (mbt == null) throw new ArgumentNullException("mbt"); - var loginName = (!string.IsNullOrEmpty(LoginName)) ? LoginName : Name; + var loginName = (string.IsNullOrEmpty(LoginName) == false) ? LoginName : Name; if (string.IsNullOrEmpty(loginName)) throw new ArgumentException("The loginname must be different from an empty string", "loginName"); @@ -268,27 +272,12 @@ namespace umbraco.cms.businesslogic.member /// The member with the specified loginname - null if no Member with the login exists public static Member GetMemberFromLoginName(string loginName) { - if (string.IsNullOrEmpty(loginName)) - throw new ArgumentException("The username of a Member must be different from an emptry string", "loginName"); - if (IsMember(loginName)) - { - var o = SqlHelper.ExecuteScalar( - "select nodeID from cmsMember where LoginName = @loginName", - SqlHelper.CreateParameter("@loginName", loginName)); + Mandate.ParameterNotNullOrEmpty(loginName, "loginName"); - if (o == null) - return null; + var found = ApplicationContext.Current.Services.MemberService.GetByUsername(loginName); + if (found == null) return null; - int tmpId; - if (!int.TryParse(o.ToString(), out tmpId)) - return null; - - return new Member(tmpId); - } - else - HttpContext.Current.Trace.Warn("No member with loginname: " + loginName + " Exists"); - - return null; + return new Member(found); } /// @@ -303,18 +292,10 @@ namespace umbraco.cms.businesslogic.member if (string.IsNullOrEmpty(email)) return null; - var o = SqlHelper.ExecuteScalar( - "select nodeID from cmsMember where Email = @email", - SqlHelper.CreateParameter("@email", email.ToLower())); + var found = ApplicationContext.Current.Services.MemberService.GetByEmail(email); + if (found == null) return null; - if (o == null) - return null; - - int tmpId; - if (!int.TryParse(o.ToString(), out tmpId)) - return null; - - return new Member(tmpId); + return new Member(found); } /// @@ -329,21 +310,11 @@ namespace umbraco.cms.businesslogic.member if (string.IsNullOrEmpty(email)) return null; - var tmp = new List(); - using (var dr = SqlHelper.ExecuteReader(string.Format(_sQLOptimizedMany.Trim(), - "Email = @email", - "umbracoNode.text"), - SqlHelper.CreateParameter("@nodeObjectType", Member._objectType), - SqlHelper.CreateParameter("@email", email.ToLower()))) - { - while (dr.Read()) - { - var m = new Member(dr.GetInt("id"), true); - m.PopulateMemberFromReader(dr); - tmp.Add(m); - } - } - return tmp.ToArray(); + int totalRecs; + var found = ApplicationContext.Current.Services.MemberService.FindMembersByEmail( + email, 0, int.MaxValue, out totalRecs, StringPropertyMatchType.Exact); + + return found.Select(x => new Member(x)).ToArray(); } /// @@ -394,9 +365,10 @@ namespace umbraco.cms.businesslogic.member return new Member(tmpId); } + [Obsolete("Use MembershipProviderExtensions.IsUmbracoMembershipProvider instead")] public static bool InUmbracoMemberMode() { - return Membership.Provider.Name == UmbracoMemberProviderName; + return Membership.Provider.IsUmbracoMembershipProvider(); } public static bool IsUsingUmbracoRoles() @@ -426,16 +398,7 @@ namespace umbraco.cms.businesslogic.member /// The membertype which are being deleted public static void DeleteFromType(MemberType dt) { - var objs = getContentOfContentType(dt); - foreach (var c in objs) - { - // due to recursive structure document might already been deleted.. - if (IsNode(c.UniqueId)) - { - var tmp = new Member(c.UniqueId); - tmp.delete(); - } - } + ApplicationContext.Current.Services.MemberService.DeleteMembersOfType(dt.Id); } #endregion @@ -774,13 +737,7 @@ namespace umbraco.cms.businesslogic.member /// public string GetPassword() { - if (string.IsNullOrEmpty(_password)) - { - _password = SqlHelper.ExecuteScalar( - "select Password from cmsMember where nodeId = @id", - SqlHelper.CreateParameter("@id", Id)); - } - return _password; + return Content.Password; } /// @@ -1121,19 +1078,7 @@ namespace umbraco.cms.businesslogic.member return HttpContext.Current.User.Identity.IsAuthenticated; } - - - /// - /// Make a lookup in the database to verify if a member truely exists - /// - /// The node id of the member - /// True is a record exists in db - private static bool MemberExists(int nodeId) - { - return ApplicationContext.Current.Services.MemberService.Exists(nodeId); - } - - + /// /// Gets the current visitors memberid /// diff --git a/src/umbraco.cms/businesslogic/member/MemberType.cs b/src/umbraco.cms/businesslogic/member/MemberType.cs index e5ff610cc2..449531315c 100644 --- a/src/umbraco.cms/businesslogic/member/MemberType.cs +++ b/src/umbraco.cms/businesslogic/member/MemberType.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using System.Xml; using Umbraco.Core.Logging; using umbraco.cms.businesslogic.propertytype; @@ -236,6 +237,7 @@ namespace umbraco.cms.businesslogic.member /// /// The name of the MemberType /// Creator of the MemberType + [MethodImpl(MethodImplOptions.Synchronized)] public static MemberType MakeNew(User u, string Text) { int ParentId = -1; @@ -243,7 +245,10 @@ namespace umbraco.cms.businesslogic.member Guid uniqueId = Guid.NewGuid(); CMSNode n = CMSNode.MakeNew(ParentId, _objectType, u.Id, level, Text, uniqueId); - ContentType.Create(n.Id, Text, ""); + ContentType.Create(n.Id, Text, "", + //don't format the alias if it equals this. See: http://issues.umbraco.org/issue/U4-3968 + Text != "_umbracoSystemDefaultProtectType"); + MemberType mt = new MemberType(n.Id); mt.IconUrl = "member.gif"; NewEventArgs e = new NewEventArgs(); diff --git a/src/umbraco.cms/businesslogic/utilities/Diff.cs b/src/umbraco.cms/businesslogic/utilities/Diff.cs index 62397f2495..3aa159de7e 100644 --- a/src/umbraco.cms/businesslogic/utilities/Diff.cs +++ b/src/umbraco.cms/businesslogic/utilities/Diff.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Text; using System.Text.RegularExpressions; +//TODO: We've alraedy moved most of this logic to Core.Strings - need to review this as it has slightly more functionality but should be moved to core and obsoleted! + namespace umbraco.cms.businesslogic.utilities { /// /// This Class implements the Difference Algorithm published in diff --git a/src/umbraco.cms/businesslogic/workflow/Diff.cs b/src/umbraco.cms/businesslogic/workflow/Diff.cs index e4e4d505a9..39cbc1d2c3 100644 --- a/src/umbraco.cms/businesslogic/workflow/Diff.cs +++ b/src/umbraco.cms/businesslogic/workflow/Diff.cs @@ -18,7 +18,7 @@ namespace umbraco.cms.businesslogic.workflow /// Copyright (c) by Matthias Hertel, http://www.mathertel.de /// This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx /// - + [Obsolete("This class will be removed from the codebase in the future")] public class Diff { @@ -489,6 +489,7 @@ namespace umbraco.cms.businesslogic.workflow /// Data on one input file being compared. /// + [Obsolete("This class will be removed from the codebase in the future, logic has moved to the Core project")] internal class DiffData { diff --git a/src/umbraco.cms/businesslogic/workflow/Notification.cs b/src/umbraco.cms/businesslogic/workflow/Notification.cs index b272491362..4a0afbb38d 100644 --- a/src/umbraco.cms/businesslogic/workflow/Notification.cs +++ b/src/umbraco.cms/businesslogic/workflow/Notification.cs @@ -72,138 +72,19 @@ namespace umbraco.cms.businesslogic.workflow } } - ///TODO: Include update with html mail notification and document contents + //TODO: Include update with html mail notification and document contents private static void SendNotification(User performingUser, User mailingUser, Document documentObject, IAction action) { - // retrieve previous version of the document - DocumentVersionList[] versions = documentObject.GetVersions(); - int versionCount = (versions.Length > 1) ? (versions.Length - 2) : (versions.Length - 1); - var oldDoc = new Document(documentObject.Id, versions[versionCount].Version); + var nService = ApplicationContext.Current.Services.NotificationService; + var pUser = ApplicationContext.Current.Services.UserService.GetById(performingUser.Id); - // build summary - var summary = new StringBuilder(); - var props = documentObject.GenericProperties; - foreach (Property p in props) - { - // check if something was changed and display the changes otherwise display the fields - Property oldProperty = oldDoc.getProperty(p.PropertyType.Alias); - string oldText = oldProperty.Value != null ? oldProperty.Value.ToString() : ""; - string newText = p.Value != null ? p.Value.ToString() : ""; - - // replace html with char equivalent - ReplaceHtmlSymbols(ref oldText); - ReplaceHtmlSymbols(ref newText); - - // make sure to only highlight changes done using TinyMCE editor... other changes will be displayed using default summary - //TODO PPH: Had to change this, as a reference to the editorcontrols is not allowed, so a string comparison is the only way, this should be a DIFF or something instead.. - if (p.PropertyType.DataTypeDefinition.DataType.ToString() == - "umbraco.editorControls.tinymce.TinyMCEDataType" && - string.CompareOrdinal(oldText, newText) != 0) - { - summary.Append(""); - summary.Append(" Note: "); - summary.Append( - " Red for deleted characters Yellow for inserted characters"); - summary.Append(""); - summary.Append(""); - summary.Append(" New " + - p.PropertyType.Name + ""); - summary.Append("" + - ReplaceLinks(CompareText(oldText, newText, true, false, - "", string.Empty)) + - ""); - summary.Append(""); - summary.Append(""); - summary.Append(" Old " + - oldProperty.PropertyType.Name + ""); - summary.Append("" + - ReplaceLinks(CompareText(newText, oldText, true, false, - "", string.Empty)) + - ""); - summary.Append(""); - } - else - { - summary.Append(""); - summary.Append("" + - p.PropertyType.Name + ""); - summary.Append("" + newText + ""); - summary.Append(""); - } - summary.Append( - " "); - } - - string protocol = GlobalSettings.UseSSL ? "https" : "http"; - - - string[] subjectVars = { - HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" + - HttpContext.Current.Request.Url.Port + - IOHelper.ResolveUrl(SystemDirectories.Umbraco), ui.Text(action.Alias) - , - documentObject.Text - }; - string[] bodyVars = { - mailingUser.Name, ui.Text(action.Alias), documentObject.Text, performingUser.Name, - HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" + - HttpContext.Current.Request.Url.Port + - IOHelper.ResolveUrl(SystemDirectories.Umbraco), - documentObject.Id.ToString(), summary.ToString(), - String.Format("{2}://{0}/{1}", - HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" + - HttpContext.Current.Request.Url.Port, - /*umbraco.library.NiceUrl(documentObject.Id))*/ - documentObject.Id + ".aspx", - protocol) - //TODO: PPH removed the niceURL reference... cms.dll cannot reference the presentation project... - //TODO: This should be moved somewhere else.. - }; - - // create the mail message - var mail = new MailMessage(UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress, mailingUser.Email); - - // populate the message - mail.Subject = ui.Text("notifications", "mailSubject", subjectVars, mailingUser); - if (UmbracoConfig.For.UmbracoSettings().Content.DisableHtmlEmail) - { - mail.IsBodyHtml = false; - mail.Body = ui.Text("notifications", "mailBody", bodyVars, mailingUser); - } - else - { - mail.IsBodyHtml = true; - mail.Body = - @" - - -" + - ui.Text("notifications", "mailBodyHtml", bodyVars, mailingUser) + ""; - } - - // nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here - // adding the server name to make sure we don't replace external links - if (GlobalSettings.UseSSL && string.IsNullOrEmpty(mail.Body) == false) - { - string serverName = HttpContext.Current.Request.ServerVariables["SERVER_NAME"]; - mail.Body = mail.Body.Replace( - string.Format("http://{0}", serverName), - string.Format("https://{0}", serverName)); - } - - // send it - var sender = new SmtpClient(); - sender.Send(mail); - } - - private static string ReplaceLinks(string text) - { - string domain = GlobalSettings.UseSSL ? "https://" : "http://"; - domain += HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + ":" + - HttpContext.Current.Request.Url.Port + "/"; - text = text.Replace("href=\"/", "href=\"" + domain); - text = text.Replace("src=\"/", "src=\"" + domain); - return text; + nService.SendNotifications( + pUser, documentObject.Content, action.Letter.ToString(CultureInfo.InvariantCulture), ui.Text(action.Alias), + new HttpContextWrapper(HttpContext.Current), + (user, strings) => ui.Text("notifications", "mailSubject", strings, mailingUser), + (user, strings) => UmbracoSettings.NotificationDisableHtmlEmail + ? ui.Text("notifications", "mailBody", strings, mailingUser) + : ui.Text("notifications", "mailBodyHtml", strings, mailingUser)); } /// @@ -328,80 +209,5 @@ namespace umbraco.cms.businesslogic.workflow MakeNew(user, node, c); } - /// - /// Replaces the HTML symbols with the character equivalent. - /// - /// The old string. - private static void ReplaceHtmlSymbols(ref string oldString) - { - oldString = oldString.Replace(" ", " "); - oldString = oldString.Replace("’", "'"); - oldString = oldString.Replace("&", "&"); - oldString = oldString.Replace("“", ""); - oldString = oldString.Replace("”", ""); - oldString = oldString.Replace(""", "\""); - } - - /// - /// Compares the text. - /// - /// The old text. - /// The new text. - /// if set to true [display inserted text]. - /// if set to true [display deleted text]. - /// The inserted style. - /// The deleted style. - /// - private static string CompareText(string oldText, string newText, bool displayInsertedText, - bool displayDeletedText, string insertedStyle, string deletedStyle) - { - var sb = new StringBuilder(); - Diff.Item[] diffs = Diff.DiffText1(oldText, newText); - - int pos = 0; - for (int n = 0; n < diffs.Length; n++) - { - Diff.Item it = diffs[n]; - - // write unchanged chars - while ((pos < it.StartB) && (pos < newText.Length)) - { - sb.Append(newText[pos]); - pos++; - } // while - - // write deleted chars - if (displayDeletedText && it.deletedA > 0) - { - sb.Append(deletedStyle); - for (int m = 0; m < it.deletedA; m++) - { - sb.Append(oldText[it.StartA + m]); - } // for - sb.Append(""); - } - - // write inserted chars - if (displayInsertedText && pos < it.StartB + it.insertedB) - { - sb.Append(insertedStyle); - while (pos < it.StartB + it.insertedB) - { - sb.Append(newText[pos]); - pos++; - } // while - sb.Append(""); - } // if - } // while - - // write rest of unchanged chars - while (pos < newText.Length) - { - sb.Append(newText[pos]); - pos++; - } // while - - return sb.ToString(); - } } } \ No newline at end of file diff --git a/src/umbraco.cms/umbraco.cms.csproj b/src/umbraco.cms/umbraco.cms.csproj index 595e07b40d..ea9437e8a4 100644 --- a/src/umbraco.cms/umbraco.cms.csproj +++ b/src/umbraco.cms/umbraco.cms.csproj @@ -106,7 +106,7 @@ false - + False ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll diff --git a/src/umbraco.controls/umbraco.controls.csproj b/src/umbraco.controls/umbraco.controls.csproj index 9c3c80c1ac..abd0f53dce 100644 --- a/src/umbraco.controls/umbraco.controls.csproj +++ b/src/umbraco.controls/umbraco.controls.csproj @@ -68,7 +68,7 @@ false - + False ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll diff --git a/src/umbraco.editorControls/umbraco.editorControls.csproj b/src/umbraco.editorControls/umbraco.editorControls.csproj index 50aabbe197..865b574024 100644 --- a/src/umbraco.editorControls/umbraco.editorControls.csproj +++ b/src/umbraco.editorControls/umbraco.editorControls.csproj @@ -114,7 +114,7 @@ {651E1350-91B6-44B7-BD60-7207006D7003} Umbraco.Web - + False ..\packages\ClientDependency.1.7.1.1\lib\ClientDependency.Core.dll diff --git a/src/umbraco.providers/members/UmbracoMembershipProvider.cs b/src/umbraco.providers/members/UmbracoMembershipProvider.cs index 35cc2607de..e8f6c0b00a 100644 --- a/src/umbraco.providers/members/UmbracoMembershipProvider.cs +++ b/src/umbraco.providers/members/UmbracoMembershipProvider.cs @@ -693,59 +693,62 @@ namespace umbraco.providers.members if (isLocked) { LogHelper.Info("Cannot validate member " + username + " because they are currently locked out"); - m = null; + return false; } } } } //check for approve status. If not approved, then set the member property to null - if (m != null && CheckApproveStatus(m) == false) + if (CheckApproveStatus(m) == false) { LogHelper.Info("Cannot validate member " + username + " because they are not approved"); - m = null; + return false; } // maybe update login date - if (m != null && string.IsNullOrEmpty(LastLoginPropertyTypeAlias) == false) + if (string.IsNullOrEmpty(LastLoginPropertyTypeAlias) == false) { UpdateMemberProperty(m, LastLoginPropertyTypeAlias, DateTime.Now); } // maybe reset password attempts - if (m != null && string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) + if (string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) { UpdateMemberProperty(m, FailedPasswordAttemptsPropertyTypeAlias, 0); } // persist data - if (m != null) - m.Save(); + m.Save(); + + return true; } - else if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false + + + // update fail rate if it's approved + if (string.IsNullOrEmpty(LockPropertyTypeAlias) == false && string.IsNullOrEmpty(FailedPasswordAttemptsPropertyTypeAlias) == false) - { - var updateMemberDataObject = Member.GetMemberFromLoginName(username); - // update fail rate if it's approved - if (updateMemberDataObject != null && CheckApproveStatus(updateMemberDataObject)) + { + if (CheckApproveStatus(m)) { - int failedAttempts = 0; - int.TryParse(GetMemberProperty(updateMemberDataObject, FailedPasswordAttemptsPropertyTypeAlias, false), out failedAttempts); + var failedAttempts = 0; + int.TryParse(GetMemberProperty(m, FailedPasswordAttemptsPropertyTypeAlias, false), out failedAttempts); failedAttempts = failedAttempts + 1; - UpdateMemberProperty(updateMemberDataObject, FailedPasswordAttemptsPropertyTypeAlias, failedAttempts); + UpdateMemberProperty(m, FailedPasswordAttemptsPropertyTypeAlias, failedAttempts); // lock user? if (failedAttempts >= MaxInvalidPasswordAttempts) { - UpdateMemberProperty(updateMemberDataObject, LockPropertyTypeAlias, 1); - UpdateMemberProperty(updateMemberDataObject, LastLockedOutPropertyTypeAlias, DateTime.Now); + UpdateMemberProperty(m, LockPropertyTypeAlias, 1); + UpdateMemberProperty(m, LastLockedOutPropertyTypeAlias, DateTime.Now); LogHelper.Info("Member " + username + " is now locked out, max invalid password attempts exceeded"); } - updateMemberDataObject.Save(); + m.Save(); } } - return (m != null); + + return false; } private static void UpdateMemberProperty(Member m, string propertyTypeAlias, object propertyValue) diff --git a/src/umbraco.sln b/src/umbraco.sln index 6c29d5c1f1..60199204c4 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -1,5 +1,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI", "Umbraco.Web.UI\Umbraco.Web.UI.csproj", "{4C4C194C-B5E4-4991-8F87-4373E24CC19F}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2849E9D4-3B4E-40A3-A309-F3CB4F0E125F}" ProjectSection(SolutionItems) = preProject ..\build\Build.bat = ..\build\Build.bat @@ -16,13 +20,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{FD962632-1 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{B5BD12C1-A454-435E-8A46-FF4A364C0382}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{66AB0414-D4F2-435C-9868-E25B6865D9A9}" - ProjectSection(SolutionItems) = preProject - .nuget\NuGet.Config = .nuget\NuGet.Config - .nuget\NuGet.exe = .nuget\NuGet.exe - .nuget\NuGet.targets = .nuget\NuGet.targets - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UmbracoExamine Libraries", "UmbracoExamine Libraries", "{DD32977B-EF54-475B-9A1B-B97A502C6E58}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{227C3B55-80E5-4E7E-A802-BE16C5128B9D}" @@ -31,8 +28,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{227C ..\build\NuSpecs\UmbracoCms.nuspec = ..\build\NuSpecs\UmbracoCms.nuspec EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Web.UI", "Umbraco.Web.UI\Umbraco.Web.UI.csproj", "{4C4C194C-B5E4-4991-8F87-4373E24CC19F}" -EndProject Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Web.UI.Client", "http://localhost:3961", "{3819A550-DCEC-4153-91B4-8BA9F7F0B9B4}" ProjectSection(WebsiteProperties) = preProject UseIISExpress = "true" @@ -87,6 +82,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UmbracoExamine.Azure", "Umb EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UmbracoExamine.PDF.Azure", "UmbracoExamine.PDF.Azure\UmbracoExamine.PDF.Azure.csproj", "{B555AAE6-0F56-442F-AC9F-EF497DB38DE7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{D24664A0-49B7-44A8-B2A5-5E5B1CCB7A58}" + ProjectSection(SolutionItems) = preProject + .nuget\NuGet.Config = .nuget\NuGet.Config + .nuget\NuGet.exe = .nuget\NuGet.exe + .nuget\NuGet.targets = .nuget\NuGet.targets + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU