Merge branch 'v8/dev' into 6174-saving-content-type-segment-variation

This commit is contained in:
Daniël Knippers
2019-10-15 11:07:22 +02:00
129 changed files with 1452 additions and 996 deletions

View File

@@ -30,7 +30,6 @@
<dependency id="ImageProcessor.Web" version="[4.10.0.100,4.999999)" />
<dependency id="ImageProcessor.Web.Config" version="[2.5.0.100,2.999999)" />
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[2.0.1,2.999999)" />
<dependency id="Microsoft.Net.Compilers" version="[2.10.0,2.999999)" />
</group>

View File

@@ -85,12 +85,7 @@ namespace Umbraco.Core
/// ListView.
/// </summary>
public const string ListView = "Umbraco.ListView";
/// <summary>
/// Macro Container.
/// </summary>
public const string MacroContainer = "Umbraco.MacroContainer";
/// <summary>
/// Media Picker.
/// </summary>

View File

@@ -227,8 +227,8 @@ namespace Umbraco.Core.Migrations.Install
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 32, UniqueId = 32.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLockoutDate, Name = Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 33, UniqueId = 33.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLoginDate, Name = Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 34, UniqueId = 34.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastPasswordChangeDate, Name = Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 35, UniqueId = 35.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordQuestion, Name = Constants.Conventions.Member.PasswordQuestionLabel, SortOrder = 7, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 36, UniqueId = 36.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordAnswer, Name = Constants.Conventions.Member.PasswordAnswerLabel, SortOrder = 8, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 35, UniqueId = 35.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.PasswordQuestion, Name = Constants.Conventions.Member.PasswordQuestionLabel, SortOrder = 7, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 36, UniqueId = 36.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.PasswordAnswer, Name = Constants.Conventions.Member.PasswordAnswerLabel, SortOrder = 8, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
}

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Core.Models.Entities
{
public interface IMemberEntitySlim : IContentEntitySlim
{
}
}

View File

@@ -0,0 +1,13 @@
namespace Umbraco.Core.Models.Entities
{
public class MemberEntitySlim : EntitySlim, IMemberEntitySlim
{
public string ContentTypeAlias { get; set; }
/// <inheritdoc />
public string ContentTypeIcon { get; set; }
/// <inheritdoc />
public string ContentTypeThumbnail { get; set; }
}
}

View File

@@ -15,6 +15,12 @@ namespace Umbraco.Core.Models.Membership
return new ReadOnlyUserGroup(group.Id, group.Name, group.Icon, group.StartContentId, group.StartMediaId, group.Alias, group.AllowedSections, group.Permissions);
}
public static bool IsSystemUserGroup(this IUserGroup group) =>
IsSystemUserGroup(group.Alias);
public static bool IsSystemUserGroup(this IReadOnlyUserGroup group) =>
IsSystemUserGroup(group.Alias);
public static IReadOnlyUserGroup ToReadOnlyGroup(this UserGroupDto group)
{
return new ReadOnlyUserGroup(group.Id, group.Name, group.Icon,
@@ -22,5 +28,12 @@ namespace Umbraco.Core.Models.Membership
group.UserGroup2AppDtos.Select(x => x.AppAlias).ToArray(),
group.DefaultPermissions == null ? Enumerable.Empty<string>() : group.DefaultPermissions.ToCharArray().Select(x => x.ToString()));
}
private static bool IsSystemUserGroup(this string groupAlias)
{
return groupAlias == Constants.Security.AdminGroupAlias
|| groupAlias == Constants.Security.SensitiveDataGroupAlias
|| groupAlias == Constants.Security.TranslatorGroupAlias;
}
}
}

View File

@@ -248,14 +248,63 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return dto == null ? null : MapDtoToContent(dto);
}
// deletes a specific version
public override void DeleteVersion(int versionId)
{
// TODO: test object node type?
// get the version we want to delete
var template = SqlContext.Templates.Get("Umbraco.Core.DocumentRepository.GetVersion", tsql =>
tsql.Select<ContentVersionDto>()
.AndSelect<DocumentVersionDto>()
.From<ContentVersionDto>()
.InnerJoin<DocumentVersionDto>()
.On<ContentVersionDto, DocumentVersionDto>((c, d) => c.Id == d.Id)
.Where<ContentVersionDto>(x => x.Id == SqlTemplate.Arg<int>("versionId"))
);
var versionDto = Database.Fetch<DocumentVersionDto>(template.Sql(new { versionId })).FirstOrDefault();
// nothing to delete
if (versionDto == null)
return;
// don't delete the current or published version
if (versionDto.ContentVersionDto.Current)
throw new InvalidOperationException("Cannot delete the current version.");
else if (versionDto.Published)
throw new InvalidOperationException("Cannot delete the published version.");
PerformDeleteVersion(versionDto.ContentVersionDto.NodeId, versionId);
}
// deletes all versions of an entity, older than a date.
public override void DeleteVersions(int nodeId, DateTime versionDate)
{
// TODO: test object node type?
// get the versions we want to delete, excluding the current one
var template = SqlContext.Templates.Get("Umbraco.Core.DocumentRepository.GetVersions", tsql =>
tsql.Select<ContentVersionDto>()
.From<ContentVersionDto>()
.InnerJoin<DocumentVersionDto>()
.On<ContentVersionDto, DocumentVersionDto>((c, d) => c.Id == d.Id)
.Where<ContentVersionDto>(x => x.NodeId == SqlTemplate.Arg<int>("nodeId") && !x.Current && x.VersionDate < SqlTemplate.Arg<DateTime>("versionDate"))
.Where<DocumentVersionDto>( x => !x.Published)
);
var versionDtos = Database.Fetch<ContentVersionDto>(template.Sql(new { nodeId, versionDate }));
foreach (var versionDto in versionDtos)
PerformDeleteVersion(versionDto.NodeId, versionDto.Id);
}
protected override void PerformDeleteVersion(int id, int versionId)
{
// raise event first else potential FK issues
OnUowRemovingVersion(new ScopedVersionEventArgs(AmbientScope, id, versionId));
Database.Delete<PropertyDataDto>("WHERE versionId = @versionId", new { versionId });
Database.Delete<ContentVersionDto>("WHERE id = @versionId", new { versionId });
Database.Delete<ContentVersionCultureVariationDto>("WHERE versionId = @versionId", new { versionId });
Database.Delete<DocumentVersionDto>("WHERE id = @versionId", new { versionId });
Database.Delete<ContentVersionDto>("WHERE id = @versionId", new { versionId });
}
#endregion

View File

@@ -42,8 +42,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
var isContent = objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectType == Constants.ObjectTypes.Media;
var isMember = objectType == Constants.ObjectTypes.Member;
var sql = GetBaseWhere(isContent, isMedia, false, x =>
var sql = GetBaseWhere(isContent, isMedia, isMember, false, x =>
{
if (filter == null) return;
foreach (var filterClause in filter.GetWhereClauses())
@@ -54,7 +55,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var translator = new SqlTranslator<IUmbracoEntity>(sql, query);
sql = translator.Translate();
sql = AddGroupBy(isContent, isMedia, sql, ordering.IsEmpty);
sql = AddGroupBy(isContent, isMedia, isMember, sql, ordering.IsEmpty);
if (!ordering.IsEmpty)
{
@@ -81,6 +82,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
dtos = page.Items;
totalRecords = page.TotalItems;
}
else if (isMember)
{
var page = Database.Page<MemberEntityDto>(pageIndexToFetch, pageSize, sql);
dtos = page.Items;
totalRecords = page.TotalItems;
}
else
{
var page = Database.Page<BaseDto>(pageIndexToFetch, pageSize, sql);
@@ -88,7 +95,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
totalRecords = page.TotalItems;
}
var entities = dtos.Select(x => BuildEntity(isContent, isMedia, x)).ToArray();
var entities = dtos.Select(x => BuildEntity(isContent, isMedia, isMember, x)).ToArray();
if (isContent)
BuildVariants(entities.Cast<DocumentEntitySlim>());
@@ -98,13 +105,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
public IEntitySlim Get(Guid key)
{
var sql = GetBaseWhere(false, false, false, key);
var sql = GetBaseWhere(false, false, false, false, key);
var dto = Database.FirstOrDefault<BaseDto>(sql);
return dto == null ? null : BuildEntity(false, false, dto);
return dto == null ? null : BuildEntity(false, false, false, dto);
}
private IEntitySlim GetEntity(Sql<ISqlContext> sql, bool isContent, bool isMedia)
private IEntitySlim GetEntity(Sql<ISqlContext> sql, bool isContent, bool isMedia, bool isMember)
{
//isContent is going to return a 1:M result now with the variants so we need to do different things
if (isContent)
@@ -120,7 +127,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (dto == null) return null;
var entity = BuildEntity(false, isMedia, dto);
var entity = BuildEntity(false, isMedia, isMember, dto);
return entity;
}
@@ -129,25 +136,27 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
var isContent = objectTypeId == Constants.ObjectTypes.Document || objectTypeId == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectTypeId == Constants.ObjectTypes.Media;
var isMember = objectTypeId == Constants.ObjectTypes.Member;
var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, key);
return GetEntity(sql, isContent, isMedia);
var sql = GetFullSqlForEntityType(isContent, isMedia, isMember, objectTypeId, key);
return GetEntity(sql, isContent, isMedia, isMember);
}
public IEntitySlim Get(int id)
{
var sql = GetBaseWhere(false, false, false, id);
var sql = GetBaseWhere(false, false, false, false, id);
var dto = Database.FirstOrDefault<BaseDto>(sql);
return dto == null ? null : BuildEntity(false, false, dto);
return dto == null ? null : BuildEntity(false, false, false, dto);
}
public IEntitySlim Get(int id, Guid objectTypeId)
{
var isContent = objectTypeId == Constants.ObjectTypes.Document || objectTypeId == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectTypeId == Constants.ObjectTypes.Media;
var isMember = objectTypeId == Constants.ObjectTypes.Member;
var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, id);
return GetEntity(sql, isContent, isMedia);
var sql = GetFullSqlForEntityType(isContent, isMedia, isMember, objectTypeId, id);
return GetEntity(sql, isContent, isMedia, isMember);
}
public IEnumerable<IEntitySlim> GetAll(Guid objectType, params int[] ids)
@@ -164,7 +173,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
: PerformGetAll(objectType);
}
private IEnumerable<IEntitySlim> GetEntities(Sql<ISqlContext> sql, bool isContent, bool isMedia)
private IEnumerable<IEntitySlim> GetEntities(Sql<ISqlContext> sql, bool isContent, bool isMedia, bool isMember)
{
//isContent is going to return a 1:M result now with the variants so we need to do different things
if (isContent)
@@ -180,7 +189,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
? (IEnumerable<BaseDto>)Database.Fetch<MediaEntityDto>(sql)
: Database.Fetch<BaseDto>(sql);
var entities = dtos.Select(x => BuildEntity(false, isMedia, x)).ToArray();
var entities = dtos.Select(x => BuildEntity(false, isMedia, isMember, x)).ToArray();
return entities;
}
@@ -189,9 +198,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
{
var isContent = objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectType == Constants.ObjectTypes.Media;
var isMember = objectType == Constants.ObjectTypes.Member;
var sql = GetFullSqlForEntityType(isContent, isMedia, objectType, filter);
return GetEntities(sql, isContent, isMedia);
var sql = GetFullSqlForEntityType(isContent, isMedia, isMember, objectType, filter);
return GetEntities(sql, isContent, isMedia, isMember);
}
public IEnumerable<TreeEntityPath> GetAllPaths(Guid objectType, params int[] ids)
@@ -218,26 +228,27 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
public IEnumerable<IEntitySlim> GetByQuery(IQuery<IUmbracoEntity> query)
{
var sqlClause = GetBase(false, false, null);
var sqlClause = GetBase(false, false, false, null);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var sql = translator.Translate();
sql = AddGroupBy(false, false, sql, true);
sql = AddGroupBy(false, false, false, sql, true);
var dtos = Database.Fetch<BaseDto>(sql);
return dtos.Select(x => BuildEntity(false, false, x)).ToList();
return dtos.Select(x => BuildEntity(false, false, false, x)).ToList();
}
public IEnumerable<IEntitySlim> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectType)
{
var isContent = objectType == Constants.ObjectTypes.Document || objectType == Constants.ObjectTypes.DocumentBlueprint;
var isMedia = objectType == Constants.ObjectTypes.Media;
var isMember = objectType == Constants.ObjectTypes.Member;
var sql = GetBaseWhere(isContent, isMedia, false, null, objectType);
var sql = GetBaseWhere(isContent, isMedia, isMember, false, null, objectType);
var translator = new SqlTranslator<IUmbracoEntity>(sql, query);
sql = translator.Translate();
sql = AddGroupBy(isContent, isMedia, sql, true);
sql = AddGroupBy(isContent, isMedia, isMember, sql, true);
return GetEntities(sql, isContent, isMedia);
return GetEntities(sql, isContent, isMedia, isMember);
}
public UmbracoObjectTypes GetObjectType(int id)
@@ -329,29 +340,29 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
}
// gets the full sql for a given object type and a given unique id
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectType, Guid uniqueId)
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, bool isMember, Guid objectType, Guid uniqueId)
{
var sql = GetBaseWhere(isContent, isMedia, false, objectType, uniqueId);
return AddGroupBy(isContent, isMedia, sql, true);
var sql = GetBaseWhere(isContent, isMedia, isMember, false, objectType, uniqueId);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the full sql for a given object type and a given node id
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectType, int nodeId)
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, bool isMember, Guid objectType, int nodeId)
{
var sql = GetBaseWhere(isContent, isMedia, false, objectType, nodeId);
return AddGroupBy(isContent, isMedia, sql, true);
var sql = GetBaseWhere(isContent, isMedia, isMember, false, objectType, nodeId);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the full sql for a given object type, with a given filter
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectType, Action<Sql<ISqlContext>> filter)
protected Sql<ISqlContext> GetFullSqlForEntityType(bool isContent, bool isMedia, bool isMember, Guid objectType, Action<Sql<ISqlContext>> filter)
{
var sql = GetBaseWhere(isContent, isMedia, false, filter, objectType);
return AddGroupBy(isContent, isMedia, sql, true);
var sql = GetBaseWhere(isContent, isMedia, isMember, false, filter, objectType);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the base SELECT + FROM [+ filter] sql
// always from the 'current' content version
protected Sql<ISqlContext> GetBase(bool isContent, bool isMedia, Action<Sql<ISqlContext>> filter, bool isCount = false)
protected Sql<ISqlContext> GetBase(bool isContent, bool isMedia, bool isMember, Action<Sql<ISqlContext>> filter, bool isCount = false)
{
var sql = Sql();
@@ -366,7 +377,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.AndSelect<NodeDto>(x => x.SortOrder, x => x.UniqueId, x => x.Text, x => x.NodeObjectType, x => x.CreateDate)
.Append(", COUNT(child.id) AS children");
if (isContent || isMedia)
if (isContent || isMedia || isMember)
sql
.AndSelect<ContentVersionDto>(x => Alias(x.Id, "versionId"))
.AndSelect<ContentTypeDto>(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer, x => x.Variations);
@@ -387,7 +398,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
sql
.From<NodeDto>();
if (isContent || isMedia)
if (isContent || isMedia || isMember)
{
sql
.InnerJoin<ContentVersionDto>().On<NodeDto, ContentVersionDto>((left, right) => left.NodeId == right.NodeId && right.Current)
@@ -422,49 +433,49 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// gets the base SELECT + FROM [+ filter] + WHERE sql
// for a given object type, with a given filter
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, Action<Sql<ISqlContext>> filter, Guid objectType)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Action<Sql<ISqlContext>> filter, Guid objectType)
{
return GetBase(isContent, isMedia, filter, isCount)
return GetBase(isContent, isMedia, isMember, filter, isCount)
.Where<NodeDto>(x => x.NodeObjectType == objectType);
}
// gets the base SELECT + FROM + WHERE sql
// for a given node id
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, int id)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, int id)
{
var sql = GetBase(isContent, isMedia, null, isCount)
var sql = GetBase(isContent, isMedia, isMember, null, isCount)
.Where<NodeDto>(x => x.NodeId == id);
return AddGroupBy(isContent, isMedia, sql, true);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the base SELECT + FROM + WHERE sql
// for a given unique id
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, Guid uniqueId)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Guid uniqueId)
{
var sql = GetBase(isContent, isMedia, null, isCount)
var sql = GetBase(isContent, isMedia, isMember, null, isCount)
.Where<NodeDto>(x => x.UniqueId == uniqueId);
return AddGroupBy(isContent, isMedia, sql, true);
return AddGroupBy(isContent, isMedia, isMember, sql, true);
}
// gets the base SELECT + FROM + WHERE sql
// for a given object type and node id
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, Guid objectType, int nodeId)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Guid objectType, int nodeId)
{
return GetBase(isContent, isMedia, null, isCount)
return GetBase(isContent, isMedia, isMember, null, isCount)
.Where<NodeDto>(x => x.NodeId == nodeId && x.NodeObjectType == objectType);
}
// gets the base SELECT + FROM + WHERE sql
// for a given object type and unique id
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isCount, Guid objectType, Guid uniqueId)
protected Sql<ISqlContext> GetBaseWhere(bool isContent, bool isMedia, bool isMember, bool isCount, Guid objectType, Guid uniqueId)
{
return GetBase(isContent, isMedia, null, isCount)
return GetBase(isContent, isMedia, isMember, null, isCount)
.Where<NodeDto>(x => x.UniqueId == uniqueId && x.NodeObjectType == objectType);
}
// gets the GROUP BY / ORDER BY sql
// required in order to count children
protected Sql<ISqlContext> AddGroupBy(bool isContent, bool isMedia, Sql<ISqlContext> sql, bool defaultSort)
protected Sql<ISqlContext> AddGroupBy(bool isContent, bool isMedia, bool isMember, Sql<ISqlContext> sql, bool defaultSort)
{
sql
.GroupBy<NodeDto>(x => x.NodeId, x => x.Trashed, x => x.ParentId, x => x.UserId, x => x.Level, x => x.Path)
@@ -483,7 +494,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
}
if (isContent || isMedia)
if (isContent || isMedia || isMember)
sql
.AndBy<ContentVersionDto>(x => x.Id)
.AndBy<ContentTypeDto>(x => x.Alias, x => x.Icon, x => x.Thumbnail, x => x.IsContainer, x => x.Variations);
@@ -528,6 +539,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
public string MediaPath { get; set; }
}
private class MemberEntityDto : BaseDto
{
}
public class VariantInfoDto
{
public int NodeId { get; set; }
@@ -574,12 +589,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
#region Factory
private EntitySlim BuildEntity(bool isContent, bool isMedia, BaseDto dto)
private EntitySlim BuildEntity(bool isContent, bool isMedia, bool isMember, BaseDto dto)
{
if (isContent)
return BuildDocumentEntity(dto);
if (isMedia)
return BuildMediaEntity(dto);
if (isMember)
return BuildMemberEntity(dto);
// EntitySlim does not track changes
var entity = new EntitySlim();
@@ -644,6 +661,19 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
return entity;
}
private MemberEntitySlim BuildMemberEntity(BaseDto dto)
{
// EntitySlim does not track changes
var entity = new MemberEntitySlim();
BuildEntity(entity, dto);
entity.ContentTypeAlias = dto.Alias;
entity.ContentTypeIcon = dto.Icon;
entity.ContentTypeThumbnail = dto.Thumbnail;
return entity;
}
#endregion
}
}

