Merge branch 'temp8' into temp8-dirty-tracking-on-variants

This commit is contained in:
Stephan
2018-10-23 10:51:28 +02:00
50 changed files with 1110 additions and 981 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ We recommend running the site with the Visual Studio since you'll be able to rem
### Making code changes
* _[The process for making code changes in v8 is the same as v7](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/docs/CONTRIBUTING.md)_
* _[The process for making code changes in v8 is the same as v7](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md)_
* Any .NET changes you make you just need to compile
* Any Angular/JS changes you make you will need to make sure you are running the Gulp build. Easiest way to do this is from within Visual Studio in the `Task Runner Explorer`. You can find this window by pressing `ctrl + q` and typing in `Task Runner Explorer`. In this window you'll see all Gulp tasks, double click on the `dev` task, this will compile the angular solution and start a file watcher, then any html/js changes you make are automatically built.
* When making js changes, you should have the chrome developer tools open to ensure that cache is disabled
@@ -20,7 +20,8 @@ namespace Umbraco.Core.Manifest
// show: [ // optional, default is always show
// '-content/foo', // hide for content type 'foo'
// '+content/*', // show for all other content types
// '+media/*' // show for all media types
// '+media/*', // show for all media types
// '+role/admin' // show for admin users. Role based permissions will override others.
// ]
// },
// ...
@@ -73,7 +73,7 @@ namespace Umbraco.Core.Persistence
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> Where<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, bool>> predicate, string alias = null)
{
var (s, a) = sql.SqlContext.Visit(predicate, alias);
var (s, a) = sql.SqlContext.VisitDto(predicate, alias);
return sql.Where(s, a);
}
@@ -89,7 +89,7 @@ namespace Umbraco.Core.Persistence
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> Where<TDto1, TDto2>(this Sql<ISqlContext> sql, Expression<Func<TDto1, TDto2, bool>> predicate, string alias1 = null, string alias2 = null)
{
var (s, a) = sql.SqlContext.Visit(predicate, alias1, alias2);
var (s, a) = sql.SqlContext.VisitDto(predicate, alias1, alias2);
return sql.Where(s, a);
}
@@ -321,9 +321,9 @@ namespace Umbraco.Core.Persistence
/// Appends an ORDER BY DESC clause to the Sql statement.
/// </summary>
/// <param name="sql">The Sql statement.</param>
/// <param name="fields">Expression specifying the fields.</param>
/// <param name="fields">Fields.</param>
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> OrderByDescending(this Sql<ISqlContext> sql, params object[] fields)
public static Sql<ISqlContext> OrderByDescending(this Sql<ISqlContext> sql, params string[] fields)
{
return sql.Append("ORDER BY " + string.Join(", ", fields.Select(x => x + " DESC")));
}
@@ -664,7 +664,7 @@ namespace Umbraco.Core.Persistence
/// Adds columns to a SELECT Sql statement.
/// </summary>
/// <param name="sql">The origin sql.</param>
/// <param name="fields">Expression indicating the column to select.</param>
/// <param name="fields">Columns to select.</param>
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> AndSelect(this Sql<ISqlContext> sql, params string[] fields)
{
@@ -688,7 +688,6 @@ namespace Umbraco.Core.Persistence
return sql.Append(", " + string.Join(", ", sql.GetColumns(columnExpressions: fields)));
}
/// <summary>
/// Adds columns to a SELECT Sql statement.
/// </summary>
@@ -19,6 +19,12 @@ namespace Umbraco.Core.Persistence.Repositories
/// <remarks>Current version is first, and then versions are ordered with most recent first.</remarks>
IEnumerable<TEntity> GetAllVersions(int nodeId);
/// <summary>
/// Gets versions.
/// </summary>
/// <remarks>Current version is first, and then versions are ordered with most recent first.</remarks>
IEnumerable<TEntity> GetAllVersionsSlim(int nodeId, int skip, int take);
/// <summary>
/// Gets version identifiers.
/// </summary>
@@ -53,6 +53,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// gets all versions, current first
public abstract IEnumerable<TEntity> GetAllVersions(int nodeId);
// gets all versions, current first
public virtual IEnumerable<TEntity> GetAllVersionsSlim(int nodeId, int skip, int take)
=> GetAllVersions(nodeId).Skip(skip).Take(take);
// gets all version ids, current first
public virtual IEnumerable<int> GetVersionIds(int nodeId, int maxRows)
{
@@ -252,8 +256,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column
// is empty for many nodes) - see: http://issues.umbraco.org/issue/U4-8831
var dbfield = GetQuotedFieldName("umbracoNode", "id");
(dbfield, _) = SqlContext.Visit<NodeDto>(x => x.NodeId); // fixme?!
var (dbfield, _) = SqlContext.VisitDto<NodeDto>(x => x.NodeId);
if (ordering.IsCustomField || !ordering.OrderBy.InvariantEquals("id"))
{
psql.OrderBy(GetAliasedField(dbfield, sql)); // fixme why aliased?
@@ -262,7 +265,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// create prepared sql
// ensure it's single-line as NPoco PagingHelper has issues with multi-lines
psql = Sql(psql.SQL.ToSingleLine(), psql.Arguments);
// replace the magic culture parameter (see DocumentRepository.GetBaseQuery())
if (!ordering.Culture.IsNullOrWhiteSpace())
@@ -353,6 +355,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (ordering.Culture.IsNullOrWhiteSpace())
return GetAliasedField(SqlSyntax.GetFieldName<NodeDto>(x => x.Text), sql);
// "variantName" alias is defined in DocumentRepository.GetBaseQuery
// fixme - what if it is NOT a document but a ... media or whatever?
// previously, we inserted the join+select *here* so we were sure to have it,
// but now that's not the case anymore!
return "variantName";
}
@@ -433,7 +439,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// sort and filter
sql = PreparePageSql(sql, filter, ordering);
// get a page of DTOs and the total count
var pagedResult = Database.Page<TDto>(pageIndex + 1, pageSize, sql);
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
@@ -95,6 +95,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return GetBaseQuery(queryType, true);
}
// gets the COALESCE expression for variant/invariant name
private string VariantNameSqlExpression
=> SqlContext.VisitDto<ContentVersionCultureVariationDto, NodeDto>((ccv, node) => ccv.Name ?? node.Text, "ccv").Sql;
protected virtual Sql<ISqlContext> GetBaseQuery(QueryType queryType, bool current)
{
var sql = SqlContext.Sql();
@@ -116,7 +120,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
r1.Select(documentVersionDto => documentVersionDto.ContentVersionDto))
.Select(documentDto => documentDto.PublishedVersionDto, "pdv", r1 =>
r1.Select(documentVersionDto => documentVersionDto.ContentVersionDto, "pcv")))
.AndSelect(SqlContext.Visit<ContentVersionCultureVariationDto, NodeDto>((ccv, node) => ccv.Name ?? node.Text, "ccv").Sql + " AS variantName");
// select the variant name, coalesce to the invariant name, as "variantName"
.AndSelect(VariantNameSqlExpression + " AS variantName");
break;
}
@@ -135,18 +141,17 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.LeftJoin<ContentVersionDto>(nested =>
nested.InnerJoin<DocumentVersionDto>("pdv")
.On<ContentVersionDto, DocumentVersionDto>((left, right) => left.Id == right.Id && right.Published, "pcv", "pdv"), "pcv")
.On<DocumentDto, ContentVersionDto>((left, right) => left.NodeId == right.NodeId, aliasRight: "pcv");
.On<DocumentDto, ContentVersionDto>((left, right) => left.NodeId == right.NodeId, aliasRight: "pcv")
//the magic [[[ISOCODE]]] will be replaced in ContentRepositoryBase.GetPage() by the current Iso code
sql
// left join on optional culture variation
//the magic "[[[ISOCODE]]]" parameter value will be replaced in ContentRepositoryBase.GetPage() by the actual ISO code
.LeftJoin<ContentVersionCultureVariationDto>(nested =>
nested.InnerJoin<LanguageDto>("lang").On<ContentVersionCultureVariationDto, LanguageDto>((ccv, lang) => ccv.LanguageId == lang.Id && lang.IsoCode == "[[[ISOCODE]]]", "ccv", "lang"), "ccv")
.On<ContentVersionDto, ContentVersionCultureVariationDto>((version, ccv) => version.Id == ccv.VersionId, "pcv", "ccv");
.On<ContentVersionDto, ContentVersionCultureVariationDto>((version, ccv) => version.Id == ccv.VersionId, aliasRight: "ccv");
sql
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
// this would ensure we don't get the published version - keep for reference
//sql
// .WhereAny(
@@ -157,7 +162,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (current)
sql.Where<ContentVersionDto>(x => x.Current); // always get the current version
return sql;
}
@@ -216,6 +220,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return MapDtosToContent(Database.Fetch<DocumentDto>(sql), true);
}
public override IEnumerable<IContent> GetAllVersionsSlim(int nodeId, int skip, int take)
{
var sql = GetBaseQuery(QueryType.Many, false)
.Where<NodeDto>(x => x.NodeId == nodeId)
.OrderByDescending<ContentVersionDto>(x => x.Current)
.AndByDescending<ContentVersionDto>(x => x.VersionDate);
return MapDtosToContent(Database.Fetch<DocumentDto>(sql), true, true);
}
public override IContent GetVersion(int versionId)
{
var sql = GetBaseQuery(QueryType.Single, false)
@@ -246,7 +260,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// however, it's not just so we have access to AddingEntity
// there are tons of things at the end of the methods, that can only work with a true Content
// and basically, the repository requires a Content, not an IContent
var content = (Content)entity;
var content = (Content) entity;
content.AddingEntity();
var publishing = content.PublishedState == PublishedState.Publishing;
@@ -420,7 +434,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// however, it's not just so we have access to AddingEntity
// there are tons of things at the end of the methods, that can only work with a true Content
// and basically, the repository requires a Content, not an IContent
var content = (Content)entity;
var content = (Content) entity;
// check if we need to make any database changes at all
if ((content.PublishedState == PublishedState.Published || content.PublishedState == PublishedState.Unpublished) && !content.IsEntityDirty() && !content.IsAnyUserPropertyDirty())
@@ -707,30 +721,27 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
Sql<ISqlContext> filterSql = null;
// Here we create a default where clause from a temp IContent which will look in the contentVersion table for the content name
// if we are searching in a list view that contains variants, we want to look in the contentVersionCultureVariation table instead.
// The resulting clause will be used in the foreach below to compare against the original clause that comes from the "filter" and if they are the same
// we know that we are searching a list view and the proper where clause will be replaced to look in contentVersionCultureVariation table for the names.
var temp = Query<IContent>().Where(x => x.Name.Contains("foo"));
var clause = temp.GetWhereClauses().First().Item1.Split(' ')[0];
// if we have a filter, map its clauses to an Sql statement
if (filter != null)
{
// if the clause works on "name", we need to swap the field and use the variantName instead,
// so that querying also works on variant content (for instance when searching a listview).
// figure out how the "name" field is going to look like - so we can look for it
var nameField = SqlContext.VisitModelField<IContent>(x => x.Name);
filterSql = Sql();
foreach (var filterClause in filter.GetWhereClauses())
{
// fixme - is this the right way of doing it???
var clauseSql = filterClause.Item1;
var clauseArgs = filterClause.Item2;
//
var where = filterClause.Item1.Split(' ')[0] == clause
// normally, this would be the field alias (variantName) of the coalesce result between ContentVersionCulture and NodeDto names, however
// you can't refer to field alias in a WHERE clause so we have to put the coalesce calculation instead which refers to the original field
? SqlContext.Visit<ContentVersionCultureVariationDto, NodeDto>((ccv, node) => ccv.Name ?? node.Text, "ccv").Sql
: filterClause.Item1;
// replace the name field
// we cannot reference an aliased field in a WHERE clause, so have to repeat the expression here
clauseSql = clauseSql.Replace(nameField, VariantNameSqlExpression);
filterSql.Append(
where.Contains("COALESCE") ? $"AND upper({where}) LIKE upper(@0)" : $"AND ({where})",
filterClause.Item2);
// append the clause
filterSql.Append($"AND ({clauseSql})", clauseArgs);
}
}
@@ -910,7 +921,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return base.ApplySystemOrdering(ref sql, ordering);
}
private IEnumerable<IContent> MapDtosToContent(List<DocumentDto> dtos, bool withCache = false)
private IEnumerable<IContent> MapDtosToContent(List<DocumentDto> dtos, bool withCache = false, bool slim = false)
{
var temps = new List<TempContent<Content>>();
var contentTypes = new Dictionary<int, IContentType>();
@@ -928,7 +939,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var cached = IsolatedCache.GetCacheItem<IContent>(RepositoryCacheKeys.GetKey<IContent>(dto.NodeId));
if (cached != null && cached.VersionId == dto.DocumentVersionDto.ContentVersionDto.Id)
{
content[i] = (Content)cached;
content[i] = (Content) cached;
continue;
}
}
@@ -943,18 +954,21 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var c = content[i] = ContentBaseFactory.BuildEntity(dto, contentType);
// need templates
var templateId = dto.DocumentVersionDto.TemplateId;
if (templateId.HasValue && templateId.Value > 0)
templateIds.Add(templateId.Value);
if (dto.Published)
if (!slim)
{
templateId = dto.PublishedVersionDto.TemplateId;
// need templates
var templateId = dto.DocumentVersionDto.TemplateId;
if (templateId.HasValue && templateId.Value > 0)
templateIds.Add(templateId.Value);
if (dto.Published)
{
templateId = dto.PublishedVersionDto.TemplateId;
if (templateId.HasValue && templateId.Value > 0)
templateIds.Add(templateId.Value);
}
}
// need properties
// need temps, for properties, templates and variations
var versionId = dto.DocumentVersionDto.Id;
var publishedVersionId = dto.Published ? dto.PublishedVersionDto.Id : 0;
var temp = new TempContent<Content>(dto.NodeId, versionId, publishedVersionId, contentType, c)
@@ -965,25 +979,28 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
temps.Add(temp);
}
// load all required templates in 1 query, and index
var templates = _templateRepository.GetMany(templateIds.ToArray())
.ToDictionary(x => x.Id, x => x);
// load all properties for all documents from database in 1 query - indexed by version id
var properties = GetPropertyCollections(temps);
// assign templates and properties
foreach (var temp in temps)
if (!slim)
{
// complete the item
if (temp.Template1Id.HasValue && templates.TryGetValue(temp.Template1Id.Value, out var template))
temp.Content.Template = template;
if (temp.Template2Id.HasValue && templates.TryGetValue(temp.Template2Id.Value, out template))
temp.Content.PublishTemplate = template;
temp.Content.Properties = properties[temp.VersionId];
// load all required templates in 1 query, and index
var templates = _templateRepository.GetMany(templateIds.ToArray())
.ToDictionary(x => x.Id, x => x);
// reset dirty initial properties (U4-1946)
temp.Content.ResetDirtyProperties(false);
// load all properties for all documents from database in 1 query - indexed by version id
var properties = GetPropertyCollections(temps);
// assign templates and properties
foreach (var temp in temps)
{
// complete the item
if (temp.Template1Id.HasValue && templates.TryGetValue(temp.Template1Id.Value, out var template))
temp.Content.Template = template;
if (temp.Template2Id.HasValue && templates.TryGetValue(temp.Template2Id.Value, out template))
temp.Content.PublishTemplate = template;
temp.Content.Properties = properties[temp.VersionId];
// reset dirty initial properties (U4-1946)
temp.Content.ResetDirtyProperties(false);
}
}
// set variations, if varying
@@ -8,12 +8,12 @@ using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics;
namespace Umbraco.Core.Persistence.Repositories.Implement
{
@@ -100,7 +100,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
case QueryType.Many:
sql = sql.Select<ContentDto>(r =>
r.Select(x => x.NodeDto)
.Select(x => x.ContentVersionDto));
.Select(x => x.ContentVersionDto))
// ContentRepositoryBase expects a variantName field to order by name
// for now, just return the plain invariant node name
// fixme media should support variants !!
.AndSelect<NodeDto>(x => Alias(x.Text, "variantName"));
break;
}
@@ -6,12 +6,12 @@ using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using static Umbraco.Core.Persistence.NPocoSqlExtensions.Statics;
namespace Umbraco.Core.Persistence.Repositories.Implement
{
@@ -114,9 +114,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
case QueryType.Single:
case QueryType.Many:
sql = sql.Select<MemberDto>(r =>
r.Select(x => x.ContentVersionDto)
.Select(x => x.ContentDto, r1 =>
r1.Select(x => x.NodeDto)));
r.Select(x => x.ContentVersionDto)
.Select(x => x.ContentDto, r1 =>
r1.Select(x => x.NodeDto)))
// ContentRepositoryBase expects a variantName field to order by name
// so get it here, though for members it's just the plain node name
.AndSelect<NodeDto>(x => Alias(x.Text, "variantName"));
break;
}
@@ -17,11 +17,11 @@ namespace Umbraco.Core.Persistence
/// <param name="expression">An expression to visit.</param>
/// <param name="alias">An optional table alias.</param>
/// <returns>A SQL statement, and arguments, corresponding to the expression.</returns>
public static (string Sql, object[] Args) Visit<TDto>(this ISqlContext sqlContext, Expression<Func<TDto, object>> expression, string alias = null)
public static (string Sql, object[] Args) VisitDto<TDto>(this ISqlContext sqlContext, Expression<Func<TDto, object>> expression, string alias = null)
{
var expresionist = new PocoToSqlExpressionVisitor<TDto>(sqlContext, alias);
var visited = expresionist.Visit(expression);
return (visited, expresionist.GetSqlParameters());
var visitor = new PocoToSqlExpressionVisitor<TDto>(sqlContext, alias);
var visited = visitor.Visit(expression);
return (visited, visitor.GetSqlParameters());
}
/// <summary>
@@ -33,11 +33,11 @@ namespace Umbraco.Core.Persistence
/// <param name="expression">An expression to visit.</param>
/// <param name="alias">An optional table alias.</param>
/// <returns>A SQL statement, and arguments, corresponding to the expression.</returns>
public static (string Sql, object[] Args) Visit<TDto, TOut>(this ISqlContext sqlContext, Expression<Func<TDto, TOut>> expression, string alias = null)
public static (string Sql, object[] Args) VisitDto<TDto, TOut>(this ISqlContext sqlContext, Expression<Func<TDto, TOut>> expression, string alias = null)
{
var expresionist = new PocoToSqlExpressionVisitor<TDto>(sqlContext, alias);
var visited = expresionist.Visit(expression);
return (visited, expresionist.GetSqlParameters());
var visitor = new PocoToSqlExpressionVisitor<TDto>(sqlContext, alias);
var visited = visitor.Visit(expression);
return (visited, visitor.GetSqlParameters());
}
/// <summary>
@@ -50,11 +50,11 @@ namespace Umbraco.Core.Persistence
/// <param name="alias1">An optional table alias for the first DTO.</param>
/// <param name="alias2">An optional table alias for the second DTO.</param>
/// <returns>A SQL statement, and arguments, corresponding to the expression.</returns>
public static (string Sql, object[] Args) Visit<TDto1, TDto2>(this ISqlContext sqlContext, Expression<Func<TDto1, TDto2, object>> expression, string alias1 = null, string alias2 = null)
public static (string Sql, object[] Args) VisitDto<TDto1, TDto2>(this ISqlContext sqlContext, Expression<Func<TDto1, TDto2, object>> expression, string alias1 = null, string alias2 = null)
{
var expresionist = new PocoToSqlExpressionVisitor<TDto1, TDto2>(sqlContext, alias1, alias2);
var visited = expresionist.Visit(expression);
return (visited, expresionist.GetSqlParameters());
var visitor = new PocoToSqlExpressionVisitor<TDto1, TDto2>(sqlContext, alias1, alias2);
var visited = visitor.Visit(expression);
return (visited, visitor.GetSqlParameters());
}
/// <summary>
@@ -68,11 +68,42 @@ namespace Umbraco.Core.Persistence
/// <param name="alias1">An optional table alias for the first DTO.</param>
/// <param name="alias2">An optional table alias for the second DTO.</param>
/// <returns>A SQL statement, and arguments, corresponding to the expression.</returns>
public static (string Sql, object[] Args) Visit<TDto1, TDto2, TOut>(this ISqlContext sqlContext, Expression<Func<TDto1, TDto2, TOut>> expression, string alias1 = null, string alias2 = null)
public static (string Sql, object[] Args) VisitDto<TDto1, TDto2, TOut>(this ISqlContext sqlContext, Expression<Func<TDto1, TDto2, TOut>> expression, string alias1 = null, string alias2 = null)
{
var expresionist = new PocoToSqlExpressionVisitor<TDto1, TDto2>(sqlContext, alias1, alias2);
var visited = expresionist.Visit(expression);
return (visited, expresionist.GetSqlParameters());
var visitor = new PocoToSqlExpressionVisitor<TDto1, TDto2>(sqlContext, alias1, alias2);
var visited = visitor.Visit(expression);
return (visited, visitor.GetSqlParameters());
}
/// <summary>
/// Visit a model expression.
/// </summary>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <param name="sqlContext">An <see cref="ISqlContext"/>.</param>
/// <param name="expression">An expression to visit.</param>
/// <returns>A SQL statement, and arguments, corresponding to the expression.</returns>
public static (string Sql, object[] Args) VisitModel<TModel>(this ISqlContext sqlContext, Expression<Func<TModel, object>> expression)
{
var visitor = new ModelToSqlExpressionVisitor<TModel>(sqlContext.SqlSyntax, sqlContext.Mappers);
var visited = visitor.Visit(expression);
return (visited, visitor.GetSqlParameters());
}
/// <summary>
/// Visit a model expression representing a field.
/// </summary>
/// <typeparam name="TModel">The type of the model.</typeparam>
/// <param name="sqlContext">An <see cref="ISqlContext"/>.</param>
/// <param name="field">An expression to visit, representing a field.</param>
/// <returns>The name of the field.</returns>
public static string VisitModelField<TModel>(this ISqlContext sqlContext, Expression<Func<TModel, object>> field)
{
var (sql, _) = sqlContext.VisitModel(field);
// going to return "<field> = @0"
// take the first part only
var pos = sql.IndexOf(' ');
return sql.Substring(0, pos);
}
}
}
}
+3 -3
View File
@@ -307,9 +307,9 @@ namespace Umbraco.Core.Runtime
throw new BootFailedException("A connection string is configured but Umbraco could not connect to the database.");
}
// if we already know we want to upgrade, no need to look for migrations...
if (_state.Level == RuntimeLevel.Upgrade)
return;
// if we already know we want to upgrade,
// still run EnsureUmbracoUpgradeState to get the states
// (v7 will just get a null state, that's ok)
// else
// look for a matching migration entry - bypassing services entirely - they are not 'up' yet
@@ -134,6 +134,12 @@ namespace Umbraco.Core.Services
/// <remarks>Versions are ordered with current first, then most recent first.</remarks>
IEnumerable<IContent> GetVersions(int id);
/// <summary>
/// Gets all versions of a document.
/// </summary>
/// <remarks>Versions are ordered with current first, then most recent first.</remarks>
IEnumerable<IContent> GetVersionsSlim(int id, int skip, int take);
/// <summary>
/// Gets top versions of a document.
/// </summary>
@@ -461,5 +467,21 @@ namespace Umbraco.Core.Services
IContent CreateAndSave(string name, IContent parent, string contentTypeAlias, int userId = 0);
#endregion
#region Rollback
/// <summary>
/// Rolls back the content to a specific version.
/// </summary>
/// <param name="id">The id of the content node.</param>
/// <param name="versionId">The version id to roll back to.</param>
/// <param name="culture">An optional culture to roll back.</param>
/// <param name="userId">The identifier of the user who is performing the roll back.</param>
/// <remarks>
/// <para>When no culture is specified, all cultures are rolled back.</para>
/// </remarks>
OperationResult Rollback(int id, int versionId, string culture = "*", int userId = 0);
#endregion
}
}
@@ -474,6 +474,19 @@ namespace Umbraco.Core.Services.Implement
}
}
/// <summary>
/// Gets a collection of an <see cref="IContent"/> objects versions by Id
/// </summary>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
public IEnumerable<IContent> GetVersionsSlim(int id, int skip, int take)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
scope.ReadLock(Constants.Locks.ContentTree);
return _documentRepository.GetAllVersionsSlim(id, skip, take);
}
}
/// <summary>
/// Gets a list of all version Ids for the given content item ordered so latest is first
/// </summary>
@@ -2538,5 +2551,68 @@ namespace Umbraco.Core.Services.Implement
}
#endregion
#region Rollback
public OperationResult Rollback(int id, int versionId, string culture = "*", int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
//Get the current copy of the node
var content = GetById(id);
//Get the version
var version = GetVersion(versionId);
//Good ole null checks
if (content == null || version == null)
{
return new OperationResult(OperationResultType.FailedCannot, evtMsgs);
}
//Store the result of doing the save of content for the rollback
OperationResult rollbackSaveResult;
using (var scope = ScopeProvider.CreateScope())
{
var rollbackEventArgs = new RollbackEventArgs<IContent>(content);
//Emit RollingBack event aka before
if (scope.Events.DispatchCancelable(RollingBack, this, rollbackEventArgs))
{
scope.Complete();
return OperationResult.Cancel(evtMsgs);
}
//Copy the changes from the version
content.CopyFrom(version, culture);
//Save the content for the rollback
rollbackSaveResult = Save(content, userId);
//Depending on the save result - is what we log & audit along with what we return
if (rollbackSaveResult.Success == false)
{
//Log the error/warning
Logger.Error<ContentService>("User '{UserId}' was unable to rollback content '{ContentId}' to version '{VersionId}'", userId, id, versionId);
}
else
{
//Emit RolledBack event aka after
rollbackEventArgs.CanCancel = false;
scope.Events.Dispatch(RolledBack, this, rollbackEventArgs);
//Logging & Audit message
Logger.Info<ContentService>("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId);
Audit(AuditType.RollBack, $"Content '{content.Name}' was rolled back to version '{versionId}'", userId, id);
}
scope.Complete();
}
return rollbackSaveResult;
}
#endregion
}
}
@@ -25,6 +25,7 @@ using Umbraco.Core.Services.Implement;
using Umbraco.Tests.Testing;
using Umbraco.Web.PropertyEditors;
using System.Reflection;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Tests.Services
{
@@ -2415,6 +2416,22 @@ namespace Umbraco.Tests.Services
}
Console.WriteLine("-");
var versionsSlim = ServiceContext.ContentService.GetVersionsSlim(page.Id, 0, 50).ToArray();
Assert.AreEqual(5, versionsSlim.Length);
for (var i = 0; i < 5; i++)
{
Console.Write("[{0}] ", i);
Console.WriteLine(versionsSlim[i].UpdateDate.ToString("O").Substring(11));
Console.WriteLine(" fr: {0}", versionsSlim[i].GetUpdateDate("fr")?.ToString("O").Substring(11));
Console.WriteLine(" da: {0}", versionsSlim[i].GetUpdateDate("da")?.ToString("O").Substring(11));
}
Console.WriteLine("-");
// what we do in the controller to get rollback versions
var versionsSlimFr = versionsSlim.Where(x => x.UpdateDate == x.GetUpdateDate("fr")).ToArray();
Assert.AreEqual(3, versionsSlimFr.Length);
// alas, at the moment we do *not* properly track 'dirty' for cultures, meaning
// that we cannot synchronize dates the way we do with publish dates - and so this
// would fail - the version UpdateDate is greater than the cultures'.
@@ -2830,6 +2847,107 @@ namespace Umbraco.Tests.Services
}
}
[Test]
public void Can_Get_Paged_Children_WithFilterAndOrder()
{
var languageService = ServiceContext.LocalizationService;
var langUk = new Language("en-UK") { IsDefault = true };
var langFr = new Language("fr-FR");
var langDa = new Language("da-DK");
languageService.Save(langFr);
languageService.Save(langUk);
languageService.Save(langDa);
var contentTypeService = ServiceContext.ContentTypeService;
var contentType = contentTypeService.Get("umbTextpage");
contentType.Variations = ContentVariation.Culture;
contentTypeService.Save(contentType);
var contentService = ServiceContext.ContentService;
var o = new[] { 2, 1, 3, 0, 4 }; // randomly different
for (var i = 0; i < 5; i++)
{
var contentA = new Content(null, -1, contentType);
contentA.SetCultureName("contentA" + i + "uk", langUk.IsoCode);
contentA.SetCultureName("contentA" + o[i] + "fr", langFr.IsoCode);
contentA.SetCultureName("contentX" + i + "da", langDa.IsoCode);
contentService.Save(contentA);
var contentB = new Content(null, -1, contentType);
contentB.SetCultureName("contentB" + i + "uk", langUk.IsoCode);
contentB.SetCultureName("contentB" + o[i] + "fr", langFr.IsoCode);
contentB.SetCultureName("contentX" + i + "da", langDa.IsoCode);
contentService.Save(contentB);
}
// get all
var list = contentService.GetPagedChildren(-1, 0, 100, out var total).ToList();
Console.WriteLine("ALL");
WriteList(list);
// 10 items (there's already a Home content in there...)
Assert.AreEqual(11, total);
Assert.AreEqual(11, list.Count);
// filter
list = contentService.GetPagedChildren(-1, 0, 100, out total,
SqlContext.Query<IContent>().Where(x => x.Name.Contains("contentX")),
Ordering.By("name", culture: langFr.IsoCode)).ToList();
Assert.AreEqual(0, total);
Assert.AreEqual(0, list.Count);
// filter
list = contentService.GetPagedChildren(-1, 0, 100, out total,
SqlContext.Query<IContent>().Where(x => x.Name.Contains("contentX")),
Ordering.By("name", culture: langDa.IsoCode)).ToList();
Console.WriteLine("FILTER BY NAME da:'contentX'");
WriteList(list);
Assert.AreEqual(10, total);
Assert.AreEqual(10, list.Count);
// filter
list = contentService.GetPagedChildren(-1, 0, 100, out total,
SqlContext.Query<IContent>().Where(x => x.Name.Contains("contentA")),
Ordering.By("name", culture: langFr.IsoCode)).ToList();
Console.WriteLine("FILTER BY NAME fr:'contentA', ORDER ASC");
WriteList(list);
Assert.AreEqual(5, total);
Assert.AreEqual(5, list.Count);
for (var i = 0; i < 5; i++)
Assert.AreEqual("contentA" + i + "fr", list[i].GetCultureName(langFr.IsoCode));
list = contentService.GetPagedChildren(-1, 0, 100, out total,
SqlContext.Query<IContent>().Where(x => x.Name.Contains("contentA")),
Ordering.By("name", direction: Direction.Descending, culture: langFr.IsoCode)).ToList();
Console.WriteLine("FILTER BY NAME fr:'contentA', ORDER DESC");
WriteList(list);
Assert.AreEqual(5, total);
Assert.AreEqual(5, list.Count);
for (var i = 0; i < 5; i++)
Assert.AreEqual("contentA" + (4-i) + "fr", list[i].GetCultureName(langFr.IsoCode));
}
private void WriteList(List<IContent> list)
{
foreach (var content in list)
Console.WriteLine("[{0}] {1} {2} {3} {4}", content.Id, content.Name, content.GetCultureName("en-UK"), content.GetCultureName("fr-FR"), content.GetCultureName("da-DK"));
Console.WriteLine("-");
}
[Test]
public void Can_SaveRead_Variations()
{
@@ -64,6 +64,8 @@ namespace Umbraco.Tests.TestHelpers
internal ScopeProvider ScopeProvider => Current.ScopeProvider as ScopeProvider;
protected ISqlContext SqlContext => Container.GetInstance<ISqlContext>();
public override void SetUp()
{
base.SetUp();
+7 -6
View File
@@ -6,21 +6,22 @@ var wrap = require("gulp-wrap-js");
var sort = require('gulp-sort');
var connect = require('gulp-connect');
var open = require('gulp-open');
const babel = require("gulp-babel");
var babel = require("gulp-babel");
var runSequence = require('run-sequence');
const imagemin = require('gulp-imagemin');
var imagemin = require('gulp-imagemin');
var _ = require('lodash');
var MergeStream = require('merge-stream');
// js
const eslint = require('gulp-eslint');
var eslint = require('gulp-eslint');
//Less + css
var postcss = require('gulp-postcss');
var less = require('gulp-less');
var autoprefixer = require('autoprefixer');
var cssnano = require('cssnano');
var cleanCss = require("gulp-clean-css");
// Documentation
var gulpDocs = require('gulp-ngdocs');
@@ -49,14 +50,14 @@ function processJs(files, out) {
}
function processLess(files, out) {
var processors = [
autoprefixer,
cssnano({zindex: false}),
cssnano({zindex: false})
];
return gulp.src(files)
.pipe(less())
.pipe(cleanCss())
.pipe(postcss(processors))
.pipe(rename(out))
.pipe(gulp.dest(root + targets.css));
@@ -329,7 +330,7 @@ gulp.task('dependencies', function () {
"src": [
"./node_modules/moment/min/moment.min.js",
"./node_modules/moment/min/moment-with-locales.js",
"./node_modules/moment/min/moment-with-locales.min.js",
"./node_modules/moment/min/moment-with-locales.min.js"
],
"base": "./node_modules/moment/min"
},
+109 -80
View File
@@ -1266,7 +1266,7 @@
"array-slice": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
"integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=",
"integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
"dev": true
},
"array-union": {
@@ -1440,7 +1440,7 @@
"base": {
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
"integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=",
"integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
"dev": true,
"requires": {
"cache-base": "^1.0.1",
@@ -1464,7 +1464,7 @@
"is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
"integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
@@ -1473,7 +1473,7 @@
"is-data-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
"integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
@@ -1482,7 +1482,7 @@
"is-descriptor": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
"integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
"dev": true,
"requires": {
"is-accessor-descriptor": "^1.0.0",
@@ -1680,7 +1680,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -1696,7 +1696,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -1822,7 +1822,7 @@
"braces": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
"integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=",
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
"dev": true,
"requires": {
"arr-flatten": "^1.1.0",
@@ -1948,7 +1948,7 @@
},
"uuid": {
"version": "2.0.3",
"resolved": "http://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=",
"dev": true
},
@@ -2014,7 +2014,7 @@
"cache-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
"integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=",
"integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
"dev": true,
"requires": {
"collection-visit": "^1.0.0",
@@ -2225,7 +2225,7 @@
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
"integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=",
"integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
"dev": true,
"requires": {
"arr-union": "^3.1.0",
@@ -2245,6 +2245,23 @@
}
}
},
"clean-css": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
"integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
"dev": true,
"requires": {
"source-map": "~0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@@ -2458,7 +2475,7 @@
},
"commander": {
"version": "2.8.1",
"resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
"dev": true,
"requires": {
@@ -2700,7 +2717,7 @@
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
"dev": true
},
"convert-source-map": {
@@ -3114,7 +3131,7 @@
},
"readable-stream": {
"version": "1.0.34",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"dev": true,
"requires": {
@@ -3211,7 +3228,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -3227,7 +3244,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -3318,7 +3335,7 @@
},
"readable-stream": {
"version": "1.0.34",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"dev": true,
"requires": {
@@ -3379,7 +3396,7 @@
},
"readable-stream": {
"version": "1.0.34",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"dev": true,
"requires": {
@@ -3439,7 +3456,7 @@
},
"readable-stream": {
"version": "1.0.34",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"dev": true,
"requires": {
@@ -3532,7 +3549,7 @@
"define-property": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
"integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=",
"integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
"dev": true,
"requires": {
"is-descriptor": "^1.0.2",
@@ -3542,7 +3559,7 @@
"is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
"integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
@@ -3551,7 +3568,7 @@
"is-data-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
"integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
@@ -3560,7 +3577,7 @@
"is-descriptor": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
"integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
"dev": true,
"requires": {
"is-accessor-descriptor": "^1.0.0",
@@ -3828,7 +3845,7 @@
},
"readable-stream": {
"version": "1.0.34",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"dev": true,
"requires": {
@@ -3925,7 +3942,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -3941,7 +3958,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -4034,7 +4051,7 @@
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
"integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=",
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
"dev": true,
"requires": {
"once": "^1.4.0"
@@ -4048,7 +4065,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -4064,7 +4081,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -4246,7 +4263,7 @@
"errno": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
"integrity": "sha1-RoTXF3mtOa8Xfj8AeZb3xnyFJhg=",
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
"dev": true,
"optional": true,
"requires": {
@@ -4955,7 +4972,7 @@
"extglob": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
"integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=",
"integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
"dev": true,
"requires": {
"array-unique": "^0.3.2",
@@ -4989,7 +5006,7 @@
"is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
"integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
@@ -4998,7 +5015,7 @@
"is-data-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
"integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
@@ -5007,7 +5024,7 @@
"is-descriptor": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
"integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
"dev": true,
"requires": {
"is-accessor-descriptor": "^1.0.0",
@@ -6042,7 +6059,7 @@
},
"get-stream": {
"version": "3.0.0",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true,
"optional": true
@@ -6272,7 +6289,7 @@
"global-modules": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
"integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=",
"integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
"dev": true,
"requires": {
"global-prefix": "^1.0.1",
@@ -6384,7 +6401,7 @@
},
"got": {
"version": "5.7.1",
"resolved": "http://registry.npmjs.org/got/-/got-5.7.1.tgz",
"resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz",
"integrity": "sha1-X4FjWmHkplifGAVp6k44FoClHzU=",
"dev": true,
"requires": {
@@ -6422,7 +6439,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -6510,6 +6527,18 @@
}
}
},
"gulp-clean-css": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-3.10.0.tgz",
"integrity": "sha512-7Isf9Y690o/Q5MVjEylH1H7L8WeZ89woW7DnhD5unTintOdZb67KdOayRgp9trUFo+f9UyJtuatV42e/+kghPg==",
"dev": true,
"requires": {
"clean-css": "4.2.1",
"plugin-error": "1.0.1",
"through2": "2.0.3",
"vinyl-sourcemaps-apply": "0.2.1"
}
},
"gulp-concat": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz",
@@ -6588,7 +6617,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -6604,7 +6633,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -6921,7 +6950,7 @@
},
"lodash": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz",
"resolved": "http://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz",
"integrity": "sha1-W3cjA03aTSYuWkb7LFjXzCL3FCA=",
"dev": true
},
@@ -8015,7 +8044,7 @@
"is-absolute": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
"integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=",
"integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
"dev": true,
"requires": {
"is-relative": "^1.0.0",
@@ -8119,7 +8148,7 @@
"is-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
"integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=",
"integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
"dev": true,
"requires": {
"is-accessor-descriptor": "^0.1.6",
@@ -8130,7 +8159,7 @@
"kind-of": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
"integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=",
"integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
"dev": true
}
}
@@ -8261,7 +8290,7 @@
},
"is-obj": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"dev": true
},
@@ -8353,7 +8382,7 @@
"is-relative": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
"integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=",
"integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
"dev": true,
"requires": {
"is-unc-path": "^1.0.0"
@@ -8410,7 +8439,7 @@
"is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
"integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=",
"integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
"dev": true,
"requires": {
"unc-path-regex": "^0.1.2"
@@ -8437,7 +8466,7 @@
"is-windows": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
"integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=",
"integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
"dev": true
},
"is-zip": {
@@ -8946,7 +8975,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -8962,7 +8991,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -9055,7 +9084,7 @@
"livereload-js": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.3.0.tgz",
"integrity": "sha1-w6si6Kr1vzUF2A0JjLrWdyZUjJo=",
"integrity": "sha512-j1R0/FeGa64Y+NmqfZhyoVRzcFlOZ8sNlKzHjh4VvLULFACZhn68XrX5DFg2FhMvSMJmROuFxRSa560ECWKBMg==",
"dev": true
},
"load-json-file": {
@@ -9293,7 +9322,7 @@
"lodash.merge": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
"integrity": "sha1-rcJdnLmbk5HFliTzefu6YNcRHVQ=",
"integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==",
"dev": true
},
"lodash.partialright": {
@@ -9712,7 +9741,7 @@
"make-iterator": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
"integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=",
"integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
"dev": true,
"requires": {
"kind-of": "^6.0.2"
@@ -9871,7 +9900,7 @@
"micromatch": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
"integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=",
"integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
"dev": true,
"requires": {
"arr-diff": "^4.0.0",
@@ -9934,7 +9963,7 @@
"mixin-deep": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
"integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=",
"integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
"dev": true,
"requires": {
"for-in": "^1.0.2",
@@ -9944,7 +9973,7 @@
"is-extendable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
"integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=",
"integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
"dev": true,
"requires": {
"is-plain-object": "^2.0.4"
@@ -14070,7 +14099,7 @@
"promise": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
"integrity": "sha1-BktyYCsY+Q8pGSuLG8QY/9Hr078=",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"dev": true,
"optional": true,
"requires": {
@@ -14283,7 +14312,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -14299,7 +14328,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -14506,7 +14535,7 @@
"regex-not": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
"integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=",
"integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
"dev": true,
"requires": {
"extend-shallow": "^3.0.2",
@@ -14742,7 +14771,7 @@
"ret": {
"version": "0.1.15",
"resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
"integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=",
"integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
"dev": true
},
"right-align": {
@@ -15050,7 +15079,7 @@
"set-value": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
"integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=",
"integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
@@ -15155,7 +15184,7 @@
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
"integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=",
"integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
"dev": true,
"requires": {
"base": "^0.11.1",
@@ -15191,7 +15220,7 @@
"snapdragon-node": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
"integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=",
"integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
"dev": true,
"requires": {
"define-property": "^1.0.0",
@@ -15211,7 +15240,7 @@
"is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
"integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=",
"integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
@@ -15220,7 +15249,7 @@
"is-data-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
"integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=",
"integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
"dev": true,
"requires": {
"kind-of": "^6.0.0"
@@ -15229,7 +15258,7 @@
"is-descriptor": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
"integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=",
"integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
"dev": true,
"requires": {
"is-accessor-descriptor": "^1.0.0",
@@ -15242,7 +15271,7 @@
"snapdragon-util": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
"integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=",
"integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
"dev": true,
"requires": {
"kind-of": "^3.2.0"
@@ -15441,7 +15470,7 @@
"split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
"integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=",
"integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
"dev": true,
"requires": {
"extend-shallow": "^3.0.0"
@@ -15567,7 +15596,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -15583,7 +15612,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -15758,7 +15787,7 @@
},
"strip-dirs": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz",
"resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz",
"integrity": "sha1-lgu9EoeETzl1pFWKoQOoJV4kVqA=",
"dev": true,
"requires": {
@@ -15787,7 +15816,7 @@
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
@@ -15948,7 +15977,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -16265,7 +16294,7 @@
"to-regex": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
"integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=",
"integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
"dev": true,
"requires": {
"define-property": "^2.0.2",
@@ -16365,7 +16394,7 @@
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
"integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=",
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
"dev": true,
"requires": {
"media-typer": "0.3.0",
@@ -16713,7 +16742,7 @@
"vendors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz",
"integrity": "sha1-f8te759WI7FWvOqJ7DfWNnbyGAE=",
"integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==",
"dev": true
},
"verror": {
@@ -16770,7 +16799,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -16786,7 +16815,7 @@
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
"safe-buffer": "~5.1.0"
@@ -16987,7 +17016,7 @@
"websocket-extensions": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
"integrity": "sha1-XS/yKXcAPsaHpLhwc9+7rBRszyk=",
"integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
"dev": true
},
"when": {
+1
View File
@@ -44,6 +44,7 @@
"@babel/preset-env": "^7.1.0",
"autoprefixer": "^6.5.0",
"bower-installer": "^1.2.0",
"gulp-clean-css": "3.10.0",
"cssnano": "^3.7.6",
"gulp": "^3.9.1",
"gulp-babel": "^8.0.0-beta.2",
@@ -103,6 +103,17 @@
loadContent();
}
}));
evts.push(eventsService.on("editors.content.reload", function (name, args) {
// if this content item uses the updated doc type we need to reload the content item
if(args && args.node && args.node.key === $scope.content.key) {
$scope.page.loading = true;
loadContent().then(function() {
$scope.page.loading = false;
});
}
}));
}
/**
@@ -114,6 +114,22 @@
scope.node.template = templateAlias;
};
scope.openRollback = function() {
var rollback = {
node: scope.node,
submit: function(model) {
const args = { node: scope.node };
eventsService.emit("editors.content.reload", args);
editorService.close();
},
close: function() {
editorService.close();
}
};
editorService.rollback(rollback);
};
function loadAuditTrail() {
//don't load this if it's already done
@@ -808,8 +808,106 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
),
"Failed to create blueprint from content with id " + contentId
);
}
},
/**
* @ngdoc method
* @name umbraco.resources.contentResource#getRollbackVersions
* @methodOf umbraco.resources.contentResource
*
* @description
* Returns an array of previous version id's, given a node id and a culture
*
* ##usage
* <pre>
* contentResource.getRollbackVersions(id, culture)
* .then(function(versions) {
* alert('its here!');
* });
* </pre>
*
* @param {Int} id Id of node
* @param {Int} culture if provided, the results will be for this specific culture/variant
* @returns {Promise} resourcePromise object containing the versions
*
*/
getRollbackVersions: function (contentId, culture) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl("contentApiBaseUrl", "GetRollbackVersions", {
contentId: contentId,
culture: culture
})
),
"Failed to get rollback versions for content item with id " + contentId
);
},
/**
* @ngdoc method
* @name umbraco.resources.contentResource#getRollbackVersion
* @methodOf umbraco.resources.contentResource
*
* @description
* Returns a previous version of a content item
*
* ##usage
* <pre>
* contentResource.getRollbackVersion(versionId, culture)
* .then(function(version) {
* alert('its here!');
* });
* </pre>
*
* @param {Int} versionId The version Id
* @param {Int} culture if provided, the results will be for this specific culture/variant
* @returns {Promise} resourcePromise object containing the version
*
*/
getRollbackVersion: function (versionId, culture) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl("contentApiBaseUrl", "GetRollbackVersion", {
versionId: versionId,
culture: culture
})
),
"Failed to get version for content item with id " + versionId
);
},
/**
* @ngdoc method
* @name umbraco.resources.contentResource#rollback
* @methodOf umbraco.resources.contentResource
*
* @description
* Roll backs a content item to a previous version
*
* ##usage
* <pre>
* contentResource.rollback(contentId, versionId, culture)
* .then(function() {
* alert('its here!');
* });
* </pre>
*
* @param {Int} id Id of node
* @param {Int} versionId The version Id
* @param {Int} culture if provided, the results will be for this specific culture/variant
* @returns {Promise} resourcePromise object
*
*/
rollback: function (contentId, versionId, culture) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl("contentApiBaseUrl", "PostRollbackContent", {
contentId: contentId, versionId:versionId, culture:culture
})
),
"Failed to roll back content item with id " + contentId
);
}
};
}
@@ -188,7 +188,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
hotKey: "ctrl+u",
hotKeyWhenHidden: true,
alias: "unpublish",
addEllipsis: args.content.variants && args.content.variants.length > 1 ? "true" : "false"
addEllipsis: "true"
};
case "SCHEDULE":
//schedule publish - schedule doesn't have a permission letter so
@@ -180,6 +180,25 @@
open(editor);
}
/**
* @ngdoc method
* @name umbraco.services.editorService#rollback
* @methodOf umbraco.services.editorService
*
* @description
* Opens a rollback editor in infinite editing.
* @param {String} editor.node The node to rollback
* @param {Callback} editor.submit Saves, submits, and closes the editor
* @param {Callback} editor.close Closes the editor
* @returns {Object} editor object
*/
function rollback(editor) {
editor.view = "views/common/infiniteeditors/rollback/rollback.html";
editor.size = "small";
open(editor);
}
/**
* @ngdoc method
* @name umbraco.services.editorService#linkPicker
@@ -481,6 +500,7 @@
copy: copy,
move: move,
embed: embed,
rollback: rollback,
linkPicker: linkPicker,
mediaPicker: mediaPicker,
iconPicker: iconPicker,
@@ -104,7 +104,7 @@
}
.umb-button--xs {
padding: 5px 16px;
padding: 5px 13px;
font-size: 14px;
}
@@ -8,6 +8,9 @@
.umb-box-header {
padding: 10px 20px;
border-bottom: 1px solid @gray-9;
display: flex;
align-items: center;
justify-content: space-between;
}
.umb-box-header-title {
@@ -1,18 +1,13 @@
.umb-user-cards {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: -10px;
display: grid;
grid-gap: 20px;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
.umb-user-card {
padding: 10px;
box-sizing: border-box;
flex: 0 0 100%;
max-width: 100%;
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
.umb-user-card:hover,
@@ -21,49 +16,6 @@
text-decoration: none !important;
}
@media (min-width: 768px) {
.umb-user-card {
flex: 0 0 50%;
max-width: 50%;
}
}
@media (min-width: 1200px) {
.umb-user-card {
flex: 0 0 33.33%;
max-width: 33.33%;
}
}
@media (min-width: 1400px) {
.umb-user-card {
flex: 0 0 25%;
max-width: 25%;
}
}
@media (min-width: 1700px) {
.umb-user-card {
flex: 0 0 20%;
max-width: 20%;
}
}
@media (min-width: 1900px) {
.umb-user-card {
flex: 0 0 16.66%;
max-width: 16.66%;
}
}
@media (min-width: 2200px) {
.umb-user-card {
flex: 0 0 14.28%;
max-width: 14.28%;
}
}
.umb-user-card__content {
position: relative;
padding: 15px;
@@ -2,7 +2,12 @@
// Container styles
// --------------------------------------------------
.umb-property-editor {
min-width:66.6%;
@media (max-width: 800px) {
width: 100%;
}
@media (min-width: 800px) {
min-width:66.6%;
}
&-pull {
float:left;
@@ -7,6 +7,8 @@
.ws-normal { white-space: normal; }
.nowrap { white-space: nowrap; }
.pre { white-space: pre; }
.pre-wrap { white-space: pre-wrap; }
.pre-line { white-space: pre-line; }
.truncate {
white-space: nowrap;
@@ -0,0 +1,177 @@
(function () {
"use strict";
function RollbackController($scope, contentResource, localizationService, assetsService) {
var vm = this;
vm.rollback = rollback;
vm.changeLanguage = changeLanguage;
vm.changeVersion = changeVersion;
vm.submit = submit;
vm.close = close;
//////////
function onInit() {
vm.loading = true;
vm.variantVersions = [];
vm.diff = null;
vm.currentVersion = null;
vm.rollbackButtonDisabled = true;
// find the current version for invariant nodes
if($scope.model.node.variants.length === 1) {
vm.currentVersion = $scope.model.node.variants[0];
}
// find the current version for nodes with variants
if($scope.model.node.variants.length > 1) {
var active = _.find($scope.model.node.variants, function (v) {
return v.active;
});
// preselect the language in the dropdown
if(active) {
vm.selectedLanguage = active;
vm.currentVersion = active;
}
}
// set default title
if(!$scope.model.title) {
localizationService.localize("actions_rollback").then(function(value){
$scope.model.title = value;
});
}
// Load in diff library
assetsService.loadJs('lib/jsdiff/diff.min.js', $scope).then(function () {
getVersions().then(function(){
vm.loading = false;
});
});
}
function changeLanguage(language) {
vm.currentVersion = language;
getVersions();
}
function changeVersion(version) {
if(version && version.versionId) {
const culture = $scope.model.node.variants.length > 1 ? vm.currentVersion.language.culture : null;
contentResource.getRollbackVersion(version.versionId, culture)
.then(function(data){
vm.previousVersion = data;
vm.previousVersion.versionId = version.versionId;
createDiff(vm.currentVersion, vm.previousVersion);
vm.rollbackButtonDisabled = false;
});
} else {
vm.diff = null;
vm.rollbackButtonDisabled = true;
}
}
function getVersions() {
const nodeId = $scope.model.node.id;
const culture = $scope.model.node.variants.length > 1 ? vm.currentVersion.language.culture : null;
return contentResource.getRollbackVersions(nodeId, culture)
.then(function(data){
vm.previousVersions = data.map(version => {
version.displayValue = version.versionDate + " - " + version.versionAuthorName;
return version;
});
});
}
/**
* This will load in a new version
*/
function createDiff(currentVersion, previousVersion) {
vm.diff = {};
vm.diff.properties = [];
// find diff in name
vm.diff.name = JsDiff.diffWords(currentVersion.name, previousVersion.name);
// extract all properties from the tabs and create new object for the diff
currentVersion.tabs.forEach((tab, tabIndex) => {
tab.properties.forEach((property, propertyIndex) => {
var oldProperty = previousVersion.tabs[tabIndex].properties[propertyIndex];
// we have to make properties storing values as object into strings (Grid, nested content, etc.)
if(property.value instanceof Object) {
property.value = JSON.stringify(property.value, null, 1);
property.isObject = true;
}
if(oldProperty.value instanceof Object) {
oldProperty.value = JSON.stringify(oldProperty.value, null, 1);
oldProperty.isObject = true;
}
// create new property object used in the diff table
var diffProperty = {
"alias": property.alias,
"label": property.label,
"diff": (property.value || oldProperty.value) ? JsDiff.diffWords(property.value, oldProperty.value) : "",
"isObject": (property.isObject || oldProperty.isObject) ? true : false
};
vm.diff.properties.push(diffProperty);
});
});
}
function rollback() {
vm.rollbackButtonState = "busy";
const nodeId = $scope.model.node.id;
const versionId = vm.previousVersion.versionId;
const culture = $scope.model.node.variants.length > 1 ? vm.currentVersion.language.culture : null;
return contentResource.rollback(nodeId, versionId, culture)
.then(data => {
vm.rollbackButtonState = "success";
submit();
}, error => {
vm.rollbackButtonState = "error";
});
}
function submit() {
if($scope.model.submit) {
$scope.model.submit($scope.model.submit);
}
}
function close() {
if($scope.model.close) {
$scope.model.close();
}
}
onInit();
}
angular.module("umbraco").controller("Umbraco.Editors.RollbackController", RollbackController);
})();
@@ -0,0 +1,104 @@
<div ng-controller="Umbraco.Editors.RollbackController as vm">
<umb-editor-view>
<umb-editor-header
name="model.title"
name-locked="true"
hide-alias="true"
hide-icon="true"
hide-description="true">
</umb-editor-header>
<umb-editor-container>
<umb-load-indicator
ng-if="vm.loading">
</umb-load-indicator>
<umb-box>
<umb-box-content>
<div ng-if="model.node.variants.length > 1">
<h5><localize key="general_language"></localize></h5>
<select
class="input-block-level"
ng-model="vm.selectedLanguage"
ng-options="variant as variant.language.name for variant in model.node.variants track by variant.language.culture"
ng-change="vm.changeLanguage(vm.selectedLanguage)">
</select>
</div>
<div>
<h5><localize key="rollback_currentVersion"></localize></h5>
<p>{{vm.currentVersion.name}} (Created: {{vm.currentVersion.createDate}})</p>
<h5><localize key="rollback_rollbackTo"></localize></h5>
<select
class="input-block-level"
ng-model="vm.selectedVersion"
ng-options="version.displayValue for version in vm.previousVersions track by version.versionId"
ng-change="vm.changeVersion(vm.selectedVersion)">
<option value=""><localize key="general_choose">Choose</localize>...</option>
</select>
</div>
<div ng-if="vm.diff" class="diff" style="border-top: 1px solid #e9e9eb; margin-top: 30px;">
<h5>Changes</h5>
<small style="margin-bottom: 15px; display: block;"><localize key="rollback_diffHelp"></localize></small>
<table class="table table-condensed table-bordered">
<tbody>
<tr>
<td class="bold">Name</td>
<td>
<span ng-repeat="part in vm.diff.name">
<ins ng-if="part.added">{{part.value}}</ins>
<del ng-if="part.removed">{{part.value}}</del>
<span ng-if="!part.added && !part.removed">{{part.value}}</span>
</span>
</td>
</tr>
<tr ng-repeat="property in vm.diff.properties track by property.alias">
<td class="bold">{{property.label}}</td>
<td ng-class="{'pre-line': property.isObject}">
<span ng-repeat="part in property.diff">
<ins ng-if="part.added">{{part.value}}</ins>
<del ng-if="part.removed">{{part.value}}</del>
<span ng-if="!part.added && !part.removed">{{part.value}}</span>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</umb-box-content>
</umb-box>
</umb-editor-container>
<umb-editor-footer>
<umb-editor-footer-content-right>
<umb-button
type="button"
button-style="link"
label-key="general_close"
action="vm.close()">
</umb-button>
<umb-button
type="button"
button-style="success"
state="vm.rollbackButtonState"
label-key="actions_rollback"
disabled="vm.rollbackButtonDisabled"
action="vm.rollback()">
</umb-button>
</umb-editor-footer-content-right>
</umb-editor-footer>
</umb-editor-view>
</div>
@@ -41,7 +41,19 @@
</umb-box>
<umb-box data-element="node-info-history">
<umb-box-header title-key="general_history"></umb-box-header>
<umb-box-header
title-key="general_history">
<umb-button
type="button"
button-style="outline"
action="openRollback()"
label-key="actions_rollback"
size="xs"
add-ellipsis="true">
</umb-button>
</umb-box-header>
<umb-box-content class="block-form">
<div style="position: relative;">
@@ -1,10 +1,13 @@
<div class="umb-box-header" ng-transclude>
<div class="umb-box-header-title" ng-if="title || titleKey">
<localize ng-if="titleKey" key="{{titleKey}}"></localize>
<span ng-if="title">{{title}}</span>
</div>
<div class="umb-box-header-description" ng-if="description || descriptionKey">
<localize ng-if="descriptionKey" key="{{descriptionKey}}"></localize>
<span ng-if="description">{{description}}</span>
<div class="umb-box-header">
<div>
<div class="umb-box-header-title" ng-if="title || titleKey">
<localize ng-if="titleKey" key="{{titleKey}}"></localize>
<span ng-if="title">{{title}}</span>
</div>
<div class="umb-box-header-description" ng-if="description || descriptionKey">
<localize ng-if="descriptionKey" key="{{descriptionKey}}"></localize>
<span ng-if="description">{{description}}</span>
</div>
</div>
<ng-transclude></ng-transclude>
</div>
@@ -1,3 +1,3 @@
<div class="umb-property-editor" ng-class="{'-not-clickable': preview}">
<div class="umb-property-editor db" ng-class="{'-not-clickable': preview}">
<div ng-include="propertyEditorView"></div>
</div>
@@ -1,117 +0,0 @@
(function () {
"use strict";
function ContentRollbackController($scope, assetsService) {
var vm = this;
vm.rollback = rollback;
vm.loadVersion = loadVersion;
vm.closeDialog = closeDialog;
function onInit() {
vm.loading = true;
vm.variantVersions = [];
vm.diff = null;
// Load in diff library
assetsService.loadJs('lib/jsdiff/diff.min.js', $scope).then(function () {
var currentLanguage = $scope.currentNode.metaData.culture;
vm.currentVersion = {
"id": 1,
"createDate": "22/08/2018 13.32",
"name": "Forside",
"properties": [
{
"alias": "headline",
"label": "Headline",
"value": "Velkommen"
},
{
"alias": "text",
"label": "Text",
"value": "This is my danish Content"
}
]
};
vm.previousVersions = [
{
"id": 2,
"name": "Forside",
"createDate": "21/08/2018 19.25"
}
];
vm.loading = false;
});
}
function rollback() {
console.log("rollback");
}
/**
* This will load in a new version
*/
function loadVersion(id) {
// fake load version
var currentLanguage = $scope.currentNode.metaData.culture;
vm.diff = {};
vm.diff.properties = [];
var oldVersion = {
"id": 2,
"name": "Foride",
"properties": [
{
"alias": "headline",
"label": "Headline",
"value": ""
},
{
"alias": "text",
"label": "Text",
"value": "This is my danish Content Test"
}
]
};
// find diff in name
vm.diff.name = JsDiff.diffWords(vm.currentVersion.name, oldVersion.name);
// find diff in properties
angular.forEach(vm.currentVersion.properties,
function(newProperty, index){
var oldProperty = oldVersion.properties[index];
var diffProperty = {
"alias": newProperty.alias,
"label": newProperty.label,
"diff": JsDiff.diffWords(newProperty.value, oldProperty.value)
};
vm.diff.properties.push(diffProperty);
});
}
/**
* This will close the dialog
*/
function closeDialog() {
$scope.nav.hideDialog();
}
onInit();
}
angular.module("umbraco").controller("Umbraco.Editors.Content.RollbackController", ContentRollbackController);
})();
@@ -25,7 +25,7 @@
<span>{{ variant.language.name }}</span>
</label>
<div ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<div class="umb-permission__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
@@ -1,80 +0,0 @@
<div ng-controller="Umbraco.Editors.Content.RollbackController as vm">
<div class="umb-dialog-body" ng-cloak>
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
<div class="umb-pane" ng-show="!vm.loading">
<div>
<h5>Rollback {{ currentNode.name }}</h5>
<h5><localize key="rollback_currentVersion"></localize></h5>
<p>{{vm.currentVersion.name}} (Created: {{vm.currentVersion.createDate}})</p>
<h5><localize key="rollback_rollbackTo"></localize></h5>
<select
class="input-block-level"
ng-model="vm.selectedVersion"
ng-options="version.createDate for version in vm.previousVersions track by version.id"
ng-change="vm.loadVersion(vm.selectedVersion.id)">
<option value=""><localize key="general_choose">Choose</localize>...</option>
</select>
</div>
<div ng-if="vm.diff" class="diff" style="border-top: 1px solid #e9e9eb; margin-top: 30px;">
<h5>Changes</h5>
<small style="margin-bottom: 15px; display: block;"><localize key="rollback_diffHelp"></localize></small>
<table class="table table-condensed table-bordered">
<tbody>
<tr>
<td class="bold">Name</td>
<td>
<span ng-repeat="part in vm.diff.name">
<ins ng-if="part.added">{{part.value}}</ins>
<del ng-if="part.removed">{{part.value}}</del>
<span ng-if="!part.added && !part.removed">{{part.value}}</span>
</span>
</td>
</tr>
<tr ng-repeat="property in vm.diff.properties track by property.alias">
<td class="bold">{{property.label}}</td>
<td>
<span ng-repeat="part in property.diff">
<ins ng-if="part.added">{{part.value}}</ins>
<del ng-if="part.removed">{{part.value}}</del>
<span ng-if="!part.added && !part.removed">{{part.value}}</span>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<umb-button
label-key="general_close"
disabled="vm.saveState === 'busy'"
action="vm.closeDialog()"
type="button"
button-style="link">
</umb-button>
<umb-button
label-key="buttons_rollback"
disabled="vm.saveState === 'busy'"
state="vm.saveState"
action="vm.rollback()"
type="button"
button-style="success">
</umb-button>
</div>
</div>
@@ -31,6 +31,7 @@
<thead>
<tr>
<th>Language</th>
<th>ISO</th>
<th>Default</th>
<th>Mandatory</th>
<th>Fallback</th>
@@ -42,6 +43,9 @@
<i class="icon-globe" style="color: #BBBABF; margin-right: 5px;"></i>
<span class="bold">{{ language.name }}</span>
</td>
<td>
<span>{{ language.culture }}</span>
</td>
<td>
<umb-checkmark
ng-if="language.isDefault"
@@ -262,13 +262,12 @@
</umb-button>
</div>
<umb-button type="button" ng-if="model.user.userDisplayState.key !== 'Invited'"
<umb-button type="button" ng-if="model.changePasswordModel.isChanging === false && model.user.userDisplayState.key !== 'Invited'"
button-style="[info,block]"
action="model.toggleChangePassword()"
label="Change password"
label-key="general_changePassword"
state="changePasswordButtonState"
ng-if="model.changePasswordModel.isChanging === false"
size="s">
</umb-button>
+1
View File
@@ -349,6 +349,7 @@
<Content Include="Umbraco\Developer\Packages\editPackage.aspx" />
<Content Include="Umbraco\Dialogs\protectPage.aspx" />
<Content Include="Umbraco\Dialogs\rollBack.aspx" />
<Content Include="Umbraco\Dialogs\sendToTranslation.aspx" />
<Content Include="Umbraco\Config\Create\UI.xml" />
<Content Include="Umbraco\Config\Lang\en.xml">
<SubType>Designer</SubType>
@@ -45,7 +45,7 @@
<div ng-cloak ng-animate="'fade'" id="fact" class="absolute-center clearfix" ng-show="installer.fact">
<h2>Did you know</h2>
<p ng-bind-html-unsafe="installer.fact"></p>
<p ng-bind-html="installer.fact"></p>
</div>
<h3 ng-cloak ng-animate="'fade'" id="feedback" ng-show="installer.feedback">{{installer.feedback}}</h3>
@@ -1,98 +0,0 @@
<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master"AutoEventWireup="True" Inherits="umbraco.presentation.dialogs.rollBack" %>
<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %>
<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %>
<asp:Content ContentPlaceHolderID="head" runat="server">
<script type="text/javascript">
function doSubmit() { document.Form1["ok"].click() }
var functionsFrame = this;
var tabFrame = this;
var isDialog = true;
var submitOnEnter = true;
</script>
<style type="text/css">
.propertyItemheader {
width: 140px !Important;
}
.diff {
margin-top: 10px;
height: 100%;
overflow: auto;
border-top: 1px solid #efefef;
padding: 5px;
}
.diff table td {
border-bottom: 1px solid #ccc;
padding: 3px;
}
.diff del {
background: rgb(255, 230, 230) none repeat scroll 0%;
-moz-background-clip: -moz-initial;
-moz-background-origin: -moz-initial;
-moz-background-inline-policy: -moz-initial;
}
.diff ins {
background: rgb(230, 255, 230) none repeat scroll 0%;
-moz-background-clip: -moz-initial;
-moz-background-origin: -moz-initial;
-moz-background-inline-policy: -moz-initial;
}
.diff .diffnotice {
text-align: center;
margin-bottom: 10px;
}
</style>
</asp:Content>
<asp:Content ContentPlaceHolderID="body" runat="server">
<div class="umb-dialog-body">
<umb:JsInclude ID="JsInclude1" runat="server" FilePath="js/umbracoCheckKeys.js" PathNameAlias="UmbracoRoot" />
<cc1:Feedback ID="feedBackMsg" runat="server" />
<cc1:Pane ID="pp_selectVersion" runat="server" Text="Select a version to compare with the current version">
<cc1:PropertyPanel id="pp_currentVersion" Text="Current version" runat="server">
<asp:Literal ID="currentVersionTitle" runat="server" />
<small>(<asp:Literal ID="currentVersionMeta" runat="server" />)</small></cc1:PropertyPanel>
<cc1:PropertyPanel ID="pp_rollBackTo" Text="Rollback to" runat="server">
<asp:DropDownList OnSelectedIndexChanged="version_load" ID="allVersions" runat="server" Width="400px" AutoPostBack="True" CssClass="guiInputTextTiny" />
</cc1:PropertyPanel>
<cc1:PropertyPanel id="pp_view" Text="View" runat="server">
<small>
<asp:RadioButtonList ID="rbl_mode" runat="server" OnSelectedIndexChanged="version_load" RepeatDirection="Horizontal">
<asp:ListItem Selected="True" Value="diff">Diff</asp:ListItem>
<asp:ListItem Value="html">Html</asp:ListItem>
</asp:RadioButtonList>
</small>
</cc1:PropertyPanel>
</cc1:Pane>
<asp:Panel ID="diffPanel" Visible="false" runat="server" Height="300px">
<div class="diff">
<div class="diffnotice">
<p>
<asp:Literal ID="lt_notice" runat="server" />
</p>
</div>
<table border="0" style="width: 95%;">
<asp:Literal ID="propertiesCompare" runat="server"></asp:Literal>
</table>
</div>
</asp:Panel>
</div>
<div runat="server" id="pl_buttons" class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<a href="#" class="btn btn-link" onclick="UmbClientMgr.closeModalWindow()"><%=Services.TextService.Localize("general/cancel")%></a>
<asp:Button ID="Button1" runat="server" visible="false" CssClass="btn btn-primary" OnClick="doRollback_Click"></asp:Button>
</div>
</asp:Content>
@@ -8,21 +8,21 @@ function UmbracoSpeechBubble(id) {
this.GenerateSpeechBubble();
}
UmbracoSpeechBubble.prototype.GenerateSpeechBubble = function() {
UmbracoSpeechBubble.prototype.GenerateSpeechBubble = function () {
var sbHtml = document.getElementById(this.id);
sbHtml.innerHTML = '' +
'<div class="speechBubbleTop"></div>' +
'<div class="speechBubbleContent">' +
'<img id="' + this.id + 'Icon" style="float: left; margin: 0px 5px 10px 3px;" />' +
' <img class="speechClose" onClick="UmbSpeechBubble.Hide();" id="' + this.id + 'close" src="/umbraco/images/speechBubble/speechBubble_close.gif" width="18" height="18" border="0" alt="Close"' +
' onmouseover="this.src = \'/umbraco/images/speechBubble/speechBubble_close_over.gif\';" onmouseout="this.src=\'images/speechBubble/speechBubble_close.gif\';">' +
' <div style="float: right; width: 186px; margin-right: 10px;"><h3 id="' + this.id + 'Header">The header!</h3>' +
' <p style="width: 185px" id="' + this.id + 'Message">Default Text Container!<br /></p></div><br style="clear: both" />' +
'</div>' +
'<div class="speechBubbleBottom"></div>'
}
'<div class="speechBubbleTop"></div>' +
'<div class="speechBubbleContent">' +
'<img id="' + this.id + 'Icon" style="float: left; margin: 0px 5px 10px 3px;" />' +
' <img class="speechClose" onClick="UmbSpeechBubble.Hide();" id="' + this.id + 'close" src="/umbraco/images/speechBubble/speechBubble_close.gif" width="18" height="18" border="0" alt="Close"' +
' onmouseover="this.src = \'/umbraco/images/speechBubble/speechBubble_close_over.gif\';" onmouseout="this.src=\'images/speechBubble/speechBubble_close.gif\';">' +
' <div style="float: right; width: 186px; margin-right: 10px;"><h3 id="' + this.id + 'Header">The header!</h3>' +
' <p style="width: 185px" id="' + this.id + 'Message">Default Text Container!<br /></p></div><br style="clear: both" />' +
'</div>' +
'<div class="speechBubbleBottom"></div>';
};
UmbracoSpeechBubble.prototype.ShowMessage = function (icon, header, message, dontAutoHide) {
var speechBubble = jQuery("#" + this.id);
@@ -46,7 +46,7 @@ UmbracoSpeechBubble.prototype.ShowMessage = function (icon, header, message, don
jQuery(".speechClose").show();
}
}
}
};
UmbracoSpeechBubble.prototype.Hide = function () {
if (!this.ie) {
@@ -54,7 +54,7 @@ UmbracoSpeechBubble.prototype.Hide = function () {
} else {
jQuery("#" + this.id).hide();
}
}
};
// Initialize
var UmbSpeechBubble = null
@@ -65,4 +65,4 @@ function InitUmbracoSpeechBubble() {
jQuery(document).ready(function() {
InitUmbracoSpeechBubble();
});
});
+10 -1
View File
@@ -10,6 +10,7 @@ using Umbraco.Web.Mvc;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
using Umbraco.Web._Legacy.Actions;
using Umbraco.Web.ContentApps;
// the namespace here is intentional - although defined in Umbraco.Web assembly,
// this class should be visible when using Umbraco.Core.Components, alongside
@@ -32,7 +33,15 @@ namespace Umbraco.Core.Components
/// <returns></returns>
internal static ActionCollectionBuilder Actions(this Composition composition)
=> composition.Container.GetInstance<ActionCollectionBuilder>();
/// <summary>
/// Gets the content apps collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
/// <returns></returns>
public static ContentAppDefinitionCollectionBuilder ContentApps(this Composition composition)
=> composition.Container.GetInstance<ContentAppDefinitionCollectionBuilder>();
/// <summary>
/// Gets the content finders collection builder.
/// </summary>
@@ -1702,5 +1702,93 @@ namespace Umbraco.Web.Editors
Services.NotificationService.SetNotifications(Security.CurrentUser, content, notifyOptions);
}
[HttpGet]
public IEnumerable<RollbackVersion> GetRollbackVersions(int contentId, string culture = null)
{
var rollbackVersions = new List<RollbackVersion>();
var writerIds = new HashSet<int>();
//Return a list of all versions of a specific content node
// fixme - cap at 50 versions for now?
var versions = Services.ContentService.GetVersionsSlim(contentId, 0, 50);
//Not all nodes are variants & thus culture can be null
if (culture != null)
{
//Get cultures that were published with the version = their update date is equal to the version's
versions = versions.Where(x => x.UpdateDate == x.GetUpdateDate(culture));
}
//First item is our current item/state (cant rollback to ourselves)
versions = versions.Skip(1);
foreach (var version in versions)
{
var rollbackVersion = new RollbackVersion
{
VersionId = version.VersionId,
VersionDate = version.UpdateDate,
VersionAuthorId = version.WriterId
};
rollbackVersions.Add(rollbackVersion);
writerIds.Add(version.WriterId);
}
var users = Services.UserService
.GetUsersById(writerIds.ToArray())
.ToDictionary(x => x.Id, x => x.Name);
foreach (var rollbackVersion in rollbackVersions)
{
if (users.TryGetValue(rollbackVersion.VersionAuthorId, out var userName))
rollbackVersion.VersionAuthorName = userName;
}
return rollbackVersions;
}
[HttpGet]
public ContentVariantDisplay GetRollbackVersion(int versionId, string culture = null)
{
var version = Services.ContentService.GetVersion(versionId);
var content = MapToDisplay(version);
return culture == null
? content.Variants.FirstOrDefault() //No culture set - so this is an invariant node - so just list me the first item in here
: content.Variants.FirstOrDefault(x => x.Language.IsoCode == culture);
}
[HttpPost]
public HttpResponseMessage PostRollbackContent(int contentId, int versionId, string culture = "*")
{
var rollbackResult = Services.ContentService.Rollback(contentId, versionId, culture, Security.GetUserId().ResultOr(0));
if (rollbackResult.Success)
return Request.CreateResponse(HttpStatusCode.OK);
var notificationModel = new SimpleNotificationModel();
switch (rollbackResult.Result)
{
case OperationResultType.Failed:
case OperationResultType.FailedCannot:
case OperationResultType.FailedExceptionThrown:
case OperationResultType.NoOperation:
default:
notificationModel.AddErrorNotification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
null); //TODO: There is no specific failed to save error message AFAIK
break;
case OperationResultType.FailedCancelledByEvent:
notificationModel.AddErrorNotification(
Services.TextService.Localize("speechBubbles/operationCancelledHeader"),
Services.TextService.Localize("speechBubbles/operationCancelledText"));
break;
}
return Request.CreateValidationErrorResponse(notificationModel);
}
}
}
@@ -19,13 +19,12 @@ namespace Umbraco.Web.Install.InstallSteps
{
get
{
var currentVersion = UmbracoVersion.Local;
//fixme - in this case there's a db but the version is cleared which is fine and a normal way to force the upgrader
// to execute, but before we would detect the current version via the DB like DatabaseSchemaResult.DetermineInstalledVersion
// what now, do we need to?
if (currentVersion == null)
currentVersion = new Semver.SemVersion(0);
// fixme - if UmbracoVersion.Local is null?
// it means that there is a database but the web.config version is cleared
// that was a "normal" way to force the upgrader to execute, and we would detect the current
// version via the DB like DatabaseSchemaResult.DetermineInstalledVersion - magic, do we really
// need this now?
var currentVersion = (UmbracoVersion.Local ?? new Semver.SemVersion(0)).ToString();
var newVersion = UmbracoVersion.SemanticVersion.ToString();
@@ -0,0 +1,21 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Web.Models.ContentEditing
{
[DataContract(Name = "rollbackVersion", Namespace = "")]
public class RollbackVersion
{
[DataMember(Name = "versionId")]
public int VersionId { get; set; }
[DataMember(Name = "versionDate")]
public DateTime VersionDate { get; set; }
[DataMember(Name = "versionAuthorId")]
public int VersionAuthorId { get; set; }
[DataMember(Name = "versionAuthorName")]
public string VersionAuthorName { get; set; }
}
}
@@ -238,7 +238,6 @@ namespace Umbraco.Web.Trees
AddActionNode<ActionSort>(item, menu, true);
AddActionNode<ActionRollback>(item, menu);
AddActionNode<ActionToPublish>(item, menu, convert: true);
AddActionNode<ActionAssignDomain>(item, menu);
AddActionNode<ActionRights>(item, menu, convert: true);
+2 -13
View File
@@ -147,6 +147,7 @@
<Compile Include="Media\TypeDetector\SvgDetector.cs" />
<Compile Include="Media\TypeDetector\TIFFDetector.cs" />
<Compile Include="Media\UploadAutoFillProperties.cs" />
<Compile Include="Models\ContentEditing\RollbackVersion.cs" />
<Compile Include="Models\ContentEditing\UnpublishContent.cs" />
<Compile Include="Models\Mapping\MappingOperationOptionsExtensions.cs" />
<Compile Include="ContentApps\ContentAppDefinitionCollection.cs" />
@@ -1310,12 +1311,6 @@
<Compile Include="umbraco.presentation\umbraco\developer\Packages\editPackage.aspx.designer.cs">
<DependentUpon>editPackage.aspx</DependentUpon>
</Compile>
<Compile Include="umbraco.presentation\umbraco\dialogs\rollBack.aspx.cs">
<DependentUpon>rollBack.aspx</DependentUpon>
</Compile>
<Compile Include="umbraco.presentation\umbraco\dialogs\rollBack.aspx.designer.cs">
<DependentUpon>rollBack.aspx</DependentUpon>
</Compile>
<Compile Include="umbraco.presentation\umbraco\templateControls\DisableEventValidation.cs" />
<Compile Include="umbraco.presentation\umbraco\templateControls\Item.cs" />
<Compile Include="umbraco.presentation\umbraco\templateControls\ItemRenderer.cs" />
@@ -1400,9 +1395,6 @@
<Content Include="umbraco.presentation\umbraco\developer\Packages\editPackage.aspx">
<SubType>ASPXCodeBehind</SubType>
</Content>
<Content Include="umbraco.presentation\umbraco\dialogs\rollBack.aspx">
<SubType>ASPXCodeBehind</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<None Include="Web References\org.umbraco.update\checkforupgrade.disco" />
@@ -1456,10 +1448,7 @@
<CachedSettingsPropName>umbraco_org_umbraco_update_CheckForUpgrade</CachedSettingsPropName>
</WebReferenceUrl>
</ItemGroup>
<ItemGroup>
<Folder Include="umbraco.presentation\umbraco\settings\" />
<Folder Include="umbraco.presentation\umbraco\translation\" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!--
copied from Microsoft.CSharp.targets
@@ -1,99 +0,0 @@
<%@ Page Language="c#" CodeBehind="rollBack.aspx.cs" MasterPageFile="../masterpages/umbracoDialog.Master" AutoEventWireup="True" Inherits="umbraco.presentation.dialogs.rollBack" %>
<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %>
<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %>
<asp:Content ContentPlaceHolderID="head" runat="server">
<script type="text/javascript">
function doSubmit() { document.Form1["ok"].click() }
var functionsFrame = this;
var tabFrame = this;
var isDialog = true;
var submitOnEnter = true;
</script>
<style type="text/css">
.propertyItemheader {
width: 140px !Important;
}
.diff {
margin-top: 10px;
height: 100%;
overflow: auto;
border-top: 1px solid #ccc;
border-top: 1px solid #ccc;
padding: 5px;
}
.diff table td {
border-bottom: 1px solid #ccc;
padding: 3px;
}
.diff del {
background: rgb(255, 230, 230) none repeat scroll 0%;
-moz-background-clip: -moz-initial;
-moz-background-origin: -moz-initial;
-moz-background-inline-policy: -moz-initial;
}
.diff ins {
background: rgb(230, 255, 230) none repeat scroll 0%;
-moz-background-clip: -moz-initial;
-moz-background-origin: -moz-initial;
-moz-background-inline-policy: -moz-initial;
}
.diff .diffnotice {
text-align: center;
margin-bottom: 10px;
}
</style>
</asp:Content>
<asp:Content ContentPlaceHolderID="body" runat="server">
<div class="umb-dialog-body">
<umb:JsInclude ID="JsInclude1" runat="server" FilePath="js/umbracoCheckKeys.js" PathNameAlias="UmbracoRoot" />
<cc1:Feedback ID="feedBackMsg" runat="server" />
<cc1:Pane ID="pp_selectVersion" runat="server" Text="Select a version to compare with the current version">
<cc1:PropertyPanel id="pp_currentVersion" Text="Current version" runat="server">
<asp:Literal ID="currentVersionTitle" runat="server" />
<small>(<asp:Literal ID="currentVersionMeta" runat="server" />)</small></cc1:PropertyPanel>
<cc1:PropertyPanel ID="pp_rollBackTo" Text="Rollback to" runat="server">
<asp:DropDownList OnSelectedIndexChanged="version_load" ID="allVersions" runat="server" Width="400px" AutoPostBack="True" CssClass="guiInputTextTiny" />
</cc1:PropertyPanel>
<cc1:PropertyPanel id="pp_view" Text="View" runat="server">
<small>
<asp:RadioButtonList ID="rbl_mode" runat="server" OnSelectedIndexChanged="version_load" RepeatDirection="Horizontal">
<asp:ListItem Selected="True" Value="diff">Diff</asp:ListItem>
<asp:ListItem Value="html">Html</asp:ListItem>
</asp:RadioButtonList>
</small>
</cc1:PropertyPanel>
</cc1:Pane>
<asp:Panel ID="diffPanel" Visible="false" runat="server" Height="300px">
<div class="diff">
<div class="diffnotice">
<p>
<asp:Literal ID="lt_notice" runat="server" />
</p>
</div>
<table border="0" style="width: 95%;">
<asp:Literal ID="propertiesCompare" runat="server"></asp:Literal>
</table>
</div>
</asp:Panel>
</div>
<div runat="server" id="pl_buttons" class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<a href="#" class="btn btn-link" onclick="UmbClientMgr.closeModalWindow()"><%=Services.TextService.Localize("general/cancel")%></a>
<asp:Button ID="Button1" runat="server" Visible="false" CssClass="btn btn-primary" OnClick="doRollback_Click"></asp:Button>
</div>
</asp:Content>
@@ -1,156 +0,0 @@
//TODO: REbuild this in angular and then remove
//using System;
//using System.Collections;
//using System.ComponentModel;
//using System.Data;
//using System.Drawing;
//using System.Linq;
//using System.Web;
//using System.Web.SessionState;
//using System.Web.UI;
//using System.Web.UI.WebControls;
//using System.Web.UI.HtmlControls;
//
//using umbraco.cms.businesslogic.web;
//using umbraco.cms.businesslogic.property;
//namespace umbraco.presentation.dialogs
//{
// /// <summary>
// /// Summary description for rollBack.
// /// </summary>
// public partial class rollBack : UmbracoEnsuredPage
// {
// public rollBack()
// {
// CurrentApp = Constants.Applications.Content.ToString();
// }
// private Document currentDoc = new Document(int.Parse(helper.Request("nodeId")));
// protected void version_load(object sender, EventArgs e) {
// if (allVersions.SelectedValue != "")
// {
// diffPanel.Visible = true;
// Document rollback = new Document(currentDoc.Id, new Guid(allVersions.SelectedValue));
// propertiesCompare.Text = "<tr><th style='width: 25%;' valign='top'>" + Services.TextService.Localize("general/name") + ":</th><td>" + rollback.Text + "</td></tr>";
// propertiesCompare.Text += "<tr><th style='width: 25%;' valign='top'>" + Services.TextService.Localize("content/createDate") + ":</th><td>" + rollback.VersionDate.ToLongDateString() + " " + rollback.VersionDate.ToLongTimeString() + " " + Services.TextService.Localize("general/by") + ": " + rollback.User.Name + "</td></tr>";
// if (rbl_mode.SelectedValue == "diff")
// lt_notice.Text = Services.TextService.Localize("rollback/diffHelp");
// else
// lt_notice.Text = Services.TextService.Localize("rollback/htmlHelp");
// var props = rollback.GenericProperties;
// foreach (Property p in props)
// {
// try
// {
// if (p.Value != null)
// {
// //new property value...
// string thevalue = p.Value.ToString();
// if (rbl_mode.SelectedValue == "diff")
// {
// //if display mode is set to diff...
// thevalue = library.StripHtml(p.Value.ToString());
// Property cP = currentDoc.getProperty(p.PropertyType);
// if (cP != null && cP.Value != null)
// {
// string cThevalue = library.StripHtml(cP.Value.ToString());
// propertiesCompare.Text += "<tr><th style='width: 25%;' valign='top'>" + p.PropertyType.Name + ":</th><td>" + library.ReplaceLineBreaks(cms.businesslogic.utilities.Diff.Diff2Html(cThevalue, thevalue)) + "</td></tr>";
// }
// else
// {
// //If no current version of the value... display with no diff.
// propertiesCompare.Text += "<tr><th style='width: 25%;' valign='top'>" + p.PropertyType.Name + ":</th><td>" + thevalue + "</td></tr>";
// }
// }
// else
// {
// //If display mode is html
// propertiesCompare.Text += "<tr><th style='width: 25%;' valign='top'>" + p.PropertyType.Name + ":</th><td>" + thevalue + "</td></tr>";
// }
// //previewVersionContent.Controls.Add(new LiteralControl("<div style=\"margin-top: 4px; border: 1px solid #DEDEDE; padding: 4px;\"><p style=\"padding: 0px; margin: 0px;\" class=\"guiDialogNormal\"><b>" + p.PropertyType.Name + "</b><br/>"));
// //previewVersionContent.Controls.Add(new LiteralControl(thevalue));
// }
// //previewVersionContent.Controls.Add(new LiteralControl("</p></div>"));
// }
// catch { }
// }
// Button1.Visible = true;
// }
// else
// {
// diffPanel.Visible = false;
// Button1.Visible = false;
// }
// }
// protected void Page_Load(object sender, System.EventArgs e)
// {
// if (String.IsNullOrEmpty(allVersions.SelectedValue))
// rbl_mode.AutoPostBack = false;
// else
// rbl_mode.AutoPostBack = true;
// currentVersionTitle.Text = currentDoc.Text;
// currentVersionMeta.Text = Services.TextService.Localize("content/createDate") + ": " + currentDoc.VersionDate.ToShortDateString() + " " + currentDoc.VersionDate.ToShortTimeString();
// if (!IsPostBack) {
// allVersions.Items.Add(new ListItem(Services.TextService.Localize("rollback/selectVersion")+ "...", ""));
// foreach (DocumentVersionList dl in currentDoc.GetVersions())
// {
// //we don't need to show the current version
// if (dl.Version == currentDoc.Version)
// continue;
//
// allVersions.Items.Add(new ListItem(dl.Text + " (" + Services.TextService.Localize("content/createDate") + ": " + dl.Date.ToShortDateString() + " " + dl.Date.ToShortTimeString() + ")", dl.Version.ToString()));
// }
// Button1.Text = Services.TextService.Localize("actions/rollback");
// }
// }
// protected void doRollback_Click(object sender, System.EventArgs e)
// {
// if (allVersions.SelectedValue.Trim() != "")
// {
// Document d = new Document(int.Parse(helper.Request("nodeId")));
// d.RollBack(new Guid(allVersions.SelectedValue), Security.CurrentUser);
// BusinessLogic.Log.Add(BusinessLogic.LogTypes.RollBack, Security.CurrentUser, d.Id, "Version rolled back to revision '" + allVersions.SelectedValue + "'");
// Document rollback = new Document(d.Id, new Guid(allVersions.SelectedValue));
// feedBackMsg.type = global::Umbraco.Web._Legacy.Controls.Feedback.feedbacktype.success;
// string[] vars = {rollback.Text, rollback.VersionDate.ToLongDateString()};
// feedBackMsg.Text = ui.Text("rollback", "documentRolledBack", vars, new global::umbraco.BusinessLogic.User(0)) + "</p><p><a href='#' onclick='" + ClientTools.Scripts.CloseModalWindow() + "'>" + Services.TextService.Localize("closeThisWindow") + "</a>";
// diffPanel.Visible = false;
// pl_buttons.Visible = false;
//
// ClientTools.ReloadLocationIfMatched(string.Format("/content/content/edit/{0}", d.Id));
// }
// }
// }
//}
@@ -1,150 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace umbraco.presentation.dialogs {
public partial class rollBack {
/// <summary>
/// JsInclude1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::ClientDependency.Core.Controls.JsInclude JsInclude1;
/// <summary>
/// feedBackMsg control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Umbraco.Web._Legacy.Controls.Feedback feedBackMsg;
/// <summary>
/// pp_selectVersion control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Umbraco.Web._Legacy.Controls.Pane pp_selectVersion;
/// <summary>
/// pp_currentVersion control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_currentVersion;
/// <summary>
/// currentVersionTitle control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.Literal currentVersionTitle;
/// <summary>
/// currentVersionMeta control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.Literal currentVersionMeta;
/// <summary>
/// pp_rollBackTo control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_rollBackTo;
/// <summary>
/// allVersions control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.DropDownList allVersions;
/// <summary>
/// pp_view control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_view;
/// <summary>
/// rbl_mode control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.RadioButtonList rbl_mode;
/// <summary>
/// diffPanel control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.Panel diffPanel;
/// <summary>
/// lt_notice control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.Literal lt_notice;
/// <summary>
/// propertiesCompare control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.Literal propertiesCompare;
/// <summary>
/// pl_buttons control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.HtmlControls.HtmlGenericControl pl_buttons;
/// <summary>
/// Button1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.Button Button1;
}
}