Compare commits

...

35 Commits

Author SHA1 Message Date
Emil Wangaa 1e154e8bc9 Internal Beta04 version bump - needed for Forms 2017-03-22 14:32:35 +01:00
Stephan 1ffdd1abd1 Merge pull request #1811 from umbraco/temp-U4-9660
U4-9660 EntityService is not using readonly uow where it could be
2017-03-22 11:20:29 +01:00
Claus a06de1b165 U4-9660 EntityService is not using readonly uow where it could be 2017-03-22 11:14:16 +01:00
Shannon 0fd3af6828 Fixes release version of tinymceconfig 2017-03-22 18:32:28 +11:00
Shannon fafb207799 U4-8658 RTE Editor: Edit an existing image by clicking 'media picker' in toolbar, show a blank picker 2017-03-22 18:27:18 +11:00
Shannon b1aea424e8 U4-9659 Javascript error when inserting an image into the rich text editor from the media picker 2017-03-22 18:26:11 +11:00
Shannon Deminick 3598acaddc Merge pull request #1805 from umbraco/temp-u4-9644
u4-9644 fix local link selection in the rich text editor
2017-03-22 18:25:04 +11:00
Shannon 8e03a1ffca Updates tha a and img tags in the rte to use a data-udi attribute when udi's are used 2017-03-22 18:17:59 +11:00
Shannon Deminick ded8afa8fc Merge pull request #1808 from umbraco/temp-u4-9582
U4-9582
2017-03-22 13:56:54 +11:00
Mads Rasmussen dcd2b1eb6b Merge pull request #1807 from crgrieve/U4-9648
U4-9648 Remove the relevant query rather than the first one in list.
2017-03-21 19:59:57 +01:00
Stephan c04ef47be3 Merge pull request #1806 from umbraco/temp-u4-9640
U4-9640
2017-03-21 17:48:07 +01:00
Stephan 62f3c87e8a Merge remote-tracking branch 'origin/dev-v7.6' into temp-u4-9640 2017-03-21 17:12:29 +01:00
Stephan 1b9776ecfb Merge pull request #1800 from umbraco/temp-U4-9591
U4-9591 All PetaPoco Relators for 1:M mappings need to be reviewed to…
2017-03-21 16:52:12 +01:00
mikkelhm 3f3db006e7 When selecting a local link, also update the data-id to be the udi 2017-03-21 14:43:54 +01:00
Stephan 7801cdf9ff Merge remote-tracking branch 'origin/dev-v7.6' into temp-u4-9640 2017-03-21 13:14:18 +01:00
mikkelhm de9c5b88ab Updates the legacy linkpicker to show udi links properly, i.e. translate them to nice internal urls 2017-03-21 11:48:50 +01:00
Stephan 77981f889f U4-9582 - perfs, cache converter prevalues 2017-03-21 11:10:52 +01:00
Stephan 0a132ed5ed U4-9582 - perfs, remove Yield method 2017-03-21 11:10:37 +01:00
Shannon 27ee192c8f Fixes all the logic in the CodeFileController, propery null checking, proper handling of persisting everything including renames, removes code duplication 2017-03-21 18:39:45 +11:00
Shannon dfa58183c6 Fixes deleting partial view and script files 2017-03-21 18:38:54 +11:00
Shannon e8668f3ed4 Don't pass in a value for snippets if it's not there, it will show up as a "undefined" string otherwise, adds the redirect method to the contentEditingHelper 2017-03-21 18:38:32 +11:00
Shannon c84e3dac38 Adds notes, return false if the file is not actually deleted, adds test assertions 2017-03-21 18:37:46 +11:00
Shannon 72ab51ea60 Fixes angular file controllers to not sync the tree if an item is being created, redirects to the correct path if the file is renamed 2017-03-21 18:36:14 +11:00
Shannon 1bfb63fe86 Adds validation for file names 2017-03-21 18:35:13 +11:00
Shannon 2cf68ab263 dont' render files that are invalid (no name) 2017-03-21 18:30:39 +11:00
Shannon 215bc64a83 Fixes val-server directive since it wasn't picking up on ngModel changes 2017-03-21 18:20:22 +11:00
Shannon 73f0b1992d Fixes up sorting when using relators 2017-03-21 12:37:35 +11:00
carole_grieve_eqtr 21f54dd4ff Remove the relevant query rather than the first one! 2017-03-20 17:55:48 +00:00
Stephan a5903ec419 U4-9640 - fix CodeFileController for scripts 2017-03-20 16:53:08 +01:00
Stephan a9c7a7a405 U4-9640 - use IFileSystem 2017-03-20 15:53:34 +01:00
mikkelhm ce8286dadd Sets the udi as well as the id when selecting a local link in the rich text editor 2017-03-20 15:53:32 +01:00
mikkelhm 504aac0350 Fixes variable names when selecting a local link in the rich text editor 2017-03-20 14:32:43 +01:00
Shannon fd0cb7da23 Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-U4-9591 2017-03-20 18:28:38 +11:00
Claus 6344ad28aa adding sqlsyntax. 2017-03-16 14:52:50 +01:00
Claus eadc3c8c5a U4-9591 All PetaPoco Relators for 1:M mappings need to be reviewed to ensure there is a correct sort being used 2017-03-16 14:45:59 +01:00
48 changed files with 767 additions and 516 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.6.0
beta03
beta04
+1 -1
View File
@@ -12,4 +12,4 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.0")]
[assembly: AssemblyInformationalVersion("7.6.0-beta03")]
[assembly: AssemblyInformationalVersion("7.6.0-beta04")]
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return "beta03"; } }
public static string CurrentComment { get { return "beta04"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
-17
View File
@@ -295,22 +295,5 @@ namespace Umbraco.Core
return list1Groups.Count == list2Groups.Count
&& list1Groups.All(g => g.Count() == list2Groups[g.Key].Count());
}
///<summary>
/// Returns the items of the given enumerable as a pure enumerable.
/// <remarks>
/// When quering lists using methods such as <see cref="M:List.Where"/>, the result, despite appearing to look like and quack like an
/// <see cref="T:Enumerable{T}"/> the type is actually an instance of <see cref="T:System.Linq.Enumerable.WhereEnumerableIterator"/>
/// </remarks>
/// </summary>
///<param name="source">The item to find.</param>
///<returns>The index of the first matching item, or -1 if the item was not found.</returns>
internal static IEnumerable<T> Yield<T>(this IEnumerable<T> source)
{
foreach (var element in source)
{
yield return element;
}
}
}
}
+4 -1
View File
@@ -49,7 +49,10 @@ namespace Umbraco.Core.Models
return path
.Replace('\\', System.IO.Path.DirectorySeparatorChar)
.Replace('/', System.IO.Path.DirectorySeparatorChar);
//.TrimStart(System.IO.Path.DirectorySeparatorChar);
//Don't strip the start - this was a bug fixed in 7.3, see ScriptRepositoryTests.PathTests
//.TrimStart(System.IO.Path.DirectorySeparatorChar)
//.TrimStart('/');
}
/// <summary>
@@ -101,7 +101,8 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<PropertyType>(sqlClause, query);
var sql = translator.Translate()
.OrderBy<PropertyTypeDto>(x => x.PropertyTypeGroupId, SqlSyntax);
//must be sorted this way for the relator to work
.OrderBy<PropertyTypeGroupDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<PropertyTypeGroupDto, PropertyTypeDto, DataTypeDto, PropertyTypeGroupDto>(new GroupPropertyTypeRelator().Map, sql);
@@ -468,7 +469,8 @@ AND umbracoNode.id <> @id",
.LeftJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(left => left.DataTypeId, right => right.DataTypeId)
.Where<PropertyTypeGroupDto>(x => x.ContentTypeNodeId == id)
.OrderBy<PropertyTypeGroupDto>(x => x.Id);
//must be sorted this way for the relator to work
.OrderBy<PropertyTypeGroupDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<PropertyTypeGroupDto, PropertyTypeDto, DataTypeDto, PropertyTypeGroupDto>(new GroupPropertyTypeRelator().Map, sql);
@@ -41,6 +41,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
//must be sorted this way for the relator to work
.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
var dto = Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql).FirstOrDefault();
@@ -64,6 +65,9 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("cmsDictionary.pk in (@ids)", new { ids = ids });
}
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
.Select(dto => ConvertFromDto(dto));
}
@@ -73,6 +77,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IDictionaryItem>(sqlClause, query);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
@@ -94,9 +99,9 @@ namespace Umbraco.Core.Persistence.Repositories
else
{
sql.Select("*")
.From<DictionaryDto>(SqlSyntax)
.LeftJoin<LanguageTextDto>(SqlSyntax)
.On<DictionaryDto, LanguageTextDto>(SqlSyntax, left => left.UniqueId, right => right.UniqueId);
.From<DictionaryDto>(SqlSyntax)
.LeftJoin<LanguageTextDto>(SqlSyntax)
.On<DictionaryDto, LanguageTextDto>(SqlSyntax, left => left.UniqueId, right => right.UniqueId);
}
return sql;
}
@@ -270,6 +275,8 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IDictionaryItem>(sqlClause, Query<IDictionaryItem>.Builder);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
@@ -297,6 +304,9 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<DictionaryDto> PerformFetch(Sql sql)
{
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql);
}
@@ -349,6 +359,9 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<DictionaryDto> PerformFetch(Sql sql)
{
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql);
}
@@ -37,6 +37,9 @@ namespace Umbraco.Core.Persistence.Repositories
private IMacro GetBySql(Sql sql)
{
//must be sorted this way for the relator to work
sql.OrderBy<MacroDto>(x => x.Id, SqlSyntax);
var macroDto = Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql).FirstOrDefault();
if (macroDto == null)
return null;
@@ -68,7 +71,10 @@ namespace Umbraco.Core.Persistence.Repositories
private IEnumerable<IMacro> GetAllNoIds()
{
var sql = GetBaseQuery(false);
var sql = GetBaseQuery(false)
//must be sorted this way for the relator to work
.OrderBy<MacroDto>(x => x.Id, SqlSyntax);
return ConvertFromDtos(Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql))
.ToArray();// we don't want to re-iterate again!
}
@@ -92,6 +98,9 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IMacro>(sqlClause, query);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<MacroDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql);
foreach (var dto in dtos)
@@ -114,13 +123,13 @@ namespace Umbraco.Core.Persistence.Repositories
return sql;
}
private static Sql GetBaseQuery()
private Sql GetBaseQuery()
{
var sql = new Sql();
sql.Select("*")
.From<MacroDto>()
.LeftJoin<MacroPropertyDto>()
.On<MacroDto, MacroPropertyDto>(left => left.Id, right => right.Macro);
.From<MacroDto>()
.LeftJoin<MacroPropertyDto>()
.On<MacroDto, MacroPropertyDto>(left => left.Id, right => right.Macro);
return sql;
}
@@ -46,7 +46,9 @@ namespace Umbraco.Core.Persistence.Repositories
var statement = string.Join(" OR ", ids.Select(x => string.Format("umbracoNode.id='{0}'", x)));
sql.Where(statement);
}
sql.OrderByDescending<NodeDto>(x => x.NodeId, SqlSyntax);
//must be sorted this way for the relator to work
sql.OrderBy<MemberTypeReadOnlyDto>(x => x.UniqueId, SqlSyntax);
var dtos =
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
@@ -62,7 +64,8 @@ namespace Umbraco.Core.Persistence.Repositories
var subquery = translator.Translate();
var sql = GetBaseQuery(false)
.Append(new Sql("WHERE umbracoNode.id IN (" + subquery.SQL + ")", subquery.Arguments))
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
//must be sorted this way for the relator to work
.OrderBy<MemberTypeReadOnlyDto>(x => x.UniqueId, SqlSyntax);
var dtos =
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
@@ -40,7 +40,10 @@ namespace Umbraco.Core.Persistence.Repositories
}
var factory = new PublicAccessEntryFactory();
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
sql.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
return dtos.Select(factory.BuildEntity);
}
@@ -52,7 +55,10 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = translator.Translate();
var factory = new PublicAccessEntryFactory();
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
sql.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
return dtos.Select(factory.BuildEntity);
}
@@ -63,9 +69,8 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Select("*")
.From<AccessDto>(SqlSyntax)
.LeftJoin<AccessRuleDto>(SqlSyntax)
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId)
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId);
return sql;
}
@@ -39,6 +39,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
var dto = Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql).FirstOrDefault();
@@ -60,7 +62,10 @@ namespace Umbraco.Core.Persistence.Repositories
{
sql.Where("umbracoUser.id in (@ids)", new {ids = ids});
}
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql))
.ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching.
}
@@ -71,6 +76,9 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IUser>(sqlClause, query);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql)
.DistinctBy(x => x.Id);
@@ -96,13 +104,13 @@ namespace Umbraco.Core.Persistence.Repositories
return sql;
}
private static Sql GetBaseQuery(string columns)
private Sql GetBaseQuery(string columns)
{
var sql = new Sql();
sql.Select(columns)
.From<UserDto>()
.LeftJoin<User2AppDto>()
.On<UserDto, User2AppDto>(left => left.Id, right => right.UserId);
.From<UserDto>()
.LeftJoin<User2AppDto>()
.On<UserDto, User2AppDto>(left => left.Id, right => right.UserId);
return sql;
}
@@ -300,6 +308,8 @@ namespace Umbraco.Core.Persistence.Repositories
var innerSql = GetBaseQuery("umbracoUser.id");
innerSql.Where("umbracoUser2app.app = " + SqlSyntax.GetQuotedValue(sectionAlias));
sql.Where(string.Format("umbracoUser.id IN ({0})", innerSql.SQL));
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql));
}
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
@@ -88,13 +89,26 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
/// </returns>
private bool IsRangeDataType(int dataTypeId)
{
// ** This must be cached (U4-8862) **
var enableRange =
_dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId)
.PreValuesAsDictionary.FirstOrDefault(
x => string.Equals(x.Key, "enableRange", StringComparison.InvariantCultureIgnoreCase)).Value;
// GetPreValuesCollectionByDataTypeId is cached at repository level;
// still, the collection is deep-cloned so this is kinda expensive,
// better to cache here + trigger refresh in DataTypeCacheRefresher
return enableRange != null && enableRange.Value.TryConvertTo<bool>().Result;
return Storages.GetOrAdd(dataTypeId, id =>
{
var preValue = _dataTypeService.GetPreValuesCollectionByDataTypeId(id)
.PreValuesAsDictionary
.FirstOrDefault(x => string.Equals(x.Key, "enableRange", StringComparison.InvariantCultureIgnoreCase))
.Value;
return preValue != null && preValue.Value.TryConvertTo<bool>().Result;
});
}
private static readonly ConcurrentDictionary<int, bool> Storages = new ConcurrentDictionary<int, bool>();
internal static void ClearCaches()
{
Storages.Clear();
}
}
}
@@ -1,9 +1,12 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
@@ -74,18 +77,26 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
/// </returns>
private bool JsonStorageType(int dataTypeId)
{
// ** This must be cached (U4-8862) **
var storageType =
_dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId)
.PreValuesAsDictionary.FirstOrDefault(
x => string.Equals(x.Key, "storageType", StringComparison.InvariantCultureIgnoreCase)).Value;
// GetPreValuesCollectionByDataTypeId is cached at repository level;
// still, the collection is deep-cloned so this is kinda expensive,
// better to cache here + trigger refresh in DataTypeCacheRefresher
if (storageType != null && storageType.Value.InvariantEquals("Json"))
return Storages.GetOrAdd(dataTypeId, id =>
{
return true;
}
var preValue = _dataTypeService.GetPreValuesCollectionByDataTypeId(id)
.PreValuesAsDictionary
.FirstOrDefault(x => string.Equals(x.Key, "storageType", StringComparison.InvariantCultureIgnoreCase))
.Value;
return false;
return preValue != null && preValue.Value.InvariantEquals("json");
});
}
private static readonly ConcurrentDictionary<int, bool> Storages = new ConcurrentDictionary<int, bool>();
internal static void ClearCaches()
{
Storages.Clear();
}
}
}
+21 -42
View File
@@ -79,7 +79,7 @@ namespace Umbraco.Core.Services
{
var result = _runtimeCache.GetCacheItem<int?>(CacheKeys.IdToKeyCacheKey + key, () =>
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
switch (umbracoObjectType)
{
@@ -92,7 +92,6 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.Member:
case UmbracoObjectTypes.DataType:
case UmbracoObjectTypes.DocumentTypeContainer:
uow.Commit();
return uow.Database.ExecuteScalar<int?>(new Sql().Select("id").From<NodeDto>().Where<NodeDto>(dto => dto.UniqueId == key));
case UmbracoObjectTypes.RecycleBin:
case UmbracoObjectTypes.Stylesheet:
@@ -119,7 +118,7 @@ namespace Umbraco.Core.Services
{
var result = _runtimeCache.GetCacheItem<Guid?>(CacheKeys.KeyToIdCacheKey + id, () =>
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
switch (umbracoObjectType)
{
@@ -131,7 +130,6 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.DocumentType:
case UmbracoObjectTypes.Member:
case UmbracoObjectTypes.DataType:
uow.Commit();
return uow.Database.ExecuteScalar<Guid?>(new Sql().Select("uniqueID").From<NodeDto>().Where<NodeDto>(dto => dto.NodeId == id));
case UmbracoObjectTypes.RecycleBin:
case UmbracoObjectTypes.Stylesheet:
@@ -185,11 +183,10 @@ namespace Umbraco.Core.Services
{
if (loadBaseType)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.Get(id);
uow.Commit();
return ret;
}
}
@@ -207,11 +204,10 @@ namespace Umbraco.Core.Services
if (loadBaseType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetByKey(key, objectTypeId);
uow.Commit();
return ret;
}
}
@@ -241,11 +237,10 @@ namespace Umbraco.Core.Services
if (loadBaseType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.Get(id, objectTypeId);
uow.Commit();
return ret;
}
}
@@ -276,11 +271,10 @@ namespace Umbraco.Core.Services
{
if (loadBaseType)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.Get(id);
uow.Commit();
return ret;
}
}
@@ -303,7 +297,7 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
public virtual IUmbracoEntity GetParent(int id)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
@@ -311,7 +305,6 @@ namespace Umbraco.Core.Services
if (entity.ParentId == -1 || entity.ParentId == -20 || entity.ParentId == -21)
return null;
var ret = repository.Get(entity.ParentId);
uow.Commit();
return ret;
}
}
@@ -324,7 +317,7 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
public virtual IUmbracoEntity GetParent(int id, UmbracoObjectTypes umbracoObjectType)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
@@ -334,7 +327,6 @@ namespace Umbraco.Core.Services
var objectTypeId = umbracoObjectType.GetGuid();
var ret = repository.Get(entity.ParentId, objectTypeId);
uow.Commit();
return ret;
}
}
@@ -346,13 +338,12 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
public virtual IEnumerable<IUmbracoEntity> GetChildren(int parentId)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId);
var contents = repository.GetByQuery(query);
uow.Commit();
return contents;
}
}
@@ -366,13 +357,12 @@ namespace Umbraco.Core.Services
public virtual IEnumerable<IUmbracoEntity> GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId);
var contents = repository.GetByQuery(query, objectTypeId);
uow.Commit();
return contents;
}
}
@@ -393,7 +383,7 @@ namespace Umbraco.Core.Services
string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = "")
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId && x.Trashed == false);
@@ -405,7 +395,6 @@ namespace Umbraco.Core.Services
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
@@ -426,7 +415,7 @@ namespace Umbraco.Core.Services
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
@@ -442,7 +431,6 @@ namespace Umbraco.Core.Services
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
@@ -463,7 +451,7 @@ namespace Umbraco.Core.Services
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "", bool includeTrashed = true)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
@@ -481,7 +469,6 @@ namespace Umbraco.Core.Services
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
@@ -493,7 +480,7 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
public virtual IEnumerable<IUmbracoEntity> GetDescendents(int id)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
@@ -501,7 +488,6 @@ namespace Umbraco.Core.Services
var query = Query<IUmbracoEntity>.Builder.Where(x => x.Path.StartsWith(pathMatch) && x.Id != id);
var entities = repository.GetByQuery(query);
uow.Commit();
return entities;
}
}
@@ -515,14 +501,13 @@ namespace Umbraco.Core.Services
public virtual IEnumerable<IUmbracoEntity> GetDescendents(int id, UmbracoObjectTypes umbracoObjectType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.Path.StartsWith(entity.Path) && x.Id != id);
var entities = repository.GetByQuery(query, objectTypeId);
uow.Commit();
return entities;
}
}
@@ -541,11 +526,10 @@ namespace Umbraco.Core.Services
}
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entities = repository.GetByQuery(_rootEntityQuery, objectTypeId);
uow.Commit();
return entities;
}
}
@@ -585,11 +569,10 @@ namespace Umbraco.Core.Services
});
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetAll(objectTypeId, ids);
uow.Commit();
return ret;
}
}
@@ -605,11 +588,10 @@ namespace Umbraco.Core.Services
});
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetAll(objectTypeId, keys);
uow.Commit();
return ret;
}
}
@@ -631,11 +613,10 @@ namespace Umbraco.Core.Services
("The passed in type is not supported");
});
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetAll(objectTypeId, ids);
uow.Commit();
return ret;
}
}
@@ -721,22 +702,20 @@ namespace Umbraco.Core.Services
public bool Exists(Guid key)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var exists = repository.Exists(key);
uow.Commit();
return exists;
}
}
public bool Exists(int id)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var exists = repository.Exists(id);
uow.Commit();
return exists;
}
}
+1 -1
View File
@@ -868,7 +868,7 @@ namespace Umbraco.Core.Services
if (partialView == null)
{
uow.Commit();
return true;
return false;
}
if (uow.Events.DispatchCancelable(DeletingPartialView, this, new DeleteEventArgs<IPartialView>(partialView)))
@@ -237,6 +237,14 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.AreEqual("test-path-1.js", script.Path);
Assert.AreEqual("/scripts/test-path-1.js", script.VirtualPath);
//ensure you can prefix the same path as the root path name
script = new Script("scripts/path-2/test-path-2.js") { Content = "// script" };
repository.AddOrUpdate(script);
unitOfWork.Commit();
Assert.IsTrue(_fileSystem.FileExists("scripts/path-2/test-path-2.js"));
Assert.AreEqual("scripts\\path-2\\test-path-2.js", script.Path);
Assert.AreEqual("/scripts/scripts/path-2/test-path-2.js", script.VirtualPath);
script = new Script("path-2/test-path-2.js") { Content = "// script" };
repository.AddOrUpdate(script);
unitOfWork.Commit();
@@ -191,27 +191,34 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
callback: function (data) {
if (data) {
var href = data.url;
// We want to use the Udi. If it is set, we use it, else fallback to id, and finally to null
var hasUdi = data.udi ? true : false;
var id = hasUdi ? data.udi : (data.id ? data.id : null);
//Create a json obj used to create the attributes for the tag
function createElemAttributes() {
var a = {
href: href,
title: data.name,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null
};
if (hasUdi) {
a["data-udi"] = data.udi;
}
else if (data.id) {
a["data-id"] = data.id;
}
}
function insertLink() {
if (anchorElm) {
dom.setAttribs(anchorElm, {
href: href,
title: data.name,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null,
'data-id': data.id ? data.id : null
});
dom.setAttribs(anchorElm, createElemAttributes());
selection.select(anchorElm);
editor.execCommand('mceEndTyping');
} else {
editor.execCommand('mceInsertLink', false, {
href: href,
title: data.name,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null,
'data-id': data.id ? data.id : null
});
editor.execCommand('mceInsertLink', false, createElemAttributes());
}
}
@@ -221,15 +228,11 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
}
//if we have an id, it must be a locallink:id, aslong as the isMedia flag is not set
if(data.id && (angular.isUndefined(data.isMedia) || !data.isMedia)){
if (target.udi) {
href = "/{localLink:" + target.udi + "}";
}
else {
//This shouldn't happen! but just in case we'll leave this here
href = "/{localLink:" + target.id + "}";
}
insertLink();
if (id && (angular.isUndefined(data.isMedia) || !data.isMedia)){
href = "/{localLink:" + id + "}";
insertLink();
return;
}
@@ -108,8 +108,10 @@ angular.module("umbraco.directives")
$image.load(function(){
$timeout(function(){
setDimensions();
scope.loaded = true;
scope.onImageLoaded();
scope.loaded = true;
if (scope.onImageLoaded) {
scope.onImageLoaded();
}
});
});
@@ -87,7 +87,8 @@ Use this directive to generate a list of folders presented as a flexbox grid.
scope.clickFolder = function(folder, $event, $index) {
if(scope.onClick) {
scope.onClick(folder, $event, $index);
scope.onClick(folder, $event, $index);
$event.stopPropagation();
}
};
@@ -247,6 +247,7 @@ Use this directive to generate a thumbnail grid of media items.
scope.clickItem = function(item, $event, $index) {
if (scope.onClick) {
scope.onClick(item, $event, $index);
$event.stopPropagation();
}
};
@@ -9,7 +9,7 @@ function valServerField(serverValidationManager) {
return {
require: 'ngModel',
restrict: "A",
link: function (scope, element, attr, ctrl) {
link: function (scope, element, attr, ngModel) {
var fieldName = null;
var eventBindings = [];
@@ -23,23 +23,25 @@ function valServerField(serverValidationManager) {
// resubmitted. So once a field is changed that has a server error assigned to it
// we need to re-validate it for the server side validator so the user can resubmit
// the form. Of course normal client-side validators will continue to execute.
eventBindings.push(scope.$watch('ngModel', function(newValue){
if (ctrl.$invalid) {
ctrl.$setValidity('valServerField', true);
eventBindings.push(scope.$watch(function() {
return ngModel.$modelValue;
}, function(newValue){
if (ngModel.$invalid) {
ngModel.$setValidity('valServerField', true);
}
}));
//subscribe to the server validation changes
serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
if (!isValid) {
ctrl.$setValidity('valServerField', false);
ngModel.$setValidity('valServerField', false);
//assign an error msg property to the current validator
ctrl.errorMsg = fieldErrors[0].errorMsg;
ngModel.errorMsg = fieldErrors[0].errorMsg;
}
else {
ctrl.$setValidity('valServerField', true);
ngModel.$setValidity('valServerField', true);
//reset the error message
ctrl.errorMsg = "";
ngModel.errorMsg = "";
}
});
@@ -199,11 +199,17 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) {
*/
getScaffold: function (type, id, snippetName) {
var queryString = "?type=" + type + "&id=" + id;
if (snippetName) {
queryString += "&snippetName=" + snippetName;
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"codeFileApiBaseUrl",
"GetScaffold?type=" + type + "&id=" + id + "&snippetName=" + snippetName)),
"codeFileApiBaseUrl",
"GetScaffold" + queryString)),
"Failed to get scaffold for" + type);
},
@@ -522,6 +522,26 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
return true;
}
return false;
},
/**
* @ngdoc function
* @name umbraco.services.contentEditingHelper#redirectToRenamedContent
* @methodOf umbraco.services.contentEditingHelper
* @function
*
* @description
* For some editors like scripts or entites that have names as ids, these names can change and we need to redirect
* to their new paths, this is helper method to do that.
*/
redirectToRenamedContent: function (id) {
//clear the query strings
$location.search("");
//change to new path
$location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id);
//don't add a browser history for this
$location.replace();
return true;
}
};
}
@@ -21,7 +21,9 @@ function mediaTypeHelper(mediaTypeResource, $q) {
},
getAllowedImagetypes: function (mediaId){
//TODO: This is horribly inneficient - why make one request per type!?
// Get All allowedTypes
return mediaTypeResource.getAllowedTypes(mediaId)
.then(function(types){
@@ -95,11 +95,20 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
if(selectedElm.nodeName === 'IMG'){
var img = $(selectedElm);
var hasUdi = img.attr("data-udi") ? true : false;
currentTarget = {
altText: img.attr("alt"),
url: img.attr("src"),
id: img.attr("rel")
url: img.attr("src")
};
if (hasUdi) {
currentTarget["udi"] = img.attr("data-udi");
}
else {
currentTarget["id"] = img.attr("rel");
}
}
userService.getCurrentUser().then(function(userData) {
@@ -115,13 +124,23 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
insertMediaInEditor: function(editor, img) {
if(img) {
var hasUdi = img.udi ? true : false;
var data = {
alt: img.altText || "",
src: (img.url) ? img.url : "nothing.jpg",
rel: img.id,
'data-id': img.id,
src: (img.url) ? img.url : "nothing.jpg",
id: '__mcenew'
};
};
if (hasUdi) {
data["data-udi"] = img.udi;
}
else {
//Considering these fixed because UDI will now be used and thus
// we have no need for rel http://issues.umbraco.org/issue/U4-6228, http://issues.umbraco.org/issue/U4-6595
data["rel"] = img.id;
data["data-id"] = img.id;
}
editor.insertContent(editor.dom.createHTML('img', data));
@@ -725,27 +744,36 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
insertLinkInEditor: function(editor, target, anchorElm) {
var href = target.url;
// We want to use the Udi. If it is set, we use it, else fallback to id, and finally to null
var hasUdi = target.udi ? true : false;
var id = hasUdi ? target.udi : (target.id ? target.id : null);
//Create a json obj used to create the attributes for the tag
function createElemAttributes() {
var a = {
href: href,
title: target.name,
target: target.target ? target.target : null,
rel: target.rel ? target.rel : null
};
if (hasUdi) {
a["data-udi"] = target.udi;
}
else if (target.id) {
a["data-id"] = target.id;
}
return a;
}
function insertLink() {
if (anchorElm) {
editor.dom.setAttribs(anchorElm, {
href: href,
title: target.name,
target: target.target ? target.target : null,
rel: target.rel ? target.rel : null,
'data-id': target.id ? target.id : null
});
editor.dom.setAttribs(anchorElm, createElemAttributes());
editor.selection.select(anchorElm);
editor.execCommand('mceEndTyping');
} else {
editor.execCommand('mceInsertLink', false, {
href: href,
title: target.name,
target: target.target ? target.target : null,
rel: target.rel ? target.rel : null,
'data-id': target.id ? target.id : null
});
}
else {
editor.execCommand('mceInsertLink', false, createElemAttributes());
}
}
@@ -755,14 +783,9 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
}
//if we have an id, it must be a locallink:id, aslong as the isMedia flag is not set
if(target.id && (angular.isUndefined(target.isMedia) || !target.isMedia)){
if (target.udi) {
href = "/{localLink:" + target.udi + "}";
}
else {
//This shouldn't happen! but just in case we'll leave this here
href = "/{localLink:" + target.id + "}";
}
if(id && (angular.isUndefined(target.isMedia) || !target.isMedia)){
href = "/{localLink:" + id + "}";
insertLink();
return;
@@ -22,17 +22,19 @@ angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController",
$scope.target = dialogOptions.currentTarget;
//if we have a node ID, we fetch the current node to build the form data
if ($scope.target.id) {
if ($scope.target.id || $scope.target.udi) {
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id;
if (!$scope.target.path) {
entityResource.getPath($scope.target.id, "Document").then(function (path) {
entityResource.getPath(id, "Document").then(function (path) {
$scope.target.path = path;
//now sync the tree to this path
$scope.dialogTreeEventHandler.syncTree({ path: $scope.target.path, tree: "content" });
});
}
contentResource.getNiceUrl($scope.target.id).then(function (url) {
contentResource.getNiceUrl(id).then(function (url) {
$scope.target.url = url;
});
}
@@ -59,7 +61,8 @@ angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController",
$scope.currentNode = args.node;
$scope.currentNode.selected = true;
$scope.target.id = args.node.id;
$scope.target.id = args.node.id;
$scope.target.udi = args.node.udi;
$scope.target.name = args.node.name;
if (args.node.id < 0) {
@@ -119,7 +119,7 @@ angular.module("umbraco")
localStorageService.set("umbLastOpenedMediaNodeId", folder.id);
};
$scope.clickHandler = function(image, event, index) {
$scope.clickHandler = function (image, event, index) {
if (image.isFolder) {
if ($scope.disableFolderSelect) {
$scope.gotoFolder(image);
@@ -179,27 +179,49 @@ angular.module("umbraco")
$scope.activeDrag = false;
};
function ensureWithinStartNode(node) {
// make sure that last opened node is on the same path as start node
var nodePath = node.path.split(",");
if (nodePath.indexOf($scope.startNodeId.toString()) !== -1) {
$scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" });
return true;
}
else {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
return false;
}
}
function gotoStartNode(err) {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
}
//default root item
if (!$scope.target) {
if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) {
entityResource.getById($scope.lastOpenedNode, "media")
.then(function(node) {
// make sure that las opened node is on the same path as start node
var nodePath = node.path.split(",");
if (nodePath.indexOf($scope.startNodeId.toString()) !== -1) {
$scope
.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" });
} else {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
}
},
function(err) {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
});
} else {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
.then(ensureWithinStartNode, gotoStartNode);
}
else {
gotoStartNode();
}
}
else {
//if a target is specified, go look it up - generally this target will just contain ids not the actual full
//media object so we need to look it up
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id
var altText = $scope.target.altText;
mediaResource.getById(id)
.then(function (node) {
$scope.target = node;
if (ensureWithinStartNode(node)) {
selectImage(node);
$scope.target.url = mediaHelper.resolveFile(node);
$scope.target.altText = altText;
$scope.openDetailsDialog();
}
}, gotoStartNode);
}
$scope.openDetailsDialog = function() {
@@ -308,15 +330,19 @@ angular.module("umbraco")
var folderImage = $scope.images[folderImageIndex];
var imageIsSelected = false;
for (var selectedImageIndex = 0;
selectedImageIndex < $scope.model.selectedImages.length;
selectedImageIndex++) {
var selectedImage = $scope.model.selectedImages[selectedImageIndex];
if ($scope.model && angular.isArray($scope.model.selectedImages)) {
for (var selectedImageIndex = 0;
selectedImageIndex < $scope.model.selectedImages.length;
selectedImageIndex++) {
var selectedImage = $scope.model.selectedImages[selectedImageIndex];
if (folderImage.key === selectedImage.key) {
imageIsSelected = true;
if (folderImage.key === selectedImage.key) {
imageIsSelected = true;
}
}
}
if (imageIsSelected) {
folderImage.selected = true;
}
@@ -122,9 +122,14 @@
query.filters.push({});
}
function trashFilter(query) {
query.filters.splice(query, 1);
function trashFilter(query, filter) {
for (var i = 0; i < query.filters.length; i++)
{
if (query.filters[i] == filter)
{
query.filters.splice(i, 1);
}
}
//if we remove the last one, add a new one to generate ui for it.
if (query.filters.length == 0) {
query.filters.push({});
@@ -91,7 +91,7 @@
<i class="icon-add"></i>
</a>
<a href ng-click="vm.trashFilter(vm.query)">
<a href ng-click="vm.trashFilter(vm.query, filter)">
<i class="icon-trash"></i>
</a>
@@ -12,10 +12,8 @@ function PartialViewMacrosDeleteController($scope, codefileResource, treeService
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name;
codefileResource.deleteByPath('partialViewMacros', virtualPath)
codefileResource.deleteByPath('partialViewMacros', $scope.currentNode.id)
.then(function() {
$scope.currentNode.loading = false;
//get the root node before we remove it
@@ -48,20 +48,25 @@
});
});
vm.page.saveButtonState = "success";
vm.partialViewMacro = saved;
//check if the name changed, if so we need to redirect
if (vm.partialViewMacro.id !== saved.id) {
contentEditingHelper.redirectToRenamedContent(saved.id);
}
else {
vm.page.saveButtonState = "success";
vm.partialViewMacro = saved;
//sync state
editorState.set(vm.partialViewMacro);
//sync state
editorState.set(vm.partialViewMacro);
// normal tree sync
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// clear $dirty state on form
setFormState("pristine");
// normal tree sync
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// clear $dirty state on form
setFormState("pristine");
}
}, function (err) {
vm.page.saveButtonState = "error";
@@ -237,26 +242,29 @@
}
codefileResource.getScaffold("partialViewMacros", $routeParams.id, snippet).then(function (partialViewMacro) {
ready(partialViewMacro);
ready(partialViewMacro, false);
});
} else {
codefileResource.getByPath('partialViewMacros', $routeParams.id).then(function (partialViewMacro) {
ready(partialViewMacro);
ready(partialViewMacro, true);
});
}
}
function ready(partialViewMacro) {
function ready(partialViewMacro, syncTree) {
vm.page.loading = false;
vm.partialViewMacro = partialViewMacro;
//sync state
editorState.set(vm.partialViewMacro);
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.virtualPath, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
if (syncTree) {
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
}
// ace configuration
vm.aceOption = {
@@ -12,10 +12,8 @@ function PartialViewsDeleteController($scope, codefileResource, treeService, nav
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name;
codefileResource.deleteByPath('partialViews', virtualPath)
codefileResource.deleteByPath('partialViews', $scope.currentNode.id)
.then(function() {
$scope.currentNode.loading = false;
//get the root node before we remove it
@@ -58,20 +58,25 @@
});
});
vm.page.saveButtonState = "success";
vm.partialView = saved;
//check if the name changed, if so we need to redirect
if (vm.partialView.id !== saved.id) {
contentEditingHelper.redirectToRenamedContent(saved.id);
}
else {
vm.page.saveButtonState = "success";
vm.partialView = saved;
//sync state
editorState.set(vm.partialView);
//sync state
editorState.set(vm.partialView);
// normal tree sync
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// clear $dirty state on form
setFormState("pristine");
// normal tree sync
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// clear $dirty state on form
setFormState("pristine");
}
}, function (err) {
vm.page.saveButtonState = "error";
@@ -247,18 +252,18 @@
}
codefileResource.getScaffold("partialViews", $routeParams.id, snippet).then(function (partialView) {
ready(partialView);
ready(partialView, false);
});
} else {
codefileResource.getByPath('partialViews', $routeParams.id).then(function (partialView) {
ready(partialView);
ready(partialView, true);
});
}
}
function ready(partialView) {
function ready(partialView, syncTree) {
vm.page.loading = false;
vm.partialView = partialView;
@@ -266,9 +271,11 @@
//sync state
editorState.set(vm.partialView);
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
if (syncTree) {
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
}
// ace configuration
vm.aceOption = {
@@ -12,10 +12,8 @@ function ScriptsDeleteController($scope, codefileResource, treeService, navigati
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name;
codefileResource.deleteByPath('scripts', virtualPath)
codefileResource.deleteByPath('scripts', $scope.currentNode.id)
.then(function() {
$scope.currentNode.loading = false;
//get the root node before we remove it
@@ -55,16 +55,22 @@
notificationsService.success(header, message);
});
vm.page.saveButtonState = "success";
vm.script = saved;
//check if the name changed, if so we need to redirect
if (vm.script.id !== saved.id) {
contentEditingHelper.redirectToRenamedContent(saved.id);
}
else {
vm.page.saveButtonState = "success";
vm.script = saved;
//sync state
editorState.set(vm.script);
// sync tree
navigationService.syncTree({ tree: "scripts", path: vm.script.virtualPath, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
//sync state
editorState.set(vm.script);
// sync tree
navigationService.syncTree({ tree: "scripts", path: vm.script.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
}
}, function (err) {
@@ -90,17 +96,17 @@
if ($routeParams.create) {
codefileResource.getScaffold("scripts", $routeParams.id).then(function (script) {
ready(script);
ready(script, false);
});
} else {
codefileResource.getByPath('scripts', $routeParams.id).then(function (script) {
ready(script);
ready(script, true);
});
}
}
function ready(script) {
function ready(script, syncTree) {
vm.page.loading = false;
@@ -109,9 +115,11 @@
//sync state
editorState.set(vm.script);
navigationService.syncTree({ tree: "scripts", path: vm.script.virtualPath, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
if (syncTree) {
navigationService.syncTree({ tree: "scripts", path: vm.script.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
}
vm.aceOption = {
mode: "javascript",
@@ -27,7 +27,6 @@
<command>
<umbracoAlias>undo</umbracoAlias>
<name>Undo</name>
<name>Remove Format</name>
<icon>images/editor/undo.gif</icon>
<tinyMceCommand value="" userInterface="false" frontendCommand="undo">undo</tinyMceCommand>
<priority>11</priority>
@@ -233,7 +232,6 @@
<plugin loadOnFrontend="true">code</plugin>
<plugin loadOnFrontend="true">codemirror</plugin>
<plugin loadOnFrontend="true">paste</plugin>
<plugin loadOnFrontend="true">umbracolink</plugin>
<plugin loadOnFrontend="true">anchor</plugin>
<plugin loadOnFrontend="true">charmap</plugin>
<plugin loadOnFrontend="true">table</plugin>
@@ -241,7 +239,7 @@
<plugin loadOnFrontend="true">hr</plugin>
</plugins>
<validElements>
<![CDATA[+a[id|style|rel|data-id|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|
<![CDATA[+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|
ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],
-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],
img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],
@@ -239,7 +239,7 @@
<plugin loadOnFrontend="true">hr</plugin>
</plugins>
<validElements>
<![CDATA[+a[id|style|rel|data-id|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|
<![CDATA[+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|
ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],
-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],
img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],
@@ -5,6 +5,8 @@ using Umbraco.Core.Cache;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Web.PropertyEditors.ValueConverters;
namespace Umbraco.Web.Cache
@@ -111,6 +113,10 @@ namespace Umbraco.Web.Cache
PublishedContentType.ClearDataType(payload.Id);
});
TagsValueConverter.ClearCaches();
MultipleMediaPickerPropertyConverter.ClearCaches();
SliderValueConverter.ClearCaches();
base.Refresh(jsonPayload);
}
}
+163 -125
View File
@@ -1,4 +1,5 @@
using AutoMapper;
using System;
using AutoMapper;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -18,9 +19,11 @@ using Umbraco.Web.Trees;
namespace Umbraco.Web.Editors
{
//TODO: Put some exception filters in our webapi to return 404 instead of 500 when we throw ArgumentNullException
// ref: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
[PluginController("UmbracoApi")]
[PrefixlessBodyModelValidator]
[UmbracoApplicationAuthorizeAttribute(Core.Constants.Applications.Settings)]
[UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)]
public class CodeFileController : BackOfficeNotificationsController
{
@@ -33,6 +36,9 @@ namespace Umbraco.Web.Editors
[ValidationFilter]
public HttpResponseMessage PostCreate(string type, CodeFileDisplay display)
{
if (display == null) throw new ArgumentNullException("display");
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
switch (type)
{
case Core.Constants.Trees.PartialViews:
@@ -67,12 +73,11 @@ namespace Umbraco.Web.Editors
[HttpPost]
public CodeFileDisplay PostCreateContainer(string type, string parentId, string name)
{
if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(name))
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(parentId)) throw new ArgumentException("Value cannot be null or whitespace.", "parentId");
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name");
// if the parentId is root (-1) then we just need an empty string as we are
// if the parentId is root (-1) then we just need an empty string as we are
// creating the path below and we don't wan't -1 in the path
if (parentId == Core.Constants.System.Root.ToInvariantString())
{
@@ -120,10 +125,8 @@ namespace Umbraco.Web.Editors
/// <returns>The file and its contents from the virtualPath</returns>
public CodeFileDisplay GetByPath(string type, string virtualPath)
{
if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(virtualPath))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(virtualPath)) throw new ArgumentException("Value cannot be null or whitespace.", "virtualPath");
virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath);
@@ -139,7 +142,7 @@ namespace Umbraco.Web.Editors
display.Id = System.Web.HttpUtility.UrlEncode(view.Path);
return display;
}
return null;
throw new HttpResponseException(HttpStatusCode.NotFound);
case Core.Constants.Trees.PartialViewMacros:
var viewMacro = Services.FileService.GetPartialViewMacro(virtualPath);
@@ -151,7 +154,7 @@ namespace Umbraco.Web.Editors
display.Id = System.Web.HttpUtility.UrlEncode(viewMacro.Path);
return display;
}
return null;
throw new HttpResponseException(HttpStatusCode.NotFound);
case Core.Constants.Trees.Scripts:
var script = Services.FileService.GetScriptByName(virtualPath);
@@ -163,7 +166,7 @@ namespace Umbraco.Web.Editors
display.Id = System.Web.HttpUtility.UrlEncode(script.Path);
return display;
}
return null;
throw new HttpResponseException(HttpStatusCode.NotFound);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -176,10 +179,7 @@ namespace Umbraco.Web.Editors
/// <returns>Returns a list of <see cref="SnippetDisplay"/> if a correct type is sent</returns>
public IEnumerable<SnippetDisplay> GetSnippets(string type)
{
if (string.IsNullOrWhiteSpace(type))
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
IEnumerable<string> snippets;
switch (type)
@@ -209,15 +209,10 @@ namespace Umbraco.Web.Editors
/// <param name="id"></param>
/// <param name="snippetName"></param>
/// <returns></returns>
public CodeFileDisplay GetScaffold(string type, string id = null, string snippetName = null)
public CodeFileDisplay GetScaffold(string type, string id, string snippetName = null)
{
if (string.IsNullOrWhiteSpace(type))
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
if (id.IsNullOrWhiteSpace())
id = string.Empty;
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentException("Value cannot be null or whitespace.", "id");
CodeFileDisplay codeFileDisplay;
@@ -246,15 +241,15 @@ namespace Umbraco.Web.Editors
// Make sure that the root virtual path ends with '/'
codeFileDisplay.VirtualPath = codeFileDisplay.VirtualPath.EnsureEndsWith("/");
if (id.IsNullOrWhiteSpace() == false && id != Core.Constants.System.Root.ToInvariantString())
if (id != Core.Constants.System.Root.ToInvariantString())
{
codeFileDisplay.VirtualPath += id.TrimStart("/").EnsureEndsWith("/");
//if it's not new then it will have a path, otherwise it won't
codeFileDisplay.Path = Url.GetTreePathFromFilePath(id);
}
codeFileDisplay.VirtualPath = codeFileDisplay.VirtualPath.TrimStart("~");
codeFileDisplay.Path = Url.GetTreePathFromFilePath(id);
codeFileDisplay.VirtualPath = codeFileDisplay.VirtualPath.TrimStart("~");
codeFileDisplay.FileType = type;
return codeFileDisplay;
}
@@ -268,52 +263,52 @@ namespace Umbraco.Web.Editors
[HttpPost]
public HttpResponseMessage Delete(string type, string virtualPath)
{
if (string.IsNullOrWhiteSpace(type) == false && string.IsNullOrWhiteSpace(virtualPath) == false)
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(virtualPath)) throw new ArgumentException("Value cannot be null or whitespace.", "virtualPath");
virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath);
switch (type)
{
virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath);
case Core.Constants.Trees.PartialViews:
if (IsDirectory(virtualPath, SystemDirectories.PartialViews))
{
Services.FileService.DeletePartialViewFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.DeletePartialView(virtualPath, Security.CurrentUser.Id))
{
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View or folder found with the specified path");
switch (type)
{
case Core.Constants.Trees.PartialViews:
if (IsDirectory(virtualPath, SystemDirectories.PartialViews))
{
Services.FileService.DeletePartialViewFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.DeletePartialView(virtualPath, Security.CurrentUser.Id))
{
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View or folder found with the specified path");
case Core.Constants.Trees.PartialViewMacros:
if (IsDirectory(virtualPath, SystemDirectories.MacroPartials))
{
Services.FileService.DeletePartialViewMacroFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id))
{
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro or folder found with the specified path");
case Core.Constants.Trees.PartialViewMacros:
if (IsDirectory(virtualPath, SystemDirectories.MacroPartials))
{
Services.FileService.DeletePartialViewMacroFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id))
{
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro or folder found with the specified path");
case Core.Constants.Trees.Scripts:
if (IsDirectory(virtualPath, SystemDirectories.Scripts))
{
Services.FileService.DeleteScriptFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.GetScriptByName(virtualPath) != null)
{
Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id);
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script or folder found with the specified path");
case Core.Constants.Trees.Scripts:
if (IsDirectory(virtualPath, SystemDirectories.Scripts))
{
Services.FileService.DeleteScriptFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.GetScriptByName(virtualPath) != null)
{
Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id);
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script or folder found with the specified path");
default:
return Request.CreateResponse(HttpStatusCode.NotFound);
}
default:
return Request.CreateResponse(HttpStatusCode.NotFound);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -326,16 +321,13 @@ namespace Umbraco.Web.Editors
/// <returns>The updated CodeFileDisplay model</returns>
public CodeFileDisplay PostSave(CodeFileDisplay display)
{
if (display == null) throw new ArgumentNullException("display");
if (ModelState.IsValid == false)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
}
if (display == null || string.IsNullOrWhiteSpace(display.FileType))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
switch (display.FileType)
{
case Core.Constants.Trees.PartialViews:
@@ -362,35 +354,29 @@ namespace Umbraco.Web.Editors
display.Id = System.Web.HttpUtility.UrlEncode(partialViewMacroResult.Result.Path);
return display;
}
display.AddErrorNotification(
Services.TextService.Localize("speechBubbles/partialViewErrorHeader"),
Services.TextService.Localize("speechBubbles/partialViewErrorText"));
break;
case Core.Constants.Trees.Scripts:
var virtualPath = display.VirtualPath;
var script = Services.FileService.GetScriptByName(display.VirtualPath);
if (script != null)
{
script.Path = display.Name;
display = Mapper.Map(script, display);
display.Path = Url.GetTreePathFromFilePath(script.Path);
display.Id = System.Web.HttpUtility.UrlEncode(script.Path);
return display;
}
else
{
var fileName = EnsurePartialViewExtension(display.Name, ".js");
script = new Script(virtualPath + fileName);
}
script.Content = display.Content;
var scriptResult = CreateOrUpdateScript(display);
display = Mapper.Map(scriptResult, display);
display.Path = Url.GetTreePathFromFilePath(scriptResult.Path);
display.Id = System.Web.HttpUtility.UrlEncode(scriptResult.Path);
return display;
//display.AddErrorNotification(
// Services.TextService.Localize("speechBubbles/partialViewErrorHeader"),
// Services.TextService.Localize("speechBubbles/partialViewErrorText"));
Services.FileService.SaveScript(script, Security.CurrentUser.Id);
break;
default:
throw new HttpResponseException(HttpStatusCode.NotFound);
}
@@ -398,11 +384,86 @@ namespace Umbraco.Web.Editors
return display;
}
/// <summary>
/// Create or Update a Script
/// </summary>
/// <param name="display"></param>
/// <returns></returns>
/// <remarks>
/// It's important to note that Scripts are DIFFERENT from cshtml files since scripts use IFileSystem and cshtml files
/// use a normal file system because they must exist on a real file system for ASP.NET to work.
/// </remarks>
private Script CreateOrUpdateScript(CodeFileDisplay display)
{
//must always end with the correct extension
display.Name = EnsureCorrectFileExtension(display.Name, ".js");
var virtualPath = display.VirtualPath ?? string.Empty;
// this is all weird, should be using relative paths everywhere!
var relPath = FileSystemProviderManager.Current.ScriptsFileSystem.GetRelativePath(virtualPath);
if (relPath.EndsWith(".js") == false)
{
//this would typically mean it's new
relPath = relPath.IsNullOrWhiteSpace()
? relPath + display.Name
: relPath.EnsureEndsWith('/') + display.Name;
}
var script = Services.FileService.GetScriptByName(relPath);
if (script != null)
{
// might need to find the path
var orgPath = script.OriginalPath.Substring(0, script.OriginalPath.IndexOf(script.Name));
script.Path = orgPath + display.Name;
script.Content = display.Content;
//try/catch? since this doesn't return an Attempt?
Services.FileService.SaveScript(script, Security.CurrentUser.Id);
}
else
{
script = new Script(relPath);
script.Content = display.Content;
Services.FileService.SaveScript(script, Security.CurrentUser.Id);
}
return script;
}
private Attempt<IPartialView> CreateOrUpdatePartialView(CodeFileDisplay display)
{
return CreateOrUpdatePartialView(display, SystemDirectories.MacroPartials,
Services.FileService.GetPartialView, Services.FileService.SavePartialView, Services.FileService.CreatePartialView);
}
private Attempt<IPartialView> CreateOrUpdatePartialViewMacro(CodeFileDisplay display)
{
return CreateOrUpdatePartialView(display, SystemDirectories.MacroPartials,
Services.FileService.GetPartialViewMacro, Services.FileService.SavePartialViewMacro, Services.FileService.CreatePartialViewMacro);
}
/// <summary>
/// Helper method to take care of persisting partial views or partial view macros - so we're not duplicating the same logic
/// </summary>
/// <param name="display"></param>
/// <param name="systemDirectory"></param>
/// <param name="getView"></param>
/// <param name="saveView"></param>
/// <param name="createView"></param>
/// <returns></returns>
private Attempt<IPartialView> CreateOrUpdatePartialView(
CodeFileDisplay display, string systemDirectory,
Func<string, IPartialView> getView,
Func<IPartialView, int, Attempt<IPartialView>> saveView,
Func<IPartialView, string, int, Attempt<IPartialView>> createView)
{
//must always end with the correct extension
display.Name = EnsureCorrectFileExtension(display.Name, ".cshtml");
Attempt<IPartialView> partialViewResult;
string virtualPath = NormalizeVirtualPath(display.VirtualPath, SystemDirectories.PartialViews);
var view = Services.FileService.GetPartialView(virtualPath);
var virtualPath = NormalizeVirtualPath(display.VirtualPath, systemDirectory);
var view = getView(virtualPath);
if (view != null)
{
// might need to find the path
@@ -410,14 +471,13 @@ namespace Umbraco.Web.Editors
view.Path = orgPath + display.Name;
view.Content = display.Content;
partialViewResult = Services.FileService.SavePartialView(view, Security.CurrentUser.Id);
partialViewResult = saveView(view, Security.CurrentUser.Id);
}
else
{
var fileName = EnsurePartialViewExtension(display.Name, ".cshtml");
view = new PartialView(virtualPath + fileName);
view = new PartialView(virtualPath + display.Name);
view.Content = display.Content;
partialViewResult = Services.FileService.CreatePartialView(view, display.Snippet, Security.CurrentUser.Id);
partialViewResult = createView(view, display.Snippet, Security.CurrentUser.Id);
}
return partialViewResult;
@@ -437,29 +497,7 @@ namespace Umbraco.Web.Editors
return virtualPath;
}
private Attempt<IPartialView> CreateOrUpdatePartialViewMacro(CodeFileDisplay display)
{
Attempt<IPartialView> partialViewMacroResult;
var virtualPath = display.VirtualPath ?? string.Empty;
var viewMacro = Services.FileService.GetPartialViewMacro(virtualPath);
if (viewMacro != null)
{
viewMacro.Content = display.Content;
viewMacro.Path = display.Name;
partialViewMacroResult = Services.FileService.SavePartialViewMacro(viewMacro, Security.CurrentUser.Id);
}
else
{
var fileName = EnsurePartialViewExtension(display.Name, ".cshtml");
viewMacro = new PartialView(virtualPath + fileName);
viewMacro.Content = display.Content;
partialViewMacroResult = Services.FileService.CreatePartialViewMacro(viewMacro, display.Snippet, Security.CurrentUser.Id);
}
return partialViewMacroResult;
}
private string EnsurePartialViewExtension(string value, string extension)
private string EnsureCorrectFileExtension(string value, string extension)
{
if (value.EndsWith(extension) == false)
value += extension;
+39 -1
View File
@@ -53,6 +53,7 @@ namespace Umbraco.Web.Editors
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetChildren", "id", typeof(int), typeof(Guid), typeof(Udi), typeof(string))));
}
}
@@ -122,7 +123,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Gets the content json for the content id
/// Gets the media item by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
@@ -141,6 +142,43 @@ namespace Umbraco.Web.Editors
return Mapper.Map<IMedia, MediaItemDisplay>(foundContent);
}
/// <summary>
/// Gets the media item by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
[EnsureUserPermissionForMedia("id")]
public MediaItemDisplay GetById(Guid id)
{
var foundContent = GetObjectFromRequest(() => Services.MediaService.GetById(id));
if (foundContent == null)
{
HandleContentNotFound(id);
//HandleContentNotFound will throw an exception
return null;
}
return Mapper.Map<IMedia, MediaItemDisplay>(foundContent);
}
/// <summary>
/// Gets the media item by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
[EnsureUserPermissionForMedia("id")]
public MediaItemDisplay GetById(Udi id)
{
var guidUdi = id as GuidUdi;
if (guidUdi != null)
{
return GetById(guidUdi.Guid);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
/// <summary>
/// Return media for the specified ids
/// </summary>
@@ -1,16 +1,23 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
namespace Umbraco.Web.Models.ContentEditing
{
[DataContract(Name = "scriptFile", Namespace = "")]
public class CodeFileDisplay : INotificationModel
public class CodeFileDisplay : INotificationModel, IValidatableObject
{
public CodeFileDisplay()
{
Notifications = new List<Notification>();
}
/// <summary>
/// VirtualPath is the path to the file on disk
/// /views/partials/file.cshtml
@@ -47,5 +54,27 @@ namespace Umbraco.Web.Models.ContentEditing
public string Id { get; set; }
public List<Notification> Notifications { get; private set; }
/// <summary>
/// Some custom validation is required for valid file names
/// </summary>
/// <param name="validationContext"></param>
/// <returns></returns>
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var illegalChars = System.IO.Path.GetInvalidFileNameChars();
if (Name.ContainsAny(illegalChars))
{
yield return new ValidationResult(
"The file name cannot contain illegal characters",
new[] { "Name" });
}
else if (System.IO.Path.GetFileNameWithoutExtension(Name).IsNullOrWhiteSpace())
{
yield return new ValidationResult(
"The file name cannot be empty",
new[] { "Name" });
}
}
}
}
@@ -132,7 +132,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
if (nodeIds.Length > 0)
{
var umbHelper = new UmbracoHelper(UmbracoContext.Current);
var objectType = UmbracoObjectTypes.Unknown;
foreach (var nodeId in nodeIds)
@@ -148,8 +147,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
}
}
}
//TODO: Get rid of this Yield thing
return multiNodeTreePicker.Yield().Where(x => x != null);
return multiNodeTreePicker;
}
// return the first nodeId as this is one of the excluded properties that expects a single id
@@ -170,11 +169,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
{
var item = udi.ToPublishedContent();
if (item != null)
{
multiNodeTreePicker.Add(item);
}
}
}
//TODO: Get rid of this Yield thing
return multiNodeTreePicker.Yield().Where(x => x != null);
return multiNodeTreePicker;
}
// return the first nodeId as this is one of the excluded properties that expects a single id
@@ -8,6 +8,7 @@
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@@ -223,13 +224,26 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
/// </returns>
public bool IsMultipleDataType(int dataTypeId)
{
// ** This must be cached (U4-8862) **
var multiPickerPreValue =
_dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId)
.PreValuesAsDictionary.FirstOrDefault(
x => string.Equals(x.Key, "multiPicker", StringComparison.InvariantCultureIgnoreCase)).Value;
// GetPreValuesCollectionByDataTypeId is cached at repository level;
// still, the collection is deep-cloned so this is kinda expensive,
// better to cache here + trigger refresh in DataTypeCacheRefresher
return multiPickerPreValue != null && multiPickerPreValue.Value.TryConvertTo<bool>().Result;
return Storages.GetOrAdd(dataTypeId, id =>
{
var preValue = _dataTypeService.GetPreValuesCollectionByDataTypeId(id)
.PreValuesAsDictionary
.FirstOrDefault(x => string.Equals(x.Key, "multiPicker", StringComparison.InvariantCultureIgnoreCase))
.Value;
return preValue != null && preValue.Value.TryConvertTo<bool>().Result;
});
}
private static readonly ConcurrentDictionary<int, bool> Storages = new ConcurrentDictionary<int, bool>();
internal static void ClearCaches()
{
Storages.Clear();
}
}
}
@@ -1,90 +1,71 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http.Formatting;
using umbraco.BusinessLogic.Actions;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Web.Models.Trees;
using System.Web;
namespace Umbraco.Web.Trees
{
public abstract class FileSystemTreeController : TreeController
{
protected abstract string FilePath { get; }
protected abstract string FileSearchPattern { get; }
protected abstract IFileSystem2 FileSystem { get; }
protected abstract string[] Extensions { get; }
protected abstract string FileIcon { get; }
/// <summary>
/// Inheritors can override this method to modify the file node that is created.
/// </summary>
/// <param name="xNode"></param>
/// <param name="treeNode"></param>
protected virtual void OnRenderFileNode(ref TreeNode treeNode) { }
/// <summary>
/// Inheritors can override this method to modify the folder node that is created.
/// </summary>
/// <param name="xNode"></param>
/// <param name="treeNode"></param>
protected virtual void OnRenderFolderNode(ref TreeNode treeNode) { }
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
string orgPath = "";
string path = "";
if (!string.IsNullOrEmpty(id) && id != "-1")
{
orgPath = System.Web.HttpUtility.UrlDecode(id);
path = IOHelper.MapPath(FilePath + "/" + orgPath);
orgPath += "/";
}
else
{
path = IOHelper.MapPath(FilePath);
}
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.Root.ToInvariantString()
? HttpUtility.UrlDecode(id).TrimStart("/")
: "";
DirectoryInfo dirInfo = new DirectoryInfo(path);
DirectoryInfo[] dirInfos = dirInfo.GetDirectories();
var directories = FileSystem.GetDirectories(path);
var nodes = new TreeNodeCollection();
foreach (DirectoryInfo dir in dirInfos)
foreach (var directory in directories)
{
if ((dir.Attributes & FileAttributes.Hidden) == 0)
{
var HasChildren = dir.GetFiles().Length > 0 || dir.GetDirectories().Length > 0;
var node = CreateTreeNode(System.Web.HttpUtility.UrlEncode(orgPath + dir.Name), orgPath, queryStrings, dir.Name, "icon-folder", HasChildren);
var hasChildren = FileSystem.GetFiles(directory).Any() || FileSystem.GetDirectories(directory).Any();
OnRenderFolderNode(ref node);
if(node != null)
nodes.Add(node);
}
var name = Path.GetFileName(directory);
var node = CreateTreeNode(HttpUtility.UrlEncode(directory), path, queryStrings, name, "icon-folder", hasChildren);
OnRenderFolderNode(ref node);
if(node != null)
nodes.Add(node);
}
//this is a hack to enable file system tree to support multiple file extension look-up
//so the pattern both support *.* *.xml and xml,js,vb for lookups
string[] allowedExtensions = new string[0];
bool filterByMultipleExtensions = FileSearchPattern.Contains(",");
FileInfo[] fileInfo;
if (filterByMultipleExtensions)
var files = FileSystem.GetFiles(path).Where(x =>
{
fileInfo = dirInfo.GetFiles();
allowedExtensions = FileSearchPattern.ToLower().Split(',');
}
else
fileInfo = dirInfo.GetFiles(FileSearchPattern);
var extension = Path.GetExtension(x);
return extension != null && Extensions.Contains(extension.Trim('.'), StringComparer.InvariantCultureIgnoreCase);
});
foreach (FileInfo file in fileInfo)
{
if ((file.Attributes & FileAttributes.Hidden) == 0)
foreach (var file in files)
{
var withoutExt = Path.GetFileNameWithoutExtension(file);
if (withoutExt.IsNullOrWhiteSpace() == false)
{
if (filterByMultipleExtensions && Array.IndexOf<string>(allowedExtensions, file.Extension.ToLower().Trim('.')) < 0)
continue;
var node = CreateTreeNode(System.Web.HttpUtility.UrlEncode(orgPath + file.Name), orgPath, queryStrings, file.Name, FileIcon, false);
var name = Path.GetFileName(file);
var node = CreateTreeNode(HttpUtility.UrlEncode(file), path, queryStrings, name, FileIcon, false);
OnRenderFileNode(ref node);
if(node != null)
if (node != null)
nodes.Add(node);
}
}
@@ -109,27 +90,22 @@ namespace Umbraco.Web.Trees
return menu;
}
string path;
if (string.IsNullOrEmpty(id) == false)
{
var orgPath = System.Web.HttpUtility.UrlDecode(id);
path = IOHelper.MapPath(FilePath + "/" + orgPath);
}
else
{
path = IOHelper.MapPath(FilePath);
}
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.Root.ToInvariantString()
? System.Web.HttpUtility.UrlDecode(id).TrimStart("/")
: "";
var dirInfo = new DirectoryInfo(path);
//check if it's a directory
if (dirInfo.Attributes == FileAttributes.Directory)
var isFile = FileSystem.FileExists(path);
var isDirectory = FileSystem.DirectoryExists(path);
if (isDirectory)
{
//set the default to create
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
//create action
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
var hasChildren = dirInfo.GetFiles().Length > 0 || dirInfo.GetDirectories().Length > 0;
var hasChildren = FileSystem.GetFiles(path).Any() || FileSystem.GetDirectories(path).Any();
//We can only delete folders if it doesn't have any children (folders or files)
if (hasChildren == false)
{
@@ -140,10 +116,9 @@ namespace Umbraco.Web.Trees
//refresh action
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
}
//if it's not a directory then we only allow to delete the item
else
else if (isFile)
{
//delete action
//if it's not a directory then we only allow to delete the item
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)));
}
@@ -10,14 +10,16 @@ namespace Umbraco.Web.Trees
[Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)]
public class PartialViewMacrosTreeController : FileSystemTreeController
{
protected override string FilePath
protected override IFileSystem2 FileSystem
{
get { return SystemDirectories.MacroPartials; }
get { return FileSystemProviderManager.Current.MacroPartialsFileSystem; }
}
protected override string FileSearchPattern
private static readonly string[] ExtensionsStatic = { "cshtml" };
protected override string[] Extensions
{
get { return "*.cshtml"; }
get { return ExtensionsStatic; }
}
protected override string FileIcon
@@ -25,11 +27,6 @@ namespace Umbraco.Web.Trees
get { return "icon-article"; }
}
protected override void OnRenderFileNode(ref TreeNode treeNode)
{
base.OnRenderFileNode(ref treeNode);
}
protected override void OnRenderFolderNode(ref TreeNode treeNode)
{
//TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
@@ -10,25 +10,23 @@ namespace Umbraco.Web.Trees
[Tree(Constants.Applications.Settings, "partialViews", "Partial Views", sortOrder: 2)]
public class PartialViewsTreeController : FileSystemTreeController
{
protected override string FilePath
protected override IFileSystem2 FileSystem
{
get { return SystemDirectories.PartialViews; }
get { return FileSystemProviderManager.Current.PartialViewsFileSystem; }
}
protected override string FileSearchPattern
{
get { return "*.cshtml"; }
}
private static readonly string[] ExtensionsStatic = { "cshtml" };
protected override string[] Extensions
{
get { return ExtensionsStatic; }
}
protected override string FileIcon
{
get { return "icon-article"; }
}
protected override void OnRenderFileNode(ref TreeNode treeNode)
{
base.OnRenderFileNode(ref treeNode);
}
protected override void OnRenderFolderNode(ref TreeNode treeNode)
{
//TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
@@ -7,14 +7,16 @@ namespace Umbraco.Web.Trees
[Tree(Constants.Applications.Settings, "scripts", "Scripts", sortOrder: 4)]
public class ScriptTreeController : FileSystemTreeController
{
protected override string FilePath
protected override IFileSystem2 FileSystem
{
get { return SystemDirectories.Scripts; }
get { return FileSystemProviderManager.Current.ScriptsFileSystem; }
}
protected override string FileSearchPattern
private static readonly string[] ExtensionsStatic = { "js" };
protected override string[] Extensions
{
get { return "*.js"; }
get { return ExtensionsStatic; }
}
protected override string FileIcon
{
@@ -26,10 +28,5 @@ namespace Umbraco.Web.Trees
//TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);";
}
protected override void OnRenderFileNode(ref TreeNode treeNode)
{
base.OnRenderFileNode(ref treeNode);
}
}
}
@@ -22,7 +22,6 @@ namespace Umbraco.Web.WebApi.Filters
{
private readonly int? _nodeId;
private readonly string _paramName;
private DictionarySource _source;
public enum DictionarySource
{
@@ -43,14 +42,12 @@ namespace Umbraco.Web.WebApi.Filters
{
Mandate.ParameterNotNullOrEmpty(paramName, "paramName");
_paramName = paramName;
_source = DictionarySource.ActionArguments;
}
public EnsureUserPermissionForMediaAttribute(string paramName, DictionarySource source)
{
Mandate.ParameterNotNullOrEmpty(paramName, "paramName");
_paramName = paramName;
_source = source;
}
public override bool AllowMultiple
@@ -58,6 +55,33 @@ namespace Umbraco.Web.WebApi.Filters
get { return true; }
}
private int GetNodeIdFromParameter(object parameterValue)
{
if (parameterValue is int)
{
return (int) parameterValue;
}
var guidId = Guid.Empty;
if (parameterValue is Guid)
{
guidId = (Guid)parameterValue;
}
else if (parameterValue is GuidUdi)
{
guidId = ((GuidUdi) parameterValue).Guid;
}
if (guidId != Guid.Empty)
{
var found = ApplicationContext.Current.Services.EntityService.GetIdForKey(guidId, UmbracoObjectTypes.Media);
if (found)
return found.Result;
}
throw new InvalidOperationException("The id type: " + parameterValue.GetType() + " is not a supported id");
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (UmbracoContext.Current.Security.CurrentUser == null)
@@ -68,7 +92,7 @@ namespace Umbraco.Web.WebApi.Filters
int nodeId;
if (_nodeId.HasValue == false)
{
var parts = _paramName.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
var parts = _paramName.Split(new [] { '.' }, StringSplitOptions.RemoveEmptyEntries);
if (actionContext.ActionArguments[parts[0]] == null)
{
@@ -77,7 +101,7 @@ namespace Umbraco.Web.WebApi.Filters
if (parts.Length == 1)
{
nodeId = (int)actionContext.ActionArguments[parts[0]];
nodeId = GetNodeIdFromParameter(actionContext.ActionArguments[parts[0]]);
}
else
{
@@ -88,7 +112,7 @@ namespace Umbraco.Web.WebApi.Filters
{
throw new InvalidOperationException("No argument found for the current action with the name: " + _paramName);
}
nodeId = (int)prop.GetValue(actionContext.ActionArguments[parts[0]]);
nodeId = GetNodeIdFromParameter(prop.GetValue(actionContext.ActionArguments[parts[0]]));
}
}
else
@@ -109,22 +133,7 @@ namespace Umbraco.Web.WebApi.Filters
}
}
//private object GetValueFromSource(HttpActionContext actionContext, string key)
//{
// switch (_source)
// {
// case DictionarySource.ActionArguments:
// return actionContext.ActionArguments[key];
// case DictionarySource.RequestForm:
// return actionContext.Request.Properties
// case DictionarySource.RequestQueryString:
// break;
// default:
// throw new ArgumentOutOfRangeException();
// }
//}
}