View File

@@ -1848,7 +1848,7 @@ namespace Umbraco.Core.Services.Implement
scope.WriteLock(Constants.Locks.ContentTree);
var c = _documentRepository.Get(id);
if (c.VersionId != versionId) // don't delete the current version
if (c.VersionId != versionId && c.PublishedVersionId != versionId) // don't delete the current or published version
_documentRepository.DeleteVersion(versionId);
scope.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false,/* specificVersion:*/ versionId));

View File

@@ -263,7 +263,9 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\FixLanguageIsoCodeLength.cs" />
<Compile Include="Models\CultureImpact.cs" />
<Compile Include="Models\Entities\IMediaEntitySlim.cs" />
<Compile Include="Models\Entities\IMemberEntitySlim.cs" />
<Compile Include="Models\Entities\MediaEntitySlim.cs" />
<Compile Include="Models\Entities\MemberEntitySlim.cs" />
<Compile Include="Models\PublishedContent\ILivePublishedModelFactory.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentType.cs" />
<Compile Include="Models\PublishedContent\IPublishedPropertyType.cs" />

View File

@@ -52,7 +52,7 @@ namespace Umbraco.Examine
if (sqlContext == null) throw new ArgumentNullException(nameof(sqlContext));
_contentService = contentService ?? throw new ArgumentNullException(nameof(contentService));
_contentValueSetBuilder = contentValueSetBuilder ?? throw new ArgumentNullException(nameof(contentValueSetBuilder));
if (_publishedQuery != null)
if (_publishedQuery == null)
_publishedQuery = sqlContext.Query<IContent>().Where(x => x.Published);
_publishedValuesOnly = publishedValuesOnly;
_parentId = parentId;

View File

@@ -0,0 +1,12 @@
using Examine;
namespace Umbraco.Examine
{
public interface IUmbracoIndexConfig
{
IContentValueSetValidator GetContentValueSetValidator();
IContentValueSetValidator GetPublishedContentValueSetValidator();
IValueSetValidator GetMemberValueSetValidator();
}
}

View File

@@ -64,9 +64,11 @@
<Compile Include="ExamineExtensions.cs" />
<Compile Include="IContentValueSetBuilder.cs" />
<Compile Include="IContentValueSetValidator.cs" />
<Compile Include="IUmbracoIndexConfig.cs" />
<Compile Include="IIndexCreator.cs" />
<Compile Include="IIndexDiagnostics.cs" />
<Compile Include="IIndexPopulator.cs" />
<Compile Include="UmbracoIndexConfig.cs" />
<Compile Include="IndexPopulator.cs" />
<Compile Include="IndexRebuilder.cs" />
<Compile Include="IPublishedContentValueSetBuilder.cs" />

View File

@@ -0,0 +1,34 @@
using Examine;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Examine
{
public class UmbracoIndexConfig : IUmbracoIndexConfig
{
public UmbracoIndexConfig(IPublicAccessService publicAccessService)
{
PublicAccessService = publicAccessService;
}
protected IPublicAccessService PublicAccessService { get; }
public IContentValueSetValidator GetContentValueSetValidator()
{
return new ContentValueSetValidator(false, true, PublicAccessService);
}
public IContentValueSetValidator GetPublishedContentValueSetValidator()
{
return new ContentValueSetValidator(true, false, PublicAccessService);
}
/// <summary>
/// Returns the <see cref="IValueSetValidator"/> for the member indexer
/// </summary>
/// <returns></returns>
public IValueSetValidator GetMemberValueSetValidator()
{
return new MemberValueSetValidator();
}
}
}

View File

@@ -268,7 +268,7 @@ AnotherContentFinder
public void GetDataEditors()
{
var types = _typeLoader.GetDataEditors();
Assert.AreEqual(39, types.Count());
Assert.AreEqual(38, types.Count());
}
/// <summary>

View File

@@ -1087,7 +1087,7 @@ namespace Umbraco.Tests.Integration
[Test]
public void EmptyRecycleBinContent()
{
ServiceContext.ContentService.EmptyRecycleBin();
ServiceContext.ContentService.EmptyRecycleBin(Constants.Security.SuperUserId);
var content = CreateContent();
Assert.IsNotNull(content);
@@ -1095,7 +1095,7 @@ namespace Umbraco.Tests.Integration
ServiceContext.ContentService.MoveToRecycleBin(content);
ResetEvents();
ServiceContext.ContentService.EmptyRecycleBin();
ServiceContext.ContentService.EmptyRecycleBin(Constants.Security.SuperUserId);
Assert.AreEqual(2, _msgCount);
Assert.AreEqual(2, _events.Count);
@@ -1109,7 +1109,7 @@ namespace Umbraco.Tests.Integration
[Test]
public void EmptyRecycleBinContents()
{
ServiceContext.ContentService.EmptyRecycleBin();
ServiceContext.ContentService.EmptyRecycleBin(Constants.Security.SuperUserId);
var content1 = CreateContent();
Assert.IsNotNull(content1);
@@ -1120,7 +1120,7 @@ namespace Umbraco.Tests.Integration
ServiceContext.ContentService.MoveToRecycleBin(content2);
ResetEvents();
ServiceContext.ContentService.EmptyRecycleBin();
ServiceContext.ContentService.EmptyRecycleBin(Constants.Security.SuperUserId);
Assert.AreEqual(3, _msgCount);
Assert.AreEqual(4, _events.Count);
@@ -1136,7 +1136,7 @@ namespace Umbraco.Tests.Integration
[Test]
public void EmptyRecycleBinBranch()
{
ServiceContext.ContentService.EmptyRecycleBin();
ServiceContext.ContentService.EmptyRecycleBin(Constants.Security.SuperUserId);
var content1 = CreateBranch();
Assert.IsNotNull(content1);
@@ -1151,7 +1151,7 @@ namespace Umbraco.Tests.Integration
var content4C = Children(content1C[2]).ToArray();
var content5C = Children(content1C[3]).ToArray();
ServiceContext.ContentService.EmptyRecycleBin();
ServiceContext.ContentService.EmptyRecycleBin(Constants.Security.SuperUserId);
Assert.AreEqual(14, _msgCount);
Assert.AreEqual(14, _events.Count);

View File

@@ -1616,7 +1616,7 @@ namespace Umbraco.Tests.Services
Assert.IsTrue(descendants.All(x => x.Path.StartsWith("-1,-20,")));
Assert.True(descendants.All(x => x.Trashed));
contentService.EmptyRecycleBin();
contentService.EmptyRecycleBin(Constants.Security.SuperUserId);
var trashed = contentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList();
Assert.IsEmpty(trashed);
}
@@ -1628,7 +1628,7 @@ namespace Umbraco.Tests.Services
var contentService = ServiceContext.ContentService;
// Act
contentService.EmptyRecycleBin();
contentService.EmptyRecycleBin(Constants.Security.SuperUserId);
var contents = contentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList();
// Assert
@@ -1804,7 +1804,7 @@ namespace Umbraco.Tests.Services
// Act
ServiceContext.ContentService.MoveToRecycleBin(content1);
ServiceContext.ContentService.EmptyRecycleBin();
ServiceContext.ContentService.EmptyRecycleBin(Constants.Security.SuperUserId);
var contents = ServiceContext.ContentService.GetPagedContentInRecycleBin(0, int.MaxValue, out var _).ToList();
// Assert

View File

@@ -54,6 +54,7 @@
</pre>
@param {boolean} checked Set to <code>true</code> or <code>false</code> to toggle the switch.
@param {string} inputId Set the <code>id</code> of the toggle.
@param {callback} onClick The function which should be called when the toggle is clicked.
@param {string=} showLabels Set to <code>true</code> or <code>false</code> to show a "On" or "Off" label next to the switch.
@param {string=} labelOn Set a custom label for when the switched is turned on. It will default to "On".
@@ -122,6 +123,7 @@
scope: {
checked: "=",
disabled: "=",
inputId: "@",
onClick: "&",
labelOn: "@?",
labelOff: "@?",

View File

@@ -766,16 +766,6 @@
clearNotifications($scope.content);
//before we launch the dialog we want to execute all client side validations first
if (formHelper.submitForm({ scope: $scope, action: "schedule" })) {
//used to track the original values so if the user doesn't save the schedule and they close the dialog we reset the dates back to what they were.
let origDates = [];
for (let i = 0; i < $scope.content.variants.length; i++) {
origDates.push({
releaseDate: $scope.content.variants[i].releaseDate,
expireDate: $scope.content.variants[i].expireDate
});
}
if (!isContentCultureVariant()) {
//ensure the flags are set
$scope.content.variants[0].save = true;
@@ -784,10 +774,17 @@
var dialog = {
parentScope: $scope,
view: "views/content/overlays/schedule.html",
variants: $scope.content.variants, //set a model property for the dialog
variants: angular.copy($scope.content.variants), //set a model property for the dialog
skipFormValidation: true, //when submitting the overlay form, skip any client side validation
submitButtonLabelKey: "buttons_schedulePublish",
submit: function (model) {
for (let i = 0; i < $scope.content.variants.length; i++) {
$scope.content.variants[i].releaseDate = model.variants[i].releaseDate;
$scope.content.variants[i].expireDate = model.variants[i].expireDate;
$scope.content.variants[i].releaseDateFormatted = model.variants[i].releaseDateFormatted;
$scope.content.variants[i].expireDateFormatted = model.variants[i].expireDateFormatted;
}
model.submitButtonState = "busy";
clearNotifications($scope.content);
@@ -810,7 +807,7 @@
}
model.submitButtonState = "error";
//re-map the dialog model since we've re-bound the properties
dialog.variants = $scope.content.variants;
dialog.variants = angular.copy($scope.content.variants);
//don't reject, we've handled the error
return $q.when(err);
});
@@ -818,11 +815,6 @@
},
close: function () {
overlayService.close();
//restore the dates
for (let i = 0; i < $scope.content.variants.length; i++) {
$scope.content.variants[i].releaseDate = origDates[i].releaseDate;
$scope.content.variants[i].expireDate = origDates[i].expireDate;
}
}
};
overlayService.open(dialog);

View File

@@ -307,6 +307,8 @@
// get current backoffice user and format dates
userService.getCurrentUser().then(function (currentUser) {
scope.currentVariant.createDateFormatted = dateHelper.getLocalDate(scope.currentVariant.createDate, currentUser.locale, 'LLL');
scope.currentVariant.releaseDateFormatted = dateHelper.getLocalDate(scope.currentVariant.releaseDate, currentUser.locale, 'LLL');
scope.currentVariant.expireDateFormatted = dateHelper.getLocalDate(scope.currentVariant.expireDate, currentUser.locale, 'LLL');
});
}

View File

@@ -0,0 +1,45 @@
// A slightly modified version of https://github.com/myplanet/angular-deep-blur/blob/master/angular-deep-blur.js - Kudos to Ufuk Kayserilioglu (paracycle)
(function () {
'use strict';
function DeepBlurDirective($timeout){
function controller($scope, $element, $attrs) {
var leaveExpr = $attrs.deepBlur,
dom = $element[0];
function containsDom(parent, dom) {
while (dom) {
if (dom === parent) {
return true;
}
dom = dom.parentNode;
}
return false;
}
function onBlur(e) {
var targetElement = e.relatedTarget;
if (!containsDom(dom, targetElement)) {
$timeout(function () {
$scope.$apply(leaveExpr);
}, 10);
}
}
dom.addEventListener('blur', onBlur, true);
}
var directive = {
restrict: 'A',
controller: controller
};
return directive;
}
angular.module('umbraco.directives').directive('deepBlur', DeepBlurDirective);
})();

View File

@@ -1,273 +0,0 @@
/**
* @description Utillity directives for key and field events
**/
angular.module('umbraco.directives')
.directive('onDragEnter', function () {
return {
link: function (scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDragEnter);
};
elm.on("dragenter", f);
scope.$on("$destroy", function () { elm.off("dragenter", f); });
}
};
})
.directive('onDragLeave', function () {
return function (scope, elm, attrs) {
var f = function (event) {
var rect = this.getBoundingClientRect();
var getXY = function getCursorPosition(event) {
var x, y;
if (typeof event.clientX === 'undefined') {
// try touch screen
x = event.pageX + document.documentElement.scrollLeft;
y = event.pageY + document.documentElement.scrollTop;
} else {
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { x: x, y: y };
};
var e = getXY(event.originalEvent);
// Check the mouseEvent coordinates are outside of the rectangle
if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
scope.$apply(attrs.onDragLeave);
}
};
elm.on("dragleave", f);
scope.$on("$destroy", function () { elm.off("dragleave", f); });
};
})
.directive('onDragOver', function () {
return {
link: function (scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDragOver);
};
elm.on("dragover", f);
scope.$on("$destroy", function () { elm.off("dragover", f); });
}
};
})
.directive('onDragStart', function () {
return {
link: function (scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDragStart);
};
elm.on("dragstart", f);
scope.$on("$destroy", function () { elm.off("dragstart", f); });
}
};
})
.directive('onDragEnd', function () {
return {
link: function (scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDragEnd);
};
elm.on("dragend", f);
scope.$on("$destroy", function () { elm.off("dragend", f); });
}
};
})
.directive('onDrop', function () {
return {
link: function (scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDrop);
};
elm.on("drop", f);
scope.$on("$destroy", function () { elm.off("drop", f); });
}
};
})
.directive('onOutsideClick', function ($timeout, angularHelper) {
return function (scope, element, attrs) {
var eventBindings = [];
function oneTimeClick(event) {
var el = event.target.nodeName;
//ignore link and button clicks
var els = ["INPUT", "A", "BUTTON"];
if (els.indexOf(el) >= 0) { return; }
// ignore clicks on new overlay
var parents = $(event.target).parents("a,button,.umb-overlay,.umb-tour");
if (parents.length > 0) {
return;
}
// ignore clicks on dialog from old dialog service
var oldDialog = $(event.target).parents("#old-dialog-service");
if (oldDialog.length === 1) {
return;
}
// ignore clicks in tinyMCE dropdown(floatpanel)
var floatpanel = $(event.target).closest(".mce-floatpanel");
if (floatpanel.length === 1) {
return;
}
// ignore clicks in flatpickr datepicker
var flatpickr = $(event.target).closest(".flatpickr-calendar");
if (flatpickr.length === 1) {
return;
}
//ignore clicks inside this element
if ($(element).has($(event.target)).length > 0) {
return;
}
// please to not use angularHelper.safeApply here, it won't work
scope.$apply(attrs.onOutsideClick);
}
$timeout(function () {
if ("bindClickOn" in attrs) {
eventBindings.push(scope.$watch(function () {
return attrs.bindClickOn;
}, function (newValue) {
if (newValue === "true") {
$(document).on("click", oneTimeClick);
} else {
$(document).off("click", oneTimeClick);
}
}));
} else {
$(document).on("click", oneTimeClick);
}
scope.$on("$destroy", function () {
$(document).off("click", oneTimeClick);
// unbind watchers
for (var e in eventBindings) {
eventBindings[e]();
}
});
}); // Temp removal of 1 sec timeout to prevent bug where overlay does not open. We need to find a better solution.
};
})
.directive('onRightClick', function ($parse) {
document.oncontextmenu = function (e) {
if (e.target.hasAttribute('on-right-click')) {
e.preventDefault();
e.stopPropagation();
return false;
}
};
return function (scope, el, attrs) {
el.on('contextmenu', function (e) {
e.preventDefault();
e.stopPropagation();
var fn = $parse(attrs.onRightClick);
scope.$apply(function () {
fn(scope, { $event: e });
});
return false;
});
};
})
.directive('onDelayedMouseleave', function ($timeout, $parse) {
return {
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
var active = false;
var fn = $parse(attrs.onDelayedMouseleave);
var leave_f = function (event) {
var callback = function () {
fn(scope, { $event: event });
};
active = false;
$timeout(function () {
if (active === false) {
scope.$apply(callback);
}
}, 650);
};
var enter_f = function (event, args) {
active = true;
};
element.on("mouseleave", leave_f);
element.on("mouseenter", enter_f);
//unsub events
scope.$on("$destroy", function () {
element.off("mouseleave", leave_f);
element.off("mouseenter", enter_f);
});
}
};
})
// A slightly modified version of https://github.com/myplanet/angular-deep-blur/blob/master/angular-deep-blur.js - Kudos to Ufuk Kayserilioglu (paracycle)
.directive('deepBlur', function ($timeout) {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs) {
var leaveExpr = $attrs.deepBlur,
dom = $element[0];
function containsDom(parent, dom) {
while (dom) {
if (dom === parent) {
return true;
}
dom = dom.parentNode;
}
return false;
}
function onBlur(e) {
var targetElement = e.relatedTarget;
if (!containsDom(dom, targetElement)) {
$timeout(function () {
$scope.$apply(leaveExpr);
}, 10);
}
}
dom.addEventListener('blur', onBlur, true);
}
};
});

