Compare commits
133 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21426610ad | |||
| 40b0a62cc3 | |||
| 3949b78c61 | |||
| 55179d7400 | |||
| 517147346e | |||
| e1e63d079f | |||
| 86744046e3 | |||
| a5f1c3e564 | |||
| 2b2734cd66 | |||
| c2d33f3301 | |||
| 8fc999f771 | |||
| be0fb51193 | |||
| 097f5355e2 | |||
| 843fad9963 | |||
| 7b5670f275 | |||
| f4410891e4 | |||
| 9da95ce28f | |||
| 33e05625ed | |||
| b8575d04ad | |||
| b64b67ce81 | |||
| 1b6a2c7d5c | |||
| a1a90d5bff | |||
| 08733d7351 | |||
| 5d0d65c624 | |||
| 6c7c333d8f | |||
| b039fbfb13 | |||
| 428bc7fbad | |||
| fe9686f215 | |||
| e4a472f902 | |||
| 900f8970f0 | |||
| dd6c24d3b8 | |||
| 8175e68ecd | |||
| c839285f90 | |||
| 21378a07c8 | |||
| 74e6633f2a | |||
| 8693b13e68 | |||
| a0a861370b | |||
| 7282626c0f | |||
| cf604255fc | |||
| b275850aca | |||
| 8aa36a82c4 | |||
| 06132fcdaf | |||
| f1e5b3fcfa | |||
| fba4b91bec | |||
| 0ef987207b | |||
| 2d7b5b959e | |||
| 2837861d9a | |||
| 212222c98e | |||
| 15237a6d91 | |||
| 95b2ef0f3c | |||
| c452d31f25 | |||
| 7361d1f16f | |||
| 7ffb22a40f | |||
| f78e496e69 | |||
| 48ed511ba5 | |||
| 3cff6fdd6c | |||
| 81da2e49f7 | |||
| 2cd6427958 | |||
| 061f1ae0b1 | |||
| f503cd41de | |||
| 4428877158 | |||
| 876f61ac27 | |||
| 2cc541e084 | |||
| ddd3c78fa6 | |||
| 62dffedc02 | |||
| 13e5409ab2 | |||
| 927e9cf14a | |||
| 7611829f2e | |||
| 1ff5d171e3 | |||
| b8aaebf51a | |||
| 2c9bc5135c | |||
| 248177f518 | |||
| d34901fd13 | |||
| 94b12dfb02 | |||
| 0cc88a1706 | |||
| 78524398b7 | |||
| f97123015a | |||
| b1e6559f4d | |||
| 9d9c36b253 | |||
| 6d9a747b34 | |||
| a28cd8a54f | |||
| 44ad842542 | |||
| 917e05a16c | |||
| e60d5c8f2c | |||
| 03f16dfb78 | |||
| c9106065fd | |||
| 2c9e96406b | |||
| 21c7d6a667 | |||
| e0f95f18d3 | |||
| b9ffbe9a57 | |||
| dc9b2b3ca8 | |||
| baafa604fe | |||
| e79bfebb76 | |||
| 131fdab675 | |||
| efc2a8e2ea | |||
| d725f1362f | |||
| e79427619b | |||
| 4887302b51 | |||
| aea735f20e | |||
| 7a34b490c7 | |||
| 8091d9a098 | |||
| e2e6440109 | |||
| caffb36ec2 | |||
| 0d054de6ac | |||
| 5397f2c53a | |||
| 61b11025bd | |||
| 8c8a0160a1 | |||
| cbd980357b | |||
| 8b37f1dc6c | |||
| 6ec7028fe8 | |||
| 14c0055899 | |||
| e0981f1c44 | |||
| 2ca7304215 | |||
| 88f8052573 | |||
| 5afeb315ba | |||
| c2eeff6e47 | |||
| 893c2a99be | |||
| 9da18d0697 | |||
| 3bb991edd3 | |||
| dd0373b855 | |||
| fce0a4b0eb | |||
| 381de750e7 | |||
| f5d18157bf | |||
| 7c7dd0736e | |||
| 0a5cd9f113 | |||
| 9b498b8471 | |||
| 365da75d1b | |||
| 3489bf22bb | |||
| 9e82b8d693 | |||
| 8216fa1d88 | |||
| 4133574db0 | |||
| bc9a6abccf | |||
| 9f5a9f179f |
@@ -9,7 +9,7 @@ If you're interested in making changes to Belle without running Visual Studio ma
|
||||
|
||||
Note that you can always [download a nightly build](http://nightly.umbraco.org/?container=umbraco-750) so you don't have to build the code yourself.
|
||||
|
||||
## Watch a introduction video ##
|
||||
## Watch an introduction video ##
|
||||
|
||||
[](https://umbraco.tv/videos/umbraco-v7/content-editor/basics/introduction/cms-explanation/)
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
<dependency id="ClientDependency" version="[1.9.1, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
|
||||
<!-- AutoMapper can not be updated due to: https://github.com/AutoMapper/AutoMapper/issues/373#issuecomment-127644405 -->
|
||||
<dependency id="AutoMapper" version="[3.0.0, 3.1.0)" />
|
||||
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.69, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.4.4, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.6.4, 5.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.70, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.4.5, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.6.6, 5.0.0)" />
|
||||
<dependency id="semver" version="[1.1.2, 2.0.0)" />
|
||||
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
|
||||
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='HtmlAgilityPack')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='AutoMapper')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Newtonsoft.Json')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Mvc')" xdt:Transform="Remove" />
|
||||
@@ -338,6 +339,10 @@
|
||||
<assemblyIdentity name="HtmlAgilityPack" publicKeyToken="bd319b19eaf3b43a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.4.9.0" newVersion="1.4.9.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.3.1.0" newVersion="3.3.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.5.3
|
||||
7.5.4
|
||||
+2
-2
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.5.3")]
|
||||
[assembly: AssemblyInformationalVersion("7.5.3")]
|
||||
[assembly: AssemblyFileVersion("7.5.4")]
|
||||
[assembly: AssemblyInformationalVersion("7.5.4")]
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.5.3");
|
||||
private static readonly Version Version = new Version("7.5.4");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -69,6 +69,11 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string Document = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Document object.
|
||||
/// </summary>
|
||||
public static readonly Guid DocumentGuid = new Guid(Document);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Document Type object.
|
||||
/// </summary>
|
||||
|
||||
@@ -128,8 +128,14 @@ namespace Umbraco.Core.Events
|
||||
get { return TargetSemVersion.GetVersion(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the origin version of the migration, i.e. the one that is currently installed.
|
||||
/// </summary>
|
||||
public SemVersion ConfiguredSemVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target version of the migration.
|
||||
/// </summary>
|
||||
public SemVersion TargetSemVersion { get; private set; }
|
||||
|
||||
public string ProductName { get; private set; }
|
||||
|
||||
@@ -41,11 +41,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
using (var file = fs.OpenFile(path))
|
||||
{
|
||||
using (var sr = new StreamReader(file))
|
||||
{
|
||||
var str = sr.ReadToEnd();
|
||||
return str.Length;
|
||||
}
|
||||
return file.Length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Umbraco.Core.IO
|
||||
private void Initialize()
|
||||
{
|
||||
Filename = _fs.GetFileName(Path);
|
||||
Extension = _fs.GetExtension(Path) != null
|
||||
Extension = string.IsNullOrEmpty(_fs.GetExtension(Path)) == false
|
||||
? _fs.GetExtension(Path).Substring(1).ToLowerInvariant()
|
||||
: "";
|
||||
Url = _fs.GetUrl(Path);
|
||||
|
||||
@@ -107,9 +107,9 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public void ClearRules()
|
||||
{
|
||||
foreach (var rule in _ruleCollection)
|
||||
for (var i = _ruleCollection.Count - 1; i >= 0; i--)
|
||||
{
|
||||
RemoveRule(rule);
|
||||
RemoveRule(_ruleCollection[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
else if (entity is IMemberType)
|
||||
nodeObjectType = Constants.ObjectTypes.MemberTypeGuid;
|
||||
else
|
||||
throw new Exception("oops: invalid entity.");
|
||||
throw new Exception("Invalid entity.");
|
||||
|
||||
var contentTypeDto = new ContentTypeDto
|
||||
{
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
// note: no idea why Id is nullable here, but better check
|
||||
if (groupDto.Id.HasValue == false)
|
||||
throw new Exception("oops: groupDto.Id has no value.");
|
||||
throw new Exception("GroupDto.Id has no value.");
|
||||
group.Id = groupDto.Id.Value;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,11 +34,27 @@ namespace Umbraco.Core.Persistence
|
||||
return sql.Where(whereExpression, expresionist.GetSqlParameters());
|
||||
}
|
||||
|
||||
private static string GetFieldName<T>(Expression<Func<T, object>> fieldSelector, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var field = ExpressionHelper.FindProperty(fieldSelector) as PropertyInfo;
|
||||
var fieldName = field.GetColumnName();
|
||||
|
||||
var type = typeof(T);
|
||||
var tableName = type.GetTableName();
|
||||
|
||||
return sqlSyntax.GetQuotedTableName(tableName) + "." + sqlSyntax.GetQuotedColumnName(fieldName);
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload specifying ISqlSyntaxProvider instead")]
|
||||
public static Sql WhereIn<T>(this Sql sql, Expression<Func<T, object>> fieldSelector, IEnumerable values)
|
||||
{
|
||||
var expresionist = new PocoToSqlExpressionHelper<T>();
|
||||
var fieldExpression = expresionist.Visit(fieldSelector);
|
||||
return sql.Where(fieldExpression + " IN (@values)", new {@values = values});
|
||||
return sql.WhereIn(fieldSelector, values, SqlSyntaxContext.SqlSyntaxProvider);
|
||||
}
|
||||
|
||||
public static Sql WhereIn<T>(this Sql sql, Expression<Func<T, object>> fieldSelector, IEnumerable values, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var fieldName = GetFieldName(fieldSelector, sqlSyntax);
|
||||
return sql.Where(fieldName + " IN (@values)", new { values });
|
||||
}
|
||||
|
||||
[Obsolete("Use the overload specifying ISqlSyntaxProvider instead")]
|
||||
@@ -49,17 +65,7 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
public static Sql OrderBy<TColumn>(this Sql sql, Expression<Func<TColumn, object>> columnMember, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
var column = ExpressionHelper.FindProperty(columnMember) as PropertyInfo;
|
||||
var columnName = column.GetColumnName();
|
||||
|
||||
var type = typeof(TColumn);
|
||||
var tableName = type.GetTableName();
|
||||
|
||||
//need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177
|
||||
var syntax = string.Format("({0}.{1})",
|
||||
sqlSyntax.GetQuotedTableName(tableName),
|
||||
sqlSyntax.GetQuotedColumnName(columnName));
|
||||
|
||||
var syntax = "(" + GetFieldName(columnMember, sqlSyntax) + ")";
|
||||
return sql.OrderBy(syntax);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,11 +34,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
int CountDescendants(int parentId, string contentTypeAlias = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all versions for an <see cref="TEntity"/>.
|
||||
/// Gets a list of all versions for an <see cref="TEntity"/> ordered so latest is first
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="TEntity"/> to retrieve versions from</param>
|
||||
/// <returns>An enumerable list of the same <see cref="TEntity"/> object with different versions</returns>
|
||||
IEnumerable<TEntity> GetAllVersions(int id);
|
||||
IEnumerable<TEntity> GetAllVersions(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all version Ids for the given content item
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="maxRows">The maximum number of rows to return</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<Guid> GetVersionIds(int id, int maxRows);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific version of an <see cref="TEntity"/>.
|
||||
|
||||
@@ -18,6 +18,27 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public IEnumerable<Notification> GetUsersNotifications(IEnumerable<int> userIds, string action, IEnumerable<int> nodeIds, Guid objectType)
|
||||
{
|
||||
var nodeIdsA = nodeIds.ToArray();
|
||||
var syntax = ApplicationContext.Current.DatabaseContext.SqlSyntax; // bah
|
||||
var sql = new Sql()
|
||||
.Select("DISTINCT umbracoNode.id nodeId, umbracoUser.id userId, umbracoNode.nodeObjectType, umbracoUser2NodeNotify.action")
|
||||
.From<User2NodeNotifyDto>(syntax)
|
||||
.InnerJoin<NodeDto>(syntax).On<User2NodeNotifyDto, NodeDto>(syntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<UserDto>(syntax).On<User2NodeNotifyDto, UserDto>(syntax, left => left.UserId, right => right.Id)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == objectType)
|
||||
.Where<UserDto>(x => x.Disabled == false) // only approved users
|
||||
.Where<User2NodeNotifyDto>(x => x.Action == action); // on the specified action
|
||||
if (nodeIdsA.Length > 0)
|
||||
sql
|
||||
.WhereIn<NodeDto>(x => x.NodeId, nodeIdsA); // for the specified nodes
|
||||
sql
|
||||
.OrderBy<UserDto>(x => x.Id, syntax)
|
||||
.OrderBy<NodeDto>(dto => dto.NodeId, syntax);
|
||||
return _unitOfWork.Database.Fetch<dynamic>(sql).Select(x => new Notification(x.nodeId, x.userId, x.action, objectType));
|
||||
}
|
||||
|
||||
public IEnumerable<Notification> GetUserNotifications(IUser user)
|
||||
{
|
||||
var sql = new Sql()
|
||||
|
||||
@@ -565,7 +565,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
var array = tagsToInsert
|
||||
.Select(tag =>
|
||||
string.Format("select '{0}' as Tag, '{1}' as " + SqlSyntax.GetQuotedColumnName("group") + @"",
|
||||
string.Format("select N'{0}' as Tag, '{1}' as " + SqlSyntax.GetQuotedColumnName("group") + @"",
|
||||
PetaPocoExtensions.EscapeAtSymbols(tag.Text.Replace("'", "''")), tag.Group))
|
||||
.ToArray();
|
||||
return "(" + string.Join(" union ", array).Replace(" ", " ") + ") as TagSet";
|
||||
|
||||
@@ -320,51 +320,51 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </remarks>
|
||||
public IEnumerable<IUser> GetPagedResultsByQuery(IQuery<IUser> query, int pageIndex, int pageSize, out int totalRecords, Expression<Func<IUser, string>> orderBy)
|
||||
{
|
||||
if (orderBy == null) throw new ArgumentNullException("orderBy");
|
||||
if (orderBy == null)
|
||||
throw new ArgumentNullException("orderBy");
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("*").From<UserDto>();
|
||||
|
||||
Sql resultQuery;
|
||||
if (query != null)
|
||||
{
|
||||
var translator = new SqlTranslator<IUser>(sql, query);
|
||||
resultQuery = translator.Translate();
|
||||
}
|
||||
else
|
||||
{
|
||||
resultQuery = sql;
|
||||
}
|
||||
|
||||
//get the referenced column name
|
||||
// get the referenced column name and find the corresp mapped column name
|
||||
var expressionMember = ExpressionHelper.GetMemberInfo(orderBy);
|
||||
//now find the mapped column name
|
||||
var mapper = MappingResolver.Current.ResolveMapperByType(typeof(IUser));
|
||||
var mappedField = mapper.Map(expressionMember.Name);
|
||||
|
||||
if (mappedField.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new ArgumentException("Could not find a mapping for the column specified in the orderBy clause");
|
||||
}
|
||||
//need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177
|
||||
resultQuery.OrderBy(string.Format("({0})", mappedField));
|
||||
|
||||
var pagedResult = Database.Page<UserDto>(pageIndex + 1, pageSize, resultQuery);
|
||||
var sql = new Sql()
|
||||
.Select("umbracoUser.Id")
|
||||
.From<UserDto>(SqlSyntax);
|
||||
|
||||
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
|
||||
var idsQuery = query == null ? sql : new SqlTranslator<IUser>(sql, query).Translate();
|
||||
|
||||
// need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177
|
||||
idsQuery.OrderBy("(" + mappedField + ")");
|
||||
var page = Database.Page<int>(pageIndex + 1, pageSize, idsQuery);
|
||||
totalRecords = Convert.ToInt32(page.TotalItems);
|
||||
|
||||
//now that we have the user dto's we need to construct true members from the list.
|
||||
if (totalRecords == 0)
|
||||
{
|
||||
return Enumerable.Empty<IUser>();
|
||||
}
|
||||
|
||||
var ids = pagedResult.Items.Select(x => x.Id).ToArray();
|
||||
var result = ids.Length == 0 ? Enumerable.Empty<IUser>() : GetAll(ids);
|
||||
|
||||
//now we need to ensure this result is also ordered by the same order by clause
|
||||
return result.OrderBy(orderBy.Compile());
|
||||
// now get the actual users and ensure they are ordered properly (same clause)
|
||||
var ids = page.Items.ToArray();
|
||||
return ids.Length == 0 ? Enumerable.Empty<IUser>() : GetAll(ids).OrderBy(orderBy.Compile());
|
||||
}
|
||||
|
||||
|
||||
internal IEnumerable<IUser> GetNextUsers(int id, int count)
|
||||
{
|
||||
var idsQuery = new Sql()
|
||||
.Select("umbracoUser.Id")
|
||||
.From<UserDto>(SqlSyntax)
|
||||
.Where<UserDto>(x => x.Id >= id)
|
||||
.OrderBy<UserDto>(x => x.Id, SqlSyntax);
|
||||
|
||||
// first page is index 1, not zero
|
||||
var ids = Database.Page<int>(1, count, idsQuery).Items.ToArray();
|
||||
|
||||
// now get the actual users and ensure they are ordered properly (same clause)
|
||||
return ids.Length == 0 ? Enumerable.Empty<IUser>() : GetAll(ids).OrderBy(x => x.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns permissions for a given user for any number of nodes
|
||||
/// </summary>
|
||||
|
||||
@@ -40,6 +40,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region IRepositoryVersionable Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all versions for an <see cref="TEntity"/> ordered so latest is first
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the <see cref="TEntity"/> to retrieve versions from</param>
|
||||
/// <returns>An enumerable list of the same <see cref="TEntity"/> object with different versions</returns>
|
||||
public virtual IEnumerable<TEntity> GetAllVersions(int id)
|
||||
{
|
||||
var sql = new Sql();
|
||||
@@ -60,6 +65,28 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all version Ids for the given content item ordered so latest is first
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="maxRows">The maximum number of rows to return</param>
|
||||
/// <returns></returns>
|
||||
public virtual IEnumerable<Guid> GetVersionIds(int id, int maxRows)
|
||||
{
|
||||
var sql = new Sql();
|
||||
sql.Select("cmsDocument.versionId")
|
||||
.From<DocumentDto>(SqlSyntax)
|
||||
.InnerJoin<ContentDto>(SqlSyntax)
|
||||
.On<DocumentDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
|
||||
.Where<NodeDto>(x => x.NodeId == id)
|
||||
.OrderByDescending<DocumentDto>(x => x.UpdateDate, SqlSyntax);
|
||||
|
||||
return Database.Fetch<Guid>(SqlSyntax.SelectTop(sql, maxRows));
|
||||
}
|
||||
|
||||
public virtual void DeleteVersion(Guid versionId)
|
||||
{
|
||||
var dto = Database.FirstOrDefault<ContentVersionDto>("WHERE versionId = @VersionId", new { VersionId = versionId });
|
||||
@@ -583,6 +610,8 @@ WHERE EXISTS(
|
||||
return "cmsContentVersion.VersionDate";
|
||||
case "NAME":
|
||||
return "umbracoNode.text";
|
||||
case "PUBLISHED":
|
||||
return "cmsDocument.published";
|
||||
case "OWNER":
|
||||
//TODO: This isn't going to work very nicely because it's going to order by ID, not by letter
|
||||
return "umbracoNode.nodeUser";
|
||||
|
||||
@@ -113,8 +113,8 @@ namespace Umbraco.Core.Security
|
||||
|
||||
//Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd and has the back office session
|
||||
var claimsIdentity = http.User.Identity as ClaimsIdentity;
|
||||
if (claimsIdentity != null && claimsIdentity.IsAuthenticated)
|
||||
{
|
||||
if (claimsIdentity != null && claimsIdentity.IsAuthenticated && claimsIdentity.HasClaim(x => x.Type == Constants.Security.SessionIdClaimType))
|
||||
{
|
||||
try
|
||||
{
|
||||
return UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity);
|
||||
|
||||
@@ -342,11 +342,23 @@ namespace Umbraco.Core.Services
|
||||
/// <returns><see cref="IContent"/></returns>
|
||||
public IEnumerable<IContent> GetByIds(IEnumerable<int> ids)
|
||||
{
|
||||
if (ids.Any() == false) return Enumerable.Empty<IContent>();
|
||||
var idsArray = ids.ToArray();
|
||||
if (idsArray.Length == 0) return Enumerable.Empty<IContent>();
|
||||
|
||||
using (var repository = RepositoryFactory.CreateContentRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
return repository.GetAll(ids.ToArray());
|
||||
//ensure that the result has the order based on the ids passed in
|
||||
var result = repository.GetAll(idsArray);
|
||||
|
||||
var content = result.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
var sortedResult = idsArray.Select(x =>
|
||||
{
|
||||
IContent c;
|
||||
return content.TryGetValue(x, out c) ? c : null;
|
||||
}).WhereNotNull();
|
||||
|
||||
return sortedResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,6 +448,21 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all version Ids for the given content item ordered so latest is first
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="maxRows">The maximum number of rows to return</param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Guid> GetVersionIds(int id, int maxRows)
|
||||
{
|
||||
using (var repository = RepositoryFactory.CreateContentRepository(UowProvider.GetUnitOfWork()))
|
||||
{
|
||||
var versions = repository.GetVersionIds(id, maxRows);
|
||||
return versions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects, which are ancestors of the current content.
|
||||
/// </summary>
|
||||
|
||||
@@ -791,21 +791,23 @@ namespace Umbraco.Core.Services
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
|
||||
//TODO: This needs to change, if we are deleting a content type, we should just delete the data,
|
||||
// this method will recursively go lookup every content item, check if any of it's descendants are
|
||||
// of a different type, move them to the recycle bin, then permanently delete the content items.
|
||||
// The main problem with this is that for every content item being deleted, events are raised...
|
||||
// which we need for many things like keeping caches in sync, but we can surely do this MUCH better.
|
||||
|
||||
_contentService.DeleteContentOfType(contentType.Id);
|
||||
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreateContentTypeRepository(uow))
|
||||
{
|
||||
//TODO: This needs to change, if we are deleting a content type, we should just delete the data,
|
||||
// this method will recursively go lookup every content item, check if any of it's descendants are
|
||||
// of a different type, move them to the recycle bin, then permanently delete the content items.
|
||||
// The main problem with this is that for every content item being deleted, events are raised...
|
||||
// which we need for many things like keeping caches in sync, but we can surely do this MUCH better.
|
||||
|
||||
var deletedContentTypes = new List<IContentType>() {contentType};
|
||||
deletedContentTypes.AddRange(contentType.Descendants().OfType<IContentType>());
|
||||
|
||||
foreach (var deletedContentType in deletedContentTypes)
|
||||
{
|
||||
_contentService.DeleteContentOfType(deletedContentType.Id);
|
||||
}
|
||||
|
||||
repository.Delete(contentType);
|
||||
uow.Commit();
|
||||
|
||||
@@ -833,11 +835,6 @@ namespace Umbraco.Core.Services
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
foreach (var contentType in asArray)
|
||||
{
|
||||
_contentService.DeleteContentOfType(contentType.Id);
|
||||
}
|
||||
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreateContentTypeRepository(uow))
|
||||
{
|
||||
@@ -847,6 +844,15 @@ namespace Umbraco.Core.Services
|
||||
foreach (var contentType in asArray)
|
||||
{
|
||||
deletedContentTypes.AddRange(contentType.Descendants().OfType<IContentType>());
|
||||
}
|
||||
|
||||
foreach (var deletedContentType in deletedContentTypes)
|
||||
{
|
||||
_contentService.DeleteContentOfType(deletedContentType.Id);
|
||||
}
|
||||
|
||||
foreach (var contentType in asArray)
|
||||
{
|
||||
repository.Delete(contentType);
|
||||
}
|
||||
|
||||
|
||||
@@ -298,6 +298,14 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
IEnumerable<IContent> GetVersions(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all version Ids for the given content item ordered so latest is first
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="maxRows">The maximum number of rows to return</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<Guid> GetVersionIds(int id, int maxRows);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of <see cref="IContent"/> objects, which reside at the first level / root
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -15,7 +16,6 @@ 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
|
||||
{
|
||||
@@ -55,38 +55,72 @@ namespace Umbraco.Core.Services
|
||||
Func<IUser, string[], string> createSubject,
|
||||
Func<IUser, string[], string> createBody)
|
||||
{
|
||||
if ((entity is IContent) == false)
|
||||
if (entity is IContent == false)
|
||||
throw new NotSupportedException();
|
||||
|
||||
var content = (IContent) entity;
|
||||
|
||||
// lazily get versions - into a list to ensure we can enumerate multiple times
|
||||
List<IContent> allVersions = null;
|
||||
var content = (IContent) entity;
|
||||
|
||||
int totalUsers;
|
||||
var allUsers = _userService.GetAll(0, int.MaxValue, out totalUsers);
|
||||
foreach (var u in allUsers.Where(x => x.IsApproved))
|
||||
// lazily get previous version
|
||||
IContentBase prevVersion = null;
|
||||
|
||||
// do not load *all* users in memory at once
|
||||
// do not load notifications *per user* (N+1 select)
|
||||
// cannot load users & notifications in 1 query (combination btw User2AppDto and User2NodeNotifyDto)
|
||||
// => get batches of users, get all their notifications in 1 query
|
||||
// re. users:
|
||||
// users being (dis)approved = not an issue, filtered in memory not in SQL
|
||||
// users being modified or created = not an issue, ordering by ID, as long as we don't *insert* low IDs
|
||||
// users being deleted = not an issue for GetNextUsers
|
||||
var id = 0;
|
||||
var nodeIds = content.Path.Split(',').Select(int.Parse).ToArray();
|
||||
const int pagesz = 400; // load batches of 400 users
|
||||
do
|
||||
{
|
||||
var userNotifications = GetUserNotifications(u, content.Path);
|
||||
var notificationForAction = userNotifications.FirstOrDefault(x => x.Action == action);
|
||||
if (notificationForAction == null) continue;
|
||||
// users are returned ordered by id, notifications are returned ordered by user id
|
||||
var users = ((UserService) _userService).GetNextUsers(id, pagesz).Where(x => x.IsApproved).ToList();
|
||||
var notifications = GetUsersNotifications(users.Select(x => x.Id), action, nodeIds, Constants.ObjectTypes.DocumentGuid).ToList();
|
||||
if (notifications.Count == 0) break;
|
||||
|
||||
if (allVersions == null) // lazy load
|
||||
allVersions = _contentService.GetVersions(entity.Id).ToList();
|
||||
|
||||
try
|
||||
var i = 0;
|
||||
foreach (var user in users)
|
||||
{
|
||||
SendNotification(operatingUser, u, content, allVersions,
|
||||
actionName, http, createSubject, createBody);
|
||||
// continue if there's no notification for this user
|
||||
if (notifications[i].UserId != user.Id) continue; // next user
|
||||
|
||||
_logger.Debug<NotificationService>(string.Format("Notification type: {0} sent to {1} ({2})",
|
||||
action, u.Name, u.Email));
|
||||
// lazy load prev version
|
||||
if (prevVersion == null)
|
||||
{
|
||||
prevVersion = GetPreviousVersion(entity.Id);
|
||||
}
|
||||
|
||||
// queue notification
|
||||
var req = CreateNotificationRequest(operatingUser, user, content, prevVersion, actionName, http, createSubject, createBody);
|
||||
Enqueue(req);
|
||||
|
||||
// skip other notifications for this user
|
||||
while (i < notifications.Count && notifications[i++].UserId == user.Id) ;
|
||||
if (i >= notifications.Count) break; // break if no more notifications
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error<NotificationService>("An error occurred sending notification", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// load more users if any
|
||||
id = users.Count == pagesz ? users.Last().Id + 1 : -1;
|
||||
|
||||
} while (id > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous version to the latest version of the content item if there is one
|
||||
/// </summary>
|
||||
/// <param name="contentId"></param>
|
||||
/// <returns></returns>
|
||||
private IContentBase GetPreviousVersion(int contentId)
|
||||
{
|
||||
// Regarding this: http://issues.umbraco.org/issue/U4-5180
|
||||
// we know they are descending from the service so we know that newest is first
|
||||
// we are only selecting the top 2 rows since that is all we need
|
||||
var allVersions = _contentService.GetVersionIds(contentId, 2).ToList();
|
||||
var prevVersionIndex = allVersions.Count > 1 ? 1 : 0;
|
||||
return _contentService.GetByVersion(allVersions[prevVersionIndex]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -106,47 +140,76 @@ namespace Umbraco.Core.Services
|
||||
Func<IUser, string[], string> createSubject,
|
||||
Func<IUser, string[], string> createBody)
|
||||
{
|
||||
if ((entities is IEnumerable<IContent>) == false)
|
||||
if (entities is IEnumerable<IContent> == false)
|
||||
throw new NotSupportedException();
|
||||
|
||||
// ensure we can enumerate multiple times
|
||||
var entitiesL = entities as List<IContent> ?? entities.Cast<IContent>().ToList();
|
||||
|
||||
// lazily get versions - into lists to ensure we can enumerate multiple times
|
||||
var allVersionsDictionary = new Dictionary<int, List<IContent>>();
|
||||
//exit if there are no entities
|
||||
if (entitiesL.Count == 0) return;
|
||||
|
||||
int totalUsers;
|
||||
var allUsers = _userService.GetAll(0, int.MaxValue, out totalUsers);
|
||||
foreach (var u in allUsers.Where(x => x.IsApproved))
|
||||
//put all entity's paths into a list with the same indicies
|
||||
var paths = entitiesL.Select(x => x.Path.Split(',').Select(int.Parse).ToArray()).ToArray();
|
||||
|
||||
// lazily get versions
|
||||
var prevVersionDictionary = new Dictionary<int, IContentBase>();
|
||||
|
||||
// see notes above
|
||||
var id = 0;
|
||||
const int pagesz = 400; // load batches of 400 users
|
||||
do
|
||||
{
|
||||
var userNotifications = GetUserNotifications(u).ToArray();
|
||||
|
||||
foreach (var content in entitiesL)
|
||||
// users are returned ordered by id, notifications are returned ordered by user id
|
||||
var users = ((UserService)_userService).GetNextUsers(id, pagesz).Where(x => x.IsApproved).ToList();
|
||||
var notifications = GetUsersNotifications(users.Select(x => x.Id), action, Enumerable.Empty<int>(), Constants.ObjectTypes.DocumentGuid).ToList();
|
||||
if (notifications.Count == 0) break;
|
||||
|
||||
var i = 0;
|
||||
foreach (var user in users)
|
||||
{
|
||||
var userNotificationsByPath = FilterUserNotificationsByPath(userNotifications, content.Path);
|
||||
var notificationForAction = userNotificationsByPath.FirstOrDefault(x => x.Action == action);
|
||||
if (notificationForAction == null) continue;
|
||||
// continue if there's no notification for this user
|
||||
if (notifications[i].UserId != user.Id) continue; // next user
|
||||
|
||||
var allVersions = allVersionsDictionary.ContainsKey(content.Id) // lazy load
|
||||
? allVersionsDictionary[content.Id]
|
||||
: allVersionsDictionary[content.Id] = _contentService.GetVersions(content.Id).ToList();
|
||||
|
||||
try
|
||||
for (var j = 0; j < entitiesL.Count; j++)
|
||||
{
|
||||
SendNotification(operatingUser, u, content, allVersions,
|
||||
actionName, http, createSubject, createBody);
|
||||
var content = entitiesL[j];
|
||||
var path = paths[j];
|
||||
|
||||
// test if the notification applies to the path ie to this entity
|
||||
if (path.Contains(notifications[i].EntityId) == false) continue; // next entity
|
||||
|
||||
if (prevVersionDictionary.ContainsKey(content.Id) == false)
|
||||
{
|
||||
prevVersionDictionary[content.Id] = GetPreviousVersion(content.Id);
|
||||
}
|
||||
|
||||
// queue notification
|
||||
var req = CreateNotificationRequest(operatingUser, user, content, prevVersionDictionary[content.Id], actionName, http, createSubject, createBody);
|
||||
Enqueue(req);
|
||||
}
|
||||
|
||||
_logger.Debug<NotificationService>(string.Format("Notification type: {0} sent to {1} ({2})",
|
||||
action, u.Name, u.Email));
|
||||
}
|
||||
catch (Exception ex)
|
||||
// skip other notifications for this user, essentially this means moving i to the next index of notifications
|
||||
// for the next user.
|
||||
do
|
||||
{
|
||||
_logger.Error<NotificationService>("An error occurred sending notification", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
i++;
|
||||
} while (i < notifications.Count && notifications[i].UserId == user.Id);
|
||||
|
||||
if (i >= notifications.Count) break; // break if no more notifications
|
||||
}
|
||||
|
||||
// load more users if any
|
||||
id = users.Count == pagesz ? users.Last().Id + 1 : -1;
|
||||
|
||||
} while (id > 0);
|
||||
}
|
||||
|
||||
private IEnumerable<Notification> GetUsersNotifications(IEnumerable<int> userIds, string action, IEnumerable<int> nodeIds, Guid objectType)
|
||||
{
|
||||
var uow = _uowProvider.GetUnitOfWork();
|
||||
var repository = new NotificationsRepository(uow);
|
||||
return repository.GetUsersNotifications(userIds, action, nodeIds, objectType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the notifications for the user
|
||||
@@ -184,7 +247,7 @@ namespace Umbraco.Core.Services
|
||||
public IEnumerable<Notification> FilterUserNotificationsByPath(IEnumerable<Notification> userNotifications, string path)
|
||||
{
|
||||
var pathParts = path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
|
||||
return userNotifications.Where(r => pathParts.InvariantContains(r.EntityId.ToString(CultureInfo.InvariantCulture))).ToList();
|
||||
return userNotifications.Where(r => pathParts.InvariantContains(r.EntityId.ToString(CultureInfo.InvariantCulture))).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -254,29 +317,23 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="performingUser"></param>
|
||||
/// <param name="mailingUser"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="allVersions"></param>
|
||||
/// <param name="oldDoc"></param>
|
||||
/// <param name="actionName">The action readable name - currently an action is just a single letter, this is the name associated with the letter </param>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="createSubject">Callback to create the mail subject</param>
|
||||
/// <param name="createBody">Callback to create the mail body</param>
|
||||
private void SendNotification(IUser performingUser, IUser mailingUser, IContent content, IEnumerable<IContent> allVersions, string actionName, HttpContextBase http,
|
||||
private NotificationRequest CreateNotificationRequest(IUser performingUser, IUser mailingUser, IContentBase content, IContentBase oldDoc,
|
||||
string actionName, HttpContextBase http,
|
||||
Func<IUser, string[], string> createSubject,
|
||||
Func<IUser, string[], string> createBody)
|
||||
{
|
||||
if (performingUser == null) throw new ArgumentNullException("performingUser");
|
||||
if (mailingUser == null) throw new ArgumentNullException("mailingUser");
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
if (allVersions == null) throw new ArgumentNullException("allVersions");
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
if (createSubject == null) throw new ArgumentNullException("createSubject");
|
||||
if (createBody == null) throw new ArgumentNullException("createBody");
|
||||
|
||||
//Ensure they are sorted: http://issues.umbraco.org/issue/U4-5180
|
||||
var allVersionsAsArray = allVersions.OrderBy(x => x.UpdateDate).ToArray();
|
||||
|
||||
int versionCount = (allVersionsAsArray.Length > 1) ? (allVersionsAsArray.Length - 2) : (allVersionsAsArray.Length - 1);
|
||||
var oldDoc = _contentService.GetByVersion(allVersionsAsArray[versionCount].Version);
|
||||
|
||||
if (createBody == null) throw new ArgumentNullException("createBody");
|
||||
|
||||
// build summary
|
||||
var summary = new StringBuilder();
|
||||
var props = content.Properties.ToArray();
|
||||
@@ -290,16 +347,16 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
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)
|
||||
if ((p.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.TinyMCEAlias)
|
||||
&& string.CompareOrdinal(oldText, newText) != 0)
|
||||
{
|
||||
summary.Append("<tr>");
|
||||
@@ -308,26 +365,31 @@ namespace Umbraco.Core.Services
|
||||
"<td style='text-align: left; vertical-align: top;'> <span style='background-color:red;'>Red for deleted characters</span> <span style='background-color:yellow;'>Yellow for inserted characters</span></td>");
|
||||
summary.Append("</tr>");
|
||||
summary.Append("<tr>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> New " +
|
||||
p.PropertyType.Name + "</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;'>" +
|
||||
ReplaceLinks(CompareText(oldText, newText, true, false, "<span style='background-color:yellow;'>", string.Empty), http.Request) +
|
||||
"</td>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> New ");
|
||||
summary.Append(p.PropertyType.Name);
|
||||
summary.Append("</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;'>");
|
||||
summary.Append(ReplaceLinks(CompareText(oldText, newText, true, false, "<span style='background-color:yellow;'>", string.Empty), http.Request));
|
||||
summary.Append("</td>");
|
||||
summary.Append("</tr>");
|
||||
summary.Append("<tr>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> Old " +
|
||||
p.PropertyType.Name + "</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;'>" +
|
||||
ReplaceLinks(CompareText(newText, oldText, true, false, "<span style='background-color:red;'>", string.Empty), http.Request) +
|
||||
"</td>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> Old ");
|
||||
summary.Append(p.PropertyType.Name);
|
||||
summary.Append("</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;'>");
|
||||
summary.Append(ReplaceLinks(CompareText(newText, oldText, true, false, "<span style='background-color:red;'>", string.Empty), http.Request));
|
||||
summary.Append("</td>");
|
||||
summary.Append("</tr>");
|
||||
}
|
||||
else
|
||||
{
|
||||
summary.Append("<tr>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'>" +
|
||||
p.PropertyType.Name + "</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;'>" + newText + "</td>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'>");
|
||||
summary.Append(p.PropertyType.Name);
|
||||
summary.Append("</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;'>");
|
||||
summary.Append(newText);
|
||||
summary.Append("</td>");
|
||||
summary.Append("</tr>");
|
||||
}
|
||||
summary.Append(
|
||||
@@ -338,29 +400,27 @@ namespace Umbraco.Core.Services
|
||||
|
||||
|
||||
string[] subjectVars = {
|
||||
http.Request.ServerVariables["SERVER_NAME"] + ":" +
|
||||
http.Request.Url.Port +
|
||||
IOHelper.ResolveUrl(SystemDirectories.Umbraco),
|
||||
string.Concat(http.Request.ServerVariables["SERVER_NAME"], ":", http.Request.Url.Port, IOHelper.ResolveUrl(SystemDirectories.Umbraco)),
|
||||
actionName,
|
||||
content.Name
|
||||
};
|
||||
string[] bodyVars = {
|
||||
mailingUser.Name,
|
||||
actionName,
|
||||
content.Name,
|
||||
mailingUser.Name,
|
||||
actionName,
|
||||
content.Name,
|
||||
performingUser.Name,
|
||||
http.Request.ServerVariables["SERVER_NAME"] + ":" + http.Request.Url.Port + IOHelper.ResolveUrl(SystemDirectories.Umbraco),
|
||||
string.Concat(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,
|
||||
string.Concat(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",
|
||||
string.Concat(content.Id, ".aspx"),
|
||||
protocol)
|
||||
|
||||
|
||||
};
|
||||
|
||||
// create the mail message
|
||||
// create the mail message
|
||||
var mail = new MailMessage(UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress, mailingUser.Email);
|
||||
|
||||
// populate the message
|
||||
@@ -374,10 +434,10 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
mail.IsBodyHtml = true;
|
||||
mail.Body =
|
||||
@"<html><head>
|
||||
string.Concat(@"<html><head>
|
||||
</head>
|
||||
<body style='font-family: Trebuchet MS, arial, sans-serif; font-color: black;'>
|
||||
" + createBody(mailingUser, bodyVars);
|
||||
", createBody(mailingUser, bodyVars));
|
||||
}
|
||||
|
||||
// nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here
|
||||
@@ -390,32 +450,17 @@ namespace Umbraco.Core.Services
|
||||
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)
|
||||
{
|
||||
_logger.Error<NotificationService>("An error occurred sending notification", ex);
|
||||
}
|
||||
});
|
||||
return new NotificationRequest(mail, actionName, mailingUser.Name, mailingUser.Email);
|
||||
}
|
||||
|
||||
private static string ReplaceLinks(string text, HttpRequestBase request)
|
||||
{
|
||||
string domain = GlobalSettings.UseSSL ? "https://" : "http://";
|
||||
domain += request.ServerVariables["SERVER_NAME"] + ":" + request.Url.Port + "/";
|
||||
var sb = new StringBuilder(GlobalSettings.UseSSL ? "https://" : "http://");
|
||||
sb.Append(request.ServerVariables["SERVER_NAME"]);
|
||||
sb.Append(":");
|
||||
sb.Append(request.Url.Port);
|
||||
sb.Append("/");
|
||||
var domain = sb.ToString();
|
||||
text = text.Replace("href=\"/", "href=\"" + domain);
|
||||
text = text.Replace("src=\"/", "src=\"" + domain);
|
||||
return text;
|
||||
@@ -484,7 +529,7 @@ namespace Umbraco.Core.Services
|
||||
pos++;
|
||||
} // while
|
||||
sb.Append("</span>");
|
||||
} // if
|
||||
} // if
|
||||
} // while
|
||||
|
||||
// write rest of unchanged chars
|
||||
@@ -495,8 +540,95 @@ namespace Umbraco.Core.Services
|
||||
} // while
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// manage notifications
|
||||
// ideally, would need to use IBackgroundTasks - but they are not part of Core!
|
||||
|
||||
private static readonly object Locker = new object();
|
||||
private static readonly BlockingCollection<NotificationRequest> Queue = new BlockingCollection<NotificationRequest>();
|
||||
private static volatile bool _running;
|
||||
|
||||
private void Enqueue(NotificationRequest notification)
|
||||
{
|
||||
Queue.Add(notification);
|
||||
if (_running) return;
|
||||
lock (Locker)
|
||||
{
|
||||
if (_running) return;
|
||||
Process(Queue);
|
||||
_running = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class NotificationRequest
|
||||
{
|
||||
public NotificationRequest(MailMessage mail, string action, string userName, string email)
|
||||
{
|
||||
Mail = mail;
|
||||
Action = action;
|
||||
UserName = userName;
|
||||
Email = email;
|
||||
}
|
||||
|
||||
public MailMessage Mail { get; private set; }
|
||||
|
||||
public string Action { get; private set; }
|
||||
|
||||
public string UserName { get; private set; }
|
||||
|
||||
public string Email { get; private set; }
|
||||
}
|
||||
|
||||
private void Process(BlockingCollection<NotificationRequest> notificationRequests)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(state =>
|
||||
{
|
||||
var s = new SmtpClient();
|
||||
try
|
||||
{
|
||||
_logger.Debug<NotificationService>("Begin processing notifications.");
|
||||
while (true)
|
||||
{
|
||||
NotificationRequest request;
|
||||
while (notificationRequests.TryTake(out request, 8 * 1000)) // stay on for 8s
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Sendmail != null) Sendmail(s, request.Mail, _logger); else s.Send(request.Mail);
|
||||
_logger.Debug<NotificationService>(string.Format("Notification \"{0}\" sent to {1} ({2})", request.Action, request.UserName, request.Email));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error<NotificationService>("An error occurred sending notification", ex);
|
||||
s.Dispose();
|
||||
s = new SmtpClient();
|
||||
}
|
||||
finally
|
||||
{
|
||||
request.Mail.Dispose();
|
||||
}
|
||||
}
|
||||
lock (Locker)
|
||||
{
|
||||
if (notificationRequests.Count > 0) continue; // last chance
|
||||
_running = false; // going down
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
s.Dispose();
|
||||
}
|
||||
_logger.Debug<NotificationService>("Done processing notifications.");
|
||||
});
|
||||
}
|
||||
|
||||
// for tests
|
||||
internal static Action<SmtpClient, MailMessage, ILogger> Sendmail;
|
||||
//= (_, msg, logger) => logger.Debug<NotificationService>("Email " + msg.To.ToString());
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
@@ -506,6 +507,15 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<IUser> GetNextUsers(int id, int count)
|
||||
{
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var repository = (UserRepository) RepositoryFactory.CreateUserRepository(uow))
|
||||
{
|
||||
return repository.GetNextUsers(id, count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IUserService
|
||||
|
||||
@@ -142,7 +142,8 @@ namespace Umbraco.Core.Sync
|
||||
? ":" + request.ServerVariables["SERVER_PORT"]
|
||||
: "";
|
||||
|
||||
var ssl = GlobalSettings.UseSSL ? "s" : ""; // force, whatever the first request
|
||||
var useSsl = GlobalSettings.UseSSL || port == "443";
|
||||
var ssl = useSsl ? "s" : ""; // force, whatever the first request
|
||||
var url = "http" + ssl + "://" + request.ServerVariables["SERVER_NAME"] + port + IOHelper.ResolveUrl(SystemDirectories.Umbraco);
|
||||
|
||||
appContext._umbracoApplicationUrl = url.TrimEnd('/');
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AutoMapper, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll</HintPath>
|
||||
<Reference Include="AutoMapper, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="AutoMapper.Net4, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
<Reference Include="AutoMapper.Net4, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="HtmlAgilityPack, Version=1.4.9.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
|
||||
@@ -52,8 +52,8 @@
|
||||
<Reference Include="ICSharpCode.SharpZipLib">
|
||||
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ImageProcessor, Version=2.4.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.2.4.4.0\lib\net45\ImageProcessor.dll</HintPath>
|
||||
<Reference Include="ImageProcessor, Version=2.4.5.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.2.4.5.0\lib\net45\ImageProcessor.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="log4net, Version=1.2.11.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
@@ -1452,6 +1452,7 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
<Import Project="..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets" Condition="Exists('..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -48,6 +48,9 @@ namespace Umbraco.Core
|
||||
LogHelper.Error<UmbracoApplicationBase>(msg, exception);
|
||||
};
|
||||
|
||||
// this only gets called when an assembly can't be resolved
|
||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
|
||||
|
||||
//boot up the application
|
||||
GetBootManager()
|
||||
.Initialize()
|
||||
@@ -58,6 +61,22 @@ namespace Umbraco.Core
|
||||
ApplicationEventsResolver.Current.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when an assembly can't be resolved. In here we can do magic with the assembly name and try loading another.
|
||||
/// This is used for loading a signed assembly of AutoMapper (v. 3.1+) without having to recompile old code.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
|
||||
{
|
||||
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
|
||||
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow
|
||||
if (args.Name.StartsWith("AutoMapper") && args.Name.EndsWith("PublicKeyToken=null"))
|
||||
return Assembly.Load(args.Name.Replace(", PublicKeyToken=null", ", PublicKeyToken=be96cd2c38ef1005"));
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Umbraco application
|
||||
/// </summary>
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Umbraco.Core
|
||||
return;
|
||||
}
|
||||
if (n.Attributes[name] == null)
|
||||
{
|
||||
{
|
||||
var a = xml.CreateAttribute(name);
|
||||
a.Value = value;
|
||||
n.Attributes.Append(a);
|
||||
@@ -155,7 +155,7 @@ namespace Umbraco.Core
|
||||
elt = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the children of a parentNode.
|
||||
/// </summary>
|
||||
@@ -163,7 +163,7 @@ namespace Umbraco.Core
|
||||
/// <param name="childNodesXPath">An XPath expression to select children of <paramref name="parentNode"/> to sort.</param>
|
||||
/// <param name="orderBy">A function returning the value to order the nodes by.</param>
|
||||
internal static void SortNodes(
|
||||
XmlNode parentNode,
|
||||
XmlNode parentNode,
|
||||
string childNodesXPath,
|
||||
Func<XmlNode, int> orderBy)
|
||||
{
|
||||
@@ -227,7 +227,7 @@ namespace Umbraco.Core
|
||||
/// <param name="node">The child node to sort.</param>
|
||||
/// <param name="orderBy">A function returning the value to order the nodes by.</param>
|
||||
/// <returns>A value indicating whether sorting was needed.</returns>
|
||||
/// <remarks>Assuming all nodes but <paramref name="node"/> are sorted, this will move the node to
|
||||
/// <remarks>Assuming all nodes but <paramref name="node"/> are sorted, this will move the node to
|
||||
/// the right position without moving all the nodes (as SortNodes would do) - should improve perfs.</remarks>
|
||||
internal static bool SortNode(
|
||||
XmlNode parentNode,
|
||||
@@ -415,6 +415,25 @@ namespace Umbraco.Core
|
||||
return AddTextNode(xd, name, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets or creates an Xml node from its inner Xml.
|
||||
/// </summary>
|
||||
/// <param name="xd">The xmldocument.</param>
|
||||
/// <param name="parent">The node to set or create the child text node on</param>
|
||||
/// <param name="name">The node name.</param>
|
||||
/// <param name="value">The node inner Xml.</param>
|
||||
/// <returns>a XmlNode</returns>
|
||||
public static XmlNode SetInnerXmlNode(XmlDocument xd, XmlNode parent, string name, string value)
|
||||
{
|
||||
if (xd == null) throw new ArgumentNullException("xd");
|
||||
if (parent == null) throw new ArgumentNullException("parent");
|
||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name");
|
||||
|
||||
var child = parent.SelectSingleNode(name) ?? xd.CreateNode(XmlNodeType.Element, name, "");
|
||||
child.InnerXml = value;
|
||||
return child;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a cdata XmlNode with the specified name and value
|
||||
/// </summary>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
|
||||
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
|
||||
<package id="HtmlAgilityPack" version="1.4.9" targetFramework="net45" />
|
||||
<package id="ImageProcessor" version="2.4.4.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor" version="2.4.5.0" targetFramework="net45" />
|
||||
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net4" />
|
||||
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Identity.Owin" version="2.2.1" targetFramework="net45" />
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Tests.Persistence
|
||||
{
|
||||
[TestFixture]
|
||||
public class PetaPocoExpressionsTests
|
||||
{
|
||||
[Test]
|
||||
public void WhereInValueFieldTest()
|
||||
{
|
||||
var syntax = SqlSyntaxContext.SqlSyntaxProvider = new SqlCeSyntaxProvider();
|
||||
var sql = new Sql()
|
||||
.Select("*")
|
||||
.From<NodeDto>(syntax)
|
||||
.WhereIn<NodeDto>(x => x.NodeId, new[] { 1, 2, 3 }, syntax);
|
||||
Assert.AreEqual("SELECT *\nFROM [umbracoNode]\nWHERE ([umbracoNode].[id] IN (@0,@1,@2))", sql.SQL);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WhereInObjectFieldTest()
|
||||
{
|
||||
// this test used to fail because x => x.Text was evaluated as a lambda
|
||||
// and returned "[umbracoNode].[text] = @0"... had to fix WhereIn.
|
||||
|
||||
var syntax = SqlSyntaxContext.SqlSyntaxProvider = new SqlCeSyntaxProvider();
|
||||
var sql = new Sql()
|
||||
.Select("*")
|
||||
.From<NodeDto>(syntax)
|
||||
.WhereIn<NodeDto>(x => x.Text, new[] { "a", "b", "c" }, syntax);
|
||||
Assert.AreEqual("SELECT *\nFROM [umbracoNode]\nWHERE ([umbracoNode].[text] IN (@0,@1,@2))", sql.SQL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,8 +261,17 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[Test]
|
||||
public void GetCropUrl_SpecifiedCropModeTest()
|
||||
{
|
||||
var urlString = mediaPath.GetCropUrl(imageCropperValue: cropperJson1, width: 300, height: 150, imageCropMode:ImageCropMode.Max);
|
||||
Assert.AreEqual(mediaPath + "?mode=max&width=300&height=150", urlString);
|
||||
var urlStringMin = mediaPath.GetCropUrl(imageCropperValue: cropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Min);
|
||||
var urlStringBoxPad = mediaPath.GetCropUrl(imageCropperValue: cropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.BoxPad);
|
||||
var urlStringPad = mediaPath.GetCropUrl(imageCropperValue: cropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Pad);
|
||||
var urlStringMax = mediaPath.GetCropUrl(imageCropperValue: cropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Max);
|
||||
var urlStringStretch = mediaPath.GetCropUrl(imageCropperValue: cropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Stretch);
|
||||
|
||||
Assert.AreEqual(mediaPath + "?mode=min&width=300&height=150", urlStringMin);
|
||||
Assert.AreEqual(mediaPath + "?mode=boxpad&width=300&height=150", urlStringBoxPad);
|
||||
Assert.AreEqual(mediaPath + "?mode=pad&width=300&height=150", urlStringPad);
|
||||
Assert.AreEqual(mediaPath + "?mode=max&width=300&height=150", urlStringMax);
|
||||
Assert.AreEqual(mediaPath + "?mode=stretch&width=300&height=150", urlStringStretch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -68,6 +68,50 @@ namespace Umbraco.Tests.Services
|
||||
Assert.IsTrue(contentService.PublishWithStatus(content).Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Top_Version_Ids()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
|
||||
// Act
|
||||
var content = contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0);
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
content.SetValue("bodyText", "hello world " + Guid.NewGuid());
|
||||
contentService.SaveAndPublishWithStatus(content);
|
||||
}
|
||||
|
||||
|
||||
// Assert
|
||||
var allVersions = contentService.GetVersionIds(content.Id, int.MaxValue);
|
||||
Assert.AreEqual(21, allVersions.Count());
|
||||
|
||||
var topVersions = contentService.GetVersionIds(content.Id, 4);
|
||||
Assert.AreEqual(4, topVersions.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_By_Ids_Sorted()
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
|
||||
// Act
|
||||
var results = new List<IContent>();
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
results.Add(contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0));
|
||||
}
|
||||
|
||||
var sortedGet = contentService.GetByIds(new[] {results[10].Id, results[5].Id, results[12].Id}).ToArray();
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(sortedGet[0].Id, results[10].Id);
|
||||
Assert.AreEqual(sortedGet[1].Id, results[5].Id);
|
||||
Assert.AreEqual(sortedGet[2].Id, results[12].Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Count_All()
|
||||
{
|
||||
|
||||
@@ -275,6 +275,35 @@ namespace Umbraco.Tests.Services
|
||||
Assert.That(success, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Delete_Parent_ContentType_When_Child_Has_Content()
|
||||
{
|
||||
var cts = ServiceContext.ContentTypeService;
|
||||
var contentType = MockedContentTypes.CreateSimpleContentType("page", "Page", null, true);
|
||||
cts.Save(contentType);
|
||||
var childContentType = MockedContentTypes.CreateSimpleContentType("childPage", "Child Page", contentType, true, "Child Content");
|
||||
cts.Save(childContentType);
|
||||
var cs = ServiceContext.ContentService;
|
||||
var content = cs.CreateContent("Page 1", -1, childContentType.Alias);
|
||||
cs.Save(content);
|
||||
|
||||
cts.Delete(contentType);
|
||||
|
||||
Assert.IsNotNull(content.Id);
|
||||
Assert.AreNotEqual(0, content.Id);
|
||||
Assert.IsNotNull(childContentType.Id);
|
||||
Assert.AreNotEqual(0, childContentType.Id);
|
||||
Assert.IsNotNull(contentType.Id);
|
||||
Assert.AreNotEqual(0, contentType.Id);
|
||||
var deletedContent = cs.GetById(content.Id);
|
||||
var deletedChildContentType = cts.GetContentType(childContentType.Id);
|
||||
var deletedContentType = cts.GetContentType(contentType.Id);
|
||||
|
||||
Assert.IsNull(deletedChildContentType);
|
||||
Assert.IsNull(deletedContent);
|
||||
Assert.IsNull(deletedContentType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Deleting_ContentType_Sends_Correct_Number_Of_DeletedEntities_In_Events()
|
||||
{
|
||||
|
||||
@@ -142,7 +142,13 @@ namespace Umbraco.Tests.TestHelpers
|
||||
}
|
||||
else if (dateTimeFormat.IsNullOrWhiteSpace() == false && actualValue is DateTime)
|
||||
{
|
||||
Assert.AreEqual(((DateTime) expectedValue).ToString(dateTimeFormat), ((DateTime)actualValue).ToString(dateTimeFormat), "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue);
|
||||
// round to second else in some cases tests can fail ;-(
|
||||
var expectedDateTime = (DateTime) expectedValue;
|
||||
expectedDateTime = expectedDateTime.AddTicks(-(expectedDateTime.Ticks%TimeSpan.TicksPerSecond));
|
||||
var actualDateTime = (DateTime) actualValue;
|
||||
actualDateTime = actualDateTime.AddTicks(-(actualDateTime.Ticks % TimeSpan.TicksPerSecond));
|
||||
|
||||
Assert.AreEqual(expectedDateTime.ToString(dateTimeFormat), actualDateTime.ToString(dateTimeFormat), "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -51,16 +51,16 @@
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="AutoMapper, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll</HintPath>
|
||||
<Reference Include="AutoMapper, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="AutoMapper.Net4, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
<Reference Include="AutoMapper.Net4, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Examine, Version=0.1.69.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.69.0\lib\Examine.dll</HintPath>
|
||||
<Reference Include="Examine, Version=0.1.70.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.70.0\lib\Examine.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
|
||||
@@ -177,6 +177,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Migrations\MigrationIssuesTests.cs" />
|
||||
<Compile Include="Persistence\Migrations\MigrationStartupHandlerTests.cs" />
|
||||
<Compile Include="Persistence\PetaPocoExpressionsTests.cs" />
|
||||
<Compile Include="Persistence\Repositories\RedirectUrlRepositoryTests.cs" />
|
||||
<Compile Include="TestHelpers\Entities\MockedPropertyTypes.cs" />
|
||||
<Compile Include="TryConvertToTests.cs" />
|
||||
@@ -764,6 +765,7 @@
|
||||
xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" /Y /F /E /I /C /D</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
<Import Project="..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets" Condition="Exists('..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
|
||||
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.69.0" targetFramework="net45" />
|
||||
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.70.0" targetFramework="net45" />
|
||||
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
|
||||
|
||||
+7
-3
@@ -118,9 +118,13 @@ angular.module("umbraco.directives")
|
||||
$timeout(function(){
|
||||
setDimensions();
|
||||
});
|
||||
var offsetX = $overlay[0].offsetLeft;
|
||||
var offsetY = $overlay[0].offsetTop;
|
||||
calculateGravity(offsetX, offsetY);
|
||||
// Make sure we can find the offset values for the overlay(dot) before calculating
|
||||
// fixes issue with resize event when printing the page (ex. hitting ctrl+p inside the rte)
|
||||
if($overlay.is(':visible')) {
|
||||
var offsetX = $overlay[0].offsetLeft;
|
||||
var offsetY = $overlay[0].offsetTop;
|
||||
calculateGravity(offsetX, offsetY);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ angular.module("umbraco.directives")
|
||||
var curr = $(event.target); // active tab
|
||||
var prev = $(event.relatedTarget); // previous tab
|
||||
|
||||
$scope.$apply();
|
||||
|
||||
for (var c in callbacks) {
|
||||
callbacks[c].apply(this, [{current: curr, previous: prev}]);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ angular.module('umbraco.mocks').
|
||||
"view": "textstring",
|
||||
"icon": "icon-quote",
|
||||
"config": {
|
||||
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-variant: italic; font-size: 18px",
|
||||
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-style: italic; font-size: 18px",
|
||||
"markup": "<blockquote>#value#</blockquote>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ angular.module('umbraco.mocks').
|
||||
"content_nodeName": "Page Title",
|
||||
"content_otherElements": "Properties",
|
||||
"content_parentNotPublished": "This document is published but is not visible because the parent '%0%' is unpublished",
|
||||
"content_parentNotPublishedAnomaly": "Oops: this document is published but is not in the cache (internal error)",
|
||||
"content_parentNotPublishedAnomaly": "This document is published but is not in the cache",
|
||||
"content_publish": "Publish",
|
||||
"content_publishStatus": "Publication Status",
|
||||
"content_releaseDate": "Publish at",
|
||||
|
||||
@@ -20,7 +20,6 @@ function dashboardResource($q, $http, umbRequestHelper) {
|
||||
*
|
||||
*/
|
||||
getDashboard: function (section) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
@@ -28,7 +27,53 @@ function dashboardResource($q, $http, umbRequestHelper) {
|
||||
"GetDashboard",
|
||||
[{ section: section }])),
|
||||
'Failed to get dashboard ' + section);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.dashboardResource#getRemoteDashboardContent
|
||||
* @methodOf umbraco.resources.dashboardResource
|
||||
*
|
||||
* @description
|
||||
* Retrieves dashboard content from a remote source for a given section
|
||||
*
|
||||
* @param {string} section Alias of section to retrieve dashboard content for
|
||||
* @returns {Promise} resourcePromise object containing the user array.
|
||||
*
|
||||
*/
|
||||
getRemoteDashboardContent: function (section, baseurl) {
|
||||
|
||||
//build request values with optional params
|
||||
var values = [{ section: section }];
|
||||
if (baseurl)
|
||||
{
|
||||
values.push({ baseurl: baseurl });
|
||||
}
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"dashboardApiBaseUrl",
|
||||
"GetRemoteDashboardContent",
|
||||
values)), "Failed to get dashboard content");
|
||||
},
|
||||
|
||||
getRemoteDashboardCssUrl: function (section, baseurl) {
|
||||
|
||||
//build request values with optional params
|
||||
var values = [{ section: section }];
|
||||
if (baseurl) {
|
||||
values.push({ baseurl: baseurl });
|
||||
}
|
||||
|
||||
return umbRequestHelper.getApiUrl(
|
||||
"dashboardApiBaseUrl",
|
||||
"GetRemoteDashboardCss",
|
||||
values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,12 @@ angular.module('umbraco.security.retryQueue', [])
|
||||
.factory('securityRetryQueue', ['$q', '$log', function ($q, $log) {
|
||||
|
||||
var retryQueue = [];
|
||||
var retryUser = null;
|
||||
|
||||
var service = {
|
||||
// The security service puts its own handler in here!
|
||||
onItemAddedCallbacks: [],
|
||||
|
||||
|
||||
hasMore: function() {
|
||||
return retryQueue.length > 0;
|
||||
},
|
||||
@@ -23,13 +25,20 @@ angular.module('umbraco.security.retryQueue', [])
|
||||
}
|
||||
});
|
||||
},
|
||||
pushRetryFn: function(reason, retryFn) {
|
||||
pushRetryFn: function(reason, userName, retryFn) {
|
||||
// The reason parameter is optional
|
||||
if ( arguments.length === 1) {
|
||||
retryFn = reason;
|
||||
if ( arguments.length === 2) {
|
||||
retryFn = userName;
|
||||
userName = reason;
|
||||
reason = undefined;
|
||||
}
|
||||
|
||||
if ((retryUser && retryUser !== userName) || userName === null) {
|
||||
throw new Error('invalid user');
|
||||
}
|
||||
|
||||
retryUser = userName;
|
||||
|
||||
// The deferred object that will be resolved or rejected by calling retry or cancel
|
||||
var deferred = $q.defer();
|
||||
var retryItem = {
|
||||
@@ -59,8 +68,19 @@ angular.module('umbraco.security.retryQueue', [])
|
||||
while(service.hasMore()) {
|
||||
retryQueue.shift().cancel();
|
||||
}
|
||||
retryUser = null;
|
||||
},
|
||||
retryAll: function() {
|
||||
retryAll: function (userName) {
|
||||
|
||||
if (retryUser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (retryUser !== userName) {
|
||||
service.cancelAll();
|
||||
return;
|
||||
}
|
||||
|
||||
while(service.hasMore()) {
|
||||
retryQueue.shift().retry();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ angular.module('umbraco.security.interceptor')
|
||||
return promise.then(
|
||||
function(originalResponse) {
|
||||
// Intercept successful requests
|
||||
|
||||
|
||||
//Here we'll check if our custom header is in the response which indicates how many seconds the user's session has before it
|
||||
//expires. Then we'll update the user in the user service accordingly.
|
||||
var headers = originalResponse.headers();
|
||||
@@ -15,11 +15,11 @@ angular.module('umbraco.security.interceptor')
|
||||
var userService = $injector.get('userService');
|
||||
userService.setUserTimeout(headers["x-umb-user-seconds"]);
|
||||
}
|
||||
|
||||
|
||||
return promise;
|
||||
}, function(originalResponse) {
|
||||
// Intercept failed requests
|
||||
|
||||
|
||||
//Here we'll check if we should ignore the error, this will be based on an original header set
|
||||
var headers = originalResponse.config ? originalResponse.config.headers : {};
|
||||
if (headers["x-umb-ignore-error"] === "ignore") {
|
||||
@@ -36,17 +36,24 @@ angular.module('umbraco.security.interceptor')
|
||||
//A 401 means that the user is not logged in
|
||||
if (originalResponse.status === 401) {
|
||||
|
||||
// The request bounced because it was not authorized - add a new request to the retry queue
|
||||
promise = queue.pushRetryFn('unauthorized-server', function retryRequest() {
|
||||
var userService = $injector.get('userService'); // see above
|
||||
|
||||
//Associate the user name with the retry to ensure we retry for the right user
|
||||
promise = userService.getCurrentUser()
|
||||
.then(function (user) {
|
||||
var userName = user ? user.name : null;
|
||||
//The request bounced because it was not authorized - add a new request to the retry queue
|
||||
return queue.pushRetryFn('unauthorized-server', userName, function retryRequest() {
|
||||
// We must use $injector to get the $http service to prevent circular dependency
|
||||
return $injector.get('$http')(originalResponse.config);
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (originalResponse.status === 404) {
|
||||
|
||||
//a 404 indicates that the request was not found - this could be due to a non existing url, or it could
|
||||
//be due to accessing a url with a parameter that doesn't exist, either way we should notifiy the user about it
|
||||
|
||||
|
||||
var errMsg = "The URL returned a 404 (not found): <br/><i>" + originalResponse.config.url.split('?')[0] + "</i>";
|
||||
if (originalResponse.data && originalResponse.data.ExceptionMessage) {
|
||||
errMsg += "<br/> with error: <br/><i>" + originalResponse.data.ExceptionMessage + "</i>";
|
||||
@@ -58,17 +65,17 @@ angular.module('umbraco.security.interceptor')
|
||||
notifications.error(
|
||||
"Request error",
|
||||
errMsg);
|
||||
|
||||
|
||||
}
|
||||
else if (originalResponse.status === 403) {
|
||||
//if the status was a 403 it means the user didn't have permission to do what the request was trying to do.
|
||||
//How do we deal with this now, need to tell the user somehow that they don't have permission to do the thing that was
|
||||
//How do we deal with this now, need to tell the user somehow that they don't have permission to do the thing that was
|
||||
//requested. We can either deal with this globally here, or we can deal with it globally for individual requests on the umbRequestHelper,
|
||||
// or completely custom for services calling resources.
|
||||
|
||||
//http://issues.umbraco.org/issue/U4-2749
|
||||
|
||||
//It was decided to just put these messages into the normal status messages.
|
||||
//It was decided to just put these messages into the normal status messages.
|
||||
|
||||
var msg = "Unauthorized access to URL: <br/><i>" + originalResponse.config.url.split('?')[0] + "</i>";
|
||||
if (originalResponse.config.data) {
|
||||
|
||||
@@ -354,7 +354,7 @@ angular.module('umbraco.services')
|
||||
* @description
|
||||
* Opens a content picker tree in a modal, the callback returns an array of selected documents
|
||||
* @param {Object} options content picker dialog options object
|
||||
* @param {Boolean} options.multipicker should the picker return one or multiple items
|
||||
* @param {Boolean} options.multiPicker should the picker return one or multiple items
|
||||
* @param {Function} options.callback callback function
|
||||
* @returns {Object} modal object
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,7 @@ angular.module('umbraco.services')
|
||||
loginDialog = null;
|
||||
|
||||
if (success) {
|
||||
securityRetryQueue.retryAll();
|
||||
securityRetryQueue.retryAll(currentUser.name);
|
||||
}
|
||||
else {
|
||||
securityRetryQueue.cancelAll();
|
||||
@@ -39,9 +39,9 @@ angular.module('umbraco.services')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This methods will set the current user when it is resolved and
|
||||
will then start the counter to count in-memory how many seconds they have
|
||||
/**
|
||||
This methods will set the current user when it is resolved and
|
||||
will then start the counter to count in-memory how many seconds they have
|
||||
remaining on the auth session
|
||||
*/
|
||||
function setCurrentUser(usr) {
|
||||
@@ -54,8 +54,8 @@ angular.module('umbraco.services')
|
||||
countdownUserTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
Method to count down the current user's timeout seconds,
|
||||
/**
|
||||
Method to count down the current user's timeout seconds,
|
||||
this will continually count down their current remaining seconds every 5 seconds until
|
||||
there are no more seconds remaining.
|
||||
*/
|
||||
@@ -70,8 +70,8 @@ angular.module('umbraco.services')
|
||||
//if there are more than 30 remaining seconds, recurse!
|
||||
if (currentUser.remainingAuthSeconds > 30) {
|
||||
|
||||
//we need to check when the last time the timeout was set from the server, if
|
||||
// it has been more than 30 seconds then we'll manually go and retrieve it from the
|
||||
//we need to check when the last time the timeout was set from the server, if
|
||||
// it has been more than 30 seconds then we'll manually go and retrieve it from the
|
||||
// server - this helps to keep our local countdown in check with the true timeout.
|
||||
if (lastServerTimeoutSet != null) {
|
||||
var now = new Date();
|
||||
@@ -79,7 +79,7 @@ angular.module('umbraco.services')
|
||||
|
||||
if (seconds > 30) {
|
||||
|
||||
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
|
||||
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
|
||||
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
|
||||
lastServerTimeoutSet = null;
|
||||
|
||||
@@ -98,7 +98,7 @@ angular.module('umbraco.services')
|
||||
}
|
||||
else {
|
||||
|
||||
//we are either timed out or very close to timing out so we need to show the login dialog.
|
||||
//we are either timed out or very close to timing out so we need to show the login dialog.
|
||||
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
|
||||
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
@@ -109,14 +109,14 @@ angular.module('umbraco.services')
|
||||
}
|
||||
finally {
|
||||
userAuthExpired();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
//we've got less than 30 seconds remaining so let's check the server
|
||||
|
||||
if (lastServerTimeoutSet != null) {
|
||||
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
|
||||
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
|
||||
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
|
||||
lastServerTimeoutSet = null;
|
||||
|
||||
@@ -211,8 +211,8 @@ angular.module('umbraco.services')
|
||||
return result;
|
||||
});
|
||||
},
|
||||
|
||||
/** Logs the user out
|
||||
|
||||
/** Logs the user out
|
||||
*/
|
||||
logout: function () {
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"view": "textstring",
|
||||
"icon": "icon-quote",
|
||||
"config": {
|
||||
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-variant: italic; font-size: 18px",
|
||||
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-style: italic; font-size: 18px",
|
||||
"markup": "<blockquote>#value#</blockquote>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,15 @@
|
||||
background-color: @blueDark;
|
||||
}
|
||||
|
||||
.umb-era-button.-red {
|
||||
background: @btnDangerBackground;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.umb-era-button.-red:hover {
|
||||
background-color: darken(@btnDangerBackground, 5%);
|
||||
}
|
||||
|
||||
.umb-era-button.-link {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
<div class="umb-media-grid__image-background" ng-if="item.thumbnail || item.extension == 'svg'"></div>
|
||||
|
||||
<!-- Image thumbnail -->
|
||||
<img class="umb-media-grid__item-image" width="{{item.width}}" height="{{item.height}}" ng-if="item.thumbnail" ng-src="{{item.thumbnail}}" alt="{{item.name}}" draggable="false" />
|
||||
<img class="umb-media-grid__item-image" width="{{item.width}}" height="{{item.height}}" ng-if="item.thumbnail" ng-src="{{item.thumbnail}}" alt="{{item.name}}" title="{{item.name}}" draggable="false" />
|
||||
|
||||
<!-- SVG -->
|
||||
<img class="umb-media-grid__item-image" width="{{item.width}}" height="{{item.height}}" ng-if="!item.thumbnail && item.extension == 'svg'" ng-src="{{item.file}}" alt="{{item.name}}" draggable="false" />
|
||||
<img class="umb-media-grid__item-image" width="{{item.width}}" height="{{item.height}}" ng-if="!item.thumbnail && item.extension == 'svg'" ng-src="{{item.file}}" alt="{{item.name}}" title="{{item.name}}" draggable="false" />
|
||||
|
||||
<!-- Transparent image - fallback - used to keep image proportions on wrapper-->
|
||||
<img class="umb-media-grid__item-image-placeholder" ng-if="!item.thumbnail && item.extension != 'svg'" src="assets/img/transparent.png" alt="{{item.name}}" draggable="false" />
|
||||
|
||||
@@ -14,9 +14,42 @@ function startUpVideosDashboardController($scope, xmlhelper, $log, $http) {
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Dashboard.StartupVideosController", startUpVideosDashboardController);
|
||||
|
||||
|
||||
function startUpDynamicContentController(dashboardResource, assetsService) {
|
||||
var vm = this;
|
||||
vm.loading = true;
|
||||
vm.showDefault = false;
|
||||
|
||||
//proxy remote css through the local server
|
||||
assetsService.loadCss( dashboardResource.getRemoteDashboardCssUrl("content") );
|
||||
dashboardResource.getRemoteDashboardContent("content").then(
|
||||
function (data) {
|
||||
|
||||
vm.loading = false;
|
||||
|
||||
//test if we have received valid data
|
||||
//we capture it like this, so we avoid UI errors - which automatically triggers ui based on http response code
|
||||
if (data && data.sections) {
|
||||
vm.dashboard = data;
|
||||
} else{
|
||||
vm.showDefault = true;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
function (exception) {
|
||||
console.error(exception);
|
||||
vm.loading = false;
|
||||
vm.showDefault = true;
|
||||
});
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Dashboard.StartUpDynamicContentController", startUpDynamicContentController);
|
||||
|
||||
|
||||
function FormsController($scope, $route, $cookieStore, packageResource, localizationService) {
|
||||
$scope.installForms = function(){
|
||||
$scope.state = localizationService.localize("packager_installStateDownloading");
|
||||
@@ -192,27 +225,43 @@ function startupLatestEditsController($scope) {
|
||||
}
|
||||
angular.module("umbraco").controller("Umbraco.Dashboard.StartupLatestEditsController", startupLatestEditsController);
|
||||
|
||||
function MediaFolderBrowserDashboardController($rootScope, $scope, contentTypeResource) {
|
||||
function MediaFolderBrowserDashboardController($rootScope, $scope, $location, contentTypeResource, userService) {
|
||||
|
||||
//get the system media listview
|
||||
contentTypeResource.getPropertyTypeScaffold(-96)
|
||||
.then(function(dt) {
|
||||
var currentUser = {};
|
||||
|
||||
$scope.fakeProperty = {
|
||||
alias: "contents",
|
||||
config: dt.config,
|
||||
description: "",
|
||||
editor: dt.editor,
|
||||
hideLabel: true,
|
||||
id: 1,
|
||||
label: "Contents:",
|
||||
validation: {
|
||||
mandatory: false,
|
||||
pattern: null
|
||||
},
|
||||
value: "",
|
||||
view: dt.view
|
||||
};
|
||||
userService.getCurrentUser().then(function (user) {
|
||||
|
||||
currentUser = user;
|
||||
|
||||
// check if the user start node is the dashboard
|
||||
if(currentUser.startMediaId === -1) {
|
||||
|
||||
//get the system media listview
|
||||
contentTypeResource.getPropertyTypeScaffold(-96)
|
||||
.then(function(dt) {
|
||||
|
||||
$scope.fakeProperty = {
|
||||
alias: "contents",
|
||||
config: dt.config,
|
||||
description: "",
|
||||
editor: dt.editor,
|
||||
hideLabel: true,
|
||||
id: 1,
|
||||
label: "Contents:",
|
||||
validation: {
|
||||
mandatory: false,
|
||||
pattern: null
|
||||
},
|
||||
value: "",
|
||||
view: dt.view
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
} else {
|
||||
// redirect to start node
|
||||
$location.path("/media/media/edit/" + currentUser.startMediaId);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -1,44 +1,95 @@
|
||||
<div class="umb-dashboard-grid" ng-controller="Umbraco.Dashboard.StartUpDynamicContentController as vm">
|
||||
|
||||
<div style="max-width: 1200px">
|
||||
<h3><strong>Welcome to The Friendly CMS</strong></h3>
|
||||
<p style="font-size: 16px; line-height: 1.5; margin-bottom: 30px; max-width: 760px;">Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible:</p>
|
||||
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
|
||||
|
||||
<div class="row">
|
||||
<div class="span4">
|
||||
<a href="http://umbraco.tv/?utm_source=core&utm_medium=dashboard&utm_content=image&utm_campaign=tv" target="_blank" class="nounderline">
|
||||
<img src="views/dashboard/default/umbracotv.png" alt="Umbraco.TV - Hours of Umbraco Video Tutorials" style="margin: 10px 0 10px 0"/>
|
||||
</a>
|
||||
<div class="row clearfix" ng-if="vm.dashboard">
|
||||
<!-- Sections -->
|
||||
<div class="grid-section" ng-repeat="section in vm.dashboard.sections">
|
||||
<div class="{{'span' + section.grid + ' column dash-section'}}">
|
||||
|
||||
<!-- rows -->
|
||||
<div class="row clearfix dash-row" ng-repeat="row in section.rows">
|
||||
<div class="dash-inner-row">
|
||||
|
||||
<a href="http://umbraco.tv/?utm_source=core&utm_medium=dashboard&utm_content=header&utm_campaign=tv" target="_blank" class="btn-link -underline">
|
||||
<h4> <strong>Umbraco.TV - Learn from the source!</strong> </h4>
|
||||
</a>
|
||||
<!-- areas -->
|
||||
<div ng-repeat="area in row.areas"
|
||||
class="{{'span' + area.grid + ' column dash-area'}}">
|
||||
<div class="dash-inner-area">
|
||||
|
||||
<p style="line-height: 1.5;">
|
||||
Umbraco.TV will help you go from zero to Umbraco
|
||||
hero at a pace that suits you. Our easy to follow
|
||||
online training videos will give you the fundamental
|
||||
knowledge to start building awesome Umbraco websites.
|
||||
</p>
|
||||
<!-- Controls -->
|
||||
<div ng-repeat="control in area.controls" class="dash-control dash-control-{{control.alias}}">
|
||||
<div class="dash-inner-control">
|
||||
<div ng-bind-html-unsafe="control.html"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Controls end -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- Area end -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Section end -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- grid container end -->
|
||||
|
||||
<div class="span4 offset1">
|
||||
|
||||
<a href="http://our.umbraco.org/?utm_source=core&utm_medium=dashboard&utm_content=image&utm_campaign=our" target="_blank" class="nounderline">
|
||||
<img src="views/dashboard/default/ourumbraco.png" alt="Our Umbraco" style="margin: 10px 0 10px 0"/>
|
||||
</a>
|
||||
<!-- Default content in case we cannot fetch content from the outside -->
|
||||
<div style="max-width: 1200px" ng-if="vm.showDefault">
|
||||
<h3><strong>Welcome to The Friendly CMS</strong></h3>
|
||||
<p style="font-size: 16px; line-height: 1.5; margin-bottom: 30px; max-width: 760px;">
|
||||
<span>Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible.</span>
|
||||
</p>
|
||||
<h4>Find out more:</h4>
|
||||
<ul>
|
||||
<li>Find the answers to your Umbraco questions in <a href="https://our.umbraco.org/documentation/" target="_blank" class="btn-link-underline">our Documentation</a></li>
|
||||
<li>Ask a question in the <a href="https://our.umbraco.org" target="_blank" class="btn-link-underline">Community Forum</a></li>
|
||||
<li>Find <a href="https://our.umbraco.org/projects" target="_blank" class="btn-link-underline">an add-on package</a> to help you get going quickly</li>
|
||||
<li>Watch <a href="https://umbraco.tv" class="btn-link-underline">our tutorial videos</a> (some are free, some require a subscription)</li>
|
||||
<li>Find out about <a href="https://umbraco.com/products-and-support" target="_blank" class="btn-link-underline">our productivity boosting tools and commercial support</a></li>
|
||||
<li>Find out about <a href="https://umbraco.com/products/training" target="_blank" class="btn-link-underline">real-life training and certification opportunities</a></li>
|
||||
</ul>
|
||||
|
||||
<a href="http://our.umbraco.org/?utm_source=core&utm_medium=dashboard&utm_content=header&utm_campaign=our" target="_blank" class="btn-link -underline">
|
||||
<h4><strong>Our Umbraco - The Friendliest Community</strong></h4>
|
||||
</a>
|
||||
<div class="row">
|
||||
<div class="span4">
|
||||
<a href="http://umbraco.tv/?utm_source=core&utm_medium=dashboard&utm_content=image&utm_campaign=tv" target="_blank" class="nounderline">
|
||||
<img src="views/dashboard/default/umbracotv.png" alt="Umbraco.TV - Hours of Umbraco Video Tutorials" style="margin: 10px 0 10px 0" />
|
||||
</a>
|
||||
|
||||
<p style="line-height: 1.5;">
|
||||
Our Umbraco - the official community site is your one
|
||||
stop for everything Umbraco. Whether you need a
|
||||
question answered or looking for cool plugins, the
|
||||
worlds best community is just a click away.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<a href="http://umbraco.tv/?utm_source=core&utm_medium=dashboard&utm_content=header&utm_campaign=tv" target="_blank" class="btn-link -underline">
|
||||
<h4> <strong>Umbraco.TV - Learn from the source!</strong> </h4>
|
||||
</a>
|
||||
|
||||
<p style="line-height: 1.5;">
|
||||
Umbraco.TV will help you go from zero to Umbraco
|
||||
hero at a pace that suits you. Our easy to follow
|
||||
online training videos will give you the fundamental
|
||||
knowledge to start building awesome Umbraco websites.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="span4 offset1">
|
||||
|
||||
<a href="http://our.umbraco.org/?utm_source=core&utm_medium=dashboard&utm_content=image&utm_campaign=our" target="_blank" class="nounderline">
|
||||
<img src="views/dashboard/default/ourumbraco.png" alt="Our Umbraco" style="margin: 10px 0 10px 0" />
|
||||
</a>
|
||||
|
||||
<a href="http://our.umbraco.org/?utm_source=core&utm_medium=dashboard&utm_content=header&utm_campaign=our" target="_blank" class="btn-link -underline">
|
||||
<h4><strong>Our Umbraco - The Friendliest Community</strong></h4>
|
||||
</a>
|
||||
|
||||
<p style="line-height: 1.5;">
|
||||
Our Umbraco - the official community site is your one
|
||||
stop for everything Umbraco. Whether you need a
|
||||
question answered or looking for cool plugins, the
|
||||
worlds best community is just a click away.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -77,7 +77,7 @@
|
||||
<a class="umb-table-body__link" href="{{redirectUrl.destinationUrl}}" target="_blank" title="{{redirectUrl.destinationUrl}}">{{redirectUrl.destinationUrl}}</a>
|
||||
</div>
|
||||
|
||||
<button type="button" class="umb-era-button -gray umb-button--s" ng-click="vm.removeRedirect(redirectUrl)"><localize key="redirectUrls_removeButton">Remove</localize></button>
|
||||
<button type="button" class="umb-era-button umb-button--s -red" ng-click="vm.removeRedirect(redirectUrl)"><localize key="redirectUrls_removeButton">Remove</localize></button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
|
||||
<div class="umb-package-details__information-item" ng-if="vm.package.created">
|
||||
<div class="umb-package-details__information-item-label">Created:</div>
|
||||
<div class="umb-package-details__information-item-content">{{vm.package.created}}</div>
|
||||
<div class="umb-package-details__information-item-content">{{vm.package.created | date:'yyyy-MM-dd HH:mm:ss'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-package-details__information-item" ng-if="vm.package.latestVersion">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<ul class="unstyled">
|
||||
<li ng-repeat="preval in model.prevalues">
|
||||
<label class="checkbox">
|
||||
<input type="radio" ng-model="model.value" value="{{preval}}" /> {{preval}}
|
||||
</label>
|
||||
<label class="checkbox">
|
||||
<input type="radio" ng-model="model.value" value="{{preval.value || preval}}" /> {{preval.label || preval.value || preval}}
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
+7
-4
@@ -16,7 +16,7 @@ angular.module("umbraco")
|
||||
};
|
||||
|
||||
$scope.scaleDown = function(section){
|
||||
var remove = (section.grid > 1) ? 1 : section.grid;
|
||||
var remove = (section.grid > 1) ? 1 : 0;
|
||||
section.grid = section.grid-remove;
|
||||
};
|
||||
|
||||
@@ -49,9 +49,12 @@ angular.module("umbraco")
|
||||
$scope.currentSection = section;
|
||||
};
|
||||
|
||||
|
||||
$scope.deleteSection = function(index){
|
||||
$scope.currentTemplate.sections.splice(index, 1);
|
||||
$scope.deleteSection = function(section, template) {
|
||||
if ($scope.currentSection === section) {
|
||||
$scope.currentSection = undefined;
|
||||
}
|
||||
var index = template.sections.indexOf(section)
|
||||
template.sections.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.closeSection = function(){
|
||||
|
||||
@@ -35,17 +35,24 @@
|
||||
<div ng-if="currentSection" style="padding-bottom: 50px;">
|
||||
|
||||
<umb-control-group label="@general_width">
|
||||
<div class="grid-size-scaler" >
|
||||
<div class="grid-size-scaler">
|
||||
<a href ng-click="scaleDown(currentSection)">
|
||||
<i class="icon icon-navigation-down"></i>
|
||||
<i class="icon icon-remove"></i>
|
||||
</a>
|
||||
{{currentSection.grid}}
|
||||
<a href ng-click="scaleUp(currentSection, availableLayoutSpace)">
|
||||
<i class="icon icon-navigation-up"></i>
|
||||
<i class="icon icon-add"></i>
|
||||
</a>
|
||||
</div>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group hide-label="true">
|
||||
<i class="icon-delete red"></i>
|
||||
<a class="btn btn-small btn-link" href="" ng-click="deleteSection(currentSection, currentLayout)">
|
||||
<localize key="general_delete" class="ng-isolate-scope ng-scope">Delete</localize>
|
||||
</a>
|
||||
</umb-control-group>
|
||||
|
||||
|
||||
<umb-control-group hide-label="true">
|
||||
<ul class="unstyled">
|
||||
|
||||
+8
-3
@@ -14,7 +14,7 @@ function RowConfigController($scope) {
|
||||
};
|
||||
|
||||
$scope.scaleDown = function(section) {
|
||||
var remove = (section.grid > 1) ? 1 : section.grid;
|
||||
var remove = (section.grid > 1) ? 1 : 0;
|
||||
section.grid = section.grid - remove;
|
||||
};
|
||||
|
||||
@@ -58,9 +58,14 @@ function RowConfigController($scope) {
|
||||
}
|
||||
};
|
||||
|
||||
$scope.deleteArea = function(index) {
|
||||
$scope.currentRow.areas.splice(index, 1);
|
||||
$scope.deleteArea = function (cell, row) {
|
||||
if ($scope.currentCell === cell) {
|
||||
$scope.currentCell = undefined;
|
||||
}
|
||||
var index = row.areas.indexOf(cell)
|
||||
row.areas.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.closeArea = function() {
|
||||
$scope.currentCell = undefined;
|
||||
};
|
||||
|
||||
@@ -6,9 +6,14 @@
|
||||
<p><localize key="grid_addRowConfigurationDetail" /></p>
|
||||
|
||||
<div class="alert alert-warn ng-scope" ng-show="nameChanged">
|
||||
Modifying a row configuration name will result in loss of
|
||||
data for any existing content that is based on this configuration.
|
||||
<p>Modifying a row configuration name will result in loss of
|
||||
data for any existing content that is based on this configuration.</p>
|
||||
<p><strong>Modifying only the label will not result in data loss.</strong></p>
|
||||
</div>
|
||||
|
||||
<umb-control-group label="@general_label">
|
||||
<input type="text" ng-model="currentRow.label" placeholder="Overrides name" />
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="@general_name">
|
||||
<input type="text" ng-model="currentRow.name" />
|
||||
@@ -39,19 +44,23 @@
|
||||
<div ng-if="currentCell" style="padding-bottom: 50px;">
|
||||
|
||||
<umb-control-group label="@general_width">
|
||||
<div class="grid-size-scaler" >
|
||||
<div class="grid-size-scaler">
|
||||
<a href ng-click="scaleDown(currentCell)">
|
||||
<i class="icon icon-remove"></i>
|
||||
</a>
|
||||
<div class="grid-size-scaler">
|
||||
<a href ng-click="scaleDown(currentCell)">
|
||||
<i class="icon icon-remove"></i>
|
||||
</a>
|
||||
{{currentCell.grid}}
|
||||
<a href ng-click="scaleUp(currentCell, availableRowSpace, true)">
|
||||
<i class="icon icon-add"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<a href ng-click="scaleUp(currentCell, availableRowSpace, true)">
|
||||
<i class="icon icon-add"></i>
|
||||
</a>
|
||||
</div>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group hide-label="true">
|
||||
<i class="icon-delete red"></i>
|
||||
<a class="btn btn-small btn-link" href="" ng-click="deleteArea(currentCell, currentRow)">
|
||||
<localize key="general_delete" class="ng-isolate-scope ng-scope">Delete</localize>
|
||||
</a>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group hide-label="true">
|
||||
<ul class="unstyled">
|
||||
|
||||
@@ -343,6 +343,37 @@ angular.module("umbraco")
|
||||
}
|
||||
};
|
||||
|
||||
var shouldApply = function(item, itemType, gridItem) {
|
||||
if (item.applyTo === undefined || item.applyTo === null || item.applyTo === "") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof (item.applyTo) === "string") {
|
||||
return item.applyTo === itemType;
|
||||
}
|
||||
|
||||
if (itemType === "row") {
|
||||
if (item.applyTo.row === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (item.applyTo.row === null || item.applyTo.row === "") {
|
||||
return true;
|
||||
}
|
||||
var rows = item.applyTo.row.split(',');
|
||||
return _.indexOf(rows, gridItem.name) !== -1;
|
||||
} else if (itemType === "cell") {
|
||||
if (item.applyTo.cell === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (item.applyTo.cell === null || item.applyTo.cell === "") {
|
||||
return true;
|
||||
}
|
||||
var cells = item.applyTo.cell.split(',');
|
||||
var cellSize = gridItem.grid.toString();
|
||||
return _.indexOf(cells, cellSize) !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.editGridItemSettings = function (gridItem, itemType) {
|
||||
|
||||
placeHolder = "{0}";
|
||||
@@ -352,8 +383,8 @@ angular.module("umbraco")
|
||||
styles = null;
|
||||
config = angular.copy(gridItem.editor.config.settings);
|
||||
} else {
|
||||
styles = _.filter(angular.copy($scope.model.config.items.styles), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); });
|
||||
config = _.filter(angular.copy($scope.model.config.items.config), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); });
|
||||
styles = _.filter(angular.copy($scope.model.config.items.styles), function (item) { return shouldApply(item, itemType, gridItem); });
|
||||
config = _.filter(angular.copy($scope.model.config.items.config), function (item) { return shouldApply(item, itemType, gridItem); });
|
||||
}
|
||||
|
||||
if(angular.isObject(gridItem.config)){
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
<div class="umb-row-title-bar">
|
||||
|
||||
<div class=".umb-grid-right">
|
||||
<div class="umb-row-title">{{row.name}}</div>
|
||||
<div class="umb-row-title">{{row.label || row.name}}</div>
|
||||
|
||||
<div class="umb-grid-has-config" ng-if="row.hasConfig && !sortMode">
|
||||
<localize key="grid_settingsApplied" />
|
||||
@@ -282,7 +282,7 @@
|
||||
<div class="preview-overlay">
|
||||
</div>
|
||||
|
||||
<small>{{layout.name}}</small>
|
||||
<small>{{layout.label || layout.name}}</small>
|
||||
|
||||
</div> <!-- .templates-preview-rows -->
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ angular.module("umbraco")
|
||||
|
||||
layouts:[
|
||||
{
|
||||
label: "Headline",
|
||||
name: "Headline",
|
||||
areas: [
|
||||
{
|
||||
@@ -57,6 +58,7 @@ angular.module("umbraco")
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "Article",
|
||||
name: "Article",
|
||||
areas: [
|
||||
{
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{layout.name}}<br />
|
||||
{{layout.label || layout.name}}<br />
|
||||
<i class="icon-delete red"></i>
|
||||
<a class="btn btn-small btn-link" href ng-click="deleteLayout($index)">
|
||||
<localize key="general_delete" />
|
||||
|
||||
+1
-1
@@ -118,7 +118,7 @@ angular.module('umbraco')
|
||||
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
|
||||
// they contain different data structures so if we need to query against it we need to be aware of this.
|
||||
mediaHelper.registerFileResolver("Umbraco.ImageCropper", function (property, entity, thumbnail) {
|
||||
if (property.value.src) {
|
||||
if (property.value && property.value.src) {
|
||||
|
||||
if (thumbnail === true) {
|
||||
return property.value.src + "?width=500&mode=max&animationprocessmode=first";
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
center="model.value.focalPoint"
|
||||
on-image-loaded="imageLoaded">
|
||||
</umb-image-gravity>
|
||||
<a href class="btn btn-link btn-crop-delete" ng-show="imageIsLoaded" ng-click="clear()"><i class="icon-delete red"></i> <localize key="content_uploadClear">Remove file</localize></a>
|
||||
<a href class="btn btn-link btn-crop-delete" ng-click="clear()"><i class="icon-delete red"></i> <localize key="content_uploadClear">Remove file</localize></a>
|
||||
</div>
|
||||
|
||||
<ul class="umb-sortable-thumbnails cropList clearfix">
|
||||
|
||||
+2
-1
@@ -36,7 +36,8 @@
|
||||
}
|
||||
|
||||
function clickItem(item) {
|
||||
$location.path($scope.entityType + '/' +$scope.entityType + '/edit/' +item.id);
|
||||
// if item.id is 2147483647 (int.MaxValue) use item.key
|
||||
$location.path($scope.entityType + '/' +$scope.entityType + '/edit/' + (item.id === 2147483647 ? item.key : item.id));
|
||||
}
|
||||
|
||||
function isSortDirection(col, direction) {
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
<li style="width: 120px; height: 100px; overflow: hidden;" ng-repeat="image in images">
|
||||
|
||||
<!-- IMAGE -->
|
||||
<img ng-src="{{image.thumbnail}}" alt="" ng-show="image.thumbnail" />
|
||||
<img ng-src="{{image.thumbnail}}" alt="" ng-show="image.thumbnail" title="{{image.name}}" />
|
||||
|
||||
<!-- SVG -->
|
||||
<img ng-if="image.metaData.umbracoExtension.Value === 'svg'" ng-src="{{image.metaData.umbracoFile.Value}}" alt="" />
|
||||
<img ng-if="image.metaData.umbracoExtension.Value === 'svg'" ng-src="{{image.metaData.umbracoFile.Value}}" alt="" title="{{image.name}}" />
|
||||
<img ng-if="image.extension === 'svg'" ng-src="{{image.file}}" alt="" />
|
||||
|
||||
<!-- FILE -->
|
||||
|
||||
@@ -107,12 +107,12 @@
|
||||
<Project>{07fbc26b-2927-4a22-8d96-d644c667fecc}</Project>
|
||||
<Name>UmbracoExamine</Name>
|
||||
</ProjectReference>
|
||||
<Reference Include="AutoMapper, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll</HintPath>
|
||||
<Reference Include="AutoMapper, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="AutoMapper.Net4, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
<Reference Include="AutoMapper.Net4, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ClientDependency.Core, Version=1.9.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
@@ -127,20 +127,20 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\dotless.1.4.1.0\lib\dotless.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Examine, Version=0.1.69.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.69.0\lib\Examine.dll</HintPath>
|
||||
<Reference Include="Examine, Version=0.1.70.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.70.0\lib\Examine.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ImageProcessor, Version=2.4.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.2.4.4.0\lib\net45\ImageProcessor.dll</HintPath>
|
||||
<Reference Include="ImageProcessor, Version=2.4.5.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.2.4.5.0\lib\net45\ImageProcessor.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImageProcessor.Web, Version=4.6.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.Web.4.6.4.0\lib\net45\ImageProcessor.Web.dll</HintPath>
|
||||
<Reference Include="ImageProcessor.Web, Version=4.6.6.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ImageProcessor.Web.4.6.6.0\lib\net45\ImageProcessor.Web.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="log4net, Version=1.2.11.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
@@ -2412,9 +2412,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
|
||||
<WebProjectProperties>
|
||||
<UseIIS>True</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>7530</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>7540</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:7530</IISUrl>
|
||||
<IISUrl>http://localhost:7540</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
@@ -2453,4 +2453,5 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
|
||||
</ReadLinesFromFile>
|
||||
<Exec WorkingDirectory="$(ProjectDir)\..\..\build\" Command="BuildBelle.bat $(VersionNumber)" ConsoleToMSBuild="true" IgnoreExitCode="true" ContinueOnError="WarnAndContinue" />
|
||||
</Target>
|
||||
<Import Project="..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets" Condition="Exists('..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets')" />
|
||||
</Project>
|
||||
@@ -2,32 +2,9 @@
|
||||
<urlrewritingnet xmlns="http://www.urlrewriting.net/schemas/config/2006/07">
|
||||
<rewrites>
|
||||
<!--
|
||||
Urlrewriting.Net is a cool tool, what can make your urls look nice.
|
||||
The rewriting is controlled with regular expressions. To get more help
|
||||
look at http://www.urlrewriting.net/.
|
||||
|
||||
Remember to read the manual!
|
||||
http://our.umbraco.org/wiki/recommendations/recommended-reading-for-web-developers
|
||||
|
||||
The sample below rewrites a url from
|
||||
"/product/someproductid.aspx" to
|
||||
"/product.aspx?productid=someproductid"
|
||||
|
||||
The user will not see the rewritten path! The page that will be
|
||||
loaded from umbraco will instead be:
|
||||
"/product.aspx?productid=someproductid"
|
||||
|
||||
<add name="produktidrewrite"
|
||||
virtualUrl="^~/product/(.*).aspx"
|
||||
rewriteUrlParameter="ExcludeFromClientQueryString"
|
||||
destinationUrl="~/product.aspx?productid=$1"
|
||||
ignoreCase="true" />
|
||||
|
||||
This sample is usefull for a productpage, where the product comes from a
|
||||
dynamic datasource, e.g. a database. The querystring "productid" can be loaded
|
||||
from the template, into a macro, that then loads the product!
|
||||
|
||||
Any bugs or problems with the rewriter, contact Anders/Duckie
|
||||
-->
|
||||
URLRewriting.net is obsolete and will be removed from Umbraco in the future.
|
||||
If you want to do rewrites, make sure to use IIS URL rewrite: https://www.iis.net/downloads/microsoft/url-rewrite
|
||||
The advantage of using IIS rewrite is that it is much faster, much less CPU intensive and much less memory intensive.
|
||||
-->
|
||||
</rewrites>
|
||||
</urlrewritingnet>
|
||||
|
||||
@@ -2,32 +2,9 @@
|
||||
<urlrewritingnet xmlns="http://www.urlrewriting.net/schemas/config/2006/07">
|
||||
<rewrites>
|
||||
<!--
|
||||
Urlrewriting.Net is a cool tool, what can make your urls look nice.
|
||||
The rewriting is controlled with regular expressions. To get more help
|
||||
look at http://www.urlrewriting.net/.
|
||||
|
||||
Remember to read the manual!
|
||||
http://our.umbraco.org/wiki/recommendations/recommended-reading-for-web-developers
|
||||
|
||||
The sample below rewrites a url from
|
||||
"/product/someproductid.aspx" to
|
||||
"/product.aspx?productid=someproductid"
|
||||
|
||||
The user will not see the rewritten path! The page that will be
|
||||
loaded from umbraco will instead be:
|
||||
"/product.aspx?productid=someproductid"
|
||||
|
||||
<add name="produktidrewrite"
|
||||
virtualUrl="^~/product/(.*).aspx"
|
||||
rewriteUrlParameter="ExcludeFromClientQueryString"
|
||||
destinationUrl="~/product.aspx?productid=$1"
|
||||
ignoreCase="true" />
|
||||
|
||||
This sample is usefull for a productpage, where the product comes from a
|
||||
dynamic datasource, e.g. a database. The querystring "productid" can be loaded
|
||||
from the template, into a macro, that then loads the product!
|
||||
|
||||
Any bugs or problems with the rewriter, contact Anders/Duckie
|
||||
-->
|
||||
URLRewriting.net is obsolete and will be removed from Umbraco in the future.
|
||||
If you want to do rewrites, make sure to use IIS URL rewrite: https://www.iis.net/downloads/microsoft/url-rewrite
|
||||
The advantage of using IIS rewrite is that it is much faster, much less CPU intensive and much less memory intensive.
|
||||
-->
|
||||
</rewrites>
|
||||
</urlrewritingnet>
|
||||
</urlrewritingnet>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"view": "textstring",
|
||||
"icon": "icon-quote",
|
||||
"config": {
|
||||
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-variant: italic; font-size: 18px",
|
||||
"style": "border-left: 3px solid #ccc; padding: 10px; color: #ccc; font-family: serif; font-style: italic; font-size: 18px",
|
||||
"markup": "<blockquote>#value#</blockquote>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<div class="logo"></div>
|
||||
|
||||
<h1>Welcome to your Umbraco installation</h1>
|
||||
<h3>You're seeing the wonderful page because your website doesn't contain any published content yet.</h3>
|
||||
<h3>You're seeing this wonderful page because your website doesn't contain any published content yet.</h3>
|
||||
|
||||
<div class="cta">
|
||||
<a href="<%= IOHelper.ResolveUrl(SystemDirectories.Umbraco) %>" class="button">Open Umbraco</a>
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<div class="col">
|
||||
<h2>Be a part of the community</h2>
|
||||
<p>The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we’re sure that you can get your answers from the community.</p>
|
||||
<p>The Umbraco community is the best of its kind, be sure to visit, and if you have any questions, we’re sure that you can get your answers from the community.</p>
|
||||
|
||||
<a href="http://our.umbraco.org?ref=ourFromInstaller" target="_blank">our.Umbraco →</a>
|
||||
</div>
|
||||
@@ -58,4 +58,4 @@
|
||||
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,319 +1,319 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<settings>
|
||||
<content>
|
||||
<imaging>
|
||||
<!-- what file extension that should cause umbraco to create thumbnails -->
|
||||
<imageFileTypes>jpeg,jpg,gif,bmp,png,tiff,tif</imageFileTypes>
|
||||
<!-- what attributes that are allowed in the editor on an img tag -->
|
||||
<allowedAttributes>src,alt,border,class,style,align,id,name,onclick,usemap</allowedAttributes>
|
||||
<!-- automatically updates dimension, filesize and extension attributes on upload -->
|
||||
<autoFillImageProperties>
|
||||
<uploadField alias="umbracoFile">
|
||||
<widthFieldAlias>umbracoWidth</widthFieldAlias>
|
||||
<heightFieldAlias>umbracoHeight</heightFieldAlias>
|
||||
<lengthFieldAlias>umbracoBytes</lengthFieldAlias>
|
||||
<extensionFieldAlias>umbracoExtension</extensionFieldAlias>
|
||||
</uploadField>
|
||||
</autoFillImageProperties>
|
||||
|
||||
</imaging>
|
||||
<scripteditor>
|
||||
<!-- Path to script folder - no ending "/" -->
|
||||
<scriptFolderPath>/scripts</scriptFolderPath>
|
||||
<!-- what files can be opened/created in the script editor -->
|
||||
<scriptFileTypes>js,xml</scriptFileTypes>
|
||||
<!-- disable the codemirror editor and use a simple textarea instead -->
|
||||
<scriptDisableEditor>false</scriptDisableEditor>
|
||||
</scripteditor>
|
||||
|
||||
<!-- should umbraco store the uploaded files like /media/xxx/filename.ext or like /media/xxx-filename.ext
|
||||
should be set to false if the application pool's user account hasn't got readrights of the driveroot up to the /media directory -->
|
||||
<UploadAllowDirectories>True</UploadAllowDirectories>
|
||||
<errors>
|
||||
<!-- the id of the page that should be shown if the page is not found -->
|
||||
<!--<error404>
|
||||
<errorPage culture="default">1</errorPage>
|
||||
<errorPage culture="en-US">200</errorPage>
|
||||
</error404>-->
|
||||
<error404>
|
||||
<errorPage culture="default">1</errorPage>
|
||||
<errorPage culture="fr-FR">1079</errorPage>
|
||||
<errorPage culture="en-US">1080</errorPage>
|
||||
</error404>
|
||||
</errors>
|
||||
<notifications>
|
||||
<!-- the email that should be used as from mail when umbraco sends a notification -->
|
||||
<email>your@email.here</email>
|
||||
</notifications>
|
||||
|
||||
<!-- if true umbraco will ensure that no page under the same parent has an identical name -->
|
||||
<ensureUniqueNaming>True</ensureUniqueNaming>
|
||||
|
||||
<!-- clean editor content with use of tidy -->
|
||||
<TidyEditorContent>False</TidyEditorContent>
|
||||
|
||||
<!-- the encoding type for tidy. Default is UTF8, options are ASCII, Raw, Latin1, UTF8, ISO2022, MacroMan-->
|
||||
<TidyCharEncoding>UTF8</TidyCharEncoding>
|
||||
|
||||
<!-- to enable new content schema, this needs to be false -->
|
||||
<UseLegacyXmlSchema>false</UseLegacyXmlSchema>
|
||||
|
||||
<!-- Whether to force safe aliases (no spaces, no special characters) at businesslogic level on contenttypes and propertytypes -->
|
||||
<!-- HIGHLY recommend to keep this to true to ensure valid and beautiful XML Schemas -->
|
||||
<ForceSafeAliases>true</ForceSafeAliases>
|
||||
|
||||
<!-- Enable / disable xml content cache -->
|
||||
<XmlCacheEnabled>True</XmlCacheEnabled>
|
||||
|
||||
<!-- Update disk cache every time content has changed -->
|
||||
<ContinouslyUpdateXmlDiskCache>True</ContinouslyUpdateXmlDiskCache>
|
||||
|
||||
<!-- Update in-memory cache if xml file is changed -->
|
||||
<XmlContentCheckForDiskChanges>False</XmlContentCheckForDiskChanges>
|
||||
|
||||
<!-- Show the /config/splashes/booting.aspx page while initializing content -->
|
||||
<EnableSplashWhileLoading>False</EnableSplashWhileLoading>
|
||||
|
||||
<!-- Show property descriptions in editing view "icon|text|none" -->
|
||||
<PropertyContextHelpOption>text</PropertyContextHelpOption>
|
||||
|
||||
<!-- The html injected into a (x)html page if Umbraco is running in preview mode -->
|
||||
<PreviewBadge>
|
||||
<![CDATA[<a id="umbracoPreviewBadge" style="position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;" href="#" OnClick="javascript:window.top.location.href = '{0}/endPreview.aspx?redir={2}'"><span style="display:none;">In Preview Mode - click to end</span></a>
|
||||
]]></PreviewBadge>
|
||||
|
||||
<!-- Cache cycle of Media and Member data fetched from the umbraco.library methods -->
|
||||
<!-- In seconds. 0 will disable cache -->
|
||||
<UmbracoLibraryCacheDuration>1800</UmbracoLibraryCacheDuration>
|
||||
|
||||
<!-- Url Resolving ensures that all links works if you run Umbraco in virtual directories -->
|
||||
<!-- Setting this to true can increase render time for pages with a large number of links -->
|
||||
<!-- If running Umbraco in virtual directory this *must* be set to true! -->
|
||||
<ResolveUrlsFromTextString>false</ResolveUrlsFromTextString>
|
||||
|
||||
<!-- How Umbraco should handle errors during macro execution. Can be one of the following values:
|
||||
- inline - show an inline error within the macro but allow the page to continue rendering. Historial Umbraco behaviour.
|
||||
- silent - Silently suppress the error and do not render the offending macro.
|
||||
- throw - Throw an exception which can be caught by the global error handler defined in Application_OnError. If no such
|
||||
error handler is defined then you'll see the Yellow Screen Of Death (YSOD) error page.
|
||||
Note the error can also be handled by the umbraco.macro.Error event, where you can log/alarm with your own code and change the behaviour per event. -->
|
||||
<MacroErrors>throw</MacroErrors>
|
||||
|
||||
<!-- These file types will not be allowed to be uploaded via the upload control for media and content -->
|
||||
<disallowedUploadFiles>ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,html,htm,svg,php,htaccess</disallowedUploadFiles>
|
||||
|
||||
<!-- Defines the default document type property used when adding properties in the back-office (if missing or empty, defaults to Textstring -->
|
||||
<defaultDocumentTypeProperty>Textstring</defaultDocumentTypeProperty>
|
||||
|
||||
</content>
|
||||
|
||||
<security>
|
||||
<!-- set to true to auto update login interval (and there by disabling the lock screen -->
|
||||
<keepUserLoggedIn>true</keepUserLoggedIn>
|
||||
|
||||
<!-- change in 4.8: Disabled users are now showed dimmed and last in the tree. If you prefer not to display them set this to true -->
|
||||
<hideDisabledUsersInBackoffice>false</hideDisabledUsersInBackoffice>
|
||||
|
||||
<!-- set to true to enable the UI and API to allow back-office users to reset their passwords -->
|
||||
<allowPasswordReset>true</allowPasswordReset>
|
||||
|
||||
</security>
|
||||
|
||||
<requestHandler>
|
||||
<!-- this will ensure that urls are unique when running with multiple root nodes -->
|
||||
<useDomainPrefixes>false</useDomainPrefixes>
|
||||
<!-- this will add a trailing slash (/) to urls when in directory url mode -->
|
||||
<addTrailingSlash>true</addTrailingSlash>
|
||||
<urlReplacing removeDoubleDashes="true">
|
||||
<char org=" ">-</char>
|
||||
<char org="""></char>
|
||||
<char org="'"></char>
|
||||
<char org="%"></char>
|
||||
<char org="."></char>
|
||||
<char org=";"></char>
|
||||
<char org="/"></char>
|
||||
<char org="\"></char>
|
||||
<char org=":"></char>
|
||||
<char org="#"></char>
|
||||
<char org="+">plus</char>
|
||||
<char org="*">star</char>
|
||||
<char org="&"></char>
|
||||
<char org="?"></char>
|
||||
<char org="æ">ae</char>
|
||||
<char org="ø">oe</char>
|
||||
<char org="Ã¥">aa</char>
|
||||
<char org="ä">ae</char>
|
||||
<char org="ö">oe</char>
|
||||
<char org="ü">ue</char>
|
||||
<char org="ß">ss</char>
|
||||
<char org="Ä">ae</char>
|
||||
<char org="Ö">oe</char>
|
||||
<char org="|">-</char>
|
||||
<char org="<"></char>
|
||||
<char org=">"></char>
|
||||
</urlReplacing>
|
||||
</requestHandler>
|
||||
|
||||
<templates>
|
||||
<!-- If you want to switch to Mvc then do NOT change useAspNetMasterPages to false -->
|
||||
<!-- This (old!) setting is still used to control how macros are inserted into your pages -->
|
||||
<useAspNetMasterPages>true</useAspNetMasterPages>
|
||||
|
||||
<!-- To switch the default rendering engine to MVC, change this value from WebForms to Mvc -->
|
||||
<!-- Do not set useAspNetMasterPages to false, it is not relevant to MVC usage -->
|
||||
<defaultRenderingEngine>Mvc</defaultRenderingEngine>
|
||||
</templates>
|
||||
|
||||
|
||||
<!-- this is used by Umbraco to determine if there's valid classes in the /App_Code folder to be used for Rest/XSLT extensions -->
|
||||
<developer>
|
||||
<appCodeFileExtensions>
|
||||
<ext>cs</ext>
|
||||
<ext>vb</ext>
|
||||
</appCodeFileExtensions>
|
||||
</developer>
|
||||
|
||||
<scripting>
|
||||
<razor>
|
||||
<!-- razor DynamicNode typecasting detects XML and returns DynamicXml - Root elements that won't convert to DynamicXml -->
|
||||
<notDynamicXmlDocumentElements>
|
||||
<element>p</element>
|
||||
<element>div</element>
|
||||
<element>ul</element>
|
||||
<element>span</element>
|
||||
</notDynamicXmlDocumentElements>
|
||||
<dataTypeModelStaticMappings>
|
||||
<!--
|
||||
<mapping dataTypeGuid="00000000-0000-0000-0000-000000000000">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping documentTypeAlias="textPage" nodeTypeAlias="propertyAlias">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping dataTypeGuid="00000000-0000-0000-0000-000000000000" documentTypeAlias="textPage" nodeTypeAlias="propertyAlias">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping dataTypeGuid="00000000-0000-0000-0000-000000000000" documentTypeAlias="textPage">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping dataTypeGuid="00000000-0000-0000-0000-000000000000" nodeTypeAlias="propertyAlias">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping nodeTypeAlias="propertyAlias">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
-->
|
||||
</dataTypeModelStaticMappings>
|
||||
</razor>
|
||||
</scripting>
|
||||
|
||||
<!-- This moves the asp.net viewstate data to the end of the html document instead of having it in the beginning-->
|
||||
<viewstateMoverModule enable="false" />
|
||||
|
||||
<!--
|
||||
Now that we have Log4Net logging enabled, this section is only used for what gets logged to the umbracoLog table
|
||||
which currently logs items used in the audit trail and roll back scenarios.
|
||||
-->
|
||||
<logging>
|
||||
<enableLogging>true</enableLogging>
|
||||
<enableAsyncLogging>true</enableAsyncLogging>
|
||||
<disabledLogTypes>
|
||||
<!-- <logTypeAlias>[alias-of-log-type-in-lowercase]</logTypeAlias> -->
|
||||
</disabledLogTypes>
|
||||
<!-- You can add your own logging tool by implementing the umbraco.BusinessLogic.Interfaces.ILog interface and add the reference here -->
|
||||
<!-- The external logger can also act as the audit trail storage by setting the logAuditTrail attribute to true -->
|
||||
<!--<externalLogger assembly="~/bin/assemblyFileName.dll" type="fully.qualified.namespace.and.type" logAuditTrail="false" /> -->
|
||||
</logging>
|
||||
|
||||
<scheduledTasks>
|
||||
<!-- add tasks that should be called with an interval (seconds) -->
|
||||
<!-- <task log="true" alias="test60" interval="60" url="http://localhost/umbraco/test.aspx"/>-->
|
||||
</scheduledTasks>
|
||||
|
||||
<!-- distributed calls must be enabled when using Umbraco in a load balanced environment -->
|
||||
<distributedCall enable="false">
|
||||
<!-- the id of the user who's making the calls -->
|
||||
<!-- needed for security, umbraco will automatically look up correct login and passwords -->
|
||||
<user>0</user>
|
||||
|
||||
<!--
|
||||
When distributed call is enabled, you need to add all of the servers part taking in load balancing
|
||||
to the server list below.
|
||||
-->
|
||||
|
||||
<servers>
|
||||
|
||||
<!--
|
||||
Add ip number or hostname, make sure that it can be reached from all servers
|
||||
you can also add optional attributes to force a protocol or port number.
|
||||
|
||||
Examples:
|
||||
|
||||
<server>127.0.0.1</server>
|
||||
<server forceProtocol="http|https" forcePortnumber="80|443">127.0.0.1</server>
|
||||
|
||||
Generally when setting up load balancing you will designate a 'master' server,
|
||||
Umbraco will always assume that the FIRST server listed in this list is the 'master'.
|
||||
(NOTE: Not all load balancing scenarios have a 'master', depends on how you are setting it up)
|
||||
|
||||
In order for scheduled tasks (including scheduled publishing) to work properly when load balancing, each
|
||||
server in the load balanced environment needs to know if it is the 'master'. In order for servers
|
||||
to know this or not, they need to compare some values against the servers listed. These values
|
||||
are either: serverName or appId. You should not enter both values but appId will always supersede serverName.
|
||||
The serverName is the easiest and will work so long as you are not load balancing your site on the same server.
|
||||
If you are doing this, then you will need to use appId which is equivalent to the value returned from
|
||||
HttpRuntime.AppDomainAppId. It is recommended that you set either the serverName or appId for all servers
|
||||
registered here if possible, not just the first one.
|
||||
|
||||
Examples:
|
||||
|
||||
<server serverName="MyServer">server1.mysite.com</server>
|
||||
<server appId="/LM/W3SVC/69/ROOT">server1.mysite.com</server>
|
||||
-->
|
||||
|
||||
|
||||
</servers>
|
||||
</distributedCall>
|
||||
|
||||
<!-- Configuration for repositories -->
|
||||
<!-- Add or remove repositories here. You will need the repository's unique key to be able to connect to it.-->
|
||||
<repositories>
|
||||
<repository name="Umbraco package Repository" guid="65194810-1f85-11dd-bd0b-0800200c9a66" />
|
||||
</repositories>
|
||||
|
||||
<providers>
|
||||
<users>
|
||||
<!-- if you wish to use your own membershipprovider for authenticating to the umbraco back office -->
|
||||
<!-- specify it here (remember to add it to the web.config as well) -->
|
||||
<DefaultBackofficeProvider>UsersMembershipProvider</DefaultBackofficeProvider>
|
||||
</users>
|
||||
</providers>
|
||||
|
||||
<!-- Maps language, usertype, application and application_url to help pages -->
|
||||
<help defaultUrl="http://our.umbraco.org/wiki/umbraco-help/{0}/{1}">
|
||||
<!-- Add links that should open custom help pages -->
|
||||
<!--<link application="content" applicationUrl="dashboard.aspx" language="en" userType="Administrators" helpUrl="http://www.xyz.no?{0}/{1}/{2}/{3}" /> -->
|
||||
</help>
|
||||
|
||||
<!--
|
||||
web.routing
|
||||
@trySkipIisCustomErrors
|
||||
Tries to skip IIS custom errors.
|
||||
Starting with IIS 7.5, this must be set to true for Umbraco 404 pages to show. Else, IIS will take
|
||||
over and render its built-in error page. See MS doc for HttpResponseBase.TrySkipIisCustomErrors.
|
||||
The default value is false, for backward compatibility reasons, which means that IIS _will_ take
|
||||
over, and _prevent_ Umbraco 404 pages to show.
|
||||
@internalRedirectPreservesTemplate
|
||||
By default as soon as we're not displaying the initial document, we reset the template set by the
|
||||
finder or by the alt. template. Set this option to true to preserve the template set by the finder
|
||||
or by the alt. template, in case of an internal redirect.
|
||||
(false by default, and in fact should remain false unless you know what you're doing)
|
||||
@disableAlternativeTemplates
|
||||
By default you can add a altTemplate querystring or append a template name to the current URL which
|
||||
will make Umbraco render the content on the current page with the template you requested, for example:
|
||||
http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
|
||||
"About Us" page with a template with the alias Home. Setting this setting to true stops that behavior
|
||||
@disableFindContentByIdPath
|
||||
By default you can call any content Id in the url and show the content with that id, for example:
|
||||
http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting
|
||||
this setting to true stops that behavior
|
||||
@umbracoApplicationUrl
|
||||
The url of the Umbraco application. By default, Umbraco will figure it out from the first request.
|
||||
Configure it here if you need anything specific. Needs to be a complete url with scheme and umbraco
|
||||
path, eg http://mysite.com/umbraco. NOT just "mysite.com" or "mysite.com/umbraco" or "http://mysite.com".
|
||||
-->
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<settings>
|
||||
<content>
|
||||
<imaging>
|
||||
<!-- what file extension that should cause umbraco to create thumbnails -->
|
||||
<imageFileTypes>jpeg,jpg,gif,bmp,png,tiff,tif</imageFileTypes>
|
||||
<!-- what attributes that are allowed in the editor on an img tag -->
|
||||
<allowedAttributes>src,alt,border,class,style,align,id,name,onclick,usemap</allowedAttributes>
|
||||
<!-- automatically updates dimension, filesize and extension attributes on upload -->
|
||||
<autoFillImageProperties>
|
||||
<uploadField alias="umbracoFile">
|
||||
<widthFieldAlias>umbracoWidth</widthFieldAlias>
|
||||
<heightFieldAlias>umbracoHeight</heightFieldAlias>
|
||||
<lengthFieldAlias>umbracoBytes</lengthFieldAlias>
|
||||
<extensionFieldAlias>umbracoExtension</extensionFieldAlias>
|
||||
</uploadField>
|
||||
</autoFillImageProperties>
|
||||
|
||||
</imaging>
|
||||
<scripteditor>
|
||||
<!-- Path to script folder - no ending "/" -->
|
||||
<scriptFolderPath>/scripts</scriptFolderPath>
|
||||
<!-- what files can be opened/created in the script editor -->
|
||||
<scriptFileTypes>js,xml</scriptFileTypes>
|
||||
<!-- disable the codemirror editor and use a simple textarea instead -->
|
||||
<scriptDisableEditor>false</scriptDisableEditor>
|
||||
</scripteditor>
|
||||
|
||||
<!-- should umbraco store the uploaded files like /media/xxx/filename.ext or like /media/xxx-filename.ext
|
||||
should be set to false if the application pool's user account hasn't got readrights of the driveroot up to the /media directory -->
|
||||
<UploadAllowDirectories>True</UploadAllowDirectories>
|
||||
<errors>
|
||||
<!-- the id of the page that should be shown if the page is not found -->
|
||||
<!--<error404>
|
||||
<errorPage culture="default">1</errorPage>
|
||||
<errorPage culture="en-US">200</errorPage>
|
||||
</error404>-->
|
||||
<error404>
|
||||
<errorPage culture="default">1</errorPage>
|
||||
<errorPage culture="fr-FR">1079</errorPage>
|
||||
<errorPage culture="en-US">1080</errorPage>
|
||||
</error404>
|
||||
</errors>
|
||||
<notifications>
|
||||
<!-- the email that should be used as from mail when umbraco sends a notification -->
|
||||
<email>your@email.here</email>
|
||||
</notifications>
|
||||
|
||||
<!-- if true umbraco will ensure that no page under the same parent has an identical name -->
|
||||
<ensureUniqueNaming>True</ensureUniqueNaming>
|
||||
|
||||
<!-- clean editor content with use of tidy -->
|
||||
<TidyEditorContent>False</TidyEditorContent>
|
||||
|
||||
<!-- the encoding type for tidy. Default is UTF8, options are ASCII, Raw, Latin1, UTF8, ISO2022, MacroMan-->
|
||||
<TidyCharEncoding>UTF8</TidyCharEncoding>
|
||||
|
||||
<!-- to enable new content schema, this needs to be false -->
|
||||
<UseLegacyXmlSchema>false</UseLegacyXmlSchema>
|
||||
|
||||
<!-- Whether to force safe aliases (no spaces, no special characters) at businesslogic level on contenttypes and propertytypes -->
|
||||
<!-- HIGHLY recommend to keep this to true to ensure valid and beautiful XML Schemas -->
|
||||
<ForceSafeAliases>true</ForceSafeAliases>
|
||||
|
||||
<!-- Enable / disable xml content cache -->
|
||||
<XmlCacheEnabled>True</XmlCacheEnabled>
|
||||
|
||||
<!-- Update disk cache every time content has changed -->
|
||||
<ContinouslyUpdateXmlDiskCache>True</ContinouslyUpdateXmlDiskCache>
|
||||
|
||||
<!-- Update in-memory cache if xml file is changed -->
|
||||
<XmlContentCheckForDiskChanges>False</XmlContentCheckForDiskChanges>
|
||||
|
||||
<!-- Show the /config/splashes/booting.aspx page while initializing content -->
|
||||
<EnableSplashWhileLoading>False</EnableSplashWhileLoading>
|
||||
|
||||
<!-- Show property descriptions in editing view "icon|text|none" -->
|
||||
<PropertyContextHelpOption>text</PropertyContextHelpOption>
|
||||
|
||||
<!-- The html injected into a (x)html page if Umbraco is running in preview mode -->
|
||||
<PreviewBadge>
|
||||
<![CDATA[<a id="umbracoPreviewBadge" style="position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{1}/preview/previewModeBadge.png') no-repeat;" href="#" OnClick="javascript:window.top.location.href = '{0}/endPreview.aspx?redir={2}'"><span style="display:none;">In Preview Mode - click to end</span></a>
|
||||
]]></PreviewBadge>
|
||||
|
||||
<!-- Cache cycle of Media and Member data fetched from the umbraco.library methods -->
|
||||
<!-- In seconds. 0 will disable cache -->
|
||||
<UmbracoLibraryCacheDuration>1800</UmbracoLibraryCacheDuration>
|
||||
|
||||
<!-- Url Resolving ensures that all links works if you run Umbraco in virtual directories -->
|
||||
<!-- Setting this to true can increase render time for pages with a large number of links -->
|
||||
<!-- If running Umbraco in virtual directory this *must* be set to true! -->
|
||||
<ResolveUrlsFromTextString>false</ResolveUrlsFromTextString>
|
||||
|
||||
<!-- How Umbraco should handle errors during macro execution. Can be one of the following values:
|
||||
- inline - show an inline error within the macro but allow the page to continue rendering. Historial Umbraco behaviour.
|
||||
- silent - Silently suppress the error and do not render the offending macro.
|
||||
- throw - Throw an exception which can be caught by the global error handler defined in Application_OnError. If no such
|
||||
error handler is defined then you'll see the Yellow Screen Of Death (YSOD) error page.
|
||||
Note the error can also be handled by the umbraco.macro.Error event, where you can log/alarm with your own code and change the behaviour per event. -->
|
||||
<MacroErrors>throw</MacroErrors>
|
||||
|
||||
<!-- These file types will not be allowed to be uploaded via the upload control for media and content -->
|
||||
<disallowedUploadFiles>ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,html,htm,svg,php,htaccess</disallowedUploadFiles>
|
||||
|
||||
<!-- Defines the default document type property used when adding properties in the back-office (if missing or empty, defaults to Textstring -->
|
||||
<defaultDocumentTypeProperty>Textstring</defaultDocumentTypeProperty>
|
||||
|
||||
</content>
|
||||
|
||||
<security>
|
||||
<!-- set to true to auto update login interval (and there by disabling the lock screen -->
|
||||
<keepUserLoggedIn>true</keepUserLoggedIn>
|
||||
|
||||
<!-- change in 4.8: Disabled users are now showed dimmed and last in the tree. If you prefer not to display them set this to true -->
|
||||
<hideDisabledUsersInBackoffice>false</hideDisabledUsersInBackoffice>
|
||||
|
||||
<!-- set to true to enable the UI and API to allow back-office users to reset their passwords -->
|
||||
<allowPasswordReset>true</allowPasswordReset>
|
||||
|
||||
</security>
|
||||
|
||||
<requestHandler>
|
||||
<!-- this will ensure that urls are unique when running with multiple root nodes -->
|
||||
<useDomainPrefixes>false</useDomainPrefixes>
|
||||
<!-- this will add a trailing slash (/) to urls when in directory url mode -->
|
||||
<addTrailingSlash>true</addTrailingSlash>
|
||||
<urlReplacing removeDoubleDashes="true">
|
||||
<char org=" ">-</char>
|
||||
<char org="""></char>
|
||||
<char org="'"></char>
|
||||
<char org="%"></char>
|
||||
<char org="."></char>
|
||||
<char org=";"></char>
|
||||
<char org="/"></char>
|
||||
<char org="\"></char>
|
||||
<char org=":"></char>
|
||||
<char org="#"></char>
|
||||
<char org="+">plus</char>
|
||||
<char org="*">star</char>
|
||||
<char org="&"></char>
|
||||
<char org="?"></char>
|
||||
<char org="æ">ae</char>
|
||||
<char org="ø">oe</char>
|
||||
<char org="Ã¥">aa</char>
|
||||
<char org="ä">ae</char>
|
||||
<char org="ö">oe</char>
|
||||
<char org="ü">ue</char>
|
||||
<char org="ß">ss</char>
|
||||
<char org="Ä">ae</char>
|
||||
<char org="Ö">oe</char>
|
||||
<char org="|">-</char>
|
||||
<char org="<"></char>
|
||||
<char org=">"></char>
|
||||
</urlReplacing>
|
||||
</requestHandler>
|
||||
|
||||
<templates>
|
||||
<!-- If you want to switch to Mvc then do NOT change useAspNetMasterPages to false -->
|
||||
<!-- This (old!) setting is still used to control how macros are inserted into your pages -->
|
||||
<useAspNetMasterPages>true</useAspNetMasterPages>
|
||||
|
||||
<!-- To switch the default rendering engine to MVC, change this value from WebForms to Mvc -->
|
||||
<!-- Do not set useAspNetMasterPages to false, it is not relevant to MVC usage -->
|
||||
<defaultRenderingEngine>Mvc</defaultRenderingEngine>
|
||||
</templates>
|
||||
|
||||
|
||||
<!-- this is used by Umbraco to determine if there's valid classes in the /App_Code folder to be used for Rest/XSLT extensions -->
|
||||
<developer>
|
||||
<appCodeFileExtensions>
|
||||
<ext>cs</ext>
|
||||
<ext>vb</ext>
|
||||
</appCodeFileExtensions>
|
||||
</developer>
|
||||
|
||||
<scripting>
|
||||
<razor>
|
||||
<!-- razor DynamicNode typecasting detects XML and returns DynamicXml - Root elements that won't convert to DynamicXml -->
|
||||
<notDynamicXmlDocumentElements>
|
||||
<element>p</element>
|
||||
<element>div</element>
|
||||
<element>ul</element>
|
||||
<element>span</element>
|
||||
</notDynamicXmlDocumentElements>
|
||||
<dataTypeModelStaticMappings>
|
||||
<!--
|
||||
<mapping dataTypeGuid="00000000-0000-0000-0000-000000000000">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping documentTypeAlias="textPage" nodeTypeAlias="propertyAlias">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping dataTypeGuid="00000000-0000-0000-0000-000000000000" documentTypeAlias="textPage" nodeTypeAlias="propertyAlias">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping dataTypeGuid="00000000-0000-0000-0000-000000000000" documentTypeAlias="textPage">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping dataTypeGuid="00000000-0000-0000-0000-000000000000" nodeTypeAlias="propertyAlias">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
<mapping nodeTypeAlias="propertyAlias">Fully.Qualified.Type.Name.For.ModelBinder,Assembly.Name.Excluding.Dot.Dll</mapping>
|
||||
-->
|
||||
</dataTypeModelStaticMappings>
|
||||
</razor>
|
||||
</scripting>
|
||||
|
||||
<!-- This moves the asp.net viewstate data to the end of the html document instead of having it in the beginning-->
|
||||
<viewstateMoverModule enable="false" />
|
||||
|
||||
<!--
|
||||
Now that we have Log4Net logging enabled, this section is only used for what gets logged to the umbracoLog table
|
||||
which currently logs items used in the audit trail and roll back scenarios.
|
||||
-->
|
||||
<logging>
|
||||
<enableLogging>true</enableLogging>
|
||||
<enableAsyncLogging>true</enableAsyncLogging>
|
||||
<disabledLogTypes>
|
||||
<!-- <logTypeAlias>[alias-of-log-type-in-lowercase]</logTypeAlias> -->
|
||||
</disabledLogTypes>
|
||||
<!-- You can add your own logging tool by implementing the umbraco.BusinessLogic.Interfaces.ILog interface and add the reference here -->
|
||||
<!-- The external logger can also act as the audit trail storage by setting the logAuditTrail attribute to true -->
|
||||
<!--<externalLogger assembly="~/bin/assemblyFileName.dll" type="fully.qualified.namespace.and.type" logAuditTrail="false" /> -->
|
||||
</logging>
|
||||
|
||||
<scheduledTasks>
|
||||
<!-- add tasks that should be called with an interval (seconds) -->
|
||||
<!-- <task log="true" alias="test60" interval="60" url="http://localhost/umbraco/test.aspx"/>-->
|
||||
</scheduledTasks>
|
||||
|
||||
<!-- distributed calls must be enabled when using Umbraco in a load balanced environment -->
|
||||
<distributedCall enable="false">
|
||||
<!-- the id of the user who's making the calls -->
|
||||
<!-- needed for security, umbraco will automatically look up correct login and passwords -->
|
||||
<user>0</user>
|
||||
|
||||
<!--
|
||||
When distributed call is enabled, you need to add all of the servers part taking in load balancing
|
||||
to the server list below.
|
||||
-->
|
||||
|
||||
<servers>
|
||||
|
||||
<!--
|
||||
Add ip number or hostname, make sure that it can be reached from all servers
|
||||
you can also add optional attributes to force a protocol or port number.
|
||||
|
||||
Examples:
|
||||
|
||||
<server>127.0.0.1</server>
|
||||
<server forceProtocol="http|https" forcePortnumber="80|443">127.0.0.1</server>
|
||||
|
||||
Generally when setting up load balancing you will designate a 'master' server,
|
||||
Umbraco will always assume that the FIRST server listed in this list is the 'master'.
|
||||
(NOTE: Not all load balancing scenarios have a 'master', depends on how you are setting it up)
|
||||
|
||||
In order for scheduled tasks (including scheduled publishing) to work properly when load balancing, each
|
||||
server in the load balanced environment needs to know if it is the 'master'. In order for servers
|
||||
to know this or not, they need to compare some values against the servers listed. These values
|
||||
are either: serverName or appId. You should not enter both values but appId will always supersede serverName.
|
||||
The serverName is the easiest and will work so long as you are not load balancing your site on the same server.
|
||||
If you are doing this, then you will need to use appId which is equivalent to the value returned from
|
||||
HttpRuntime.AppDomainAppId. It is recommended that you set either the serverName or appId for all servers
|
||||
registered here if possible, not just the first one.
|
||||
|
||||
Examples:
|
||||
|
||||
<server serverName="MyServer">server1.mysite.com</server>
|
||||
<server appId="/LM/W3SVC/69/ROOT">server1.mysite.com</server>
|
||||
-->
|
||||
|
||||
|
||||
</servers>
|
||||
</distributedCall>
|
||||
|
||||
<!-- Configuration for repositories -->
|
||||
<!-- Add or remove repositories here. You will need the repository's unique key to be able to connect to it.-->
|
||||
<repositories>
|
||||
<repository name="Umbraco package Repository" guid="65194810-1f85-11dd-bd0b-0800200c9a66" />
|
||||
</repositories>
|
||||
|
||||
<providers>
|
||||
<users>
|
||||
<!-- if you wish to use your own membershipprovider for authenticating to the umbraco back office -->
|
||||
<!-- specify it here (remember to add it to the web.config as well) -->
|
||||
<DefaultBackofficeProvider>UsersMembershipProvider</DefaultBackofficeProvider>
|
||||
</users>
|
||||
</providers>
|
||||
|
||||
<!-- Maps language, usertype, application and application_url to help pages -->
|
||||
<help defaultUrl="http://our.umbraco.org/wiki/umbraco-help/{0}/{1}">
|
||||
<!-- Add links that should open custom help pages -->
|
||||
<!--<link application="content" applicationUrl="dashboard.aspx" language="en" userType="Administrators" helpUrl="http://www.xyz.no?{0}/{1}/{2}/{3}" /> -->
|
||||
</help>
|
||||
|
||||
<!--
|
||||
web.routing
|
||||
@trySkipIisCustomErrors
|
||||
Tries to skip IIS custom errors.
|
||||
Starting with IIS 7.5, this must be set to true for Umbraco 404 pages to show. Else, IIS will take
|
||||
over and render its built-in error page. See MS doc for HttpResponseBase.TrySkipIisCustomErrors.
|
||||
The default value is false, for backward compatibility reasons, which means that IIS _will_ take
|
||||
over, and _prevent_ Umbraco 404 pages to show.
|
||||
@internalRedirectPreservesTemplate
|
||||
By default as soon as we're not displaying the initial document, we reset the template set by the
|
||||
finder or by the alt. template. Set this option to true to preserve the template set by the finder
|
||||
or by the alt. template, in case of an internal redirect.
|
||||
(false by default, and in fact should remain false unless you know what you're doing)
|
||||
@disableAlternativeTemplates
|
||||
By default you can add a altTemplate querystring or append a template name to the current URL which
|
||||
will make Umbraco render the content on the current page with the template you requested, for example:
|
||||
http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
|
||||
"About Us" page with a template with the alias Home. Setting this setting to true stops that behavior
|
||||
@disableFindContentByIdPath
|
||||
By default you can call any content Id in the url and show the content with that id, for example:
|
||||
http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting
|
||||
this setting to true stops that behavior
|
||||
@umbracoApplicationUrl
|
||||
The url of the Umbraco application. By default, Umbraco will figure it out from the first request.
|
||||
Configure it here if you need anything specific. Needs to be a complete url with scheme and umbraco
|
||||
path, eg http://mysite.com/umbraco. NOT just "mysite.com" or "mysite.com/umbraco" or "http://mysite.com".
|
||||
-->
|
||||
<web.routing
|
||||
trySkipIisCustomErrors="false"
|
||||
internalRedirectPreservesTemplate="false" disableAlternativeTemplates="false" disableFindContentByIdPath="false"
|
||||
umbracoApplicationUrl="">
|
||||
</web.routing>
|
||||
|
||||
</web.routing>
|
||||
|
||||
</settings>
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
|
||||
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
|
||||
<package id="ClientDependency" version="1.9.1" targetFramework="net45" />
|
||||
<package id="ClientDependency-Mvc5" version="1.8.0.0" targetFramework="net45" />
|
||||
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.69.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor" version="2.4.4.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor.Web" version="4.6.4.0" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.70.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor" version="2.4.5.0" targetFramework="net45" />
|
||||
<package id="ImageProcessor.Web" version="4.6.6.0" targetFramework="net45" />
|
||||
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
|
||||
|
||||
@@ -147,9 +147,9 @@
|
||||
<key alias="nodeName">Page Title</key>
|
||||
<key alias="otherElements">Properties</key>
|
||||
<key alias="parentNotPublished">This document is published but is not visible because the parent '%0%' is unpublished</key>
|
||||
<key alias="parentNotPublishedAnomaly">Oops: this document is published but is not in the cache (internal error - see log)</key>
|
||||
<key alias="getUrlException">Oops: could not get the url (internal error - see log)</key>
|
||||
<key alias="routeError">Oops: this document is published but its url would collide with content %0%</key>
|
||||
<key alias="parentNotPublishedAnomaly">This document is published but is not in the cache</key>
|
||||
<key alias="getUrlException">Could not get the url</key>
|
||||
<key alias="routeError">This document is published but its url would collide with content %0%</key>
|
||||
<key alias="publish">Publish</key>
|
||||
<key alias="publishStatus">Publication Status</key>
|
||||
<key alias="releaseDate">Publish at</key>
|
||||
@@ -1356,6 +1356,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="xmlDataIntegrityCheckMedia">Media - Total XML: %0%, Total: %1%, Total invalid %2%</key>
|
||||
<key alias="xmlDataIntegrityCheckContent">Content - Total XML: %0%, Total published: %1%, Total invalid %2%</key>
|
||||
|
||||
<key alias="httpsCheckValidCertificate">Your site certificate was marked as valid.</key>
|
||||
<key alias="httpsCheckInvalidCertificate">Certificate validation error: '%0%'</key>
|
||||
<key alias="httpsCheckInvalidUrl">Error pinging the URL %0% - '%1%'</key>
|
||||
<key alias="httpsCheckIsCurrentSchemeHttps">You are currently %0% viewing the site using the HTTPS scheme.</key>
|
||||
|
||||
@@ -148,9 +148,9 @@
|
||||
<key alias="nodeName">Page Title</key>
|
||||
<key alias="otherElements">Properties</key>
|
||||
<key alias="parentNotPublished">This document is published but is not visible because the parent '%0%' is unpublished</key>
|
||||
<key alias="parentNotPublishedAnomaly">Oops: this document is published but is not in the cache (internal error - see log)</key>
|
||||
<key alias="getUrlException">Oops: could not get the url (internal error - see log)</key>
|
||||
<key alias="routeError">Oops: this document is published but its url would collide with content %0%</key>
|
||||
<key alias="parentNotPublishedAnomaly">This document is published but is not in the cache</key>
|
||||
<key alias="getUrlException">Could not get the url</key>
|
||||
<key alias="routeError">This document is published but its url would collide with content %0%</key>
|
||||
<key alias="publish">Publish</key>
|
||||
<key alias="publishStatus">Publication Status</key>
|
||||
<key alias="releaseDate">Publish at</key>
|
||||
@@ -460,6 +460,7 @@
|
||||
<key alias="install">Install</key>
|
||||
<key alias="invalid">Invalid</key>
|
||||
<key alias="justify">Justify</key>
|
||||
<key alias="label">Label</key>
|
||||
<key alias="language">Language</key>
|
||||
<key alias="layout">Layout</key>
|
||||
<key alias="loading">Loading</key>
|
||||
@@ -1360,6 +1361,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="xmlDataIntegrityCheckMedia">Media - Total XML: %0%, Total: %1%, Total invalid: %2%</key>
|
||||
<key alias="xmlDataIntegrityCheckContent">Content - Total XML: %0%, Total published: %1%, Total invalid: %2%</key>
|
||||
|
||||
<key alias="httpsCheckValidCertificate">Your site certificate was marked as valid.</key>
|
||||
<key alias="httpsCheckInvalidCertificate">Certificate validation error: '%0%'</key>
|
||||
<key alias="httpsCheckInvalidUrl">Error pinging the URL %0% - '%1%'</key>
|
||||
<key alias="httpsCheckIsCurrentSchemeHttps">You are currently %0% viewing the site using the HTTPS scheme.</key>
|
||||
|
||||
@@ -499,6 +499,7 @@
|
||||
<key alias="install">Установить</key>
|
||||
<key alias="invalid">Неверно</key>
|
||||
<key alias="justify">Выравнивание</key>
|
||||
<key alias="label">Название</key>
|
||||
<key alias="language">Язык</key>
|
||||
<key alias="layout">Макет</key>
|
||||
<key alias="loading">Загрузка</key>
|
||||
@@ -654,6 +655,7 @@
|
||||
<key alias="xmlDataIntegrityCheckMedia">Медиа - всего в XML: %0%, всего: %1%Б с ошибками: %2%</key>
|
||||
<key alias="xmlDataIntegrityCheckContent">Содержимое - всего в XML: %0%, всего опубликовано: %1%, с ошибками: %2%</key>
|
||||
|
||||
<key alias="httpsCheckValidCertificate">Сертификат Вашего сайта отмечен как проверенный.</key>
|
||||
<key alias="httpsCheckInvalidCertificate">Ошибка проверки сертификата: '%0%'</key>
|
||||
<key alias="httpsCheckInvalidUrl">Ошибка проверки адреса URL %0% - '%1%'</key>
|
||||
<key alias="httpsCheckIsCurrentSchemeHttps">Сейчас Вы %0% просматриваете сайт, используя протокол HTTPS.</key>
|
||||
@@ -1065,6 +1067,23 @@
|
||||
Чтобы опубликовать ранее неопубликованные документы среди дочерних, отметьте опцию <em>Включая неопубликованные дочерние документы</em>.
|
||||
]]></key>
|
||||
</area>
|
||||
<area alias="redirectUrls">
|
||||
<key alias="disableUrlTracker">Остановить отслеживание URL</key>
|
||||
<key alias="enableUrlTracker">Запустить отслеживание URL</key>
|
||||
<key alias="originalUrl">Первоначальный URL</key>
|
||||
<key alias="redirectedTo">Перенаправлен в</key>
|
||||
<key alias="noRedirects">На данный момент нет ни одного перенаправления</key>
|
||||
<key alias="noRedirectsDescription">Если опубликованный документ переименовывается или меняет свое расположение в дереве, а следовательно, меняется адрес (URL), автоматически создается перенаправление на новое местоположение этого документа.</key>
|
||||
<key alias="removeButton">Удалить</key>
|
||||
<key alias="confirmRemove">Вы уверены, что хотите удалить перенаправление с '%0%' на '%1%'?</key>
|
||||
<key alias="redirectRemoved">Перенаправление удалено.</key>
|
||||
<key alias="redirectRemoveError">Ошибка удаления перенаправления.</key>
|
||||
<key alias="confirmDisable">Вы уверены, что хотите остановить отслеживание URL?</key>
|
||||
<key alias="disabledConfirm">Отслеживание URL в настоящий момент остановлено.</key>
|
||||
<key alias="disableError">Ошибка остановки отслеживания URL, более подробные сведения находятся в системном журнале.</key>
|
||||
<key alias="enabledConfirm">Отслеживание URL в настоящий момент запущено.</key>
|
||||
<key alias="enableError">Ошибка запуска отслеживания URL, более подробные сведения находятся в системном журнале.</key>
|
||||
</area>
|
||||
<area alias="relatedlinks">
|
||||
<key alias="caption">Заголовок</key>
|
||||
<key alias="captionPlaceholder">Укажите заголовок</key>
|
||||
|
||||
@@ -108,16 +108,14 @@
|
||||
</div>
|
||||
</asp:PlaceHolder>
|
||||
|
||||
<asp:PlaceHolder ID="SaveAndCancelPlaceholder" runat="server">
|
||||
<br />
|
||||
<p>
|
||||
<div class="btn-toolbar umb-btn-toolbar">
|
||||
<asp:PlaceHolder ID="SaveAndCancelPlaceholder" runat="server">
|
||||
<a href="#" class="btn btn-link" onclick="UmbClientMgr.closeModalWindow()"><%=umbraco.ui.Text("cancel")%></a>
|
||||
<asp:PlaceHolder ID="SavePlaceholder" runat="server">
|
||||
<asp:Button ID="ValidateAndSave" runat="server" OnClick="ValidateAndSave_Click" />
|
||||
<em> <%= umbraco.ui.Text("or") %> </em>
|
||||
</asp:PlaceHolder>
|
||||
<a href="#" style="color: blue" onclick="UmbClientMgr.closeModalWindow()"><%=umbraco.ui.Text("general", "cancel", Security.CurrentUser)%></a>
|
||||
</p>
|
||||
</asp:PlaceHolder>
|
||||
|
||||
<asp:Button ID="ValidateAndSave" runat="server" CssClass="btn btn-primary" Text="Create" OnClick="ValidateAndSave_Click"></asp:Button>
|
||||
</asp:PlaceHolder>
|
||||
</asp:Placeholder>
|
||||
</div>
|
||||
|
||||
</asp:Content>
|
||||
|
||||
@@ -389,12 +389,12 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls");
|
||||
processData: false,
|
||||
success: function (data, textStatus) {
|
||||
if (textStatus == "error") {
|
||||
alert("Oops. Could not update sort order");
|
||||
alert("Could not update sort order");
|
||||
self._getChildNodes();
|
||||
}
|
||||
},
|
||||
error: function(data) {
|
||||
alert("Oops. Could not update sort order. Err: " + data.statusText);
|
||||
alert("Could not update sort order. Err: " + data.statusText);
|
||||
self._getChildNodes();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -381,6 +381,11 @@
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.3.1.0" newVersion="3.3.1.0" />
|
||||
</dependentAssembly>
|
||||
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
|
||||
@@ -77,6 +77,8 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public override void RefreshAll()
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
|
||||
ClearAllIsolatedCacheByEntityType<IContent>();
|
||||
ClearAllIsolatedCacheByEntityType<PublicAccessEntry>();
|
||||
DistributedCache.Instance.ClearDomainCacheOnCurrentServer();
|
||||
@@ -85,6 +87,8 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public override void Refresh(int id)
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
|
||||
ClearRepositoryCacheItemById(id);
|
||||
ClearAllIsolatedCacheByEntityType<PublicAccessEntry>();
|
||||
content.Instance.UpdateSortOrder(id);
|
||||
@@ -94,6 +98,8 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public override void Remove(int id)
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
|
||||
ClearRepositoryCacheItemById(id);
|
||||
ClearAllIsolatedCacheByEntityType<PublicAccessEntry>();
|
||||
DistributedCache.Instance.ClearDomainCacheOnCurrentServer();
|
||||
@@ -103,6 +109,8 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public override void Refresh(IContent instance)
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
|
||||
ClearRepositoryCacheItemById(instance.Id);
|
||||
ClearAllIsolatedCacheByEntityType<PublicAccessEntry>();
|
||||
content.Instance.UpdateSortOrder(instance);
|
||||
@@ -112,6 +120,8 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public override void Remove(IContent instance)
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
|
||||
ClearRepositoryCacheItemById(instance.Id);
|
||||
ClearAllIsolatedCacheByEntityType<PublicAccessEntry>();
|
||||
DistributedCache.Instance.ClearDomainCacheOnCurrentServer();
|
||||
@@ -124,6 +134,8 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="jsonPayload"></param>
|
||||
public void Refresh(string jsonPayload)
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey);
|
||||
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey);
|
||||
ClearAllIsolatedCacheByEntityType<PublicAccessEntry>();
|
||||
|
||||
foreach (var payload in DeserializeFromJsonPayload(jsonPayload))
|
||||
|
||||
@@ -215,7 +215,7 @@ namespace Umbraco.Web.Editors
|
||||
if (user != null && user.IsLockedOut == false)
|
||||
{
|
||||
var code = await UserManager.GeneratePasswordResetTokenAsync(identityUser.Id);
|
||||
var callbackUrl = ConstuctCallbackUrl(identityUser.Id, code);
|
||||
var callbackUrl = ConstructCallbackUrl(identityUser.Id, code);
|
||||
|
||||
var message = Services.TextService.Localize("resetPasswordEmailCopyFormat",
|
||||
//Ensure the culture of the found user is used for the email!
|
||||
@@ -233,12 +233,11 @@ namespace Umbraco.Web.Editors
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
private string ConstuctCallbackUrl(int userId, string code)
|
||||
private string ConstructCallbackUrl(int userId, string code)
|
||||
{
|
||||
//get an mvc helper to get the url
|
||||
// Get an mvc helper to get the url
|
||||
var http = EnsureHttpContext();
|
||||
var urlHelper = new UrlHelper(http.Request.RequestContext);
|
||||
|
||||
var action = urlHelper.Action("ValidatePasswordResetCode", "BackOffice",
|
||||
new
|
||||
{
|
||||
@@ -247,12 +246,10 @@ namespace Umbraco.Web.Editors
|
||||
r = code
|
||||
});
|
||||
|
||||
//TODO: Virtual path?
|
||||
|
||||
return string.Format("{0}://{1}{2}",
|
||||
http.Request.Url.Scheme,
|
||||
http.Request.Url.Host + (http.Request.Url.Port == 80 ? string.Empty : ":" + http.Request.Url.Port),
|
||||
action);
|
||||
// Construct full URL using configured application URL (which will fall back to request)
|
||||
var applicationUri = new Uri(ApplicationContext.UmbracoApplicationUrl);
|
||||
var callbackUri = new Uri(applicationUri, action);
|
||||
return callbackUri.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -467,7 +467,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the sort order for media
|
||||
/// Change the sort order for content
|
||||
/// </summary>
|
||||
/// <param name="sorted"></param>
|
||||
/// <returns></returns>
|
||||
@@ -485,23 +485,24 @@ namespace Umbraco.Web.Editors
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
var contentService = Services.ContentService;
|
||||
var sortedContent = new List<IContent>();
|
||||
try
|
||||
{
|
||||
sortedContent.AddRange(Services.ContentService.GetByIds(sorted.IdSortOrder));
|
||||
var contentService = Services.ContentService;
|
||||
|
||||
// content service GetByIds does order the content items based on the order of Ids passed in
|
||||
var content = contentService.GetByIds(sorted.IdSortOrder);
|
||||
|
||||
// Save content with new sort order and update content xml in db accordingly
|
||||
if (contentService.Sort(sortedContent) == false)
|
||||
if (contentService.Sort(content) == false)
|
||||
{
|
||||
LogHelper.Warn<MediaController>("Content sorting failed, this was probably caused by an event being cancelled");
|
||||
LogHelper.Warn<ContentController>("Content sorting failed, this was probably caused by an event being cancelled");
|
||||
return Request.CreateValidationErrorResponse("Content sorting failed, this was probably caused by an event being cancelled");
|
||||
}
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<MediaController>("Could not update content sort order", ex);
|
||||
LogHelper.Error<ContentController>("Could not update content sort order", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,125 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using Umbraco.Core.IO;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
|
||||
//we need to fire up the controller like this to enable loading of remote css directly from this controller
|
||||
[PluginController("UmbracoApi")]
|
||||
public class DashboardController : UmbracoAuthorizedJsonController
|
||||
[ValidationFilter]
|
||||
[AngularJsonOnlyConfiguration]
|
||||
[IsBackOffice]
|
||||
[WebApi.UmbracoAuthorize]
|
||||
public class DashboardController : UmbracoApiController
|
||||
{
|
||||
//we have baseurl as a param to make previewing easier, so we can test with a dev domain from client side
|
||||
[ValidateAngularAntiForgeryToken]
|
||||
public async Task<JObject> GetRemoteDashboardContent(string section, string baseUrl = "https://dashboard.umbraco.org/")
|
||||
{
|
||||
var context = UmbracoContext.Current;
|
||||
if (context == null)
|
||||
throw new HttpResponseException(HttpStatusCode.InternalServerError);
|
||||
|
||||
var user = Security.CurrentUser;
|
||||
var userType = user.UserType.Alias;
|
||||
var allowedSections = string.Join(",", user.AllowedSections);
|
||||
var language = user.Language;
|
||||
var version = UmbracoVersion.GetSemanticVersion().ToSemanticString();
|
||||
|
||||
var url = string.Format(baseUrl + "{0}?section={0}&type={1}&allowed={2}&lang={3}&version={4}", section, userType, allowedSections, language, version);
|
||||
var key = "umbraco-dynamic-dashboard-" + userType + language + allowedSections.Replace(",", "-") + section;
|
||||
|
||||
var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem<JObject>(key);
|
||||
var result = new JObject();
|
||||
if (content != null)
|
||||
{
|
||||
result = content;
|
||||
}
|
||||
else
|
||||
{
|
||||
//content is null, go get it
|
||||
try
|
||||
{
|
||||
using (var web = new HttpClient())
|
||||
{
|
||||
//fetch dashboard json and parse to JObject
|
||||
var json = await web.GetStringAsync(url);
|
||||
content = JObject.Parse(json);
|
||||
result = content;
|
||||
}
|
||||
|
||||
ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem<JObject>(key, () => result, new TimeSpan(0, 30, 0));
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
LogHelper.Debug<DashboardController>(string.Format("Error getting dashboard content from '{0}': {1}\n{2}", url, ex.Message, ex.InnerException));
|
||||
|
||||
//it's still new JObject() - we return it like this to avoid error codes which triggers UI warnings
|
||||
ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem<JObject>(key, () => result, new TimeSpan(0, 5, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> GetRemoteDashboardCss(string section, string baseUrl = "https://dashboard.umbraco.org/")
|
||||
{
|
||||
var url = string.Format(baseUrl + "css/dashboard.css?section={0}", section);
|
||||
var key = "umbraco-dynamic-dashboard-css-" + section;
|
||||
|
||||
var content = ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem<string>(key);
|
||||
var result = string.Empty;
|
||||
|
||||
if (content != null)
|
||||
{
|
||||
result = content;
|
||||
}
|
||||
else
|
||||
{
|
||||
//content is null, go get it
|
||||
try
|
||||
{
|
||||
using (var web = new HttpClient())
|
||||
{
|
||||
//fetch remote css
|
||||
content = await web.GetStringAsync(url);
|
||||
|
||||
//can't use content directly, modified closure problem
|
||||
result = content;
|
||||
|
||||
//save server content for 30 mins
|
||||
ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem<string>(key, () => result, new TimeSpan(0, 30, 0));
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
LogHelper.Debug<DashboardController>(string.Format("Error getting dashboard CSS from '{0}': {1}\n{2}", url, ex.Message, ex.InnerException));
|
||||
|
||||
//it's still string.Empty - we return it like this to avoid error codes which triggers UI warnings
|
||||
ApplicationContext.ApplicationCache.RuntimeCache.InsertCacheItem<string>(key, () => result, new TimeSpan(0, 5, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
Content = new StringContent(result, Encoding.UTF8, "text/css")
|
||||
};
|
||||
}
|
||||
|
||||
[ValidateAngularAntiForgeryToken]
|
||||
public IEnumerable<Tab<DashboardControl>> GetDashboard(string section)
|
||||
{
|
||||
var tabs = new List<Tab<DashboardControl>>();
|
||||
@@ -23,50 +132,47 @@ namespace Umbraco.Web.Editors
|
||||
foreach( var dashboardSection in UmbracoConfig.For.DashboardSettings().Sections.Where(x => x.Areas.Contains(section)))
|
||||
{
|
||||
//we need to validate access to this section
|
||||
if (DashboardSecurity.AuthorizeAccess(dashboardSection, Security.CurrentUser, Services.SectionService))
|
||||
if (DashboardSecurity.AuthorizeAccess(dashboardSection, Security.CurrentUser, Services.SectionService) == false)
|
||||
continue;
|
||||
|
||||
//User is authorized
|
||||
foreach (var tab in dashboardSection.Tabs)
|
||||
{
|
||||
//User is authorized
|
||||
foreach (var dashTab in dashboardSection.Tabs)
|
||||
//we need to validate access to this tab
|
||||
if (DashboardSecurity.AuthorizeAccess(tab, Security.CurrentUser, Services.SectionService) == false)
|
||||
continue;
|
||||
|
||||
var dashboardControls = new List<DashboardControl>();
|
||||
|
||||
foreach (var control in tab.Controls)
|
||||
{
|
||||
//we need to validate access to this tab
|
||||
if (DashboardSecurity.AuthorizeAccess(dashTab, Security.CurrentUser, Services.SectionService))
|
||||
{
|
||||
var props = new List<DashboardControl>();
|
||||
if (DashboardSecurity.AuthorizeAccess(control, Security.CurrentUser, Services.SectionService) == false)
|
||||
continue;
|
||||
|
||||
foreach (var dashCtrl in dashTab.Controls)
|
||||
{
|
||||
if (DashboardSecurity.AuthorizeAccess(dashCtrl, Security.CurrentUser,
|
||||
Services.SectionService))
|
||||
{
|
||||
var ctrl = new DashboardControl();
|
||||
var controlPath = dashCtrl.ControlPath.Trim(' ', '\r', '\n');
|
||||
ctrl.Path = IOHelper.FindFile(controlPath);
|
||||
if (controlPath.ToLower().EndsWith(".ascx"))
|
||||
{
|
||||
ctrl.ServerSide = true;
|
||||
}
|
||||
props.Add(ctrl);
|
||||
}
|
||||
}
|
||||
var dashboardControl = new DashboardControl();
|
||||
var controlPath = control.ControlPath.Trim();
|
||||
dashboardControl.Path = IOHelper.FindFile(controlPath);
|
||||
if (controlPath.ToLowerInvariant().EndsWith(".ascx".ToLowerInvariant()))
|
||||
dashboardControl.ServerSide = true;
|
||||
|
||||
tabs.Add(new Tab<DashboardControl>
|
||||
{
|
||||
Id = i,
|
||||
Alias = dashTab.Caption.ToSafeAlias(),
|
||||
IsActive = i == 1,
|
||||
Label = dashTab.Caption,
|
||||
Properties = props
|
||||
});
|
||||
i++;
|
||||
}
|
||||
dashboardControls.Add(dashboardControl);
|
||||
}
|
||||
|
||||
tabs.Add(new Tab<DashboardControl>
|
||||
{
|
||||
Id = i,
|
||||
Alias = tab.Caption.ToSafeAlias(),
|
||||
IsActive = i == 1,
|
||||
Label = tab.Caption,
|
||||
Properties = dashboardControls
|
||||
});
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
//In case there are no tabs or a user doesn't have access the empty tabs list is returned
|
||||
return tabs;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an empty content item for the
|
||||
/// Gets an empty content item for the
|
||||
/// </summary>
|
||||
/// <param name="contentTypeAlias"></param>
|
||||
/// <param name="parentId"></param>
|
||||
@@ -213,7 +213,7 @@ namespace Umbraco.Web.Editors
|
||||
/// Moves an item to the recycle bin, if it is already there then it will permanently delete it
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
/// <returns></returns>
|
||||
[EnsureUserPermissionForMedia("id")]
|
||||
[HttpPost]
|
||||
public HttpResponseMessage DeleteById(int id)
|
||||
@@ -231,7 +231,7 @@ namespace Umbraco.Web.Editors
|
||||
var moveResult = Services.MediaService.WithResult().MoveToRecycleBin(foundMedia, (int)Security.CurrentUser.Id);
|
||||
if (moveResult == false)
|
||||
{
|
||||
//returning an object of INotificationModel will ensure that any pending
|
||||
//returning an object of INotificationModel will ensure that any pending
|
||||
// notification messages are added to the response.
|
||||
return Request.CreateValidationErrorResponse(new SimpleNotificationModel());
|
||||
}
|
||||
@@ -241,7 +241,7 @@ namespace Umbraco.Web.Editors
|
||||
var deleteResult = Services.MediaService.WithResult().Delete(foundMedia, (int)Security.CurrentUser.Id);
|
||||
if (deleteResult == false)
|
||||
{
|
||||
//returning an object of INotificationModel will ensure that any pending
|
||||
//returning an object of INotificationModel will ensure that any pending
|
||||
// notification messages are added to the response.
|
||||
return Request.CreateValidationErrorResponse(new SimpleNotificationModel());
|
||||
}
|
||||
@@ -270,7 +270,7 @@ namespace Umbraco.Web.Editors
|
||||
/// <summary>
|
||||
/// Saves content
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <returns></returns>
|
||||
[FileUploadCleanupFilter]
|
||||
[MediaPostValidate]
|
||||
public MediaItemDisplay PostSave(
|
||||
@@ -290,7 +290,7 @@ namespace Umbraco.Web.Editors
|
||||
// * We still need to save the entity even if there are validation value errors
|
||||
// * Depending on if the entity is new, and if there are non property validation errors (i.e. the name is null)
|
||||
// then we cannot continue saving, we can only display errors
|
||||
// * If there are validation errors and they were attempting to publish, we can only save, NOT publish and display
|
||||
// * If there are validation errors and they were attempting to publish, we can only save, NOT publish and display
|
||||
// a message indicating this
|
||||
if (ModelState.IsValid == false)
|
||||
{
|
||||
@@ -314,7 +314,7 @@ namespace Umbraco.Web.Editors
|
||||
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
|
||||
HandleInvalidModelState(display);
|
||||
|
||||
//put the correct msgs in
|
||||
//put the correct msgs in
|
||||
switch (contentItem.Action)
|
||||
{
|
||||
case ContentSaveAction.Save:
|
||||
@@ -461,7 +461,7 @@ namespace Umbraco.Web.Editors
|
||||
Services.MediaService, parentId) == false)
|
||||
{
|
||||
return Request.CreateResponse(
|
||||
HttpStatusCode.Unauthorized,
|
||||
HttpStatusCode.Forbidden,
|
||||
new SimpleNotificationModel(new Notification(
|
||||
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
|
||||
Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"),
|
||||
@@ -534,7 +534,7 @@ namespace Umbraco.Web.Editors
|
||||
if (UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes.Contains(ext))
|
||||
mediaType = Constants.Conventions.MediaTypes.Image;
|
||||
|
||||
//TODO: make the media item name "nice" since file names could be pretty ugly, we have
|
||||
//TODO: make the media item name "nice" since file names could be pretty ugly, we have
|
||||
// string extensions to do much of this but we'll need:
|
||||
// * Pascalcase the name (use string extensions)
|
||||
// * strip the file extension
|
||||
@@ -595,7 +595,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return Request.CreateResponse(HttpStatusCode.OK, tempFiles);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the item can be moved/copied to the new location
|
||||
/// </summary>
|
||||
@@ -654,7 +654,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a permissions check for the user to check if it has access to the node based on
|
||||
/// Performs a permissions check for the user to check if it has access to the node based on
|
||||
/// start node and/or permissions for the node
|
||||
/// </summary>
|
||||
/// <param name="storage">The storage to add the content item to so it can be reused</param>
|
||||
@@ -668,7 +668,7 @@ namespace Umbraco.Web.Editors
|
||||
if (media == null && nodeId != Constants.System.Root && nodeId != Constants.System.RecycleBinMedia)
|
||||
{
|
||||
media = mediaService.GetById(nodeId);
|
||||
//put the content item into storage so it can be retreived
|
||||
//put the content item into storage so it can be retreived
|
||||
// in the controller (saves a lookup)
|
||||
storage[typeof(IMedia).ToString()] = media;
|
||||
}
|
||||
|
||||
@@ -2,31 +2,22 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http;
|
||||
using System.Web.UI.WebControls;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using umbraco;
|
||||
using Umbraco.Core.Auditing;
|
||||
using umbraco.BusinessLogic;
|
||||
using umbraco.cms.businesslogic.packager;
|
||||
using umbraco.cms.businesslogic.packager.repositories;
|
||||
using umbraco.cms.businesslogic.web;
|
||||
using umbraco.cms.presentation.Trees;
|
||||
using umbraco.presentation.developer.packages;
|
||||
using umbraco.webservices;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
@@ -36,7 +27,6 @@ using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using File = System.IO.File;
|
||||
using Notification = Umbraco.Web.Models.ContentEditing.Notification;
|
||||
using Settings = umbraco.cms.businesslogic.packager.Settings;
|
||||
using Version = System.Version;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
@@ -212,6 +202,9 @@ namespace Umbraco.Web.Editors
|
||||
var file = IOHelper.FindFile(item);
|
||||
if (file != null)
|
||||
{
|
||||
if (file.StartsWith("/") == false)
|
||||
file = string.Format("/{0}", file);
|
||||
|
||||
var filePath = IOHelper.MapPath(file);
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
@@ -63,7 +64,8 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
var url = HealthCheckContext.HttpContext.Request.Url;
|
||||
|
||||
// Access the site home page and check for the click-jack protection header or meta tag
|
||||
var address = string.Format("http://{0}:{1}", url.Host.ToLower(), url.Port);
|
||||
var useSsl = GlobalSettings.UseSSL || HealthCheckContext.HttpContext.Request.ServerVariables["SERVER_PORT"] == "443";
|
||||
var address = string.Format("http{0}://{1}:{2}", useSsl ? "s" : "", url.Host.ToLower(), url.Port);
|
||||
var request = WebRequest.Create(address);
|
||||
request.Method = "GET";
|
||||
try
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
@@ -45,10 +46,11 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
{
|
||||
var message = string.Empty;
|
||||
var success = false;
|
||||
var url = HealthCheckContext.HttpContext.Request.Url;
|
||||
|
||||
// Access the site home page and check for the headers
|
||||
var address = string.Format("http://{0}:{1}", url.Host.ToLower(), url.Port);
|
||||
var url = HealthCheckContext.HttpContext.Request.Url;
|
||||
|
||||
// Access the site home page and check for the headers
|
||||
var useSsl = GlobalSettings.UseSSL || HealthCheckContext.HttpContext.Request.ServerVariables["SERVER_PORT"] == "443";
|
||||
var address = string.Format("http{0}://{1}:{2}", useSsl ? "s" : "", url.Host.ToLower(), url.Port);
|
||||
var request = WebRequest.Create(address);
|
||||
request.Method = "HEAD";
|
||||
try
|
||||
|
||||
@@ -84,6 +84,9 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
|
||||
var actions = new List<HealthCheckAction>();
|
||||
|
||||
if (success)
|
||||
message = _textService.Localize("healthcheck/httpsCheckValidCertificate");
|
||||
|
||||
return
|
||||
new HealthCheckStatus(message)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
Top,
|
||||
Right,
|
||||
Bottom,
|
||||
Left
|
||||
Left,
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ namespace Umbraco.Web.Models
|
||||
Crop,
|
||||
Max,
|
||||
Stretch,
|
||||
Pad
|
||||
Pad,
|
||||
BoxPad,
|
||||
Min
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
//FROM MembershipUser TO IMember - used when using a non-umbraco membership provider
|
||||
config.CreateMap<MembershipUser, IMember>()
|
||||
.ConstructUsing(user => MemberService.CreateGenericMembershipProviderMember(user.UserName, user.Email, user.UserName, ""))
|
||||
//we're giving this entity an ID - we cannot really map it but it needs an id so the system knows it's not a new entity
|
||||
//we're giving this entity an ID of 0 - we cannot really map it but it needs an id so the system knows it's not a new entity
|
||||
.ForMember(member => member.Id, expression => expression.MapFrom(user => int.MaxValue))
|
||||
.ForMember(member => member.Comments, expression => expression.MapFrom(user => user.Comment))
|
||||
.ForMember(member => member.CreateDate, expression => expression.MapFrom(user => user.CreationDate))
|
||||
@@ -118,7 +118,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
//FROM MembershipUser TO MemberBasic
|
||||
config.CreateMap<MembershipUser, MemberBasic>()
|
||||
//we're giving this entity an ID - we cannot really map it but it needs an id so the system knows it's not a new entity
|
||||
//we're giving this entity an ID of 0 - we cannot really map it but it needs an id so the system knows it's not a new entity
|
||||
.ForMember(member => member.Id, expression => expression.MapFrom(user => int.MaxValue))
|
||||
.ForMember(member => member.CreateDate, expression => expression.MapFrom(user => user.CreationDate))
|
||||
.ForMember(member => member.UpdateDate, expression => expression.MapFrom(user => user.LastActivityDate))
|
||||
@@ -183,7 +183,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
var url = urlHelper.GetUmbracoApiService<MemberTreeController>(controller => controller.GetTreeNode(display.Key.ToString("N"), null));
|
||||
display.TreeNodeUrl = url;
|
||||
}
|
||||
|
||||
|
||||
var genericProperties = new List<ContentPropertyDisplay>
|
||||
{
|
||||
new ContentPropertyDisplay
|
||||
@@ -234,7 +234,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
|
||||
TabsAndPropertiesResolver.MapGenericProperties(member, display, localizedText, genericProperties);
|
||||
|
||||
|
||||
//check if there's an approval field
|
||||
var provider = membersProvider as global::umbraco.providers.members.UmbracoMembershipProvider;
|
||||
if (member.HasIdentity == false && provider != null)
|
||||
@@ -258,7 +258,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If the membership provider installed is the umbraco membership provider, then we will allow changing the username, however if
|
||||
/// the membership provider is a custom one, we cannot allow chaning the username because MembershipProvider's do not actually natively
|
||||
/// the membership provider is a custom one, we cannot allow chaning the username because MembershipProvider's do not actually natively
|
||||
/// allow that.
|
||||
/// </remarks>
|
||||
internal static ContentPropertyDisplay GetLoginProperty(IMemberService memberService, IMember member, MemberDisplay display, ILocalizedTextService localizedText)
|
||||
@@ -267,11 +267,11 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
Alias = string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
|
||||
Label = localizedText.Localize("login"),
|
||||
Value = display.Username
|
||||
Value = display.Username
|
||||
};
|
||||
|
||||
var scenario = memberService.GetMembershipScenario();
|
||||
|
||||
|
||||
//only allow editing if this is a new member, or if the membership provider is the umbraco one
|
||||
if (member.HasIdentity == false || scenario == MembershipScenario.NativeUmbraco)
|
||||
{
|
||||
@@ -287,20 +287,21 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
internal static IDictionary<string, bool> GetMemberGroupValue(string username)
|
||||
{
|
||||
var result = new Dictionary<string, bool>();
|
||||
foreach (var role in Roles.GetAllRoles().Distinct())
|
||||
{
|
||||
var userRoles = username.IsNullOrWhiteSpace() ? null : Roles.GetRolesForUser(username);
|
||||
|
||||
// create a dictionary of all roles (except internal roles) + "false"
|
||||
var result = Roles.GetAllRoles().Distinct()
|
||||
// if a role starts with __umbracoRole we won't show it as it's an internal role used for public access
|
||||
if (role.StartsWith(Constants.Conventions.Member.InternalRolePrefix) == false)
|
||||
{
|
||||
result.Add(role, false);
|
||||
if (username.IsNullOrWhiteSpace()) continue;
|
||||
if (Roles.IsUserInRole(username, role))
|
||||
{
|
||||
result[role] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
.Where(x => x.StartsWith(Constants.Conventions.Member.InternalRolePrefix) == false)
|
||||
.ToDictionary(x => x, x => false);
|
||||
|
||||
// if user has no roles, just return the dictionary
|
||||
if (userRoles == null) return result;
|
||||
|
||||
// else update the dictionary to "true" for the user roles (except internal roles)
|
||||
foreach (var userRole in userRoles.Where(x => x.StartsWith(Constants.Conventions.Member.InternalRolePrefix) == false))
|
||||
result[userRole] = true;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -318,7 +319,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
//remove all membership properties, these values are set with the membership provider.
|
||||
var exclude = defaultProps.Select(x => x.Value.Alias).ToArray();
|
||||
|
||||
|
||||
return source.Properties
|
||||
.Where(x => exclude.Contains(x.Alias) == false)
|
||||
.Select(Mapper.Map<Property, ContentPropertyDto>);
|
||||
@@ -331,7 +332,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This also ensures that the IsLocked out property is readonly when the member is not locked out - this is because
|
||||
/// an admin cannot actually set isLockedOut = true, they can only unlock.
|
||||
/// an admin cannot actually set isLockedOut = true, they can only unlock.
|
||||
/// </remarks>
|
||||
internal class MemberTabsAndPropertiesResolver : TabsAndPropertiesResolver
|
||||
{
|
||||
@@ -370,7 +371,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
isLockedOutProperty.Value = _localizedTextService.Localize("general/no");
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -386,10 +387,10 @@ namespace Umbraco.Web.Models.Mapping
|
||||
isLockedOutProperty.Value = _localizedTextService.Localize("general/no");
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +434,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{Constants.Conventions.Member.IsLockedOut, Constants.Conventions.Member.IsLockedOut},
|
||||
{Constants.Conventions.Member.IsApproved, Constants.Conventions.Member.IsApproved},
|
||||
{Constants.Conventions.Member.Comments, Constants.Conventions.Member.Comments}
|
||||
};
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -444,12 +445,12 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{Constants.Conventions.Member.IsLockedOut, umbracoProvider.LockPropertyTypeAlias},
|
||||
{Constants.Conventions.Member.IsApproved, umbracoProvider.ApprovedPropertyTypeAlias},
|
||||
{Constants.Conventions.Member.Comments, umbracoProvider.CommentPropertyTypeAlias}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors.ParameterEditors
|
||||
{
|
||||
[ParameterEditor(Constants.PropertyEditors.MultiNodeTreePickerAlias, "Multiple Content Picker", "contentpicker")]
|
||||
public class MultipleContentPickerParameterEditor : ParameterEditor
|
||||
{
|
||||
public MultipleContentPickerParameterEditor()
|
||||
{
|
||||
Configuration.Add("multiPicker", "1");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,16 @@ namespace Umbraco.Web.Security.Identity
|
||||
{
|
||||
public static class AuthenticationOptionsExtensions
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// When trying to implement an Azure AD B2C provider or other OAuth provider that requires a customized Challenge Result in order to work then
|
||||
/// this must be used.
|
||||
/// </summary>
|
||||
/// <param name="authOptions"></param>
|
||||
/// <param name="authProperties"></param>
|
||||
/// <remarks>
|
||||
/// See: http://issues.umbraco.org/issue/U4-7353
|
||||
/// </remarks>
|
||||
public static void SetSignInChallengeResultCallback(
|
||||
this AuthenticationOptions authOptions,
|
||||
Func<IOwinContext, AuthenticationProperties> authProperties)
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
using Umbraco.Core.Services;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// This will execute after upgrading to rebuild the xml cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cannot execute as part of a db migration since we need access to the services/repos.
|
||||
///
|
||||
/// This will execute for specific versions -
|
||||
///
|
||||
/// * If current is less than or equal to 7.0.0
|
||||
/// </remarks>
|
||||
public class RebuildMediaXmlCacheAfterUpgrade : MigrationStartupHander
|
||||
{
|
||||
protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e)
|
||||
{
|
||||
if (e.ProductName != GlobalSettings.UmbracoMigrationName) return;
|
||||
|
||||
var target70 = new Version(7, 0, 0);
|
||||
|
||||
if (e.ConfiguredVersion <= target70)
|
||||
{
|
||||
var mediasvc = (MediaService)ApplicationContext.Current.Services.MediaService;
|
||||
mediasvc.RebuildXmlStructures();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using umbraco;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
using Umbraco.Core.Services;
|
||||
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
|
||||
|
||||
namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Rebuilds the Xml caches after upgrading.
|
||||
/// This will execute after upgrading to rebuild the xml cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This cannot execute as part of a DB migration since it needs access to services and repositories.</para>
|
||||
/// <para>Executes for:
|
||||
/// - Media Xml : if current is less than, or equal to, 7.0.0 (superceeded by the next rule)
|
||||
/// - Media & Content Xml : if current is less than, or equal to, 7.3.0 - because 7.3.0 adds .Key to cached items
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class RebuildXmlCachesAfterUpgrade : MigrationStartupHander
|
||||
{
|
||||
protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e)
|
||||
{
|
||||
if (e.ProductName != GlobalSettings.UmbracoMigrationName) return;
|
||||
|
||||
var v730 = new Semver.SemVersion(new Version(7, 3, 0));
|
||||
|
||||
var doMedia = e.ConfiguredSemVersion < v730;
|
||||
var doContent = e.ConfiguredSemVersion < v730;
|
||||
|
||||
if (doMedia)
|
||||
{
|
||||
var mediaService = (MediaService) ApplicationContext.Current.Services.MediaService;
|
||||
mediaService.RebuildXmlStructures();
|
||||
|
||||
// note: not re-indexing medias?
|
||||
}
|
||||
|
||||
if (doContent)
|
||||
{
|
||||
// rebuild Xml in database
|
||||
var contentService = (ContentService) ApplicationContext.Current.Services.ContentService;
|
||||
contentService.RebuildXmlStructures();
|
||||
|
||||
// refresh the Xml cache
|
||||
content.Instance.RefreshContentFromDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,14 +97,12 @@
|
||||
<Project>{07fbc26b-2927-4a22-8d96-d644c667fecc}</Project>
|
||||
<Name>UmbracoExamine</Name>
|
||||
</ProjectReference>
|
||||
<Reference Include="AutoMapper, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Reference Include="AutoMapper, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="AutoMapper.Net4, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Reference Include="AutoMapper.Net4, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ClientDependency.Core, Version=1.9.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
@@ -118,8 +116,8 @@
|
||||
<Reference Include="dotless.Core">
|
||||
<HintPath>..\packages\dotless.1.4.1.0\lib\dotless.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Examine, Version=0.1.69.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.69.0\lib\Examine.dll</HintPath>
|
||||
<Reference Include="Examine, Version=0.1.70.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.70.0\lib\Examine.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="HtmlAgilityPack, Version=1.4.9.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
|
||||
@@ -389,6 +387,7 @@
|
||||
<Compile Include="PropertyEditors\DatePreValueEditor.cs" />
|
||||
<Compile Include="PropertyEditors\DateTimePreValueEditor.cs" />
|
||||
<Compile Include="PropertyEditors\DecimalPropertyEditor.cs" />
|
||||
<Compile Include="PropertyEditors\ParameterEditors\MultipleContentPickerParameterEditor.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\ImageCropDataSetConverter.cs" />
|
||||
<Compile Include="PropertyEditors\ValueConverters\ImageCropperValueConverter.cs" />
|
||||
<Compile Include="Routing\RedirectTrackingEventHandler.cs" />
|
||||
@@ -916,7 +915,7 @@
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UI\JavaScript\ServerVariablesParser.cs" />
|
||||
<Compile Include="Strategies\Migrations\RebuildMediaXmlCacheAfterUpgrade.cs" />
|
||||
<Compile Include="Strategies\Migrations\RebuildXmlCachesAfterUpgrade.cs" />
|
||||
<Compile Include="Strategies\PublicAccessEventHandler.cs" />
|
||||
<Compile Include="UI\Bundles\JsApplicationLib.cs" />
|
||||
<Compile Include="UI\Bundles\JsJQueryCore.cs" />
|
||||
@@ -2265,4 +2264,5 @@
|
||||
<!--<PostBuildEvent>xcopy "$(ProjectDir)..\..\lib\*.dll" "$(TargetDir)*.dll" /Y</PostBuildEvent>-->
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
<Import Project="..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets" Condition="Exists('..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets')" />
|
||||
</Project>
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
|
||||
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
|
||||
<package id="ClientDependency" version="1.9.1" targetFramework="net45" />
|
||||
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.69.0" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.70.0" targetFramework="net45" />
|
||||
<package id="HtmlAgilityPack" version="1.4.9" targetFramework="net45" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
|
||||
<package id="Markdown" version="1.14.7" targetFramework="net45" />
|
||||
|
||||
@@ -82,8 +82,8 @@
|
||||
<AssemblyOriginatorKeyFile>..\Solution Items\TheFARM-Public.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Examine, Version=0.1.69.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.69.0\lib\Examine.dll</HintPath>
|
||||
<Reference Include="Examine, Version=0.1.70.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.70.0\lib\Examine.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Examine" version="0.1.69.0" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.70.0" targetFramework="net45" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
|
||||
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
|
||||
</packages>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Examine" version="0.1.69.0" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.70.0" targetFramework="net45" />
|
||||
<package id="HtmlAgilityPack" version="1.4.9" targetFramework="net45" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user