View File

@@ -0,0 +1,49 @@
(function () {
'use strict';
function onDelayedMouseleaveDirective($timeout, $parse){
function link(scope, element, attrs, ctrl) {
var active = false;
var fn = $parse(attrs.onDelayedMouseleave);
var leave_f = function (event) {
var callback = function () {
fn(scope, { $event: event });
};
active = false;
$timeout(function () {
if (active === false) {
scope.$apply(callback);
}
}, 650);
};
var enter_f = function (event, args) {
active = true;
};
element.on("mouseleave", leave_f);
element.on("mouseenter", enter_f);
//unsub events
scope.$on("$destroy", function () {
element.off("mouseleave", leave_f);
element.off("mouseenter", enter_f);
});
}
var directive = {
restrict: 'A',
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('onDelayedMouseleave', onDelayedMouseleaveDirective);
})();

View File

@@ -0,0 +1,24 @@
(function () {
'use strict';
function onDragEndDirective(){
function link(scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDragEnd);
};
elm.on("dragend", f);
scope.$on("$destroy", function () { elm.off("dragend", f); });
}
var directive = {
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('onDragEnd', onDragEndDirective);
})();

View File

@@ -0,0 +1,24 @@
(function () {
'use strict';
function onDragEnterDirective(){
function link(scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDragEnter);
};
elm.on("dragenter", f);
scope.$on("$destroy", function () { elm.off("dragenter", f); });
}
var directive = {
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('onDragEnter', onDragEnterDirective);
})();

View File

@@ -0,0 +1,40 @@
(function () {
'use strict';
function onDragLeaveDirective($timeout){
return function (scope, elm, attrs) {
var f = function (event) {
var rect = this.getBoundingClientRect();
var getXY = function getCursorPosition(event) {
var x, y;
if (typeof event.clientX === 'undefined') {
// try touch screen
x = event.pageX + document.documentElement.scrollLeft;
y = event.pageY + document.documentElement.scrollTop;
} else {
x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
return { x: x, y: y };
};
var e = getXY(event.originalEvent);
// Check the mouseEvent coordinates are outside of the rectangle
if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
scope.$apply(attrs.onDragLeave);
}
};
elm.on("dragleave", f);
scope.$on("$destroy", function () { elm.off("dragleave", f); });
};
}
angular.module('umbraco.directives').directive('onDragLeave', onDragLeaveDirective);
})();

View File

@@ -0,0 +1,24 @@
(function () {
'use strict';
function onDragOverDirective(){
function link(scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDragOver);
};
elm.on("dragover", f);
scope.$on("$destroy", function () { elm.off("dragover", f); });
}
var directive = {
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('onDragOver', onDragOverDirective);
})();

View File

@@ -0,0 +1,24 @@
(function () {
'use strict';
function onDragStartDirective($timeout){
function link(scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDragStart);
};
elm.on("dragstart", f);
scope.$on("$destroy", function () { elm.off("dragstart", f); });
}
var directive = {
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('onDragStart', onDragStartDirective);
})();

View File

@@ -0,0 +1,24 @@
(function () {
'use strict';
function onDropDirective(){
function link(scope, elm, attrs) {
var f = function () {
scope.$apply(attrs.onDrop);
};
elm.on("drop", f);
scope.$on("$destroy", function () { elm.off("drop", f); });
}
var directive = {
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('onDrop', onDropDirective);
})();

View File

@@ -0,0 +1,86 @@
(function () {
'use strict';
function onOutsideClickDirective($timeout, angularHelper){
return function (scope, element, attrs) {
var eventBindings = [];
function oneTimeClick(event) {
var el = event.target.nodeName;
//ignore link and button clicks
var els = ["INPUT", "A", "BUTTON"];
if (els.indexOf(el) >= 0) { return; }
// ignore clicks on new overlay
var parents = $(event.target).parents("a,button,.umb-overlay,.umb-tour");
if (parents.length > 0) {
return;
}
// ignore clicks on dialog from old dialog service
var oldDialog = $(event.target).parents("#old-dialog-service");
if (oldDialog.length === 1) {
return;
}
// ignore clicks in tinyMCE dropdown(floatpanel)
var floatpanel = $(event.target).closest(".mce-floatpanel");
if (floatpanel.length === 1) {
return;
}
// ignore clicks in flatpickr datepicker
var flatpickr = $(event.target).closest(".flatpickr-calendar");
if (flatpickr.length === 1) {
return;
}
//ignore clicks inside this element
if ($(element).has($(event.target)).length > 0) {
return;
}
// please to not use angularHelper.safeApply here, it won't work
scope.$apply(attrs.onOutsideClick);
}
$timeout(function () {
if ("bindClickOn" in attrs) {
eventBindings.push(scope.$watch(function () {
return attrs.bindClickOn;
}, function (newValue) {
if (newValue === "true") {
$(document).on("click", oneTimeClick);
} else {
$(document).off("click", oneTimeClick);
}
}));
} else {
$(document).on("click", oneTimeClick);
}
scope.$on("$destroy", function () {
$(document).off("click", oneTimeClick);
// unbind watchers
for (var e in eventBindings) {
eventBindings[e]();
}
});
}); // Temp removal of 1 sec timeout to prevent bug where overlay does not open. We need to find a better solution.
};
}
angular.module('umbraco.directives').directive('onOutsideClick', onOutsideClickDirective);
})();

View File

@@ -0,0 +1,30 @@
(function () {
'use strict';
function onRightClickDirective($parse){
document.oncontextmenu = function (e) {
if (e.target.hasAttribute('on-right-click')) {
e.preventDefault();
e.stopPropagation();
return false;
}
};
return function (scope, el, attrs) {
el.on('contextmenu', function (e) {
e.preventDefault();
e.stopPropagation();
var fn = $parse(attrs.onRightClick);
scope.$apply(function () {
fn(scope, { $event: e });
});
return false;
});
};
}
angular.module('umbraco.directives').directive('onRightClick', onRightClickDirective);
})();

View File

@@ -418,6 +418,10 @@
};
scope.canRemoveGroup = function(group){
return _.find(group.properties, function(property) { return property.locked === true; }) == null;
}
scope.removeGroup = function(groupIndex) {
scope.model.groups.splice(groupIndex, 1);
};

View File

@@ -193,6 +193,9 @@ Use this directive to generate a thumbnail grid of media items.
* Returns wether a item should be selectable or not.
*/
function getSelectableState(item) {
if (item.filtered) {
return false;
}
// check if item is a folder or image
if (item.isFolder === true) {

View File

@@ -77,8 +77,7 @@
}
}
// filter items if there is a filter and it's not advanced
// ** ignores advanced filter at the moment
// filter items if there is a filter and it's not advanced (advanced filtering is handled below)
if (scope.entityTypeFilter && scope.entityTypeFilter.filter && !scope.entityTypeFilter.filterAdvanced) {
var a = scope.entityTypeFilter.filter.toLowerCase().replace(/\s/g, '').split(',');
var found = a.indexOf(c.metaData.ContentTypeAlias.toLowerCase()) >= 0;
@@ -88,6 +87,15 @@
}
}
});
// advanced item filtering is handled here
if (scope.entityTypeFilter && scope.entityTypeFilter.filter && scope.entityTypeFilter.filterAdvanced) {
var filtered = angular.isFunction(scope.entityTypeFilter.filter)
? _.filter(miniListView.children, scope.entityTypeFilter.filter)
: _.where(miniListView.children, scope.entityTypeFilter.filter);
_.each(filtered, (node) => node.allowed = false);
}
// update pagination
miniListView.pagination.totalItems = data.totalItems;
miniListView.pagination.totalPages = data.totalPages;

View File

@@ -298,7 +298,8 @@
* Called when the file collection changes (i.e. a new file has been selected but maybe it wasn't this instance that caused the change)
*/
onFilesChanged: "&",
onInit: "&"
onInit: "&",
required: "@"
},
transclude: true,
controllerAs: 'vm',

View File

@@ -187,7 +187,6 @@ angular.module('umbraco.mocks').
"defaultdialogs_linkinternal": "Internal link:",
"defaultdialogs_linklocaltip": "When using local links, insert '#' infront of link",
"defaultdialogs_linknewwindow": "Open in new window?",
"defaultdialogs_macroContainerSettings": "Macro Settings",
"defaultdialogs_macroDoesNotHaveProperties": "This macro does not contain any properties you can edit",
"defaultdialogs_paste": "Paste",
"defaultdialogs_permissionsEdit": "Edit Permissions for",

View File

@@ -464,6 +464,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
"properties",
"apps",
"createDateFormatted",
"releaseDateFormatted",
"expireDateFormatted",
"releaseDate",
"expireDate"
], function (i) {

View File

@@ -898,6 +898,27 @@ When building a custom infinite editor view you can use the same components as a
open(editor);
}
/**
* @ngdoc method
* @name umbraco.services.editorService#memberEditor
* @methodOf umbraco.services.editorService
*
* @description
* Opens a member editor in infinite editing, the submit callback returns the updated member
* @param {Object} editor rendering options
* @param {String} editor.id The id (GUID) of the member
* @param {Boolean} editor.create Create new member
* @param {Function} editor.submit Callback function when the submit button is clicked. Returns the editor model object
* @param {Function} editor.close Callback function when the close button is clicked.
* @param {String} editor.doctype If editor.create is true, provide member type for the creation of the member
*
* @returns {Object} editor object
*/
function memberEditor(editor) {
editor.view = "views/member/edit.html";
open(editor);
}
///////////////////////
/**
@@ -978,7 +999,8 @@ When building a custom infinite editor view you can use the same components as a
itemPicker: itemPicker,
macroPicker: macroPicker,
memberGroupPicker: memberGroupPicker,
memberPicker: memberPicker
memberPicker: memberPicker,
memberEditor: memberEditor
};
return service;

View File

@@ -588,12 +588,15 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
var selectedElm = editor.selection.getNode(),
currentTarget;
currentTarget,
imgDomElement;
if (selectedElm.nodeName === 'IMG') {
var img = $(selectedElm);
imgDomElement = selectedElm;
var hasUdi = img.attr("data-udi") ? true : false;
var hasDataTmpImg = img.attr("data-tmpimg") ? true : false;
currentTarget = {
altText: img.attr("alt"),
@@ -605,12 +608,16 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
} else {
currentTarget["id"] = img.attr("rel");
}
if(hasDataTmpImg){
currentTarget["tmpimg"] = img.attr("data-tmpimg");
}
}
userService.getCurrentUser().then(function (userData) {
if (callback) {
angularHelper.safeApply($rootScope, function() {
callback(currentTarget, userData);
callback(currentTarget, userData, imgDomElement);
});
}
});
@@ -618,25 +625,77 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
});
},
insertMediaInEditor: function (editor, img) {
insertMediaInEditor: function (editor, img, imgDomElement) {
if (img) {
// imgElement is only definied if updating an image
// if null/undefinied then its a BRAND new image
if(imgDomElement){
// Check if the img src has changed
// If it has we will need to do some resizing/recalc again
var hasImageSrcChanged = false;
var data = {
alt: img.altText || "",
src: (img.url) ? img.url : "nothing.jpg",
id: '__mcenew',
'data-udi': img.udi
};
if(img.url !== editor.dom.getAttrib(imgDomElement, "src")){
hasImageSrcChanged = true;
}
editor.selection.setContent(editor.dom.createHTML('img', data));
// If null/undefinied it will remove the attribute
editor.dom.setAttrib(imgDomElement, "alt", img.altText);
$timeout(function () {
var imgElm = editor.dom.get('__mcenew');
sizeImageInEditor(editor, imgElm, img.url);
editor.dom.setAttrib(imgElm, 'id', null);
editor.fire('Change');
// It's possible to pick a NEW image - so need to ensure this gets updated
if(img.udi){
editor.dom.setAttrib(imgDomElement, "data-udi", img.udi);
}
}, 500);
// It's possible to pick a NEW image - so need to ensure this gets updated
if(img.url){
editor.dom.setAttrib(imgDomElement, "src", img.url);
}
// Remove width & height attributes (ONLY if imgSrc changed)
// So native image size is used as this needed to re-calc width & height
// For the function sizeImageInEditor() & apply the image resizing querystrings etc..
if(hasImageSrcChanged){
editor.dom.setAttrib(imgDomElement, "width", null);
editor.dom.setAttrib(imgDomElement, "height", null);
//Re-calc the image dimensions
sizeImageInEditor(editor, imgDomElement, img.url);
}
} else{
// We need to create a NEW DOM <img> element to insert
// setting an attribute of ID to __mcenew, so we can gather a reference to the node, to be able to update its size accordingly to the size of the image.
var data = {
alt: img.altText || "",
src: (img.url) ? img.url : "nothing.jpg",
id: "__mcenew",
"data-udi": img.udi
};
editor.selection.setContent(editor.dom.createHTML('img', data));
// Using settimeout to wait for a DoM-render, so we can find the new element by ID.
$timeout(function () {
var imgElm = editor.dom.get("__mcenew");
editor.dom.setAttrib(imgElm, "id", null);
// When image is loaded we are ready to call sizeImageInEditor.
var onImageLoaded = function() {
sizeImageInEditor(editor, imgElm, img.url);
editor.fire("Change");
}
// Check if image already is loaded.
if(imgElm.complete === true) {
onImageLoaded();
} else {
imgElm.onload = onImageLoaded;
}
});
}
}
},
@@ -1287,8 +1346,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
var content = e.content;
// Upload BLOB images (dragged/pasted ones)
if(content.indexOf('<img src="blob:') > -1){
// find src attribute where value starts with `blob:`
// search is case-insensitive and allows single or double quotes
if(content.search(/src=["']blob:.*?["']/gi) !== -1){
args.editor.uploadImages(function(data) {
// Once all images have been uploaded
data.forEach(function(item) {
@@ -1402,7 +1462,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
});
//Create the insert media plugin
self.createMediaPicker(args.editor, function (currentTarget, userData) {
self.createMediaPicker(args.editor, function (currentTarget, userData, imgDomElement) {
var startNodeId, startNodeIsVirtual;
if (!args.model.config.startNodeId) {
@@ -1425,7 +1485,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
startNodeIsVirtual: startNodeIsVirtual,
dataTypeKey: args.model.dataTypeKey,
submit: function (model) {
self.insertMediaInEditor(args.editor, model.selection[0]);
self.insertMediaInEditor(args.editor, model.selection[0], imgDomElement);
editorService.close();
},
close: function () {

View File

@@ -47,7 +47,11 @@ app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService',
var originalTitle = "";
$rootScope.$on('$changeTitle', function (event, titlePrefix) {
$rootScope.locationTitle = titlePrefix + " - " + originalTitle;
if (titlePrefix) {
$rootScope.locationTitle = titlePrefix + " - " + originalTitle;
} else {
$rootScope.locationTitle = originalTitle;
}
});
/** execute code on each successful route */

View File

@@ -12,17 +12,20 @@
align-items: center;
justify-content: space-between;
padding: 0 20px;
cursor: pointer;
background: transparent;
border: 0 none;
border-bottom: 1px solid @gray-9;
height: @editorHeaderHeight;
box-sizing: border-box;
color: @ui-option-type;
width: 100%;
}
.umb-language-picker__expand {
font-size: 14px;
}
.umb-language-picker__toggle:focus,
.umb-language-picker__toggle:hover {
background: @ui-option-hover;
color:@ui-option-type-hover;
@@ -43,20 +46,24 @@
overflow: auto;
}
.umb-language-picker__dropdown a {
.umb-language-picker__dropdown-item {
background: transparent;
border: 0 none;
padding: 8px 20px;
display: block;
font-size: 14px;
width: 100%;
text-align: left;
}
.umb-language-picker__dropdown a:hover,
.umb-language-picker__dropdown a:focus {
.umb-language-picker__dropdown-item:hover,
.umb-language-picker__dropdown-item:focus {
background: @ui-option-hover;
text-decoration: none;
color:@ui-option-type-hover;
}
.umb-language-picker__dropdown a.umb-language-picker__dropdown-item--current {
.umb-language-picker__dropdown .umb-language-picker__dropdown-item.umb-language-picker__dropdown-item--current {
padding-left: 16px;
border-left: 4px solid @ui-light-active-border;
color:@ui-light-active-type;

View File

@@ -151,6 +151,10 @@
bottom: 0;
border: none;
box-shadow: 0 0 20px rgba(0,0,0,0.19), 0 0 6px rgba(0,0,0,0.23);
.umb-drawer-is-visible & {
right:400px;
}
}
.umb-overlay.umb-overlay-right .umb-overlay-header {

View File

@@ -3,6 +3,7 @@
display: flex;
box-sizing: border-box;
border-bottom: 1px solid @gray-9;
flex-wrap: wrap;
}
.umb-editor-wrapper .umb-node-preview {
@@ -64,6 +65,7 @@
flex: 0 0 auto;
display: flex;
align-items: center;
margin-left: auto;
}
.umb-node-preview__action {

View File

@@ -5,6 +5,7 @@
var vm = this;
vm.labels = {};
vm.hideSearch = hideSearch;
vm.selectResult = selectResult;
vm.onSearchResults = onSearchResults;
@@ -12,7 +13,6 @@
vm.close = close;
var dialogOptions = $scope.model;
var searchText = "Search...";
var node = dialogOptions.currentNode;
$scope.model.relateToOriginal = true;
@@ -29,23 +29,30 @@
// get entity type based on the section
$scope.entityType = entityHelper.getEntityTypeFromSection(dialogOptions.section);
function onInit() {
function onInit() {
if(!$scope.model.title) {
localizationService.localize("general_copy").then(function (value) {
$scope.model.title = value;
});
}
var labelKeys = [
"general_copy",
"defaultdialogs_relateToOriginalLabel"
];
localizationService.localize("general_search").then(function (value) {
searchText = value + "...";
});
}
localizationService.localizeMany(labelKeys).then(function (data) {
vm.labels.title = data[0];
vm.labels.relateToOriginal = data[1];
setTitle(vm.labels.title);
});
}
function setTitle(value) {
if (!$scope.model.title) {
$scope.model.title = value;
}
}
function nodeSelectHandler(args) {
if(args && args.event) {
if (args && args.event) {
args.event.preventDefault();
args.event.stopPropagation();
}
@@ -108,19 +115,18 @@
}
function submit() {
if($scope.model && $scope.model.submit) {
if ($scope.model && $scope.model.submit) {
$scope.model.submit($scope.model);
}
}
function close() {
if($scope.model && $scope.model.close) {
if ($scope.model && $scope.model.close) {
$scope.model.close();
}
}
onInit();
}
angular.module("umbraco").controller("Umbraco.Editors.CopyController", CopyController);

View File

@@ -60,10 +60,9 @@
</div>
<div class="umb-control-group -no-border">
<label>
<input type="checkbox" class="pull-left" style="margin-right: 10px;" ng-model="model.relateToOriginal" />
<localize key="defaultdialogs_relateToOriginalLabel">Relate to originalt</localize>
</label>
<div class="flex">
<umb-checkbox input-id="relateToOriginal" model="model.relateToOriginal" text="{{vm.labels.relateToOriginal}}" />
</div>
</div>
</umb-box-content>
</umb-box>

View File

@@ -37,7 +37,7 @@ angular.module("umbraco")
$scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId");
$scope.lockedFolder = true;
$scope.allowMediaEdit = dialogOptions.allowMediaEdit ? dialogOptions.allowMediaEdit : false;
var userStartNodes = [];
var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings;
@@ -124,11 +124,15 @@ angular.module("umbraco")
gotoStartNode();
}
} else {
//if a target is specified, go look it up - generally this target will just contain ids not the actual full
//media object so we need to look it up
// if a target is specified, go look it up - generally this target will just contain ids not the actual full
// media object so we need to look it up
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id;
var altText = $scope.target.altText;
entityResource.getById(id, "Media")
// ID of a UDI or legacy int ID still could be null/undefinied here
// As user may dragged in an image that has not been saved to media section yet
if(id){
entityResource.getById(id, "Media")
.then(function (node) {
$scope.target = node;
if (ensureWithinStartNode(node)) {
@@ -138,6 +142,12 @@ angular.module("umbraco")
openDetailsDialog();
}
}, gotoStartNode);
}
else {
// No ID set - then this is going to be a tmpimg that has not been uploaded
// User editing this will want to be changing the ALT text
openDetailsDialog();
}
}
}
@@ -216,7 +226,7 @@ angular.module("umbraco")
}
function clickHandler(media, event, index) {
if (media.isFolder) {
if ($scope.disableFolderSelect) {
gotoFolder(media);
@@ -443,21 +453,25 @@ angular.module("umbraco")
function getChildren(id) {
vm.loading = true;
return entityResource.getChildren(id, "Media", vm.searchOptions).then(function (data) {
for (var i = 0; i < data.length; i++) {
if (data[i].metaData.MediaPath !== null) {
data[i].thumbnail = mediaHelper.resolveFileFromEntity(data[i], true);
data[i].image = mediaHelper.resolveFileFromEntity(data[i], false);
}
var allowedTypes = dialogOptions.filter ? dialogOptions.filter.split(",") : null;
for (var i = 0; i < data.length; i++) {
if (data[i].metaData.MediaPath !== null) {
data[i].thumbnail = mediaHelper.resolveFileFromEntity(data[i], true);
data[i].image = mediaHelper.resolveFileFromEntity(data[i], false);
}
vm.searchOptions.filter = "";
$scope.images = data ? data : [];
data[i].filtered = allowedTypes && allowedTypes.indexOf(data[i].metaData.ContentTypeAlias) < 0;
}
// set already selected medias to selected
preSelectMedia();
vm.loading = false;
});
vm.searchOptions.filter = "";
$scope.images = data ? data : [];
// set already selected medias to selected
preSelectMedia();
vm.loading = false;
});
}
function preSelectMedia() {

View File

@@ -64,7 +64,7 @@
</li>
<li class="umb-breadcrumbs__ancestor" ng-show="!lockedFolder">
<a href ng-hide="model.showFolderInput" ng-click="model.showFolderInput = true">
<a role="button" aria-label="Create new folder" ng-hide="model.showFolderInput" ng-click="model.showFolderInput = true">
<i class="icon icon-add small"></i>
</a>

View File

@@ -6,13 +6,29 @@
<div class="navigation-inner-container">
<div class="umb-language-picker" ng-if="currentSection === 'content' && languages.length > 1" on-outside-click="page.languageSelectorIsOpen = false">
<div class="umb-language-picker__toggle" ng-click="toggleLanguageSelector()">
<div>{{selectedLanguage.name}}</div>
<ins class="umb-language-picker__expand" ng-class="{'icon-navigation-down': !page.languageSelectorIsOpen, 'icon-navigation-up': page.languageSelectorIsOpen}" class="icon-navigation-right">&nbsp;</ins>
</div>
<div class="umb-language-picker" ng-if="currentSection === 'content' && languages.length > 1" deep-blur="page.languageSelectorIsOpen = false" on-outside-click="page.languageSelectorIsOpen = false">
<button type="button" class="umb-language-picker__toggle" ng-click="toggleLanguageSelector()" aria-haspopup="true" aria-expanded="{{page.languageSelectorIsOpen}}">
<span>
<span class="sr-only">
<localize key="visuallyHiddenTexts_currentLanguage">Current language</localize>
</span>
<span>: {{selectedLanguage.name}}</span>
</span>
<i class="umb-language-picker__expand" ng-class="{'icon-navigation-down': !page.languageSelectorIsOpen, 'icon-navigation-up': page.languageSelectorIsOpen}" class="icon-navigation-right" aria-hidden="true"></i>
</button>
<div class="umb-language-picker__dropdown" ng-if="page.languageSelectorIsOpen">
<a class="umb-language-picker__dropdown-item" ng-class="{'umb-language-picker__dropdown-item--current': language.active}" ng-click="selectLanguage(language)" ng-repeat="language in languages" href="">{{language.name}}</a>
<button
type="button"
class="umb-language-picker__dropdown-item"
ng-class="{'umb-language-picker__dropdown-item--current': language.active}"
ng-click="selectLanguage(language)"
ng-repeat="language in languages"
>
<span class="sr-only">
<localize key="visuallyHiddenTexts_switchLanguage">Switch language to</localize>
</span>
<span>: {{language.name}}</span>
</button>
</div>
</div>

View File

@@ -1,4 +1,4 @@
<button ng-click="click()" type="button" class="umb-toggle" ng-disabled="disabled" ng-class="{'umb-toggle--checked': checked, 'umb-toggle--disabled': disabled}">
<button role="checkbox" aria-checked="{{checked}}" ng-click="click()" type="button" class="umb-toggle" ng-disabled="disabled" ng-class="{'umb-toggle--checked': checked, 'umb-toggle--disabled': disabled}" id="{{inputId}}">
<span ng-if="!labelPosition && showLabels === 'true' || labelPosition === 'left' && showLabels === 'true'">
<span ng-if="!checked" class="umb-toggle__label umb-toggle__label--left">{{ displayLabelOff }}</span>

View File

@@ -140,6 +140,14 @@
{{currentVariant.createDateFormatted}}
</umb-control-group>
<umb-control-group ng-show="node.id !== 0 && currentVariant.releaseDateFormatted" data-element="node-info-publish-date" label="@content_releaseDate">
{{currentVariant.releaseDateFormatted}}
</umb-control-group>
<umb-control-group ng-show="node.id !== 0 && currentVariant.expireDateFormatted" data-element="node-info-expire-date" label="@content_expireDate">
{{currentVariant.expireDateFormatted}}
</umb-control-group>
<umb-control-group data-element="node-info-document-type" label="@content_documentType">
<umb-node-preview
style="min-width: 100%; margin-bottom: 0;"

View File

@@ -98,7 +98,7 @@
</div>
</ng-form>
<div class="umb-group-builder__group-remove" ng-if="!sortingMode">
<div class="umb-group-builder__group-remove" ng-if="!sortingMode && canRemoveGroup(tab)">
<i class="icon-trash" ng-click="togglePrompt(tab)"></i>
<umb-confirm-action
ng-if="tab.deletePrompt"
@@ -243,7 +243,7 @@
</umb-property-editor>
</ng-form>
<button class="umb-group-builder__open-settings" ng-if="!property.inherited && !property.locked" ng-click="editPropertyTypeSettings(property, tab)"></button>
<button aria-label="Open settings" class="umb-group-builder__open-settings" ng-if="!property.inherited && !property.locked" ng-click="editPropertyTypeSettings(property, tab)"></button>
</div>
@@ -254,12 +254,12 @@
<!-- settings for property -->
<div class="umb-group-builder__property-action">
<button class="icon icon-settings" ng-click="editPropertyTypeSettings(property, tab)"></button>
<button aria-label="Open settings" class="icon icon-settings" ng-click="editPropertyTypeSettings(property, tab)"></button>
</div>
<!-- delete property -->
<div ng-if="!property.locked" class="umb-group-builder__property-action">
<button class="icon-trash" ng-click="togglePrompt(property)"></button>
<button aria-label="Delete property" class="icon-trash" ng-click="togglePrompt(property)"></button>
<umb-confirm-action
ng-if="property.deletePrompt"
direction="left"

View File

@@ -2,11 +2,11 @@
<div class="umb-locked-field__wrapper">
<button ng-if="locked" ng-click="unlock()" class="umb-locked-field__toggle">
<button aria-label="Unlock" ng-if="locked" ng-click="unlock()" class="umb-locked-field__toggle">
<i class="umb-locked-field__lock-icon icon-lock" aria-hidden="true"></i>
</button>
<button ng-if="!locked" ng-click="lock()" class="umb-locked-field__toggle">
<button aria-label="Lock" ng-if="!locked" ng-click="lock()" class="umb-locked-field__toggle">
<i class="umb-locked-field__lock-icon icon-unlocked -unlocked" aria-hidden="true"></i>
</button>

View File

@@ -1,15 +1,17 @@
<div class="umb-node-preview" ng-class="{'umb-node-preview--sortable': sortable, 'umb-node-preview--unpublished': published === false }">
<i ng-if="icon" class="umb-node-preview__icon {{ icon }}" aria-hidden="true"></i>
<div class="umb-node-preview__content">
<div class="flex"> <!-- div keeps icon and nodename from wrapping -->
<i ng-if="icon" class="umb-node-preview__icon {{ icon }}" aria-hidden="true"></i>
<div class="umb-node-preview__content">
<div class="umb-node-preview__name" ng-attr-title="{{alias}}">{{ name }}</div>
<div class="umb-node-preview__description" ng-if="description">{{ description }}</div>
<div class="umb-node-preview__name" ng-attr-title="{{alias}}">{{ name }}</div>
<div class="umb-node-preview__description" ng-if="description">{{ description }}</div>
<div class="umb-user-group-preview__permissions" ng-if="permissions">
<span>
<span class="bold"><localize key="general_rights">Permissions</localize>:</span>
<span ng-repeat="permission in permissions" class="umb-user-group-preview__permission">{{ permission.name }}</span>
</span>
<div class="umb-user-group-preview__permissions" ng-if="permissions">
<span>
<span class="bold"><localize key="general_rights">Permissions</localize>:</span>
<span ng-repeat="permission in permissions" class="umb-user-group-preview__permission">{{ permission.name }}</span>
</span>
</div>
</div>
</div>
<div class="umb-node-preview__actions">

View File

@@ -1,6 +1,7 @@
<div class="umb-property-file-upload">
<ng-form name="vm.fileUploadForm">
<input type="hidden" ng-model="mandatoryValidator" ng-required="vm.required && !vm.files.length" />
<div class="fileinput-button umb-upload-button-big"
style="margin-bottom: 5px;"

View File

@@ -14,26 +14,32 @@ function contentCreateController($scope,
navigationService,
blueprintConfig,
authResource,
contentResource) {
contentResource,
$q) {
var mainCulture = $routeParams.mculture ? $routeParams.mculture : null;
function initialize() {
$scope.loading = true;
$scope.allowedTypes = null;
contentTypeResource.getAllowedTypes($scope.currentNode.id).then(function (data) {
var getAllowedTypes = contentTypeResource.getAllowedTypes($scope.currentNode.id).then(function (data) {
$scope.allowedTypes = iconHelper.formatContentTypeIcons(data);
});
if ($scope.currentNode.id > -1) {
authResource.getCurrentUser().then(function(currentUser) {
if (currentUser.allowedSections.indexOf("settings") > -1) {
$scope.hasSettingsAccess = true;
contentResource.getById($scope.currentNode.id).then(function(data) {
var getCurrentUser = authResource.getCurrentUser().then(function (currentUser) {
if (currentUser.allowedSections.indexOf("settings") > -1) {
$scope.hasSettingsAccess = true;
if ($scope.currentNode.id > -1) {
contentResource.getById($scope.currentNode.id).then(function (data) {
$scope.contentTypeId = data.contentTypeId;
});
}
});
}
}
});
$q.all([getAllowedTypes, getCurrentUser]).then(function() {
$scope.loading = false;
});
$scope.selectContentType = true;
$scope.selectBlueprint = false;
@@ -97,7 +103,12 @@ function contentCreateController($scope,
navigationService.hideDialog(showMenu);
};
$scope.editContentType = function() {
$scope.createContentType = function () {
$location.path("/settings/documenttypes/edit/-1").search("create", "true");
close();
}
$scope.editContentType = function () {
$location.path("/settings/documenttypes/edit/" + $scope.contentTypeId).search("view", "permissions");
close();
}

View File

@@ -1,22 +1,34 @@
<div ng-controller="Umbraco.Editors.Content.CreateController">
<div class="umb-dialog-body with-footer" ng-cloak>
<umb-load-indicator ng-if="loading"></umb-load-indicator>
<div ng-if="!loading" class="umb-dialog-body with-footer" ng-cloak>
<div class="umb-pane">
<h5 ng-show="selectContentType"><localize key="create_createUnder">Create a page under</localize> {{currentNode.name}}</h5>
<h5 ng-show="selectBlueprint"><localize key="blueprints_selectBlueprint">Select a blueprint</localize></h5>
<div ng-if="allowedTypes && allowedTypes.length === 0">
<p class="abstract" ng-if="!hasSettingsAccess"><localize key="create_noDocumentTypesWithNoSettingsAccess"/></p>
<div ng-if="hasSettingsAccess">
<p class="abstract"><localize key="create_noDocumentTypes" /></p>
<p class="abstract" ng-if="!hasSettingsAccess"><localize key="create_noDocumentTypesWithNoSettingsAccess" /></p>
<div ng-if="hasSettingsAccess && currentNode.id >= 0">
<p class="abstract">
<localize key="create_noDocumentTypes" />
</p>
<button class="btn umb-outline" ng-click="editContentType()">
<localize key="create_noDocumentTypesEditPermissions"/>
<localize key="create_noDocumentTypesEditPermissions" />
</button>
</div>
<div ng-if="hasSettingsAccess && currentNode.id < 0">
<p class="abstract">
<localize key="create_noDocumentTypesAtRoot" />
</p>
<button class="btn umb-outline" ng-click="createContentType()">
<localize key="create_noDocumentTypesCreateNew" />
</button>
</div>
</div>
<ul class="umb-actions umb-actions-child" ng-if="selectContentType && allowedTypes.length > 0" >
<ul class="umb-actions umb-actions-child" ng-if="selectContentType && allowedTypes.length > 0">
<li class="umb-action" data-element="action-create-{{docType.alias}}" ng-repeat="docType in allowedTypes | orderBy:'name':false">
<button class="umb-action-link umb-outline btn-reset" ng-click="createOrSelectBlueprintIfAny(docType)" umb-auto-focus ng-if="$index === 0">
@@ -29,14 +41,14 @@
</span>
</button>
<button class="umb-action-link umb-outline btn-reset" ng-click="createOrSelectBlueprintIfAny(docType)" ng-if="$index !== 0">
<i class="large icon {{docType.icon}}"></i>
<span class="menu-label">
{{docType.name}}
<small>
{{docType.description}}
</small>
</span>
</button>
<i class="large icon {{docType.icon}}"></i>
<span class="menu-label">
{{docType.name}}
<small>
{{docType.description}}
</small>
</span>
</button>
</li>
</ul>
@@ -65,7 +77,7 @@
</div>
</div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<div ng-if="!loading" class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<button class="btn btn-info umb-outline" ng-click="closeDialog(true)">
<localize key="buttons_somethingElse">All Actions</localize>
</button>

View File

@@ -1,6 +1,6 @@
<div ng-controller="Umbraco.Overlays.PublishController as vm">
<div style="margin-bottom: 15px;">
<div class="mb3">
<p>{{vm.headline}}</p>
</div>
@@ -48,14 +48,14 @@
</div>
<div class="umb-list umb-list--condensed" ng-if="!vm.loading && vm.hasPristineVariants">
<div style="margin-bottom: 15px; font-weight: bold;">
<div class="bold mb3">
<p><localize key="content_publishedLanguages"></localize></p>
</div>
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.pristineVariantFilter">
<div>
<div style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
<span>{{variant.language.name}}</span>
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
</div>

View File

@@ -49,8 +49,8 @@
server-validation-field="{{variant.htmlId}}"/>
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 0;">
<span>{{ variant.language.name }}</span>
<label for="{{variant.htmlId}}" class="mb0">
<span>{{variant.language.name}}</span>
<span class="db umb-list-item__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>

View File

@@ -1,13 +1,11 @@
<div ng-controller="Umbraco.Overlays.SaveContentController as vm">
<div ng-if="vm.loading" style="min-height: 50px; position: relative;">
<umb-load-indicator></umb-load-indicator>
</div>
<div ng-if="!vm.loading">
<div style="margin-bottom: 15px;">
<div class="mb3">
<p>
<localize ng-if="!vm.isNew" key="content_languagesToSave"></localize>
<localize ng-if="vm.isNew" key="content_languagesToSaveForFirstTime"></localize>
@@ -53,7 +51,7 @@
</div>
<div class="umb-list umb-list--condensed" ng-if="vm.hasPristineVariants">
<div style="margin-bottom: 15px; font-weight: bold;">
<div class="bold mb3">
<p>
<localize ng-if="!vm.isNew" key="content_unmodifiedLanguages"></localize>
<localize ng-if="vm.isNew" key="content_untouchedLanguagesForFirstTime"></localize>

View File

@@ -3,7 +3,7 @@
<!-- invariant nodes -->
<div ng-if="vm.variants.length === 1">
<div style="margin-bottom: 15px;">
<div class="mb3">
<p><localize key="content_schedulePublishHelp"></localize></p>
</div>
@@ -78,7 +78,7 @@
<!-- nodes with variants -->
<div ng-if="vm.variants.length > 1">
<div style="margin-bottom: 15px;">
<div class="mb3">
<p><localize key="content_languagesToSchedule"></localize></p>
</div>

View File

@@ -1,6 +1,6 @@
<div ng-controller="Umbraco.Overlays.SendToPublishController as vm">
<div style="margin-bottom: 15px;">
<div class="mb3">
<p><localize key="content_languagesToSendForApproval"></localize></p>
</div>
@@ -46,7 +46,7 @@
</div>
<div class="umb-list umb-list--condensed" ng-if="!vm.loading && (vm.variants | filter:vm.unmodifiedVariantFilter).length > 0">
<div style="margin-bottom: 15px; font-weight: bold;">
<div class="bold mb3">
<p><localize key="content_unmodifiedLanguages"></localize></p>
</div>

View File

@@ -1,6 +1,6 @@
<div ng-controller="Umbraco.Overlays.UnpublishController as vm">
<!-- 1 language -->
<!-- Single language -->
<div ng-if="vm.variants.length === 1">
<p><localize key="prompt_confirmUnpublish"></localize></p>
</div>
@@ -43,7 +43,7 @@
</div>
<div class="umb-list umb-list--condensed" ng-if="!vm.loading && (vm.variants | filter:vm.unpublishedVariantFilter).length > 0">
<div style="margin-bottom: 15px; font-weight: bold;">
<div class="bold mb3">
<p><localize key="content_unpublishedLanguages"></localize></p>
</div>

View File

@@ -1,4 +1,4 @@
function ExamineManagementController($scope, $http, $q, $timeout, umbRequestHelper, localizationService, overlayService) {
function ExamineManagementController($scope, $http, $q, $timeout, $location, umbRequestHelper, localizationService, overlayService, editorService) {
var vm = this;
@@ -20,6 +20,7 @@ function ExamineManagementController($scope, $http, $q, $timeout, umbRequestHelp
vm.nextSearchResultPage = nextSearchResultPage;
vm.prevSearchResultPage = prevSearchResultPage;
vm.goToPageSearchResultPage = goToPageSearchResultPage;
vm.goToResult = goToResult;
vm.infoOverlay = null;
@@ -50,6 +51,45 @@ function ExamineManagementController($scope, $http, $q, $timeout, umbRequestHelp
search(vm.selectedIndex ? vm.selectedIndex : vm.selectedSearcher, null, pageNumber);
}
function goToResult(result, event) {
if (!result.editUrl) {
return;
}
// targeting a new tab/window?
if (event.ctrlKey ||
event.shiftKey ||
event.metaKey || // apple
(event.button && event.button === 1) // middle click, >IE9 + everyone else
) {
// yes, let the link open itself
return;
}
const editor = {
id: result.editId,
submit: function (model) {
editorService.close();
},
close: function () {
editorService.close();
}
};
switch (result.editSection) {
case "content":
editorService.contentEditor(editor);
break;
case "media":
editorService.mediaEditor(editor);
break;
case "member":
editorService.memberEditor(editor);
break;
}
event.stopPropagation();
event.preventDefault();
}
function setViewState(state) {
vm.searchResults = null;
vm.viewState = state;
@@ -125,6 +165,23 @@ function ExamineManagementController($scope, $http, $q, $timeout, umbRequestHelp
vm.searchResults.pageNumber = pageNumber ? pageNumber : 1;
//20 is page size
vm.searchResults.totalPages = Math.ceil(vm.searchResults.totalRecords / 20);
// add URLs to edit well known entities
_.each(vm.searchResults.results, function (result) {
var section = result.values["__IndexType"];
switch (section) {
case "content":
case "media":
result.editUrl = "/" + section + "/" + section + "/edit/" + result.values["__NodeId"];
result.editId = result.values["__NodeId"];
result.editSection = section;
break;
case "member":
result.editUrl = "/member/member/edit/" + result.values["__Key"];
result.editId = result.values["__Key"];
result.editSection = section;
break;
}
});
});
}

View File

@@ -157,7 +157,9 @@
<td>{{result.score}}</td>
<td>{{result.id}}</td>
<td>
<span>{{result.values['nodeName']}}</span>&nbsp;
<a ng-show="result.editUrl" ng-click="vm.goToResult(result, $event)" ng-href="#{{result.editUrl}}">{{result.values['nodeName']}}</a>
<span ng-hide="result.editUrl">{{result.values['nodeName']}}</span>
&nbsp;
<a class="color-green" href="" ng-click="vm.showSearchResultDialog(result.values)">
<em>({{result.fieldCount}} fields)</em>
</a>
@@ -298,7 +300,9 @@
<td>{{result.score}}</td>
<td>{{result.id}}</td>
<td>
<span>{{result.values['nodeName']}}</span>&nbsp;
<a ng-show="result.editUrl" ng-click="vm.goToResult(result, $event)" ng-href="#{{result.editUrl}}">{{result.values['nodeName']}}</a>
<span ng-hide="result.editUrl">{{result.values['nodeName']}}</span>
&nbsp;
<a class="color-green" href="" ng-click="vm.showSearchResultDialog(result.values)">
<em>({{result.fieldCount}} fields)</em>
</a>

View File

@@ -1,26 +1,30 @@
<div ng-controller="Umbraco.Dashboard.ProfilerController as vm" ng-hide="vm.loading">
<umb-box>
<div ng-controller="Umbraco.Dashboard.ProfilerController as vm">
<div ng-show="vm.loading">
<umb-load-indicator></umb-load-indicator>
</div>
<umb-box ng-hide="vm.loading">
<umb-box-content>
<h3 class="bold">Performance profiling</h3>
<div ng-show="vm.profilerEnabled">
<p>
Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages.
</p>
<p>
If you want to activate the profiler for a specific page rendering, simply add <b>umbDebug=true</b> to the querystring when requesting the page.
</p>
<p>
If you want the profiler to be activated by default for all page renderings, you can use the toggle below.
It will set a cookie in your browser, which then activates the profiler automatically.
In other words, the profiler will only be active by default in <i>your</i> browser - not everyone else's.
</p>
<p>&nbsp;</p>
<div class="sub-view-columns">
<div class="sub-view-column-left">
<h5>Activate the profiler by default</h5>
</div>
<div class="sub-view-column-right">
<umb-toggle checked="vm.alwaysOn" on-click="vm.toggle()"></umb-toggle>
<div class="mb4">
<p>
Umbraco currently runs in debug mode. This means you can use the built-in performance profiler to assess the performance when rendering pages.
</p>
<p>
If you want to activate the profiler for a specific page rendering, simply add <b>umbDebug=true</b> to the querystring when requesting the page.
</p>
<p>
If you want the profiler to be activated by default for all page renderings, you can use the toggle below.
It will set a cookie in your browser, which then activates the profiler automatically.
In other words, the profiler will only be active by default in <i>your</i> browser - not everyone else's.
</p>
</div>
<div class="mb4">
<div class="flex items-center">
<umb-toggle checked="vm.alwaysOn" id="profilerAlwaysOn" on-click="vm.toggle()"></umb-toggle>
<label for="profilerAlwaysOn" class="mb0 ml2">Activate the profiler by default</label>
</div>
</div>
<h4>Friendly reminder</h4>

View File

@@ -1,5 +1,6 @@
<div ng-controller="Umbraco.PrevalueEditors.BooleanController">
<umb-toggle
input-id="{{model.alias}}"
checked="toggleValue"
on-click="toggle()">
</umb-toggle>

View File

@@ -27,7 +27,7 @@ function mediaFolderPickerController($scope, editorService, entityResource) {
$scope.add = function() {
var mediaPickerOptions = {
view: "mediapicker",
multiPicker: true,
multiPicker: false, // We only want to allow you to pick one folder at a given time
disableFolderSelect: false,
onlyImages: false,
onlyFolders: true,

View File

@@ -20,14 +20,14 @@
<div class="umb-sortable-thumbnails__actions" data-element="sortable-thumbnail-actions">
<a class="umb-sortable-thumbnails__action -red" data-element="action-remove" href="" ng-click="remove()">
<a role="button" aria-label="Remove" class="umb-sortable-thumbnails__action -red" data-element="action-remove" ng-click="remove()">
<i class="icon icon-delete"></i>
</a>
</div>
</li>
<li style="border: none;" class="add-wrapper unsortable" ng-hide="model.value">
<a data-element="sortable-thumbnails-add" href="#" class="add-link add-link-square" ng-click="add()" prevent-default>
<a role="button" aria-label="Open media picker" data-element="sortable-thumbnails-add" class="add-link add-link-square" ng-click="add()" prevent-default>
<i class="icon icon-add large"></i>
</a>
</li>

View File

@@ -1,5 +1,6 @@
<div class="umb-property-editor umb-boolean" ng-controller="Umbraco.PropertyEditors.BooleanController">
<umb-toggle
input-id="{{model.alias}}"
checked="renderModel.value"
on-click="toggle()"
show-labels="{{model.config.labelOn ? 'true': 'false'}}"
@@ -7,4 +8,4 @@
label-on="{{model.config.labelOn}}"
label-off="{{model.config.labelOn}}">
</umb-toggle>
</div>
</div>

View File

@@ -203,7 +203,9 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
//now we need to filter based on what is stored in the pre-vals, this logic duplicates what is in the treepicker.controller,
// but not much we can do about that since members require special filtering.
var filterItem = currFilter.toLowerCase().split(',');
var found = filterItem.indexOf(i.metaData.contentType.toLowerCase()) >= 0;
// NOTE: when used in a mini list view, the item content type alias is metaData.ContentTypeAlias (in regular views it's metaData.contentType)
var itemContentType = i.metaData.contentType || i.metaData.ContentTypeAlias;
var found = filterItem.indexOf(itemContentType.toLowerCase()) >= 0;
if (!currFilter.startsWith("!") && !found || currFilter.startsWith("!") && found) {
return true;
}

View File

@@ -2,6 +2,7 @@
<umb-property-file-upload culture="{{model.culture}}"
property-alias="{{model.alias}}"
value="model.value"
required="model.validation.mandatory"
on-files-selected="fileChanged(value)">
</umb-property-file-upload>
</div>

View File

@@ -5,6 +5,7 @@
<umb-property-file-upload culture="{{model.culture}}"
property-alias="{{model.alias}}"
value="model.value.src"
required="model.validation.mandatory"
on-files-selected="filesSelected(value, files)"
on-files-changed="filesChanged(files)"
on-init="fileUploaderInit(value, files)"

View File

@@ -0,0 +1,31 @@
function iconPreValsController($scope, editorService) {
if (!$scope.model.value) {
$scope.model.value = "icon-list";
}
$scope.openIconPicker = function () {
var iconPicker = {
icon: $scope.model.value.split(' ')[0],
color: $scope.model.value.split(' ')[1],
submit: function (model) {
if (model.icon) {
if (model.color) {
$scope.model.value = model.icon + " " + model.color;
} else {
$scope.model.value = model.icon;
}
$scope.iconForm.$setDirty();
}
editorService.close();
},
close: function () {
editorService.close();
}
};
editorService.iconPicker(iconPicker);
};
}
angular.module("umbraco").controller("Umbraco.PrevalueEditors.IconPickerController", iconPreValsController);

View File

@@ -0,0 +1,11 @@
<div ng-controller="Umbraco.PrevalueEditors.IconPickerController">
<ng-form data-element="editor-icon" name="iconForm">
<div class="umb-panel-header-icon" ng-if="!hideIcon" ng-click="openIconPicker()" ng-class="{'-placeholder': model.value==='' || model.value===null}"
title="{{model.value}}">
<i class="icon {{model.value}}" ng-if="model.value!=='' && model.value!==null"></i>
<div class="umb-panel-header-icon-text" ng-if="model.value==='' || model.value===null">
<localize key="settings_addIcon"></localize>
</div>
</div>
</ng-form>
</div>

View File

@@ -262,10 +262,10 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
$scope.getContent = function (contentId) {
$scope.reloadView($scope.contentId);
$scope.reloadView($scope.contentId, true);
}
$scope.reloadView = function (id) {
$scope.reloadView = function (id, reloadActiveNode) {
$scope.viewLoaded = false;
$scope.folders = [];
@@ -297,9 +297,20 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
$scope.options.pageNumber = $scope.listViewResultSet.totalPages;
//reload!
$scope.reloadView(id);
$scope.reloadView(id, reloadActiveNode);
}
// in the media section, the list view items are by default also shown in the tree, so we need
// to refresh the current tree node when changing the folder contents (adding and removing)
else if (reloadActiveNode && section === "media") {
var activeNode = appState.getTreeState("selectedNode");
if (activeNode) {
if (activeNode.expanded) {
navigationService.reloadNode(activeNode);
}
} else {
navigationService.reloadSection(section);
}
}
});
};
@@ -411,7 +422,7 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
var key = (total === 1 ? "bulk_deletedItem" : "bulk_deletedItems");
return localizationService.localize(key, [total]);
}).then(function () {
$scope.reloadView($scope.contentId);
$scope.reloadView($scope.contentId, true);
});
}
@@ -489,6 +500,7 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
const dialog = {
view: "views/propertyeditors/listview/overlays/listviewunpublish.html",
submitButtonLabelKey: "actions_unpublish",
submitButtonStyle: "warning",
submit: function (model) {
// create a comma separated array of selected cultures
@@ -536,7 +548,7 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
var key = (total === 1 ? "bulk_unpublishedItem" : "bulk_unpublishedItems");
return localizationService.localize(key, [total]);
}).then(function () {
$scope.reloadView($scope.contentId);
$scope.reloadView($scope.contentId, true);
});
}

View File

@@ -1,41 +1,46 @@
<div ng-controller="Umbraco.Overlays.ListViewPublishController as vm">
<!-- Single language -->
<div ng-if="!vm.languages">
<p><localize key="prompt_confirmListViewPublish"></localize></p>
</div>
<div ng-if="vm.languages.length > 1">
<div ng-if="vm.loading" style="min-height: 50px; position: relative;">
<umb-load-indicator></umb-load-indicator>
</div>
<!-- Multiple languages -->
<div ng-if="vm.languages.length > 1 && !vm.loading">
<div class="mb3">
<p><localize key="content_languagesToPublish"></localize></p>
</div>
<div ng-if="vm.loading" style="min-height: 50px; position: relative;">
<umb-load-indicator></umb-load-indicator>
</div>
<div class="umb-list umb-list--condensed">
<div class="umb-list umb-list--condensed" ng-if="!vm.loading">
<div class="bold mb1">
<localize key="treeHeaders_languages"></localize>
</div>
<div class="umb-list-item" ng-repeat="language in vm.languages track by language.id">
<ng-form name="publishLanguageSelectorForm">
<div class="flex">
<input
id="{{language.culture}}"
name="publishLanguageSelector"
type="checkbox"
ng-model="language.publish"
ng-change="vm.changeSelection(language)"
style="margin-right: 8px;" />
<umb-checkbox input-id="publishLanguage_{{language.culture}}"
name="publishLanguageSelector"
model="language.publish"
on-change="vm.changeSelection(language)" />
<div>
<label for="{{language.culture}}" style="margin-bottom: 2px;">
<span>{{ language.name }}</span>
<label for="publishLanguage_{{language.culture}}" class="mb0">
<span>{{language.name}}</span>
<span class="db umb-list-item__description">
<span ng-if="language.isMandatory"><localize key="languages_mandatoryLanguage"></localize></span>
</span>
</label>
</div>
<div class="umb-list-item__description">
<span ng-if="language.isMandatory"><localize key="languages_mandatoryLanguage"></localize></span>
</div>
</div>
</div>
</ng-form>

View File

@@ -1,12 +1,16 @@
<div ng-controller="Umbraco.Overlays.ListViewUnpublishController as vm">
<!-- 1 language -->
<!-- Single language -->
<div ng-if="!vm.languages">
<p><localize key="prompt_confirmListViewUnpublish"></localize></p>
</div>
<div ng-if="vm.loading" style="min-height: 50px; position: relative;">
<umb-load-indicator></umb-load-indicator>
</div>
<!-- Multiple languages -->
<div ng-if="vm.languages.length > 0">
<div ng-if="vm.languages.length > 1 && !vm.loading">
<div class="mb3">
<p><localize key="content_languagesToUnpublish"></localize></p>
@@ -14,26 +18,29 @@
<div class="umb-list umb-list--condensed">
<div class="bold mb1">
<localize key="treeHeaders_languages"></localize>
</div>
<div class="umb-list-item" ng-repeat="language in vm.languages track by language.id">
<ng-form name="unpublishLanguageSelectorForm">
<div class="flex">
<input id="{{language.culture}}"
name="unpublishLanguageSelector"
type="checkbox"
ng-model="language.unpublish"
ng-change="vm.changeSelection(language)"
ng-disabled="language.disabled"
style="margin-right: 8px;" />
<umb-checkbox input-id="unpublishLanguage_{{language.culture}}"
name="unpublishLanguageSelector"
model="language.unpublish"
on-change="vm.changeSelection(language)" />
<div>
<label for="{{language.culture}}" style="margin-bottom: 2px;">
<span>{{ language.name }}</span>
<label for="unpublishLanguage_{{language.culture}}" class="mb0">
<span>{{language.name}}</span>
<span class="db umb-list-item__description">
<span ng-if="language.isMandatory"><localize key="languages_mandatoryLanguage"></localize></span>
</span>
</label>
<div class="umb-list-item__description">
<span ng-if="language.isMandatory"><localize key="languages_mandatoryLanguage"></localize></span>
</div>
</div>
</div>
</ng-form>

View File

@@ -1,138 +0,0 @@
//DO NOT DELETE THIS, this is in use...
angular.module('umbraco')
.controller("Umbraco.PropertyEditors.MacroContainerController",
function($scope, macroService){
$scope.renderModel = [];
$scope.allowOpenButton = true;
$scope.allowRemoveButton = true;
$scope.sortableOptions = {};
if($scope.model.value){
var macros = $scope.model.value.split('>');
angular.forEach(macros, function(syntax, key){
if(syntax && syntax.length > 10){
//re-add the char we split on
syntax = syntax + ">";
var parsed = macroService.parseMacroSyntax(syntax);
if(!parsed){
parsed = {};
}
parsed.syntax = syntax;
collectDetails(parsed);
$scope.renderModel.push(parsed);
setSortingState($scope.renderModel);
}
});
}
function collectDetails(macro){
macro.details = "";
macro.icon = "icon-settings-alt";
if(macro.macroParamsDictionary){
angular.forEach((macro.macroParamsDictionary), function(value, key){
macro.details += key + ": " + value + " ";
});
}
}
function openDialog(index){
var dialogData = {
allowedMacros: $scope.model.config.allowed
};
if(index !== null && $scope.renderModel[index]) {
var macro = $scope.renderModel[index];
dialogData["macroData"] = macro;
}
$scope.macroPickerOverlay = {};
$scope.macroPickerOverlay.view = "macropicker";
$scope.macroPickerOverlay.dialogData = dialogData;
$scope.macroPickerOverlay.show = true;
$scope.macroPickerOverlay.submit = function(model) {
var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
collectDetails(macroObject);
//update the raw syntax and the list...
if(index !== null && $scope.renderModel[index]) {
$scope.renderModel[index] = macroObject;
} else {
$scope.renderModel.push(macroObject);
}
setSortingState($scope.renderModel);
$scope.macroPickerOverlay.show = false;
$scope.macroPickerOverlay = null;
};
$scope.macroPickerOverlay.close = function(oldModel) {
$scope.macroPickerOverlay.show = false;
$scope.macroPickerOverlay = null;
};
}
$scope.edit =function(index){
openDialog(index);
};
$scope.add = function () {
if ($scope.model.config.max && $scope.model.config.max > 0 && $scope.renderModel.length >= $scope.model.config.max) {
//cannot add more than the max
return;
}
openDialog();
};
$scope.remove =function(index){
$scope.renderModel.splice(index, 1);
setSortingState($scope.renderModel);
};
$scope.clear = function() {
$scope.model.value = "";
$scope.renderModel = [];
};
var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
var syntax = [];
angular.forEach($scope.renderModel, function(value, key){
syntax.push(value.syntax);
});
$scope.model.value = syntax.join("");
});
//when the scope is destroyed we need to unsubscribe
$scope.$on('$destroy', function () {
unsubscribe();
});
function trim(str, chr) {
var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g');
return str.replace(rgxtrim, '');
}
function setSortingState(items) {
// disable sorting if the list only consist of one item
if(items.length > 1) {
$scope.sortableOptions.disabled = false;
} else {
$scope.sortableOptions.disabled = true;
}
}
});

View File

@@ -1,33 +0,0 @@
<div ng-controller="Umbraco.PropertyEditors.MacroContainerController" class="umb-property-editor umb-macrocontainer">
<div ui-sortable="sortableOptions" ng-model="renderModel">
<div ng-repeat="macro in renderModel">
<umb-node-preview
name="macro.macroAlias"
icon="macro.icon"
description="macro.details"
sortable="!sortableOptions.disabled"
allow-remove="allowRemoveButton"
allow-open="allowOpenButton && macro.details"
on-remove="remove($index)"
on-open="edit($index)">
</umb-node-preview>
</div>
</div>
<a ng-hide="model.config.max && model.config.max > 0 && renderModel.length >= model.config.max"
class="umb-node-preview-add"
href=""
ng-click="add()"
prevent-default>
<localize key="general_add">Add</localize>
</a>
<umb-overlay
ng-if="macroPickerOverlay.show"
model="macroPickerOverlay"
view="macroPickerOverlay.view"
position="right">
</umb-overlay>
</div>

View File

@@ -1,15 +0,0 @@
function MacroListController($scope, entityResource) {
$scope.items = [];
entityResource.getAll("Macro").then(function(items) {
_.each(items, function(i) {
$scope.items.push({ name: i.name, alias: i.alias });
});
});
}
angular.module("umbraco").controller("Umbraco.PrevalueEditors.MacroList", MacroListController);

View File

@@ -1,7 +0,0 @@
<div ng-controller="Umbraco.PrevalueEditors.MacroList">
<select multiple ng-multiple="true"
ng-model="model.value"
ng-options="i.alias as i.name for i in items"></select>
</div>

View File

@@ -36,16 +36,16 @@
</umb-file-icon>
<div class="umb-sortable-thumbnails__actions" data-element="sortable-thumbnail-actions">
<a ng-if="allowEditMedia" class="umb-sortable-thumbnails__action" data-element="action-edit" href="" ng-click="vm.editItem(media)">
<a role="button" aria-label="Edit media" ng-if="allowEditMedia" class="umb-sortable-thumbnails__action" data-element="action-edit" ng-click="vm.editItem(media)">
<i class="icon icon-edit"></i>
</a>
<a class="umb-sortable-thumbnails__action -red" data-element="action-remove" href="" ng-click="vm.remove($index)">
<a role="button" aria-label="Remove" class="umb-sortable-thumbnails__action -red" data-element="action-remove" ng-click="vm.remove($index)">
<i class="icon icon-delete"></i>
</a>
</div>
</li>
<li style="border: none;" class="add-wrapper unsortable" ng-if="vm.showAdd() && allowAddMedia">
<a data-element="sortable-thumbnails-add" href="#" class="add-link" ng-click="vm.add()" ng-class="{'add-link-square': (mediaItems.length === 0 || isMultiPicker)}" prevent-default>
<a role="button" aria-label="Open media picker" data-element="sortable-thumbnails-add" class="add-link" ng-click="vm.add()" ng-class="{'add-link-square': (mediaItems.length === 0 || isMultiPicker)}" prevent-default>
<i class="icon icon-add large"></i>
</a>
</li>

View File

@@ -81,7 +81,7 @@
}
// Disallow selection of the admin/translators group, the checkbox is not visible in the UI, but clicking(and thus selecting) is still possible.
// Currently selection can only be used for deleting, and the Controller will also disallow deleting the admin group.
if (userGroup.alias === "admin" || userGroup.alias === "translator")
if (userGroup.isSystemUserGroup)
return;
listViewHelper.selectHandler(userGroup, $index, vm.userGroups, vm.selection, $event);

View File

@@ -86,7 +86,7 @@
<div class="umb-table-row"
ng-repeat="group in vm.filteredUserGroups track by $index"
ng-click="vm.selectUserGroup(group, $index, $event)"
ng-class="{'-selected': group.selected, '-selectable': group.hasAccess && group.alias !== 'admin' && group.alias !== 'translator'}">
ng-class="{'-selected': group.selected, '-selectable': group.hasAccess && !group.isSystemUserGroup}">
<div class="umb-table-cell">
<i ng-if="group.icon" class="umb-table-body__icon umb-table-body__fileicon {{ group.icon }}"></i>

View File

@@ -94,10 +94,6 @@
<PackageReference Include="Microsoft.AspNet.WebApi" Version="5.2.7" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.10.0" />
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="2.0.1" />
<PackageReference Include="Microsoft.Net.Compilers" Version="2.10.0">
<PrivateAssets>all</PrivateAssets>
<!-- development dependency -->
</PackageReference>
<PackageReference Include="Microsoft.Owin.Host.SystemWeb" Version="4.0.1" />
<PackageReference Include="Microsoft.Owin.Security.Cookies" Version="4.0.1" />
<PackageReference Include="Microsoft.Owin.Security.OAuth" Version="4.0.1" />
@@ -429,4 +425,4 @@
<Message Text="ConfigFile: $(OriginalFileName) -&gt; $(OutputFileName)" Importance="high" Condition="Exists('$(ModifiedFileName)')" />
<Copy SourceFiles="$(ModifiedFileName)" DestinationFiles="$(OutputFileName)" OverwriteReadOnlyFiles="true" SkipUnchangedFiles="false" Condition="Exists('$(ModifiedFileName)')" />
</Target>
</Project>
</Project>

View File

@@ -225,7 +225,6 @@
<key alias="linkinternal">Místní odkaz:</key>
<key alias="linklocaltip">Při používání místních odkazů vložte znak "#" před odkaz</key>
<key alias="linknewwindow">Otevřít v novém okně?</key>
<key alias="macroContainerSettings">Nastavení makra</key>
<key alias="macroDoesNotHaveProperties">Toto makro nemá žádné vlastnosti, které by bylo možno editovat</key>
<key alias="paste">Vložit</key>
<key alias="permissionsEdit">Editovat oprávnění pro</key>

View File

@@ -11,6 +11,7 @@
<key alias="changeDocType">Skift dokumenttype</key>
<key alias="copy">Kopier</key>
<key alias="create">Opret</key>
<key alias="export">Eksportér</key>
<key alias="createPackage">Opret pakke</key>
<key alias="createGroup">Opret gruppe</key>
<key alias="delete">Slet</key>
@@ -69,6 +70,7 @@
<key alias="move">Tillad adgang til at flytte en node</key>
<key alias="protect">Tillad adgang til at indstille og ændre offentlig adgang til en node</key>
<key alias="publish">Tillad adgang til at udgive en node</key>
<key alias="unpublish">Tillad adgang til at afpublicere en node</key>
<key alias="rights">Tillad adgang til at ændre rettigheder for en node</key>
<key alias="rollback">Tillad adgang til at returnere en node til en tidligere tilstand</key>
<key alias="sendtopublish">Tillad adgang til at sende en node til godkendelse før den udgives</key>
@@ -164,8 +166,10 @@
<key alias="sort">Brugeren har sorteret de underliggende sider</key>
<key alias="smallCopy">Kopieret</key>
<key alias="smallPublish">Udgivet</key>
<key alias="smallPublishVariant">Udgivet</key>
<key alias="smallMove">Flyttet</key>
<key alias="smallSave">Gemt</key>
<key alias="smallSaveVariant">Gemt</key>
<key alias="smallDelete">Slettet</key>
<key alias="smallUnpublish">Afpubliceret</key>
<key alias="smallRollBack">Indhold tilbagerullet</key>
@@ -230,14 +234,17 @@
<key alias="noChanges">Der er endnu ikke lavet nogle ændringer.</key>
<key alias="noDate">Ingen dato valgt</key>
<key alias="nodeName">Sidetitel</key>
<key alias="noMediaLink">Dette medie har ikke noget link</key>
<key alias="otherElements">Egenskaber</key>
<key alias="parentNotPublished">Dette dokument er udgivet, men ikke synligt da den overliggende side '%0%' ikke er udgivet!</key>
<key alias="parentCultureNotPublished">Dette sprog er udgivet, men ikke synligt, da den overliggende side '%0%' ikke er udgivet!</key>
<key alias="parentNotPublishedAnomaly">Ups: dette dokument er udgivet, men er ikke i cachen (intern fejl)</key>
<key alias="getUrlException">Kunne ikke hente url'en</key>
<key alias="routeError">Dette dokument er udgivet, men dets url ville kollidere med indholdet %0%</key>
<key alias="routeErrorCannotRoute">Dette dokument er udgivet, men dets URL kan ikke dirigeres</key>
<key alias="publish">Udgiv</key>
<key alias="published">Udgivet</key>
<key alias="publishedPendingChanges">Udgivet (Ventede ændringer)</key>
<key alias="publishedPendingChanges">Udgivet (Afventende ændringer)</key>
<key alias="publishStatus">Udgivelsesstatus</key>
<key alias="publishDescendantsHelp"><![CDATA[Klik <em>Udgiv med undersider</em> for at udgive <strong>%0%</strong> og alle sider under og dermed gøre deres indhold offentligt tilgængelige.]]></key>
<key alias="publishDescendantsWithVariantsHelp"><![CDATA[Klik <em>Udgiv med undersider</em> for at udgive <strong>de valgte sprog</strong> og de samme sprog for sider under og dermed gøre deres indhold offentligt tilgængelige.]]></key>
@@ -267,6 +274,7 @@
<key alias="nestedContentDeleteItem">Er du sikker på, at du vil slette dette element?</key>
<key alias="nestedContentEditorNotSupported">Egenskaben %0% anvender editoren %1% som ikke er understøttet af Nested Content.</key>
<key alias="nestedContentNoContentTypes">Der er ikke konfigureret nogen indholdstyper for denne egenskab.</key>
<key alias="nestedContentCopyAllItemsName">%0% fra %1%</key>
<key alias="addTextBox">Tilføj en ny tekstboks</key>
<key alias="removeTextBox">Fjern denne tekstboks</key>
<key alias="contentRoot">Indholdsrod</key>
@@ -325,7 +333,13 @@
<key alias="enterFolderName">Angiv et navn for mappen</key>
<key alias="updateData">Vælg en type og skriv en titel</key>
<key alias="noDocumentTypes" version="7.0"><![CDATA[Der kunne ikke findes nogen tilladte dokument typer. Du skal tillade disse i indstillinger under <strong>"dokument typer"</strong>.]]></key>
<key alias="noDocumentTypesAtRoot"><![CDATA[There are no document types available for creating content here. You must create these in <strong>Document Types</strong> within the <strong>Settings</strong> section.]]></key>
<key alias="noDocumentTypesWithNoSettingsAccess">Den valgte side i træet tillader ikke at sider oprettes under den.</key>
<key alias="noDocumentTypesEditPermissions">Rediger tilladelser for denne dokumenttype.</key>
<key alias="noDocumentTypesCreateNew">Opret en ny dokumenttype</key>
<key alias="noMediaTypes" version="7.0"><![CDATA[Der kunne ikke findes nogen tilladte media typer. Du skal tillade disse i indstillinger under <strong>"media typer"</strong>.]]></key>
<key alias="noMediaTypesWithNoSettingsAccess">Det valgte medie i træet tillader ikke at medier oprettes under det.</key>
<key alias="noMediaTypesEditPermissions">Rediger tilladelser for denne medietype.</key>
<key alias="documentTypeWithoutTemplate">Dokumenttype uden skabelon</key>
<key alias="newFolder">Ny mappe</key>
<key alias="newDataType">Ny datatype</key>
@@ -352,6 +366,8 @@
<key alias="discardChanges">Kassér ændringer</key>
<key alias="unsavedChanges">Du har ikke-gemte ændringer</key>
<key alias="unsavedChangesWarning">Er du sikker på du vil navigere væk fra denne side? - du har ikke-gemte ændringer</key>
<key alias="confirmListViewPublish">Udgivelse vil gøre de valgte sider synlige på sitet.</key>
<key alias="confirmListViewUnpublish">Afpublicering vil fjerne de valgte sider og deres undersider fra sitet.</key>
<key alias="confirmUnpublish">Afpublicering vil fjerne denne side og alle dets undersider fra websitet.</key>
<key alias="doctypeChangeWarning">Du har ikke-gemte ændringer. Hvis du ændrer dokumenttype, kasseres ændringerne.</key>
</area>
@@ -381,6 +397,7 @@
<area alias="defaultdialogs">
<key alias="nodeNameLinkPicker">Link titel</key>
<key alias="urlLinkPicker">Link</key>
<key alias="anchorLinkPicker">Lokalt link / querystreng</key>
<key alias="anchorInsert">Navn på lokalt link</key>
<key alias="assignDomain">Rediger domæner</key>
<key alias="closeThisWindow">Luk denne dialog</key>
@@ -404,7 +421,6 @@
<key alias="linkinternal">Internt link:</key>
<key alias="linklocaltip">Ved lokalt link, indsæt da en "#" foran linket</key>
<key alias="linknewwindow">Åben i nyt vindue?</key>
<key alias="macroContainerSettings">Makroindstillinger</key>
<key alias="macroDoesNotHaveProperties">Denne makro har ingen egenskaber du kan redigere</key>
<key alias="paste">Indsæt tekst</key>
<key alias="permissionsEdit">Rediger rettigheder for</key>
@@ -450,6 +466,10 @@
<key alias="noIconsFound">Ingen ikoner blev fundet</key>
<key alias="noMacroParams">Der er ingen parametre for denne makro</key>
<key alias="noMacros">Der er ikke tilføjet nogen makroer</key>
<key alias="externalLoginProviders">Eksterne login-udbydere</key>
<key alias="exceptionDetail">Undtagelsesdetaljer</key>
<key alias="stacktrace">Stacktrace</key>
<key alias="innerException">Indre undtagelse</key>
<key alias="linkYour">Link dit</key>
<key alias="unLinkYour">Fjern link fra dit</key>
<key alias="account">konto</key>
@@ -509,12 +529,17 @@
<key alias="email">Indtast din e-mail</key>
<key alias="enterMessage">Indtast en besked...</key>
<key alias="usernameHint">Dit brugernavn er typisk din e-mailadresse</key>
<key alias="anchor">#value eller ?key=value</key>
<key alias="enterAlias">Indtast alias...</key>
<key alias="generatingAlias">Genererer alias...</key>
<key alias="a11yCreateItem">Opret element</key>
<key alias="a11yEdit">Rediger</key>
<key alias="a11yName">Navn</key>
</area>
<area alias="editcontenttype">
<key alias="createListView" version="7.2">Opret brugerdefineret listevisning</key>
<key alias="removeListView" version="7.2">Fjern brugerdefineret listevisning</key>
<key alias="aliasAlreadyExists">En dokumenttype, medietype eller medlemstype med dette alias findes allerede</key>
</area>
<area alias="renamecontainer">
<key alias="renamed">Omdøbt</key>
@@ -560,6 +585,7 @@
<key alias="codemirroriewarning">OBS! Selvom CodeMirror er slået til i konfigurationen, så er den deaktiveret i Internet Explorer fordi den ikke er stabil nok.</key>
<key alias="contentTypeAliasAndNameNotNull">Du skal udfylde både Alias &amp; Navn på den nye egenskabstype!</key>
<key alias="filePermissionsError">Der mangler læse/skrive rettigheder til bestemte filer og mapper</key>
<key alias="macroErrorLoadingPartialView">Fejl ved indlæsning af Partial View script (fil: %0%)</key>
<key alias="missingTitle">Skriv venligst en titel</key>
<key alias="missingType">Du skal vælge en type</key>
<key alias="pictureResizeBiggerThanOrg">Du er ved at gøre billedet større end originalen. Det vil forringe kvaliteten af billedet. Ønsker du at fortsætte?</key>
@@ -576,6 +602,7 @@
<key alias="actions">Muligheder</key>
<key alias="add">Tilføj</key>
<key alias="alias">Alias</key>
<key alias="all">Alle</key>
<key alias="areyousure">Er du sikker?</key>
<key alias="back">Tilbage</key>
<key alias="backToOverview">Tilbage til oversigt</key>
@@ -601,6 +628,7 @@
<key alias="deleted">Slettet</key>
<key alias="deleting">Sletter...</key>
<key alias="design">Design</key>
<key alias="dictionary">Ordbog</key>
<key alias="dimensions">Dimensioner</key>
<key alias="down">Ned</key>
<key alias="download">Hent</key>
@@ -623,14 +651,17 @@
<key alias="id">Id</key>
<key alias="import">Importer</key>
<key alias="includeFromsubFolders">Inkludér undermapper i søgning</key>
<key alias="info">Info</key>
<key alias="innerMargin">Indre margen</key>
<key alias="insert">Indsæt</key>
<key alias="install">Installér</key>
<key alias="invalid">Ugyldig</key>
<key alias="justify">Justering</key>
<key alias="label">Mærke</key>
<key alias="language">Sprog</key>
<key alias="last">Sidste</key>
<key alias="layout">Layout</key>
<key alias="links">Links</key>
<key alias="loading">Henter</key>
<key alias="locked">Låst</key>
<key alias="login">Log ind</key>
@@ -645,8 +676,11 @@
<key alias="next">Næste</key>
<key alias="no">Nej</key>
<key alias="of">af</key>
<key alias="off">Fra</key>
<key alias="ok">OK</key>
<key alias="open">Åben</key>
<key alias="options">Valgmuligheder</key>
<key alias="on">Til</key>
<key alias="or">eller</key>
<key alias="orderBy">Sortér efter</key>
<key alias="password">Kodeord</key>
@@ -681,6 +715,7 @@
<key alias="submit">Indsend</key>
<key alias="type">Type</key>
<key alias="typeToSearch">Skriv for at søge...</key>
<key alias="under">under</key>
<key alias="up">Op</key>
<key alias="update">Opdatér</key>
<key alias="upgrade">Opdatér</key>
@@ -730,6 +765,7 @@
<key alias="moveLineDown">Move Lines Down</key>
<key alias="generalHeader">Generelt</key>
<key alias="editorHeader">Editor</key>
<key alias="toggleAllowCultureVariants">Skift tillad sprogvarianter</key>
</area>
<area alias="graphicheadline">
<key alias="backgroundcolor">Baggrundsfarve</key>
@@ -993,6 +1029,10 @@ Mange hilsner fra Umbraco robotten
<key alias="contentPublishedFailedInvalid"><![CDATA[
%0% kunne ikke publiceres da følgende egenskaber : %1% ikke overholdte valderingsreglerne.
]]></key>
<key alias="contentPublishedFailedByEvent"><![CDATA[
%0% kunne ikke publiceres, et 3. part tilføjelsesprogram annullerede handlingen.
]]>
</key>
<key alias="contentPublishedFailedByEvent">%0% kunne ikke udgives, fordi et 3. parts modul annullerede handlingen</key>
<key alias="contentPublishedFailedByMissingName"><![CDATA[%0% kan ikke udgives, fordi det mangler et navn.]]></key>
<key alias="includeUnpublished">Medtag ikke-udgivede undersider</key>
@@ -1028,6 +1068,8 @@ Mange hilsner fra Umbraco robotten
</area>
<area alias="imagecropper">
<key alias="reset">Nulstil</key>
<key alias="saveCrop">Gem beskæring</key>
<key alias="addCrop">Tilføj ny beskæring</key>
<key alias="updateEditCrop">Acceptér</key>
<key alias="undoEditCrop">Fortryd</key>
</area>
@@ -1075,6 +1117,8 @@ Mange hilsner fra Umbraco robotten
<key alias="tab">Faneblad</key>
<key alias="tabname">Titel på faneblad</key>
<key alias="tabs">Faneblade</key>
<key alias="createMatchingTemplate">Opret tilsvarende skabelon</key>
<key alias="addIcon">Tilføj ikon</key>
</area>
<area alias="sort">
<key alias="sortOrder">Sorteringsrækkefølge</key>
@@ -1091,6 +1135,7 @@ Mange hilsner fra Umbraco robotten
<key alias="invalidUserPermissionsText">Utilstrækkelige brugerrettigheder, kunne ikke fuldføre handlingen</key>
<key alias="operationCancelledHeader">Annulleret</key>
<key alias="operationCancelledText">Handlingen blev annulleret af et 3. part tilføjelsesprogram</key>
<key alias="contentPublishedFailedByEvent">Udgivelsen blev annulleret af et 3. part tilføjelsesprogram</key>
<key alias="contentTypeDublicatePropertyType">Property type eksisterer allerede</key>
<key alias="contentTypePropertyTypeCreated">Egenskabstype oprettet</key>
<key alias="contentTypePropertyTypeCreatedText"><![CDATA[Navn: %0% <br /> DataType: %1%]]></key>
@@ -1104,6 +1149,7 @@ Mange hilsner fra Umbraco robotten
<key alias="cssSavedText">Stylesheet gemt uden fejl</key>
<key alias="dataTypeSaved">Datatype gemt</key>
<key alias="dictionaryItemSaved">Ordbogsnøgle gemt</key>
<key alias="editContentPublishedFailedByParent">Udgivelse fejlede da overliggende side ikke er udgivet</key>
<key alias="editContentPublishedHeader">Indhold publiceret</key>
<key alias="editContentPublishedText">og nu synligt for besøgende</key>
<key alias="editContentSavedHeader">Indhold gemt</key>
@@ -1148,6 +1194,11 @@ Mange hilsner fra Umbraco robotten
<key alias="setUserGroupOnUsersSuccess">Brugergrupper er blevet indstillet</key>
<key alias="unlockUsersSuccess">Låste %0% brugere op</key>
<key alias="unlockUserSuccess">%0% er nu låst op</key>
<key alias="deleteUserSuccess">Brugeren %0% blev slettet</key>
<key alias="resendInviteHeader">Invitér bruger</key>
<key alias="resendInviteSuccess">Invitationen blev gensendt til %0%</key>
<key alias="documentTypeExportedSuccess">Dokumenttypen blev eksporteret til en fil</key>
<key alias="documentTypeExportedError">Der skete en fejl under eksport af en dokumenttype</key>
</area>
<area alias="stylesheet">
<key alias="addRule">Tilføj style</key>
@@ -1312,7 +1363,9 @@ Mange hilsner fra Umbraco robotten
<key alias="configuration">Konfiguration</key>
<key alias="yesDelete">Ja, slet</key>
<key alias="movedUnderneath">blev flyttet til</key>
<key alias="copiedUnderneath">blev kopieret til</key>
<key alias="folderToMove">Vælg hvor</key>
<key alias="folderToCopy">Vælg hvor</key>
<key alias="structureBelow">skal flyttes til</key>
<key alias="allDocumentTypes">Alle dokumenttyper</key>
<key alias="allDocuments">Alle dokumenter</key>
@@ -1324,8 +1377,15 @@ Mange hilsner fra Umbraco robotten
<key alias="andAllMediaItems">og alle medier, som benytter denne type</key>
<key alias="andAllMembers">og alle medlemmer, som benytter denne type</key>
<key alias="memberCanEdit">Medlem kan redigere</key>
<key alias="memberCanEditDescription">Tillad at denne egenskab kan redigeres af medlemmet på dets profil.</key>
<key alias="isSensitiveData">Er følsom data</key>
<key alias="isSensitiveDataDescription">Skjul værdien af denne egenskab for indholdsredaktører der ikke har adgang til at se følsomme data</key>
<key alias="showOnMemberProfile">Vis på medlemsprofil</key>
<key alias="showOnMemberProfileDescription">Tillad at denne egenskab kan vises på medlemmets profil.</key>
<key alias="tabHasNoSortOrder">fane har ingen sorteringsrækkefølge</key>
<key alias="compositionUsageHeading">Hvor er denne komposition brugt?</key>
<key alias="compositionUsageSpecification">Denne komposition brugt i kompositionen af de følgende indholdstyper:</key>
<key alias="variantsHeading">Tillad sprogvariation</key>
<key alias="variantsDescription">Tillad at redaktører kan oprette indhold af denne type på flere sprog.</key>
<key alias="allowVaryByCulture">Tillad sprogvariation</key>
<key alias="elementType">Element-type</key>
@@ -1354,14 +1414,28 @@ Mange hilsner fra Umbraco robotten
<key alias="parametersDescription">Definér de parametre der skal være tilgængelige, når du bruger denne makro.</key>
<key alias="selectViewFile">Vælg partial view makrofil</key>
</area>
<area alias="modelsBuilder">
<key alias="buildingModels">Bygger modeller</key>
<key alias="waitingMessage">dette kan tage lidt tid</key>
<key alias="modelsGenerated">Modeller genereret</key>
<key alias="modelsGeneratedError">Modeller kunne ikke genereres</key>
<key alias="modelsExceptionInUlog">Modelgeneration fejlet, se fejlmeddelelse i log</key>
</area>
<area alias="templateEditor">
<key alias="addFallbackField">Tilføj fallback felt</key>
<key alias="fallbackField">Fallback felt</key>
<key alias="addDefaultValue">Tilføj standard værdi</key>
<key alias="defaultValue">Standard værdi</key>
<key alias="alternativeField">Alternativt felt</key>
<key alias="alternativeText">Alternativ tekst</key>
<key alias="casing">Casing</key>
<key alias="encoding">Kodning</key>
<key alias="chooseField">Felt som skal indsættes</key>
<key alias="convertLineBreaks">Konvertér linieskift</key>
<key alias="convertLineBreaksHelp">Erstatter et linieskift med html-tag'et &amp;lt;br&amp;gt;</key>
<key alias="customFields">Custom felter</key>
<key alias="dateOnly">Ja, kun dato</key>
<key alias="formatAndEncoding">Format og kodning</key>
<key alias="formatAsDate">Formatér som dato</key>
<key alias="htmlEncode">HTML indkod</key>
<key alias="htmlEncodeHelp">Vil erstatte specielle karakterer med deres HTML jævnbyrdige.</key>
@@ -1433,6 +1507,7 @@ Mange hilsner fra Umbraco robotten
<key alias="scripts">Scripts</key>
<key alias="stylesheets">Stylesheets</key>
<key alias="templates">Skabeloner</key>
<key alias="logViewer">Logfremviser</key>
<key alias="userPermissions">Brugertilladelser</key>
<key alias="userTypes">Brugertyper</key>
<key alias="users">Brugere</key>
@@ -1461,6 +1536,7 @@ Mange hilsner fra Umbraco robotten
<key alias="confirmNewPassword">Gentag dit nye kodeord</key>
<key alias="changePasswordDescription">Du kan ændre dit kodeord, som giver dig adgang til Umbraco Back Office ved at udfylde formularen og klikke på knappen 'Skift dit kodeord'</key>
<key alias="contentChannel">Indholdskanal</key>
<key alias="createAnotherUser">Opret endnu en bruger</key>
<key alias="createUserHelp">Opret nye brugere for at give dem adgang til Umbraco. Når en ny bruger oprettes, genereres der en adgangskode, som du kan dele med brugeren.</key>
<key alias="descriptionField">Beskrivelsesfelt</key>
<key alias="disabled">Deaktivér bruger</key>
@@ -1474,7 +1550,7 @@ Mange hilsner fra Umbraco robotten
<key alias="inviteUserHelp">Invitér nye brugere til at give dem adgang til Umbraco. En invitation vil blive sendt via e-mail til brugeren med oplysninger om, hvordan man logger ind i Umbraco.</key>
<key alias="language">Sprog</key>
<key alias="languageHelp">Indstil det sprog, du vil se i menuer og dialoger</key>
<key alias="lastLockoutDate">Seneste låst ude dato</key>
<key alias="lastLockoutDate">Senest låst ude</key>
<key alias="lastLogin">Seneste login</key>
<key alias="lastPasswordChangeDate">Kodeord sidst ændret</key>
<key alias="loginname">Brugernavn</key>
@@ -1523,6 +1599,7 @@ Mange hilsner fra Umbraco robotten
<key alias="userInvited">er blevet inviteret</key>
<key alias="userInvitedSuccessHelp">En invitation er blevet sendt til den nye bruger med oplysninger om, hvordan man logger ind i Umbraco.</key>
<key alias="userinviteWelcomeMessage">Hej og velkommen til Umbraco! På bare 1 minut vil du være klar til at komme i gang, vi skal bare have dig til at oprette en adgangskode og tilføje et billede til din avatar.</key>
<key alias="userinviteExpiredMessage">Velkommen til Umbraco! Desværre er din invitation udløbet. Kontakt din administrator og bed om at gensende invitationen.</key>
<key alias="userinviteAvatarMessage">Hvis du uploader et billede af dig selv, gør du det nemt for andre brugere at genkende dig. Klik på cirklen ovenfor for at uploade et billede.</key>
<key alias="writer">Forfatter</key>
<key alias="change">Skift</key>
@@ -1636,5 +1713,7 @@ Mange hilsner fra Umbraco robotten
<key alias="openBackofficeSearch">Åben backoffice søgning</key>
<key alias="openCloseBackofficeHelp">Åben/Luk backoffice hjælp</key>
<key alias="openCloseBackofficeProfileOptions">Åben/Luk dine profil indstillinger</key>
<key alias="currentLanguage">Aktivt sprog</key>
<key alias="switchLanguage">Skift sprog til</key>
</area>
</language>

View File

@@ -430,7 +430,6 @@
<key alias="linkinternal">Anker:</key>
<key alias="linklocaltip">Wenn lokale Links verwendet werden, füge ein "#" vor den Link ein</key>
<key alias="linknewwindow">In einem neuen Fenster öffnen?</key>
<key alias="macroContainerSettings">Macro Einstellungen</key>
<key alias="macroDoesNotHaveProperties">Dieses Makro enthält keine einstellbaren Eigenschaften.</key>
<key alias="paste">Einfügen</key>
<key alias="permissionsEdit">Berechtigungen bearbeiten für</key>

View File

@@ -333,8 +333,10 @@
<key alias="enterFolderName">Enter a folder name</key>
<key alias="updateData">Choose a type and a title</key>
<key alias="noDocumentTypes" version="7.0"><![CDATA[There are no allowed document types available for creating content here. You must enable these in <strong>Document Types</strong> within the <strong>Settings</strong> section, by editing the <strong>Allowed child node types</strong> under <strong>Permissions</strong>.]]></key>
<key alias="noDocumentTypesAtRoot"><![CDATA[There are no document types available for creating content here. You must create these in <strong>Document Types</strong> within the <strong>Settings</strong> section.]]></key>
<key alias="noDocumentTypesWithNoSettingsAccess">The selected page in the content tree doesn't allow for any pages to be created below it.</key>
<key alias="noDocumentTypesEditPermissions">Edit permissions for this document type</key>
<key alias="noDocumentTypesCreateNew">Create a new document type</key>
<key alias="noMediaTypes" version="7.0"><![CDATA[There are no allowed media types available for creating media here. You must enable these in <strong>Media Types Types</strong> within the <strong>Settings</strong> section, by editing the <strong>Allowed child node types</strong> under <strong>Permissions</strong>.]]></key>
<key alias="noMediaTypesWithNoSettingsAccess">The selected media in the tree doesn't allow for any other media to be created below it.</key>
<key alias="noMediaTypesEditPermissions">Edit permissions for this media type</key>
@@ -419,7 +421,6 @@
<key alias="linkinternal">Internal link:</key>
<key alias="linklocaltip">When using local links, insert "#" in front of link</key>
<key alias="linknewwindow">Open in new window?</key>
<key alias="macroContainerSettings">Macro Settings</key>
<key alias="macroDoesNotHaveProperties">This macro does not contain any properties you can edit</key>
<key alias="paste">Paste</key>
<key alias="permissionsEdit">Edit permissions for</key>
@@ -2149,5 +2150,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="openBackofficeSearch">Open backoffice search</key>
<key alias="openCloseBackofficeHelp">Open/Close backoffice help</key>
<key alias="openCloseBackofficeProfileOptions">Open/Close your profile options</key>
<key alias="currentLanguage">Current language</key>
<key alias="switchLanguage">Switch language to</key>
</area>
</language>

View File

@@ -338,8 +338,10 @@
<key alias="enterFolderName">Enter a folder name</key>
<key alias="updateData">Choose a type and a title</key>
<key alias="noDocumentTypes" version="7.0"><![CDATA[There are no allowed document types available for creating content here. You must enable these in <strong>Document Types</strong> within the <strong>Settings</strong> section, by editing the <strong>Allowed child node types</strong> under <strong>Permissions</strong>.]]></key>
<key alias="noDocumentTypesAtRoot"><![CDATA[There are no document types available for creating content here. You must create these in <strong>Document Types</strong> within the <strong>Settings</strong> section.]]></key>
<key alias="noDocumentTypesWithNoSettingsAccess">The selected page in the content tree doesn't allow for any pages to be created below it.</key>
<key alias="noDocumentTypesEditPermissions">Edit permissions for this document type</key>
<key alias="noDocumentTypesCreateNew">Create a new document type</key>
<key alias="noMediaTypes" version="7.0"><![CDATA[There are no allowed media types available for creating media here. You must enable these in <strong>Media Types Types</strong> within the <strong>Settings</strong> section, by editing the <strong>Allowed child node types</strong> under <strong>Permissions</strong>.]]></key>
<key alias="noMediaTypesWithNoSettingsAccess">The selected media in the tree doesn't allow for any other media to be created below it.</key>
<key alias="noMediaTypesEditPermissions">Edit permissions for this media type</key> <key alias="documentTypeWithoutTemplate">Document Type without a template</key>
@@ -422,7 +424,6 @@
<key alias="linkinternal">Internal link:</key>
<key alias="linklocaltip">When using local links, insert "#" in front of link</key>
<key alias="linknewwindow">Open in new window?</key>
<key alias="macroContainerSettings">Macro Settings</key>
<key alias="macroDoesNotHaveProperties">This macro does not contain any properties you can edit</key>
<key alias="paste">Paste</key>
<key alias="permissionsEdit">Edit permissions for</key>
@@ -2163,5 +2164,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="openBackofficeSearch">Open backoffice search</key>
<key alias="openCloseBackofficeHelp">Open/Close backoffice help</key>
<key alias="openCloseBackofficeProfileOptions">Open/Close your profile options</key>
<key alias="currentLanguage">Current language</key>
<key alias="switchLanguage">Switch language to</key>
</area>
</language>

View File

@@ -311,7 +311,6 @@
<key alias="linkinternal">Enlace interno</key>
<key alias="linklocaltip">Al usar enlaces locales, insertar "#" delante del enlace</key>
<key alias="linknewwindow">¿Abrir en nueva ventana?</key>
<key alias="macroContainerSettings">Ajustes para la Macro</key>
<key alias="macroDoesNotHaveProperties">Esta macro no contiene ninguna propiedad que pueda editar</key>
<key alias="paste">Pegar</key>
<key alias="permissionsEdit">Editar permisos para</key>

View File

@@ -324,7 +324,6 @@
<key alias="linkinternal">Lien interne :</key>
<key alias="linklocaltip">Si vous utilisez des ancres, insérez # au début du lien</key>
<key alias="linknewwindow">Ouvrir dans une nouvelle fenêtre?</key>
<key alias="macroContainerSettings">Paramètres de macro</key>
<key alias="macroDoesNotHaveProperties">Cette macro ne contient aucune propriété éditable</key>
<key alias="paste">Coller</key>
<key alias="permissionsEdit">Editer les permissions pour</key>

View File

@@ -175,7 +175,6 @@
<key alias="linkinternal">קישור פנימי:</key>
<key alias="linklocaltip">בעת שימוש בקישוריים פנימיים, הוסף "#" בתחילת הקישור</key>
<key alias="linknewwindow">לפתוח בחלון חדש?</key>
<key alias="macroContainerSettings">הגדרות מאקרו</key>
<key alias="macroDoesNotHaveProperties">המאקרו לא מכיל מאפיינים שניתן לערוך</key>
<key alias="paste">הדבק</key>
<key alias="permissionsEdit">ערוך הרשאות עבור</key>

View File

@@ -180,7 +180,6 @@
<key alias="linkinternal"><![CDATA[Link interno:]]></key>
<key alias="linklocaltip"><![CDATA[Quando usi il link locale, inserisci # prima del link]]></key>
<key alias="linknewwindow"><![CDATA[Apri in nuova finestra?]]></key>
<key alias="macroContainerSettings">Impostazioni Macro</key>
<key alias="macroDoesNotHaveProperties"><![CDATA[Questa macro non contiene proprietà editabili]]></key>
<key alias="paste">Incolla</key>
<key alias="permissionsEdit">Modifica il Permesso per</key>

View File

@@ -240,7 +240,6 @@
<key alias="linkinternal">内部リンク:</key>
<key alias="linklocaltip">内部リンクを使うときは、リンクの前に "#" を挿入してください。</key>
<key alias="linknewwindow">新規ウィンドウで開きますか?</key>
<key alias="macroContainerSettings">マクロの設定</key>
<key alias="macroDoesNotHaveProperties">このマクロは編集できるプロパティがありません</key>
<key alias="paste">貼り付け</key>
<key alias="permissionsEdit">許可の編集</key>

View File

@@ -174,7 +174,6 @@
<key alias="linkinternal">내부링크:</key>
<key alias="linklocaltip">내부링크를 사용하실 때 링크앞에 "#"를 넣어주세요</key>
<key alias="linknewwindow">새 창으로 여시겠습니까?</key>
<key alias="macroContainerSettings">매크로 세팅</key>
<key alias="macroDoesNotHaveProperties">이 매크로에는 편집할 수 있는 항목이 포함되어 있지 않습니다.</key>
<key alias="paste">붙여넣기</key>
<key alias="permissionsEdit">권한 편집</key>

View File

@@ -234,7 +234,6 @@
<key alias="linkinternal">Intern link:</key>
<key alias="linklocaltip">Ved lokal link, sett inn "#" foran link</key>
<key alias="linknewwindow">Åpne i nytt vindu?</key>
<key alias="macroContainerSettings">Makroinnstillinger</key>
<key alias="macroDoesNotHaveProperties">Denne makroen har ingen egenskaper du kan endre</key>
<key alias="paste">Lim inn</key>
<key alias="permissionsEdit">Endre rettigheter for</key>

View File

@@ -263,7 +263,6 @@
<key alias="linkinternal">Interne link:</key>
<key alias="linklocaltip">Plaats een hekje (“#”) voor voor interne links.</key>
<key alias="linknewwindow">In nieuw venster openen?</key>
<key alias="macroContainerSettings">Macro Settings</key>
<key alias="macroDoesNotHaveProperties">Deze macro heeft geen eigenschappen die u kunt bewerken</key>
<key alias="paste">Plakken</key>
<key alias="permissionsEdit">Bewerk rechten voor</key>

View File

@@ -312,7 +312,6 @@
<key alias="linkinternal">Link wewnętrzny:</key>
<key alias="linklocaltip">Kiedy używasz odnośników lokalnych, wstaw znak "#" na początku linku</key>
<key alias="linknewwindow">Otworzyć w nowym oknie?</key>
<key alias="macroContainerSettings">Ustawienia Makro</key>
<key alias="macroDoesNotHaveProperties">To makro nie posiada żadnych właściwości, które można edytować</key>
<key alias="paste">Wklej</key>
<key alias="permissionsEdit">Edytuj Uprawnienia dla</key>

Some files were not shown because too many files have changed in this diff Show More