Merge branch dev-v7 into temp-u4-10006

This commit is contained in:
Stephan
2017-07-21 10:04:25 +02:00
104 changed files with 3110 additions and 748 deletions

View File

@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.4")]
[assembly: AssemblyInformationalVersion("7.6.4")]
[assembly: AssemblyFileVersion("7.6.5")]
[assembly: AssemblyInformationalVersion("7.6.5")]

View File

@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.6.4");
private static readonly Version Version = new Version("7.6.5");
/// <summary>
/// Gets the current version of Umbraco.

View File

@@ -19,6 +19,8 @@ namespace Umbraco.Core.IO
private ShadowWrapper _macroPartialFileSystem;
private ShadowWrapper _partialViewsFileSystem;
private ShadowWrapper _macroScriptsFileSystem;
private ShadowWrapper _userControlsFileSystem;
private ShadowWrapper _stylesheetsFileSystem;
private ShadowWrapper _scriptsFileSystem;
private ShadowWrapper _xsltFileSystem;
@@ -61,6 +63,8 @@ namespace Umbraco.Core.IO
{
var macroPartialFileSystem = new PhysicalFileSystem(SystemDirectories.MacroPartials);
var partialViewsFileSystem = new PhysicalFileSystem(SystemDirectories.PartialViews);
var macroScriptsFileSystem = new PhysicalFileSystem(SystemDirectories.MacroScripts);
var userControlsFileSystem = new PhysicalFileSystem(SystemDirectories.UserControls);
var stylesheetsFileSystem = new PhysicalFileSystem(SystemDirectories.Css);
var scriptsFileSystem = new PhysicalFileSystem(SystemDirectories.Scripts);
var xsltFileSystem = new PhysicalFileSystem(SystemDirectories.Xslt);
@@ -69,6 +73,8 @@ namespace Umbraco.Core.IO
_macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", ScopeProvider);
_partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", ScopeProvider);
_macroScriptsFileSystem = new ShadowWrapper(macroScriptsFileSystem, "macroScripts", ScopeProvider);
_userControlsFileSystem = new ShadowWrapper(userControlsFileSystem, "usercontrols", ScopeProvider);
_stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, "css", ScopeProvider);
_scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, "scripts", ScopeProvider);
_xsltFileSystem = new ShadowWrapper(xsltFileSystem, "xslt", ScopeProvider);
@@ -85,6 +91,10 @@ namespace Umbraco.Core.IO
public IFileSystem2 MacroPartialsFileSystem { get { return _macroPartialFileSystem; } }
public IFileSystem2 PartialViewsFileSystem { get { return _partialViewsFileSystem; } }
// Legacy /macroScripts folder
public IFileSystem2 MacroScriptsFileSystem { get { return _macroScriptsFileSystem; } }
// Legacy /usercontrols folder
public IFileSystem2 UserControlsFileSystem { get { return _userControlsFileSystem; } }
public IFileSystem2 StylesheetsFileSystem { get { return _stylesheetsFileSystem; } }
public IFileSystem2 ScriptsFileSystem { get { return _scriptsFileSystem; } }
public IFileSystem2 XsltFileSystem { get { return _xsltFileSystem; } }
@@ -252,13 +262,15 @@ namespace Umbraco.Core.IO
internal ICompletable Shadow(Guid id)
{
var typed = _wrappers.ToArray();
var wrappers = new ShadowWrapper[typed.Length + 7];
var wrappers = new ShadowWrapper[typed.Length + 9];
var i = 0;
while (i < typed.Length) wrappers[i] = typed[i++];
wrappers[i++] = _macroPartialFileSystem;
wrappers[i++] = _macroScriptsFileSystem;
wrappers[i++] = _partialViewsFileSystem;
wrappers[i++] = _stylesheetsFileSystem;
wrappers[i++] = _scriptsFileSystem;
wrappers[i++] = _userControlsFileSystem;
wrappers[i++] = _xsltFileSystem;
wrappers[i++] = _masterPagesFileSystem;
wrappers[i] = _mvcViewsFileSystem;

View File

@@ -0,0 +1,7 @@
namespace Umbraco.Core.Models
{
public interface IUserControl : IFile
{
}
}

View File

@@ -109,6 +109,30 @@ namespace Umbraco.Core.Models
IsApproved = true;
}
/// <summary>
/// Constructor for creating a Member object
/// </summary>
/// <param name="name"></param>
/// <param name="email"></param>
/// <param name="username"></param>
/// <param name="rawPasswordValue">
/// The password value passed in to this parameter should be the encoded/encrypted/hashed format of the member's password
/// </param>
/// <param name="contentType"></param>
/// <param name="isApproved"></param>
public Member(string name, string email, string username, string rawPasswordValue, IMemberType contentType, bool isApproved)
: base(name, -1, contentType, new PropertyCollection())
{
Mandate.ParameterNotNull(contentType, "contentType");
_contentTypeAlias = contentType.Alias;
_contentType = contentType;
_email = email;
_username = username;
_rawPasswordValue = rawPasswordValue;
IsApproved = isApproved;
}
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
private class PropertySelectors

View File

@@ -4,6 +4,7 @@
{
Unknown = 0, // default
PartialView = 1,
PartialViewMacro = 2
PartialViewMacro = 2,
MacroScript = 3
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a UserControl file
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class UserControl : File, IUserControl
{
public UserControl(string path)
: this(path, (Func<File, string>)null)
{ }
internal UserControl(string path, Func<File, string> getFileContent)
: base(path, getFileContent)
{ }
/// <summary>
/// Indicates whether the current entity has an identity, which in this case is a path/name.
/// </summary>
/// <remarks>
/// Overrides the default Entity identity check.
/// </remarks>
public override bool HasIdentity
{
get { return string.IsNullOrEmpty(Path) == false; }
}
}
}

View File

@@ -1,5 +1,4 @@
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -11,15 +10,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
{
public UpdateUniqueIdToHaveCorrectIndexType(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{
}
{ }
//see: http://issues.umbraco.org/issue/U4-6188, http://issues.umbraco.org/issue/U4-6187
public override void Up()
{
var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database)
var indexes = SqlSyntax.GetDefinedIndexes(Context.Database)
.Select(x => new DbIndexDefinition()
{
TableName = x.Item1,
@@ -28,24 +23,19 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
IsUnique = x.Item4
}).ToArray();
//must be non-nullable
// drop the index if it exists
if (indexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeUniqueID")))
Delete.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode");
// set uniqueID to be non-nullable
// the index *must* be dropped else 'one or more objects access this column' exception
Alter.Table("umbracoNode").AlterColumn("uniqueID").AsGuid().NotNullable();
//make sure it already exists
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeUniqueID")))
{
Delete.Index("IX_umbracoNodeUniqueID").OnTable("umbracoNode");
}
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNode_uniqueID")) == false)
{
//must be a uniqe index
Create.Index("IX_umbracoNode_uniqueID").OnTable("umbracoNode").OnColumn("uniqueID").Unique();
}
// create the index
Create.Index("IX_umbracoNode_uniqueID").OnTable("umbracoNode").OnColumn("uniqueID").Unique();
}
public override void Down()
{
}
{ }
}
}

View File

@@ -0,0 +1,13 @@
using System.IO;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IUserControlRepository : IRepository<string, UserControl>
{
bool ValidateUserControl(UserControl userControl);
Stream GetFileContentStream(string filepath);
void SetFileContent(string filepath, Stream content);
long GetFileSize(string filepath);
}
}

View File

@@ -0,0 +1,15 @@
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
internal class MacroScriptRepository : PartialViewRepository
{
public MacroScriptRepository(IUnitOfWork work, IFileSystem fileSystem)
: base(work, fileSystem)
{ }
protected override PartialViewType ViewType { get { return PartialViewType.MacroScript; } }
}
}

View File

@@ -0,0 +1,135 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
/// <summary>
/// Represents the UserControl Repository
/// </summary>
internal class UserControlRepository : FileRepository<string, UserControl>, IUserControlRepository
{
public UserControlRepository(IUnitOfWork work, IFileSystem fileSystem)
: base(work, fileSystem)
{
}
public override UserControl Get(string id)
{
var path = FileSystem.GetRelativePath(id);
path = path.EnsureEndsWith(".ascx");
if (FileSystem.FileExists(path) == false)
return null;
var created = FileSystem.GetCreated(path).UtcDateTime;
var updated = FileSystem.GetLastModified(path).UtcDateTime;
var userControl = new UserControl(path, file => GetFileContent(file.OriginalPath))
{
Key = path.EncodeAsGuid(),
CreateDate = created,
UpdateDate = updated,
Id = path.GetHashCode(),
VirtualPath = FileSystem.GetUrl(path)
};
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
userControl.ResetDirtyProperties(false);
return userControl;
}
public override void AddOrUpdate(UserControl entity)
{
base.AddOrUpdate(entity);
// ensure that from now on, content is lazy-loaded
if (entity.GetFileContent == null)
entity.GetFileContent = file => GetFileContent(file.OriginalPath);
}
public override IEnumerable<UserControl> GetAll(params string[] ids)
{
ids = ids
.Select(x => StringExtensions.EnsureEndsWith(x, ".ascx"))
.Distinct()
.ToArray();
if (ids.Any())
{
foreach (var id in ids)
{
yield return Get(id);
}
}
else
{
var files = FindAllFiles("", "*.ascx");
foreach (var file in files)
{
yield return Get(file);
}
}
}
/// <summary>
/// Gets a list of all <see cref="UserControl"/> that exist at the relative path specified.
/// </summary>
/// <param name="rootPath">
/// If null or not specified, will return the UserControl files at the root path relative to the IFileSystem
/// </param>
/// <returns></returns>
public IEnumerable<UserControl> GetUserControlsAtPath(string rootPath = null)
{
return FileSystem.GetFiles(rootPath ?? string.Empty, "*.ascx").Select(Get);
}
private static readonly List<string> ValidExtensions = new List<string> { "ascx" };
public bool ValidateUserControl(UserControl userControl)
{
// get full path
string fullPath;
try
{
// may throw for security reasons
fullPath = FileSystem.GetFullPath(userControl.Path);
}
catch
{
return false;
}
// validate path and extension
var validDir = SystemDirectories.UserControls;
var isValidPath = IOHelper.VerifyEditPath(fullPath, validDir);
var isValidExtension = IOHelper.VerifyFileExtension(userControl.Path, ValidExtensions);
return isValidPath && isValidExtension;
}
public Stream GetFileContentStream(string filepath)
{
if (FileSystem.FileExists(filepath) == false) return null;
try
{
return FileSystem.OpenFile(filepath);
}
catch
{
return null; // deal with race conds
}
}
public void SetFileContent(string filepath, Stream content)
{
FileSystem.AddFile(filepath, content, true);
}
}
}

View File

@@ -231,6 +231,18 @@ namespace Umbraco.Core.Persistence
return new PartialViewMacroRepository(uow, FileSystemProviderManager.Current.MacroPartialsFileSystem);
}
[Obsolete("MacroScripts are obsolete - this is for backwards compatibility with upgraded sites.")]
internal virtual IPartialViewRepository CreateMacroScriptRepository(IUnitOfWork uow)
{
return new MacroScriptRepository(uow, FileSystemProviderManager.Current.MacroScriptsFileSystem);
}
[Obsolete("UserControls are obsolete - this is for backwards compatibility with upgraded sites.")]
internal virtual IUserControlRepository CreateUserControlRepository(IUnitOfWork uow)
{
return new UserControlRepository(uow, FileSystemProviderManager.Current.UserControlsFileSystem);
}
public virtual IStylesheetRepository CreateStylesheetRepository(IUnitOfWork uow)
{
return new StylesheetRepository(uow, FileSystemProviderManager.Current.StylesheetsFileSystem);

View File

@@ -621,6 +621,34 @@ namespace Umbraco.Core.Services
}
}
public Stream GetMacroScriptFileContentStream(string filepath)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateMacroScriptRepository(uow);
return repository.GetFileContentStream(filepath);
}
}
public void SetMacroScriptFileContent(string filepath, Stream content)
{
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateMacroScriptRepository(uow);
repository.SetFileContent(filepath, content);
uow.Commit();
}
}
public long GetMacroScriptFileSize(string filepath)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateMacroScriptRepository(uow);
return repository.GetFileSize(filepath);
}
}
#endregion
public Stream GetStylesheetFileContentStream(string filepath)
@@ -679,6 +707,34 @@ namespace Umbraco.Core.Services
}
}
public Stream GetUserControlFileContentStream(string filepath)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateUserControlRepository(uow);
return repository.GetFileContentStream(filepath);
}
}
public void SetUserControlFileContent(string filepath, Stream content)
{
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateUserControlRepository(uow);
repository.SetFileContent(filepath, content);
uow.Commit();
}
}
public long GetUserControlFileSize(string filepath)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateUserControlRepository(uow);
return repository.GetFileSize(filepath);
}
}
public Stream GetXsltFileContentStream(string filepath)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
@@ -783,6 +839,26 @@ namespace Umbraco.Core.Services
}
}
[Obsolete("MacroScripts are obsolete - this is for backwards compatibility with upgraded sites.")]
public IPartialView GetMacroScript(string path)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateMacroScriptRepository(uow);
return repository.Get(path);
}
}
[Obsolete("UserControls are obsolete - this is for backwards compatibility with upgraded sites.")]
public IUserControl GetUserControl(string path)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateUserControlRepository(uow);
return repository.Get(path);
}
}
public IEnumerable<IPartialView> GetPartialViewMacros(params string[] names)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))

View File

@@ -17,6 +17,10 @@ namespace Umbraco.Core.Services
void DeletePartialViewMacroFolder(string folderPath);
IPartialView GetPartialView(string path);
IPartialView GetPartialViewMacro(string path);
[Obsolete("MacroScripts are obsolete - this is for backwards compatibility with upgraded sites.")]
IPartialView GetMacroScript(string path);
[Obsolete("UserControls are obsolete - this is for backwards compatibility with upgraded sites.")]
IUserControl GetUserControl(string path);
IEnumerable<IPartialView> GetPartialViewMacros(params string[] names);
IXsltFile GetXsltFile(string path);
IEnumerable<IXsltFile> GetXsltFiles(params string[] names);
@@ -263,6 +267,27 @@ namespace Umbraco.Core.Services
/// <returns>The size of the template.</returns>
long GetTemplateFileSize(string filepath);
/// <summary>
/// Gets the content of a macroscript as a stream.
/// </summary>
/// <param name="filepath">The filesystem path to the macroscript.</param>
/// <returns>The content of the macroscript.</returns>
Stream GetMacroScriptFileContentStream(string filepath);
/// <summary>
/// Sets the content of a macroscript.
/// </summary>
/// <param name="filepath">The filesystem path to the macroscript.</param>
/// <param name="content">The content of the macroscript.</param>
void SetMacroScriptFileContent(string filepath, Stream content);
/// <summary>
/// Gets the size of a macroscript.
/// </summary>
/// <param name="filepath">The filesystem path to the macroscript.</param>
/// <returns>The size of the macroscript.</returns>
long GetMacroScriptFileSize(string filepath);
/// <summary>
/// Gets the content of a stylesheet as a stream.
/// </summary>
@@ -305,6 +330,27 @@ namespace Umbraco.Core.Services
/// <returns>The size of the script file.</returns>
long GetScriptFileSize(string filepath);
/// <summary>
/// Gets the content of a usercontrol as a stream.
/// </summary>
/// <param name="filepath">The filesystem path to the usercontrol.</param>
/// <returns>The content of the usercontrol.</returns>
Stream GetUserControlFileContentStream(string filepath);
/// <summary>
/// Sets the content of a usercontrol.
/// </summary>
/// <param name="filepath">The filesystem path to the usercontrol.</param>
/// <param name="content">The content of the usercontrol.</param>
void SetUserControlFileContent(string filepath, Stream content);
/// <summary>
/// Gets the size of a usercontrol.
/// </summary>
/// <param name="filepath">The filesystem path to the usercontrol.</param>
/// <returns>The size of the usercontrol.</returns>
long GetUserControlFileSize(string filepath);
/// <summary>
/// Gets the content of a XSLT file as a stream.
/// </summary>

View File

@@ -72,6 +72,18 @@ namespace Umbraco.Core.Services
/// <returns><see cref="IMembershipUser"/></returns>
T CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias);
/// <summary>
/// Creates and persists a new <see cref="IMembershipUser"/>
/// </summary>
/// <remarks>An <see cref="IMembershipUser"/> can be of type <see cref="IMember"/> or <see cref="IUser"/></remarks>
/// <param name="username">Username of the <see cref="IMembershipUser"/> to create</param>
/// <param name="email">Email of the <see cref="IMembershipUser"/> to create</param>
/// <param name="passwordValue">This value should be the encoded/encrypted/hashed value for the password that will be stored in the database</param>
/// <param name="memberTypeAlias">Alias of the Type</param>
/// <param name="isApproved">IsApproved of the <see cref="IMembershipUser"/> to create</param>
/// <returns><see cref="IMembershipUser"/></returns>
T CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias, bool isApproved);
/// <summary>
/// Gets an <see cref="IMembershipUser"/> by its provider key
/// </summary>

View File

@@ -826,6 +826,22 @@ namespace Umbraco.Core.Services
return CreateMemberWithIdentity(username, email, username, passwordValue, memberType);
}
/// <summary>
/// Creates and persists a new <see cref="IMember"/>
/// </summary>
/// <remarks>An <see cref="IMembershipUser"/> can be of type <see cref="IMember"/> or <see cref="IUser"/></remarks>
/// <param name="username">Username of the <see cref="IMembershipUser"/> to create</param>
/// <param name="email">Email of the <see cref="IMembershipUser"/> to create</param>
/// <param name="passwordValue">This value should be the encoded/encrypted/hashed value for the password that will be stored in the database</param>
/// <param name="memberTypeAlias">Alias of the Type</param>
/// <param name="isApproved">Is the member approved</param>
/// <returns><see cref="IMember"/></returns>
IMember IMembershipMemberService<IMember>.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias, bool isApproved)
{
var memberType = FindMemberTypeByAlias(memberTypeAlias);
return CreateMemberWithIdentity(username, email, username, passwordValue, memberType, isApproved);
}
/// <summary>
/// Creates and persists a Member
/// </summary>
@@ -836,12 +852,13 @@ namespace Umbraco.Core.Services
/// <param name="name">Name of the Member to create</param>
/// <param name="passwordValue">This value should be the encoded/encrypted/hashed value for the password that will be stored in the database</param>
/// <param name="memberType">MemberType the Member should be based on</param>
/// <param name="isApproved">Optional IsApproved of the Member to create</param>
/// <returns><see cref="IMember"/></returns>
private IMember CreateMemberWithIdentity(string username, string email, string name, string passwordValue, IMemberType memberType)
private IMember CreateMemberWithIdentity(string username, string email, string name, string passwordValue, IMemberType memberType, bool isApproved = true)
{
if (memberType == null) throw new ArgumentNullException("memberType");
var member = new Member(name, email.ToLower().Trim(), username, passwordValue, memberType);
var member = new Member(name, email.ToLower().Trim(), username, passwordValue, memberType, isApproved);
using (var uow = UowProvider.GetUnitOfWork())
{

View File

@@ -19,7 +19,6 @@ namespace Umbraco.Core.Services
/// </summary>
public class UserService : ScopeRepositoryService, IUserService
{
//TODO: We need to change the isUpgrading flag to use an app state enum as described here: http://issues.umbraco.org/issue/U4-6816
// in the meantime, we will use a boolean which we are currently using during upgrades to ensure that a user object is not persisted during this phase, otherwise
// exceptions can occur if the db is not in it's correct state.
@@ -113,6 +112,26 @@ namespace Umbraco.Core.Services
return CreateUserWithIdentity(username, email, passwordValue, userType);
}
/// <summary>
/// Creates and persists a new <see cref="IUser"/>
/// </summary>
/// <param name="username">Username of the <see cref="IUser"/> to create</param>
/// <param name="email">Email of the <see cref="IUser"/> to create</param>
/// <param name="passwordValue">This value should be the encoded/encrypted/hashed value for the password that will be stored in the database</param>
/// <param name="memberTypeAlias">Alias of the Type</param>
/// <param name="isApproved">Is the member approved</param>
/// <returns><see cref="IUser"/></returns>
IUser IMembershipMemberService<IUser>.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias, bool isApproved)
{
var userType = GetUserTypeByAlias(memberTypeAlias);
if (userType == null)
{
throw new EntityNotFoundException("The user type " + memberTypeAlias + " could not be resolved");
}
return CreateUserWithIdentity(username, email, passwordValue, userType, isApproved);
}
/// <summary>
/// Creates and persists a Member
/// </summary>
@@ -122,8 +141,9 @@ namespace Umbraco.Core.Services
/// <param name="email">Email of the Member to create</param>
/// <param name="passwordValue">This value should be the encoded/encrypted/hashed value for the password that will be stored in the database</param>
/// <param name="userType">MemberType the Member should be based on</param>
/// <param name="isApproved">Is the user approved</param>
/// <returns><see cref="IUser"/></returns>
private IUser CreateUserWithIdentity(string username, string email, string passwordValue, IUserType userType)
private IUser CreateUserWithIdentity(string username, string email, string passwordValue, IUserType userType, bool isApproved = true)
{
if (userType == null) throw new ArgumentNullException("userType");
@@ -152,7 +172,7 @@ namespace Umbraco.Core.Services
StartContentId = -1,
StartMediaId = -1,
IsLockedOut = false,
IsApproved = true
IsApproved = isApproved
};
//adding default sections content and media
user.AddAllowedSection("content");

View File

@@ -69,6 +69,10 @@ namespace Umbraco.Core
[UdiType(UdiType.StringUdi)]
public const string AnyString = "any-string"; // that one is for tests
[UdiType(UdiType.StringUdi)]
public const string Language = "language";
[UdiType(UdiType.StringUdi)]
public const string MacroScript = "macroscript";
[UdiType(UdiType.StringUdi)]
public const string MediaFile = "media-file";
[UdiType(UdiType.StringUdi)]
@@ -82,9 +86,9 @@ namespace Umbraco.Core
[UdiType(UdiType.StringUdi)]
public const string PartialViewMacro = "partial-view-macro";
[UdiType(UdiType.StringUdi)]
public const string Xslt = "xslt";
public const string UserControl = "usercontrol";
[UdiType(UdiType.StringUdi)]
public const string Language = "language";
public const string Xslt = "xslt";
public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType)
{

View File

@@ -190,6 +190,17 @@ namespace Umbraco.Core
return new GuidUdi(Constants.UdiEntityType.Macro, entity.Key).EnsureClosed();
}
/// <summary>
/// Gets the entity identifier of the entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <returns>The entity identifier of the entity.</returns>
public static StringUdi GetUdi(this IUserControl entity)
{
if (entity == null) throw new ArgumentNullException("entity");
return new StringUdi(Constants.UdiEntityType.UserControl, entity.Path.TrimStart('/')).EnsureClosed();
}
/// <summary>
/// Gets the entity identifier of the entity.
/// </summary>

View File

@@ -344,7 +344,9 @@
<Compile Include="HashCodeHelper.cs" />
<Compile Include="IHttpContextAccessor.cs" />
<Compile Include="Models\EntityBase\IDeletableEntity.cs" />
<Compile Include="Models\IUserControl.cs" />
<Compile Include="Models\PublishedContent\PublishedContentTypeConverter.cs" />
<Compile Include="Models\UserControl.cs" />
<Compile Include="OrderedHashSet.cs" />
<Compile Include="Events\UninstallPackageEventArgs.cs" />
<Compile Include="Models\GridValue.cs" />
@@ -576,12 +578,15 @@
<Compile Include="Persistence\Repositories\Interfaces\IRedirectUrlRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IServerRegistrationRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IDeleteMediaFilesRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IUserControlRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IXsltFileRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\ITaskRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\ITaskTypeRepository.cs" />
<Compile Include="Persistence\Repositories\MacroScriptRepository.cs" />
<Compile Include="Persistence\Repositories\MigrationEntryRepository.cs" />
<Compile Include="Persistence\Repositories\PublicAccessRepository.cs" />
<Compile Include="Persistence\Repositories\RedirectUrlRepository.cs" />
<Compile Include="Persistence\Repositories\UserControlRepository.cs" />
<Compile Include="Persistence\Repositories\XsltFileRepository.cs" />
<Compile Include="Persistence\Repositories\TaskRepository.cs" />
<Compile Include="Persistence\Repositories\TaskTypeRepository.cs" />

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Collections.Specialized;
using System.Web.Security;
using Moq;
using NUnit.Framework;
@@ -83,10 +81,10 @@ namespace Umbraco.Tests.Membership
mServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => null);
mServiceMock.Setup(service => service.GetDefaultMemberType()).Returns("Member");
mServiceMock.Setup(
service => service.CreateWithIdentity(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Callback((string u, string e, string p, string m) =>
service => service.CreateWithIdentity(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Callback((string u, string e, string p, string m, bool isApproved) =>
{
createdMember = new Member("test", e, u, p, memberType);
createdMember = new Member("test", e, u, p, memberType, isApproved);
})
.Returns(() => createdMember);
var provider = new MembersMembershipProvider(mServiceMock.Object);
@@ -114,10 +112,10 @@ namespace Umbraco.Tests.Membership
mServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => null);
mServiceMock.Setup(service => service.GetDefaultMemberType()).Returns("Member");
mServiceMock.Setup(
service => service.CreateWithIdentity(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Callback((string u, string e, string p, string m) =>
service => service.CreateWithIdentity(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Callback((string u, string e, string p, string m, bool isApproved) =>
{
createdMember = new Member("test", e, u, p, memberType);
createdMember = new Member("test", e, u, p, memberType, isApproved);
})
.Returns(() => createdMember);
@@ -147,10 +145,10 @@ namespace Umbraco.Tests.Membership
mServiceMock.Setup(service => service.GetByEmail("test@test.com")).Returns(() => null);
mServiceMock.Setup(service => service.GetDefaultMemberType()).Returns("Member");
mServiceMock.Setup(
service => service.CreateWithIdentity(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Callback((string u, string e, string p, string m) =>
service => service.CreateWithIdentity(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Callback((string u, string e, string p, string m, bool isApproved) =>
{
createdMember = new Member("test", e, u, p, memberType);
createdMember = new Member("test", e, u, p, memberType, isApproved);
})
.Returns(() => createdMember);

View File

@@ -44,11 +44,11 @@ namespace Umbraco.Tests.PublishedContent
var propertyTypes = new[]
{
// AutoPublishedContentType will auto-generate other properties
new PublishedPropertyType("umbracoNaviHide", 0, Constants.PropertyEditors.TrueFalseAlias),
new PublishedPropertyType("selectedNodes", 0, "?"),
new PublishedPropertyType("umbracoUrlAlias", 0, "?"),
new PublishedPropertyType("content", 0, Constants.PropertyEditors.TinyMCEAlias),
new PublishedPropertyType("testRecursive", 0, "?"),
new PublishedPropertyType("umbracoNaviHide", 0, Constants.PropertyEditors.TrueFalseAlias),
new PublishedPropertyType("selectedNodes", 0, "?"),
new PublishedPropertyType("umbracoUrlAlias", 0, "?"),
new PublishedPropertyType("content", 0, Constants.PropertyEditors.TinyMCEAlias),
new PublishedPropertyType("testRecursive", 0, "?"),
};
var compositionAliases = new[] {"MyCompositionAlias"};
var type = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes);
@@ -73,7 +73,7 @@ namespace Umbraco.Tests.PublishedContent
protected override string GetXmlContent(int templateId)
{
return @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[
<!DOCTYPE root[
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
<!ELEMENT CustomDocument ANY>
@@ -87,17 +87,17 @@ namespace Umbraco.Tests.PublishedContent
<testRecursive><![CDATA[This is the recursive val]]></testRecursive>
<Home id=""1173"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""Sub1"" urlName=""sub1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173"" isDoc="""">
<content><![CDATA[<div>This is some content</div>]]></content>
<umbracoUrlAlias><![CDATA[page2/alias, 2ndpagealias]]></umbracoUrlAlias>
<umbracoUrlAlias><![CDATA[page2/alias, 2ndpagealias]]></umbracoUrlAlias>
<testRecursive><![CDATA[]]></testRecursive>
<Home id=""1174"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Sub2"" urlName=""sub2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1174"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoUrlAlias><![CDATA[only/one/alias]]></umbracoUrlAlias>
<creatorName><![CDATA[Custom data with same property name as the member name]]></creatorName>
<testRecursive><![CDATA[]]></testRecursive>
</Home>
</Home>
<CustomDocument id=""1177"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""custom sub 1"" urlName=""custom-sub-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1177"" isDoc="""" />
<CustomDocument id=""1178"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""custom sub 2"" urlName=""custom-sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1178"" isDoc="""" />
<Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc="""">
<Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc="""" key=""CDB83BBC-A83B-4BA6-93B8-AADEF67D3C09"">
<content><![CDATA[]]></content>
<umbracoNaviHide>1</umbracoNaviHide>
</Home>
@@ -211,8 +211,8 @@ namespace Umbraco.Tests.PublishedContent
[PublishedContentModel("Home")]
internal class Home : PublishedContentModel
{
public Home(IPublishedContent content)
: base(content)
public Home(IPublishedContent content)
: base(content)
{}
}
@@ -659,6 +659,28 @@ namespace Umbraco.Tests.PublishedContent
Assert.AreEqual((int)1178, (int)result.Id);
}
[Test]
public void GetKey()
{
var key = Guid.Parse("CDB83BBC-A83B-4BA6-93B8-AADEF67D3C09");
// doc is Home (a model) and GetKey unwraps and works
var doc = GetNode(1176);
Assert.IsInstanceOf<Home>(doc);
Assert.AreEqual(key, doc.GetKey());
// wrapped is PublishedContentWrapped and WithKey unwraps
var wrapped = new TestWrapped(doc);
Assert.AreEqual(key, wrapped.GetKey());
}
class TestWrapped : PublishedContentWrapped
{
public TestWrapped(IPublishedContent content)
: base(content)
{ }
}
[Test]
public void DetachedProperty1()
{

View File

@@ -0,0 +1,15 @@
/**
* @ngdoc filter
* @name umbraco.filters.preserveNewLineInHtml
* @description
* Used when rendering a string as HTML (i.e. with ng-bind-html) to convert line-breaks to <br /> tags
**/
angular.module("umbraco.filters").filter('preserveNewLineInHtml', function () {
return function (text) {
if (!text) {
return '';
}
return text.replace(/\n/g, '<br />');
};
});

View File

@@ -269,12 +269,12 @@
var isSelected = false;
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id) {
if (item.id === selectedItem.id || item.key === selectedItem.key) {
isSelected = true;
}
}
if (!isSelected) {
selection.push({ id: item.id });
selection.push({ id: item.id, key: item.key });
item.selected = true;
}
}

View File

@@ -1,28 +1,35 @@
angular.module("umbraco.install").controller("Umbraco.Installer.DataBaseController", function($scope, $http, installerService){
$scope.checking = false;
$scope.invalidDbDns = false;
$scope.dbs = [
{name: 'Microsoft SQL Server Compact (SQL CE)', id: 0},
{name: 'Microsoft SQL Server', id: 1},
{ name: 'Microsoft SQL Azure', id: 3 },
{ name: 'MySQL', id: 2 },
{name: 'Custom connection string', id: -1}];
{ name: 'Microsoft SQL Server Compact (SQL CE)', id: 0},
{ name: 'Microsoft SQL Server', id: 1},
{ name: 'Microsoft SQL Azure', id: 3 },
{ name: 'MySQL', id: 2 },
{ name: 'Custom connection string', id: -1}
];
if(installerService.status.current.model.dbType === undefined){
if ( installerService.status.current.model.dbType === undefined ) {
installerService.status.current.model.dbType = 0;
}
$scope.validateAndForward = function(){
if(!$scope.checking && this.myForm.$valid){
$scope.validateAndForward = function(){
if ( !$scope.checking && this.myForm.$valid ) {
$scope.checking = true;
$scope.invalidDbDns = false;
var model = installerService.status.current.model;
$http.post(Umbraco.Sys.ServerVariables.installApiBaseUrl + "PostValidateDatabaseConnection",
model).then(function(response){
$http.post(
Umbraco.Sys.ServerVariables.installApiBaseUrl + "PostValidateDatabaseConnection",
model ).then( function( response ) {
if(response.data === "true"){
if ( response.data === "true" ) {
installerService.forward();
}else{
}
else {
$scope.invalidDbDns = true;
}
@@ -33,4 +40,4 @@ angular.module("umbraco.install").controller("Umbraco.Installer.DataBaseControll
});
}
};
});
});

View File

@@ -1,16 +1,23 @@
<div ng-controller="Umbraco.Installer.PackagesController" id="starterKits">
<h1>Would you like to learn or demo Umbraco?</h1>
<h1>Install a starter website</h1>
<p>
Installing a starter website helps you learn how Umbraco works, and gives you a solid
and simple foundation to build on top of.
</p>
<small ng-if="!packages || packages.length == 0">Loading...</small>
<ul class="thumbnails">
<li class="span3" ng-repeat="pck in packages">
<a href ng-click="setPackageAndContinue(pck.id)" class="thumbnail">
<small>Loading...</small>
<img ng-src="http://our.umbraco.org{{pck.thumbnail}}?width=170" alt="{{pck.name}}">
</a>
</li>
</ul>
<span style="float: left; margin: 0 15px 50px 0; background-color: #efefef; width: 350px; height: 167px;"><img ng-src="http://our.umbraco.org{{packages[0].thumbnail}}?width=350" style="width: 350px;" alt="{{pck.name}}" /></span>
<p>The Starter Kit is a great way to experience some of the ways you can use Umbraco. It's a complete website with textpages, landing pages, blog, product listings and more that's easy to get started with Umbraco.
</p>
<p>
It's also a great way to learn Umbraco as the Starter Kit comes with a set of Lessons that'll teach you how to implement and extend Umbraco using short 5-15 minute tasks.
</p>
<p>
<a href ng-click="setPackageAndContinue(packages[0].id)" class="btn btn-success">Yes, I'd like a Starter Kit</a>
&nbsp;
<a href ng-click="setPackageAndContinue('00000000-0000-0000-0000-000000000000')" class="btn btn-link btn-link-reverse">
No thanks
No thanks, I do not want to install a starter website
</a>
</div>
</div>

View File

@@ -66,8 +66,9 @@ label.control-label, .control-label {
}
.form-search .icon, .form-search .icon-search {
position: absolute;
z-index: 1;
top: 6px;
left: 4px;
left: 6px;
color: @gray-8;
}
@@ -86,6 +87,9 @@ label.control-label, .control-label {
background: @white
}
.form-search .icon-search + .search-input {
padding-left: 25px !important;
}
.form-search .search-input {
font-weight: bold;
@@ -191,7 +195,7 @@ input[type="tel"],
input[type="color"],
.uneditable-input {
display: inline-block;
height: 30px;
height: @inputHeight;
padding: 4px 6px;
margin-bottom: @baseLineHeight / 2;
font-size: @baseFontSize;
@@ -206,7 +210,6 @@ input.-full-width-input {
width: 100%;
box-sizing: border-box;
padding: 4px 6px;
height: 30px;
}
// Reset appearance properties for textual inputs and textarea
@@ -579,9 +582,9 @@ input[type="checkbox"][readonly] {
.add-on {
display: inline-block;
width: auto;
height: @baseLineHeight;
min-width: 16px;
padding: 4px 5px;
height: 22px;
min-width: 18px;
padding: 4px 6px;
font-size: @baseFontSize;
font-weight: normal;
line-height: @baseLineHeight;
@@ -653,29 +656,21 @@ input.search-query {
padding-left: 14px;
padding-left: 4px \9; /* IE7-8 doesn't have border-radius, so don't indent the padding */
margin: 0; // Remove the default margin on all inputs
.border-radius(0px);
}
/* Allow for input prepend/append in search forms */
.form-search .input-append .search-query,
.form-search .input-prepend .search-query {
.border-radius(0); // Override due to specificity
.form-search {
.input-prepend {
.btn {
.border-radius(0 @borderRadiusSmall @borderRadiusSmall 0);
}
}
.input-append {
.btn {
.border-radius(0 @borderRadiusSmall @borderRadiusSmall 0);
}
}
}
.form-search .input-append .search-query {
.border-radius(14px 0 0 14px);
}
.form-search .input-append .btn {
.border-radius(0 14px 14px 0);
}
.form-search .input-prepend .search-query {
.border-radius(0 14px 14px 0);
}
.form-search .input-prepend .btn {
.border-radius(14px 0 0 14px);
}
// HORIZONTAL & VERTICAL FORMS
// ---------------------------

View File

@@ -47,10 +47,25 @@ h5.-black {
background: none;
border: none
}
.datepicker td.active,
.datepicker td span.active {
background: @turquoise !important;
.bootstrap-datetimepicker-widget {
td {
&.active, span.active {
background: @turquoise !important;
}
&.today:not(.active):before {
border-bottom-color: @purple-l1 !important;
}
a[data-action] {
padding: 0 !important;
}
.timepicker-hour,
.timepicker-minute,
.timepicker-second {
margin: 8px 0;
}
}
}
.umb-datetime-picker div.info {
vertical-align: middle
}

View File

@@ -446,8 +446,8 @@ input.umb-panel-header-name-input {
margin-bottom: 0;
font-weight: bold;
box-sizing: border-box;
height: 30px;
line-height: 30px;
height: 32px;
line-height: 32px;
width: 100%;
&:hover {
background: @white;

View File

@@ -7,7 +7,7 @@ ul.sections {
background: @purple;
height: 100%;
width: 80px;
border-right: 1px solid @purple;
border-right: 1px solid @purple-d1;
}
ul.sections li {
@@ -22,13 +22,16 @@ ul.sections li [class^="icon-"],
ul.sections li [class*=" icon-"],
ul.sections li img.icon-section {
font-size: 30px;
line-height: 20px; /* set line-height to ensure all icons use same line-height */
display: inline-block;
margin: 1px 0 0 0;
color: @purple-l2;
-webkit-transition: all .3s linear;
-moz-transition: all .3s linear;
transition: all .3s linear;
&, &:before {
line-height: 20px !important; /* set line-height to ensure all icons use same line-height */
}
}
ul.sections:hover li [class^="icon-"],
@@ -168,7 +171,7 @@ ul.sections-tray {
width: 80px;
& > li:first-child > a {
border-top: 1px solid @purple;
border-top: 1px solid @purple-d1;
}
& > li {

View File

@@ -190,7 +190,7 @@
@inputBorderRadius: 0;
@inputDisabledBackground: @gray-10;
@formActionsBackground: @gray-9;
@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border
@inputHeight: @baseLineHeight + 12px; // base line-height + 8px vertical padding + 2px top/bottom border
@controlRequiredColor: @red;

View File

@@ -2,13 +2,13 @@
<div class="umb-panel">
<div class="umb-panel-body no-header with-footer">
<umb-pane>
<umb-control-group label="Url">
<umb-control-group label="@general_url">
<input id="url" class="umb-editor input-block-level" type="text" ng-model="form.url" ng-keyup="$event.keyCode == 13 ? showPreview() : null" required />
<input type="button" ng-click="showPreview()" class="btn" value="Retrieve" />
<input type="button" ng-click="showPreview()" class="btn" localize="value" value="@general_retrieve" />
</umb-control-group>
<div ng-show="form.supportsDimensions">
<umb-control-group label="Size">
<umb-control-group label="@general_size">
<input type="text" ng-model="form.width" on-blur="changeSize('width')" style="width:50px"/> x <input type="text" ng-model="form.height" on-blur="changeSize('height')" style="width:50px"/>
<label for="constrain" style="display:inline"><localize key="general_constrain">Constrain</localize>:</label>

View File

@@ -1,8 +1,8 @@
<form ng-controller="Umbraco.Overlays.EmbedOverlay as vm">
<umb-control-group label="Url">
<umb-control-group label="@general_url">
<input id="url" class="umb-editor input-block-level" type="text" style="margin-bottom: 10px;" ng-model="model.embed.url" ng-keyup="$event.keyCode == 13 ? vm.showPreview() : null" required />
<input type="button" ng-click="vm.showPreview()" class="btn" value="Retrieve" />
<input type="button" ng-click="vm.showPreview()" class="btn" localize="value" value="@general_retrieve" />
</umb-control-group>
<umb-control-group>
@@ -11,15 +11,15 @@
</umb-control-group>
<div ng-show="model.embed.supportsDimensions">
<umb-control-group label="Width">
<umb-control-group label="@general_width">
<input type="text" ng-model="model.embed.width" on-blur="vm.changeSize('width')" />
</umb-control-group>
<umb-control-group label="Height">
<umb-control-group label="@general_height">
<input type="text" ng-model="model.embed.height" on-blur="vm.changeSize('height')" />
</umb-control-group>
<umb-control-group label="Constrain:">
<umb-control-group label="@general_constrain">
<input id="constrain" type="checkbox" ng-model="model.embed.constrain" />
</umb-control-group>
</div>

View File

@@ -21,7 +21,6 @@
vm.encodeHtml = false;
vm.encodeUrl = false;
vm.convertLinebreaks = false;
vm.removeParagraphTags = false;
vm.showAltField = false;
vm.showAltText = false;
@@ -130,7 +129,6 @@
+ (vm.encodeHtml !== false ? ', encoding: ' + "RenderFieldEncodingType.Html" : "")
+ (vm.encodeUrl !== false ? ', encoding: ' + "RenderFieldEncodingType.Url" : "")
+ (vm.convertLinebreaks !== false ? ', convertLineBreaks: ' + "true" : "")
+ (vm.removeParagraphTags !== false ? ', removeParagraphTags: ' + "true": "")
+ (vm.field ? ')' : "");
$scope.model.umbracoField = pageField;

View File

@@ -168,22 +168,6 @@
</div>
</div>
<!-- Remove paragraph tags -->
<div class="control-group umb-control-group">
<div class="umb-el-wrap">
<div class="controls">
<div>
<label class="control-label" for="paragraph">
<localize key="templateEditor_removeParagraph">Remove paragraph tags</localize>
<small><localize key="templateEditor_removeParagraphHelp">Will remove any paragraph tag in the beginning and end of the text</localize></small>
</label>
</div>
<input type="checkbox" name="paragraph" ng-model="vm.removeParagraphTags">
<localize key="templateEditor_removeParagraphDescription">Yes, remove paragraph tags</localize>
</div>
</div>
</div>
<!-- Output -->
<div class="control-group umb-control-group -no-border">
<div class="umb-el-wrap">

View File

@@ -1,7 +1,7 @@
//used for the media picker dialog
angular.module("umbraco")
.controller("Umbraco.Overlays.MediaPickerController",
function ($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, localStorageService, localizationService) {
function($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, localStorageService, localizationService) {
if (!$scope.model.title) {
$scope.model.title = localizationService.localize("defaultdialogs_selectMedia");
@@ -96,7 +96,6 @@ angular.module("umbraco")
};
$scope.gotoFolder = function(folder) {
if (!$scope.multiPicker) {
deselectAllImages($scope.model.selectedImages);
}
@@ -108,7 +107,6 @@ angular.module("umbraco")
if (folder.id > 0) {
entityResource.getAncestors(folder.id, "media")
.then(function(anc) {
// anc.splice(0,1);
$scope.path = _.filter(anc,
function(f) {
return f.path.indexOf($scope.startNodeId) !== -1;
@@ -123,12 +121,12 @@ angular.module("umbraco")
$scope.path = [];
}
getChildren(folder.id);
$scope.currentFolder = folder;
localStorageService.set("umbLastOpenedMediaNodeId", folder.id);
return getChildren(folder.id);
};
$scope.clickHandler = function (image, event, index) {
$scope.clickHandler = function(image, event, index) {
if (image.isFolder) {
if ($scope.disableFolderSelect) {
$scope.gotoFolder(image);
@@ -143,7 +141,7 @@ angular.module("umbraco")
$scope.target = image;
// handle both entity and full media object
if(image.image) {
if (image.image) {
$scope.target.url = image.image;
} else {
$scope.target.url = mediaHelper.resolveFile(image);
@@ -188,8 +186,12 @@ angular.module("umbraco")
images.length = 0;
}
$scope.onUploadComplete = function() {
$scope.gotoFolder($scope.currentFolder);
$scope.onUploadComplete = function(files) {
$scope.gotoFolder($scope.currentFolder).then(function() {
if (files.length === 1 && $scope.model.selectedImages.length === 0) {
selectImage($scope.images[$scope.images.length - 1]);
}
});
};
$scope.onFilesQueue = function() {
@@ -203,8 +205,7 @@ angular.module("umbraco")
if (nodePath.indexOf($scope.startNodeId.toString()) !== -1) {
$scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" });
return true;
}
else {
} else {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
return false;
}
@@ -219,26 +220,25 @@ angular.module("umbraco")
if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) {
entityResource.getById($scope.lastOpenedNode, "media")
.then(ensureWithinStartNode, gotoStartNode);
}
else {
} else {
gotoStartNode();
}
}
else {
} else {
//if a target is specified, go look it up - generally this target will just contain ids not the actual full
//media object so we need to look it up
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id
var altText = $scope.target.altText;
mediaResource.getById(id)
.then(function (node) {
$scope.target = node;
if (ensureWithinStartNode(node)) {
selectImage(node);
$scope.target.url = mediaHelper.resolveFile(node);
$scope.target.altText = altText;
$scope.openDetailsDialog();
}
}, gotoStartNode);
.then(function(node) {
$scope.target = node;
if (ensureWithinStartNode(node)) {
selectImage(node);
$scope.target.url = mediaHelper.resolveFile(node);
$scope.target.altText = altText;
$scope.openDetailsDialog();
}
},
gotoStartNode);
}
$scope.openDetailsDialog = function() {
@@ -260,6 +260,24 @@ angular.module("umbraco")
};
};
var debounceSearchMedia = _.debounce(function() {
$scope.$apply(function() {
if ($scope.searchOptions.filter) {
searchMedia();
} else {
// reset pagination
$scope.searchOptions = {
pageNumber: 1,
pageSize: 100,
totalItems: 0,
totalPages: 0,
filter: ''
};
getChildren($scope.currentFolder.id);
}
});
}, 500);
$scope.changeSearch = function() {
$scope.loading = true;
debounceSearchMedia();
@@ -271,50 +289,33 @@ angular.module("umbraco")
searchMedia();
};
var debounceSearchMedia = _.debounce(function () {
$scope.$apply(function () {
if ($scope.searchOptions.filter) {
searchMedia();
} else {
// reset pagination
$scope.searchOptions = {
pageNumber: 1,
pageSize: 100,
totalItems: 0,
totalPages: 0,
filter: ''
};
getChildren($scope.currentFolder.id);
}
});
}, 500);
function searchMedia() {
$scope.loading = true;
entityResource.getPagedDescendants($scope.startNodeId, "Media", $scope.searchOptions)
.then(function (data) {
.then(function(data) {
// update image data to work with image grid
angular.forEach(data.items, function(mediaItem){
// set thumbnail and src
mediaItem.thumbnail = mediaHelper.resolveFileFromEntity(mediaItem, true);
mediaItem.image = mediaHelper.resolveFileFromEntity(mediaItem, false);
// set properties to match a media object
if (mediaItem.metaData &&
mediaItem.metaData.umbracoWidth &&
mediaItem.metaData.umbracoHeight) {
mediaItem.properties = [
{
alias: "umbracoWidth",
value: mediaItem.metaData.umbracoWidth.Value
},
{
alias: "umbracoHeight",
value: mediaItem.metaData.umbracoHeight.Value
}
];
}
});
angular.forEach(data.items,
function(mediaItem) {
// set thumbnail and src
mediaItem.thumbnail = mediaHelper.resolveFileFromEntity(mediaItem, true);
mediaItem.image = mediaHelper.resolveFileFromEntity(mediaItem, false);
// set properties to match a media object
if (mediaItem.metaData &&
mediaItem.metaData.umbracoWidth &&
mediaItem.metaData.umbracoHeight) {
mediaItem.properties = [
{
alias: "umbracoWidth",
value: mediaItem.metaData.umbracoWidth.Value
},
{
alias: "umbracoHeight",
value: mediaItem.metaData.umbracoHeight.Value
}
];
}
});
// update images
$scope.images = data.items ? data.items : [];
// update pagination
@@ -332,7 +333,7 @@ angular.module("umbraco")
function getChildren(id) {
$scope.loading = true;
mediaResource.getChildren(id)
return mediaResource.getChildren(id)
.then(function(data) {
$scope.searchOptions.filter = "";
$scope.images = data.items ? data.items : [];
@@ -358,11 +359,10 @@ angular.module("umbraco")
}
}
}
if (imageIsSelected) {
folderImage.selected = true;
}
}
}
});
});

View File

@@ -28,8 +28,7 @@
<div class="upload-button">
<umb-button
type="button"
key="general_upload"
label="Upload"
label-key="general_upload"
action="upload()"
disabled="disabled">
</umb-button>

View File

@@ -11,7 +11,7 @@
<span ng-if="property.validation.mandatory">
<strong class="umb-control-required">*</strong>
</span>
<small ng-bind-html="property.description"></small>
<small ng-bind-html="property.description | preserveNewLineInHtml"></small>
</label>
<div class="controls" ng-transclude>

View File

@@ -20,7 +20,7 @@
</div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" >
<button type="button" class="btn btn-info" ng-click="nav.hideDialog()">
<button type="button" class="btn btn-info" ng-click="nav.hideDialog(true)">
<localize key="buttons_somethingElse">Do something else</localize>
</button>
</div>

View File

@@ -1 +1 @@
<input type="text" ng-model="model.value" class="umb-editor umb-textstring" />
<input type="text" ng-model="model.value" ng-trim="false" class="umb-editor umb-textstring" />

View File

@@ -4,7 +4,7 @@
ng-model="renderModel">
<li ng-repeat="node in renderModel">
<i class="icon icon-delete red hover-show pull-right" ng-click="remove($index)"></i>
<i class="icon icon-delete red hover-show pull-right" style="cursor:pointer;" ng-click="remove($index); $event.stopPropagation();"></i>
<i class="icon {{node.icon}} hover-hide"></i>
<a href prevent-default ng-click="openContentPicker()" >{{node.name}}</a>

View File

@@ -9,30 +9,29 @@
ui-sortable
ng-model="model.value.templates">
<li ng-repeat="template in model.value.templates">
<li ng-repeat="template in model.value.templates" class="clearfix">
<div ng-click="configureTemplate(template)"
class="preview-rows layout" style="margin-top: 5px; margin-bottom: 20px; float:left">
class="preview-rows layout" style="margin-top: 5px; margin-bottom: 20px; float:left">
<div class="preview-row">
<div class="preview-col"
ng-class="{last:$last}"
ng-repeat="section in template.sections | filter:zeroWidthFilter"
ng-style="{width: percentage(section.grid) + '%', height: '60px'}">
ng-class="{last:$last}"
ng-repeat="section in template.sections | filter:zeroWidthFilter"
ng-style="{width: percentage(section.grid) + '%', height: '60px', 'max-width': '100%'}">
<div class="preview-cell"></div>
</div>
</div>
</div>
<div>
{{template.name}} <br/>
{{template.name}} <br />
<i class="icon-delete red"></i>
<a class="btn btn-small btn-link"
href
ng-click="deleteTemplate($index)"><localize key="general_delete" /></a>
</div>
<br style="clear: both" />
</li>
</ul>
@@ -55,7 +54,7 @@
ui-sortable
ng-model="model.value.layouts">
<li ng-repeat="layout in model.value.layouts">
<li ng-repeat="layout in model.value.layouts" class="clearfix">
<div ng-click="configureLayout(layout)"
class="preview-rows columns" style="margin-top: 5px; margin-bottom: 25px; float:left">
@@ -64,7 +63,7 @@
<div class="preview-col"
ng-class="{last:$last}"
ng-repeat="area in layout.areas | filter:zeroWidthFilter"
ng-style="{width: percentage(area.grid) + '%'}">
ng-style="{width: percentage(area.grid) + '%', 'max-width': '100%'}">
<div class="preview-cell"></div>
</div>
@@ -79,12 +78,11 @@
</a>
</div>
<br style="clear: both" />
</li>
</ul>
<button class="btn btn-small" prevent-default ng-click="configureLayout()">
<i class="icon-add"></i> <localize key="grid_addRowConfiguration" />
<i class="icon-add"></i> <localize key="grid_addRowConfiguration" />
</button>
</div>
@@ -98,7 +96,7 @@
<umb-control-group
label="@grid_columns"
description="@grid_columnsDetails">
<input type="text" class="" ng-model="model.value.columns" />
<input type="number" min="1" ng-model="model.value.columns" />
</umb-control-group>
<umb-control-group
@@ -107,7 +105,7 @@
<ul class="unstyled list-icons umb-contentpicker"
ui-sortable
ng-model="model.config">
ng-model="model.value.config">
<li ng-repeat="configValue in model.value.config">
<i class="icon icon-navigation handle"></i>
@@ -133,8 +131,11 @@
<umb-control-group label="@grid_styles" description="@grid_stylesDetails">
<ul class="unstyled list-icons umb-contentpicker"
<umb-control-group
label="@grid_styles"
description="@grid_stylesDetails">
<ul class="unstyled list-icons umb-contentpicker"
ui-sortable
ng-model="model.value.styles">

View File

@@ -20,8 +20,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie
getListResultsCallback = contentResource.getPagedResults;
deleteItemCallback = contentResource.deleteByKey;
getIdCallback = function (selected) {
var selectedKey = getItemKey(selected.id);
return selectedKey;
return selected.key;
};
createEditUrlCallback = function (item) {
return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.key + "?page=" + $scope.options.pageNumber + "&listName=" + $scope.contentId;

View File

@@ -0,0 +1,26 @@
function textAreaController($rootScope, $scope, $log) {
$scope.model.maxlength = false;
if($scope.model.config.maxChars) {
$scope.model.maxlength = true;
if($scope.model.value == undefined) {
$scope.model.count = ($scope.model.config.maxChars * 1);
} else {
$scope.model.count = ($scope.model.config.maxChars * 1) - $scope.model.value.length;
}
}
$scope.model.change = function() {
if($scope.model.config.maxChars) {
if($scope.model.value == undefined) {
$scope.model.count = ($scope.model.config.maxChars * 1);
} else {
$scope.model.count = ($scope.model.config.maxChars * 1) - $scope.model.value.length;
}
if($scope.model.count < 0) {
$scope.model.value = $scope.model.value.substring(0, ($scope.model.config.maxChars * 1));
$scope.model.count = 0;
}
}
}
}
angular.module('umbraco').controller("Umbraco.PropertyEditors.textAreaController", textAreaController);

View File

@@ -1,3 +1,9 @@
<textarea ng-model="model.value" id="{{model.alias}}" name="textarea" rows="10" class="umb-editor umb-textarea textstring" val-server="value" ng-required="model.validation.mandatory"></textarea>
<span class="help-inline" val-msg-for="textarea" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" val-msg-for="textarea" val-toggle-msg="valServer"></span>
<div ng-controller="Umbraco.PropertyEditors.textAreaController">
<textarea ng-model="model.value" id="{{model.alias}}" name="textarea" rows="10" class="umb-editor umb-textarea textstring" val-server="value" ng-keyup="model.change()"></textarea>
<span class="help-inline" val-msg-for="textarea" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" val-msg-for="textarea" val-toggle-msg="valServer"></span>
<div class="help" ng-if="model.maxlength">
<strong>{{model.count}}</strong>
<localize key="textbox_characters_left">characters left</localize>
</div>
</div>

View File

@@ -0,0 +1,26 @@
function textboxController($rootScope, $scope, $log) {
$scope.model.maxlength = false;
if($scope.model.config.maxChars) {
$scope.model.maxlength = true;
if($scope.model.value == undefined) {
$scope.model.count = ($scope.model.config.maxChars * 1);
} else {
$scope.model.count = ($scope.model.config.maxChars * 1) - $scope.model.value.length;
}
}
$scope.model.change = function() {
if($scope.model.config.maxChars) {
if($scope.model.value == undefined) {
$scope.model.count = ($scope.model.config.maxChars * 1);
} else {
$scope.model.count = ($scope.model.config.maxChars * 1) - $scope.model.value.length;
}
if($scope.model.count < 0) {
$scope.model.value = $scope.model.value.substring(0, ($scope.model.config.maxChars * 1));
$scope.model.count = 0;
}
}
}
}
angular.module('umbraco').controller("Umbraco.PropertyEditors.textboxController", textboxController);

View File

@@ -1,9 +1,14 @@
<div>
<div ng-controller="Umbraco.PropertyEditors.textboxController">
<input type="text" name="textbox" ng-model="model.value" id="{{model.alias}}"
class="umb-editor umb-textstring textstring"
val-server="value"
ng-required="model.validation.mandatory"
ng-trim="false" />
ng-trim="false"
ng-keyup="model.change()" />
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="valServer"></span>
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
</div>
<div class="help" ng-if="model.maxlength">
<strong>{{model.count}}</strong>
<localize key="textbox_characters_left">characters left</localize>
</div>
</div>

View File

@@ -568,6 +568,8 @@
<Content Include="Config\Lang\zh-CN.user.xml" />
<Content Include="Umbraco\Config\Lang\cs.xml" />
<Content Include="Umbraco\ClientRedirect.aspx" />
<Content Include="Umbraco\Config\Lang\tr.xml" />
<Content Include="Umbraco\Config\Lang\zh_tw.xml" />
<Content Include="Umbraco\create.aspx" />
<Content Include="Umbraco\Developer\Packages\installer.aspx" />
<Content Include="Umbraco\Logout.aspx" />
@@ -2378,9 +2380,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7640</DevelopmentServerPort>
<DevelopmentServerPort>7650</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7640</IISUrl>
<IISUrl>http://localhost:7650</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>

View File

@@ -1,13 +1,13 @@
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@*
This snippet makes a breadcrumb of parents using an unordered html list.
This snippet makes a breadcrumb of parents using an unordered HTML list.
How it works:
- It uses the Ancestors() method to get all parents and then generates links so the visitor can go back
- Finally it outputs the name of the current page (without a link)
*@
@{ var selection = CurrentPage.Ancestors(); }
@{ var selection = Model.Content.Ancestors(); }
@if (selection.Any())
{
@@ -19,6 +19,6 @@
}
@* Display the current page as the last item in the list *@
<li class="active">@CurrentPage.Name</li>
<li class="active">@Model.Content.Name</li>
</ul>
}

View File

@@ -1,50 +1,50 @@
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@*
Macro to display a gallery of images from media the media section.
Macro to display a gallery of images from the Media section.
Works with either a 'Single Media Picker' or a 'Multiple Media Picker' macro parameter (see below).
How it works:
- Confirm the macro parameter has been passed in with a value
- Loop through all the media Id's passed in (might be a single item, might be many)
- Loop through all the media Ids passed in (might be a single item, might be many)
- Display any individual images, as well as any folders of images
Macro Parameters To Create, for this macro to work:
Alias:mediaIds Name:Select folders and/or images Type: Multiple Media Picker
Type: (note: you can use a Single Media Picker if that's more appropriate to your needs)
Type: (note: You can use a Single Media Picker if that's more appropriate to your needs)
*@
@{ var mediaIds = Model.MacroParameters["mediaIds"]; }
@{ var mediaIds = Model.MacroParameters["mediaIds"] as string; }
@if (mediaIds != null)
{
<ul class="thumbnails">
@foreach (var mediaId in mediaIds.ToString().Split(','))
<div class="row">
@foreach (var mediaId in mediaIds.Split(','))
{
var media = Umbraco.Media(mediaId);
var media = Umbraco.TypedMedia(mediaId);
@* a single image *@
if (media.DocumentTypeAlias == "Image")
{
@Render(media);
@Render(media as Image);
}
@* a folder with images under it *@
if (media.Children("Image").Any())
if (media.Children<Image>().Any())
{
foreach (var image in media.Children("Image"))
foreach (var image in media.Children<Image>())
{
@Render(image);
}
}
}
</ul>
</div>
}
@helper Render(dynamic item)
@helper Render(Image item)
{
<li class="span2">
<a href="@item.umbracoFile.src" class="thumbnail">
<img src="@item.umbracoFile.src" alt="@item.Name" />
<div class="col-xs-6 col-md-3">
<a href="@item.Url" class="thumbnail">
<img src="@item.GetCropUrl(width:200, height:200)" alt="@item.Name" />
</a>
</li>
</div>
}

View File

@@ -1,24 +1,24 @@
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@*
This snippet makes a list of links to the of parents of the current page using an unordered html list.
This snippet makes a list of links to the of parents of the current page using an unordered HTML list.
How it works:
- It uses the Ancestors() method to get all parents and then generates links so the visitor can go back
- Finally it outputs the name of the current page (without a link)
*@
@{ var selection = CurrentPage.Ancestors(); }
@{ var selection = Model.Content.Ancestors(); }
@if (selection.Any())
{
<ul>
@* For each page in the ancestors collection which have been ordered by Level (so we start with the highest top node first) *@
@foreach (var item in selection.OrderBy("Level"))
@foreach (var item in selection.OrderBy(x => x.Level))
{
<li><a href="@item.Url">@item.Name</a> &raquo;</li>
}
@* Display the current page as the last item in the list *@
<li>@CurrentPage.Name</li>
<li>@Model.Content.Name</li>
</ul>
}

View File

@@ -13,20 +13,19 @@
*@
@{ var startNodeId = Model.MacroParameters["startNodeId"]; }
@if (startNodeId != null)
{
@* Get the starting page *@
var startNode = Umbraco.Content(startNodeId);
var selection = startNode.Children.Where("Visible");
var startNode = Umbraco.TypedContent(startNodeId);
var selection = startNode.Children.Where(x => x.IsVisible());
if (selection.Any())
{
<ul>
@foreach (var item in selection)
{
<li>
<a href="@item.Url">@item.Name</a>
</li>
<li><a href="@item.Url">@item.Name</a></li>
}
</ul>
}

View File

@@ -1,6 +1,14 @@
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@{ var selection = CurrentPage.Children.Where("Visible"); }
@*
This snippet makes a list of links to the of children of the current page using an unordered HTML list.
How it works:
- It uses the Children method to get all child pages
- It then generates links so the visitor can go to each page
*@
@{ var selection = Model.Content.Children.Where(x => x.IsVisible()); }
@if (selection.Any())
{

View File

@@ -1,11 +1,22 @@
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@{ var selection = CurrentPage.Children.Where("Visible").OrderBy("CreateDate desc"); }
@* OrderBy() takes the property to sort by and optionally order desc/asc *@
@*
This snippet makes a list of links to the of children of the current page using an unordered HTML list.
<ul>
@foreach (var item in selection)
{
<li><a href="@item.Url">@item.Name</a></li>
}
</ul>
How it works:
- It uses the Children method to get all child pages
- It then uses the OrderByDescending() method, which takes the property to sort. In this case the page's creation date.
- It then generates links so the visitor can go to each page
*@
@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).OrderByDescending(x => x.CreateDate); }
@if (selection.Any())
{
<ul>
@foreach (var item in selection)
{
<li><a href="@item.Url">@item.Name</a></li>
}
</ul>
}

View File

@@ -1,11 +1,22 @@
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{ var selection = CurrentPage.Children.Where("Visible").OrderBy("Name"); }
@* OrderBy() takes the property to sort by *@
@*
This snippet makes a list of links to the of children of the current page using an unordered HTML list.
<ul>
@foreach (var item in selection)
{
<li><a href="@item.Url">@item.Name</a></li>
}
</ul>
How it works:
- It uses the Children method to get all child pages
- It then uses the OrderBy() method, which takes the property to sort. In this case, the page's name.
- It then generates links so the visitor can go to each page
*@
@{ var selection = Model.Content.Children.Where(x => x.IsVisible()).OrderBy(x => x.Name); }
@if (selection.Any())
{
<ul>
@foreach (var item in selection)
{
<li><a href="@item.Url">@item.Name</a></li>
}
</ul>
}

View File

@@ -15,12 +15,15 @@
@{ var propertyAlias = Model.MacroParameters["propertyAlias"]; }
@if (propertyAlias != null)
{
var selection = CurrentPage.Children.Where("Visible").OrderBy(propertyAlias);
var selection = Model.Content.Children.Where(x => x.IsVisible()).OrderBy(x => x.GetPropertyValue(propertyAlias.ToString()));
<ul>
@foreach (var item in selection)
{
<li><a href="@item.Url">@item.Name</a></li>
}
</ul>
@if (selection.Any())
{
<ul>
@foreach (var item in selection)
{
<li><a href="@item.Url">@item.Name</a></li>
}
</ul>
}
}

View File

@@ -1,17 +1,13 @@
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@*
This snippet shows how simple it is to fetch only children of a certain Document Type using Razor.
Be sure to change "DocumentTypeAlias" below to match your needs, such as "TextPage" or "NewsItems".
This snippet shows how simple it is to fetch only children of a certain Document Type.
Be sure to change "IPublishedContent" below to match your needs, such as "TextPage" or "NewsItem".
(You can find the alias of your Document Type by editing it in the Settings section)
*@
@{ var selection = CurrentPage.Children("DocumentTypeAlias").Where("Visible"); }
@*
As an example of more querying, if you have a true/false property with the alias of shouldBeFeatured:
var selection= CurrentPage.Children("DocumentTypeAlias").Where("shouldBeFeatured == true").Where("Visible");
*@
@{ var selection = Model.Content.Children<IPublishedContent>().Where(x => x.IsVisible()); }
@if (selection.Any())
{

View File

@@ -2,27 +2,27 @@
@*
This snippet creates links for every single page (no matter how deep) below
the page currently being viewed by the website visitor, displayed as nested unordered html lists.
the page currently being viewed by the website visitor, displayed as nested unordered HTML lists.
*@
@{ var selection = CurrentPage.Children.Where("Visible"); }
@{ var selection = Model.Content.Children.Where(x => x.IsVisible()); }
@* Ensure that the Current Page has children *@
@if (selection.Any())
{
@* Get the first page in the children, where the property umbracoNaviHide is not True *@
var naviLevel = CurrentPage.FirstChild().Where("Visible").Level;
var naviLevel = Model.Content.FirstChild(x => x.IsVisible()).Level;
@* Add in level for a CSS hook *@
<ul class="level-@naviLevel">
@* For each child page where the property umbracoNaviHide is not True *@
@* Loop through the selection *@
@foreach (var item in selection)
{
<li>
<a href="@item.Url">@item.Name</a>
@* if this child page has any children, where the property umbracoNaviHide is not True *@
@if (item.Children.Where("Visible").Any())
@if (item.Children.Where(x => x.IsVisible()).Any())
{
@* Call our helper to display the children *@
@childPages(item.Children)
@@ -33,7 +33,7 @@
}
@helper childPages(dynamic selection)
@helper childPages(IEnumerable<IPublishedContent> selection)
{
@* Ensure that we have a collection of pages *@
if (selection.Any())
@@ -43,13 +43,13 @@
@* Add in level for a CSS hook *@
<ul class="level-@(naviLevel)">
@foreach (var item in selection.Where("Visible"))
@foreach (var item in selection.Where(x => x.IsVisible()))
{
<li>
<a href="@item.Url">@item.Name</a>
@* if the this page has any children, where the property umbracoNaviHide is not True *@
@if (item.Children.Where("Visible").Any())
@if (item.Children.Where(x => x.IsVisible()).Any())
{
@* Call our helper to display the children *@
@childPages(item.Children)

View File

@@ -5,7 +5,7 @@
How it works:
- Confirm the macro parameter has been passed in with a value
- Loop through all the media Id's passed in (might be a single item, might be many)
- Loop through all the media Ids passed in (might be a single item, might be many)
- Display any individual images, as well as any folders of images
Macro Parameters To Create, for this macro to work:
@@ -15,9 +15,9 @@
@{ var mediaId = Model.MacroParameters["mediaId"]; }
@if (mediaId != null)
{
@* Get all the media item associated with the id passed in *@
var media = Umbraco.Media(mediaId);
var selection = media.Children("Image");
@* Get the media item associated with the id passed in *@
var media = Umbraco.TypedMedia(mediaId);
var selection = media.Children<Image>();
if (selection.Any())
{
@@ -25,7 +25,7 @@
@foreach (var item in selection)
{
<li>
<img src="@item.umbracoFile" alt="@item.Name" />
<img src="@item.Url" alt="@item.Name" />
</li>
}
</ul>

View File

@@ -1,21 +1,23 @@
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@*
This snippet lists the items from a Multinode tree picker, using the pickers default settings.
Content Values stored as xml.
This snippet lists the items from a Multinode tree picker, using the picker's default settings.
Content Values stored as XML.
To get it working with any site's data structure, set the selection equal to the property which has the
multinode treepicker (so: replace "PropertyWithPicker" with the alias of your property).
*@
@{ var selection = CurrentPage.PropertyWithPicker.Split(','); }
@{ var selection = Model.Content.GetPropertyValue<IEnumerable<IPublishedContent>>("PropertyWithPicker"); }
<ul>
@foreach (var id in selection)
{
var item = Umbraco.Content(id);
<li>
<a href="@item.Url">@item.Name</a>
</li>
}
</ul>
@if (selection.Any())
{
<ul>
@foreach (var item in selection)
{
<li>
<a href="@item.Url">@item.Name</a>
</li>
}
</ul>
}

View File

@@ -3,16 +3,19 @@
@*
This snippet displays a list of links of the pages immediately under the top-most page in the content tree.
This is the home page for a standard website.
It also highlights the current active page/section in the navigation with the css class "current".
It also highlights the current active page/section in the navigation with the CSS class "current".
*@
@{ var selection = CurrentPage.Site().Children.Where("Visible"); }
@{ var selection = Model.Content.Site().Children.Where(x => x.IsVisible()); }
<ul>
@foreach (var item in selection)
{
<li class="@(item.IsAncestorOrSelf(CurrentPage) ? "current" : null)">
<a href="@item.Url">@item.Name</a>
</li>
}
</ul>
@if (selection.Any())
{
<ul>
@foreach (var item in selection)
{
<li class="@(item.IsAncestorOrSelf(Model.Content) ? "current" : null)">
<a href="@item.Url">@item.Name</a>
</li>
}
</ul>
}

View File

@@ -1,13 +1,13 @@
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@*
This snippet makes a list of links of all visible pages of the site, as nested unordered html lists.
This snippet makes a list of links of all visible pages of the site, as nested unordered HTML lists.
How it works:
- It uses a custom Razor helper called Traverse() to select and display the markup and links.
*@
@{ var selection = CurrentPage.Site(); }
@{ var selection = Model.Content.Site(); }
<div class="sitemap">
@* Render the sitemap by passing the root node to the traverse helper, below *@
@@ -15,14 +15,14 @@
</div>
@* Helper method to travers through all descendants *@
@helper Traverse(dynamic node)
@* Helper method to traverse through all descendants *@
@helper Traverse(IPublishedContent node)
{
@* Update the level to reflect how deep you want the sitemap to go *@
var maxLevelForSitemap = 4;
@* Select visible children *@
var selection = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap);
var selection = node.Children.Where(x => x.IsVisible() && x.Level <= maxLevelForSitemap);
@* If any items are returned, render a list *@
if (selection.Any())

View File

@@ -911,8 +911,6 @@
<key alias="postContent">Vložit za polem</key>
<key alias="preContent">Vložit před polem</key>
<key alias="recursive">Rekurzivní</key>
<key alias="removeParagraph">Odstranit tagy odstavce</key>
<key alias="removeParagraphHelp">Odstraní jakékoliv &amp;lt;P&amp;gt; na začátku a na konci textu</key>
<key alias="standardFields">Standardní pole</key>
<key alias="uppercase">Velká písmena</key>
<key alias="urlEncode">Kódování URL</key>

View File

@@ -884,8 +884,6 @@ Vennlig hilsen Umbraco roboten
<key alias="postContent">Sett inn etter felt</key>
<key alias="preContent">Sett inn før felt</key>
<key alias="recursive">Rekursivt</key>
<key alias="removeParagraph">Fjern paragraftagger</key>
<key alias="removeParagraphHelp">Fjerner eventuelle &lt;P&gt; rundt teksten</key>
<key alias="standardFields">Standardfelter</key>
<key alias="uppercase">Store bokstaver</key>
<key alias="urlEncode">URL koding</key>

File diff suppressed because it is too large Load Diff

View File

@@ -1145,8 +1145,6 @@ Mange hilsner fra Umbraco robotten
<key alias="postContent">Indsæt efter felt</key>
<key alias="preContent">Indsæt før felt</key>
<key alias="recursive">Rekursivt</key>
<key alias="removeParagraph">Fjern paragraf-tags</key>
<key alias="removeParagraphHelp">Fjerner eventuelle &amp;lt;P&amp;gt; omkring teksten</key>
<key alias="standardFields">Standard felter</key>
<key alias="uppercase">Uppercase</key>
<key alias="urlEncode">URL encode</key>
@@ -1202,9 +1200,10 @@ Mange hilsner fra Umbraco robotten
<key alias="memberTypes">Medlemstype</key>
<key alias="documentTypes">Dokumenttyper</key>
<key alias="relationTypes">Relationstyper</key>
<key alias="packager">Pakker</key>
<key alias="packages">Pakker</key>
<key alias="partialViews">Partial Views</key>
<key alias="partialViewMacros">Partial View Makro Filer</key>
<key alias="python">Python</key>
<key alias="repositories">Installer fra "repository"</key>
<key alias="runway">Installer Runway</key>
@@ -1292,4 +1291,7 @@ Mange hilsner fra Umbraco robotten
<key alias="enabledConfirm">URL tracker er nu slået fra.</key>
<key alias="enableError">Der opstod en fejl under forsøget på at slå URL trackeren til, der findes mere information i logfilen.</key>
</area>
<area alias="textbox">
<key alias="characters_left">Karakterer tilbage</key>
</area>
</language>

View File

@@ -859,7 +859,7 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die
<area alias="grid">
<key alias="insertControl">Element hinzufügen</key>
<key alias="addRows">Zeilenlayout auswählen</key>
<key alias="addElement">Einfach auf &lt;i class="icon icon-add blue"&gt;&lt;/i&gt; klicken, um das erste Element anzulegen</key>
<key alias="addElement">Element mit &lt;i class="icon icon-add blue"&gt;&lt;/i&gt; hinzugen</key>
<key alias="dropElement">Drop content</key>
<key alias="clickToEmbed">Klicken, um Inhalt einzubetten</key>
<key alias="clickToInsertImage">Klicken, um Abbildung einzufügen</key>
@@ -903,8 +903,6 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die
<key alias="postContent">An den Feldinhalt anhängen</key>
<key alias="preContent">Dem Feldinhalt voranstellen</key>
<key alias="recursive">Rekursiv</key>
<key alias="removeParagraph">Textabsatz entfernen</key>
<key alias="removeParagraphHelp">Alle &lt;p&gt; am Anfang und am Ende des Feldinhalts werden entfernt</key>
<key alias="standardFields">Standardfelder</key>
<key alias="uppercase">Großbuchstaben</key>
<key alias="urlEncode">URL kodieren</key>

View File

@@ -514,6 +514,7 @@
<key alias="rename">Rename</key>
<key alias="renew">Renew</key>
<key alias="required" version="7.0">Required</key>
<key alias="retrieve">Retrieve</key>
<key alias="retry">Retry</key>
<key alias="rights">Permissions</key>
<key alias="search">Search</key>
@@ -1345,9 +1346,6 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="preContent">Insert before field</key>
<key alias="recursive">Recursive</key>
<key alias="recursiveDescr">Yes, make it recursive</key>
<key alias="removeParagraph">Remove paragraph tags</key>
<key alias="removeParagraphDescription">Yes, remove paragraph tags</key>
<key alias="removeParagraphHelp">Will remove any paragraph tag in the beginning and end of the text</key>
<key alias="separator">Separator</key>
<key alias="standardFields">Standard Fields</key>
<key alias="uppercase">Uppercase</key>
@@ -1427,6 +1425,8 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="relationTypes">Relation Types</key>
<key alias="packager">Packages</key>
<key alias="packages">Packages</key>
<key alias="partialViews">Partial Views</key>
<key alias="partialViewMacros">Partial View Macro Files</key>
<key alias="python">Python Files</key>
<key alias="repositories">Install from repository</key>
<key alias="runway">Install Runway</key>
@@ -1437,8 +1437,6 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="templates">Templates</key>
<key alias="xslt">XSLT Files</key>
<key alias="analytics">Analytics</key>
<key alias="partialViews">Partial Views</key>
<key alias="partialViewMacros">Partial View Macro Files</key>
</area>
<area alias="update">
<key alias="updateAvailable">New update ready</key>
@@ -1549,8 +1547,10 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="xmlDataIntegrityCheckMedia">Media - Total XML: %0%, Total: %1%, Total invalid: %2%</key>
<key alias="xmlDataIntegrityCheckContent">Content - Total XML: %0%, Total published: %1%, Total invalid: %2%</key>
<key alias="httpsCheckValidCertificate">Your site certificate was marked as valid.</key>
<key alias="httpsCheckValidCertificate">Your website's certificate is valid.</key>
<key alias="httpsCheckInvalidCertificate">Certificate validation error: '%0%'</key>
<key alias="httpsCheckExpiredCertificate">Your website's SSL certificate has expired.</key>
<key alias="httpsCheckExpiringCertificate">Your website's SSL certificate is expiring in %0% days.</key>
<key alias="httpsCheckInvalidUrl">Error pinging the URL %0% - '%1%'</key>
<key alias="httpsCheckIsCurrentSchemeHttps">You are currently %0% viewing the site using the HTTPS scheme.</key>
<key alias="httpsCheckConfigurationRectifyNotPossible">The appSetting 'umbracoUseSSL' is set to 'false' in your web.config file. Once you access this site using the HTTPS scheme, that should be set to 'true'.</key>
@@ -1630,4 +1630,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="emptyStates">
<key alias="emptyDictionaryTree">No Dictionary items to choose from</key>
</area>
</language>
<area alias="textbox">
<key alias="characters_left">characters left</key>
</area>
</language>

View File

@@ -515,6 +515,7 @@
<key alias="rename">Rename</key>
<key alias="renew">Renew</key>
<key alias="required" version="7.0">Required</key>
<key alias="retrieve">Retrieve</key>
<key alias="retry">Retry</key>
<key alias="rights">Permissions</key>
<key alias="search">Search</key>
@@ -1343,9 +1344,6 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="preContent">Insert before field</key>
<key alias="recursive">Recursive</key>
<key alias="recursiveDescr">Yes, make it recursive</key>
<key alias="removeParagraph">Remove paragraph tags</key>
<key alias="removeParagraphDescription">Yes, remove paragraph tags</key>
<key alias="removeParagraphHelp">Will remove any paragraph tag in the beginning and end of the text</key>
<key alias="separator">Separator</key>
<key alias="standardFields">Standard Fields</key>
<key alias="uppercase">Uppercase</key>
@@ -1545,8 +1543,10 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="xmlDataIntegrityCheckMedia">Media - Total XML: %0%, Total: %1%, Total invalid: %2%</key>
<key alias="xmlDataIntegrityCheckContent">Content - Total XML: %0%, Total published: %1%, Total invalid: %2%</key>
<key alias="httpsCheckValidCertificate">Your site certificate was marked as valid.</key>
<key alias="httpsCheckValidCertificate">Your website's certificate is valid.</key>
<key alias="httpsCheckInvalidCertificate">Certificate validation error: '%0%'</key>
<key alias="httpsCheckExpiredCertificate">Your website's SSL certificate has expired.</key>
<key alias="httpsCheckExpiringCertificate">Your website's SSL certificate is expiring in %0% days.</key>
<key alias="httpsCheckInvalidUrl">Error pinging the URL %0% - '%1%'</key>
<key alias="httpsCheckIsCurrentSchemeHttps">You are currently %0% viewing the site using the HTTPS scheme.</key>
<key alias="httpsCheckConfigurationRectifyNotPossible">The appSetting 'umbracoUseSSL' is set to 'false' in your web.config file. Once you access this site using the HTTPS scheme, that should be set to 'true'.</key>
@@ -1626,4 +1626,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="emptyStates">
<key alias="emptyDictionaryTree">No Dictionary items to choose from</key>
</area>
<area alias="textbox">
<key alias="characters_left">characters left</key>
</area>
</language>

View File

@@ -809,8 +809,6 @@
<key alias="postContent">Insertar después del campo</key>
<key alias="preContent">Insertar antes del campo</key>
<key alias="recursive">Recursivo</key>
<key alias="removeParagraph">Borrar los tags del párrafo</key>
<key alias="removeParagraphHelp">Borrará cualquier &amp;lt;P&amp;gt; al principio y al final del texto</key>
<key alias="uppercase">Mayúscula</key>
<key alias="urlEncode">Codificar URL</key>
<key alias="urlEncodeHelp">Formateará los caracteres especiales de las URLs</key>

View File

@@ -1345,9 +1345,6 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
<key alias="preContent">Insérer avant le champ</key>
<key alias="recursive">Récursif</key>
<key alias="recursiveDescr">Oui, rendre récursif</key>
<key alias="removeParagraph">Supprimer les balises de paragraphe</key>
<key alias="removeParagraphDescription">Oui, supprimer les balises de paragraphe</key>
<key alias="removeParagraphHelp">Supprimera toute balise &amp;lt;P&amp;gt; au début et à la fin du texte</key>
<key alias="separator">Séparateur</key>
<key alias="standardFields">Champs standards</key>
<key alias="uppercase">Majuscules</key>

View File

@@ -818,8 +818,6 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="postContent">הוסף אחרי השדה</key>
<key alias="preContent">הוסף לפני השדה</key>
<key alias="recursive">רקורסיבי</key>
<key alias="removeParagraph">הסר תגי פסקה</key>
<key alias="removeParagraphHelp">מסיר את כל ה- &amp;lt;P&amp;gt; בתחילת ובסוף הטקסט</key>
<key alias="uppercase">אותיות גדולות</key>
<key alias="urlEncode">קידוד URL</key>
<key alias="urlEncodeHelp">תווים מיוחדים יעוצבו ב- URL</key>

View File

@@ -789,8 +789,6 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i
<key alias="postContent"><![CDATA[Inserisci dopo il campo]]></key>
<key alias="preContent"><![CDATA[Inserisci prima il campo]]></key>
<key alias="recursive">Ricorsivo</key>
<key alias="removeParagraph"><![CDATA[Rimuovi i tag paragrafo]]></key>
<key alias="removeParagraphHelp"><![CDATA[Rimuoverà &lt;P&gt; all'inizio e alla fine del testo]]></key>
<key alias="standardFields">Campi Standard</key>
<key alias="uppercase">Maiuscolo</key>
<key alias="urlEncode"><![CDATA[Codifica URL]]></key>

View File

@@ -1108,8 +1108,6 @@ Runwayをインストールして作られた新しいウェブサイトがど
<key alias="postContent">フィールド値の後ろ追加</key>
<key alias="preContent">フィールド値の前に追加</key>
<key alias="recursive">再帰的</key>
<key alias="removeParagraph">段落タグの消去</key>
<key alias="removeParagraphHelp">段落タグ &amp;lt;P&amp;gt; を消去します</key>
<key alias="standardFields">標準フィールド</key>
<key alias="uppercase">大文字変換</key>
<key alias="urlEncode">URLエンコード</key>

View File

@@ -794,8 +794,6 @@
<key alias="postContent">필드뒤에 삽입</key>
<key alias="preContent">필드앞에 삽입</key>
<key alias="recursive">Recursive</key>
<key alias="removeParagraph">단락 태그삭제</key>
<key alias="removeParagraphHelp">문서 시작과 끝의 &amp;amp;lt;P&amp;amp;gt; 를 삭제하시겠습니까</key>
<key alias="uppercase">대문자</key>
<key alias="urlEncode">URL 인코딩</key>
<key alias="urlEncodeHelp">URL의 특수문자를 포맷하겠습니까</key>

View File

@@ -1173,8 +1173,6 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="postContent">Invoegen na veld</key>
<key alias="preContent">Invoegen voor veld</key>
<key alias="recursive">Recursief</key>
<key alias="removeParagraph">Verwijder paragraaf tags</key>
<key alias="removeParagraphHelp"><![CDATA[Alle <P> tags aan het begin en einde van de tekst worden verwijderd]]></key>
<key alias="standardFields">Standaard velden</key>
<key alias="uppercase">Hoofdletters</key>
<key alias="urlEncode">URL-encoderen</key>

View File

@@ -1280,8 +1280,6 @@ Naciśnij przycisk <strong>instaluj</strong>, aby zainstalować bazę danych Umb
<key alias="postContent">Wstaw za polem</key>
<key alias="preContent">Wstaw przed polem</key>
<key alias="recursive">Rekurencyjne</key>
<key alias="removeParagraph">Usuń znaki paragrafu</key>
<key alias="removeParagraphHelp">Usuwa wszystkie &amp;lt;P&amp;gt; z początku i końca tekstu</key>
<key alias="standardFields">Standardowe Pola</key>
<key alias="uppercase">Wielkie litery</key>
<key alias="urlEncode">Kodowanie URL</key>

View File

@@ -783,8 +783,6 @@ Você pode publicar esta página e todas suas sub-páginas ao selecionar <em>pub
<key alias="postContent">Inserir após campo</key>
<key alias="preContent">Inserir antes do campo</key>
<key alias="recursive">Recursivo</key>
<key alias="removeParagraph">Remover etiquetas de parágrafo</key>
<key alias="removeParagraphHelp">Removerá quaisquer &amp;lt;P&amp;gt; do começo ao fim do texto</key>
<key alias="uppercase">Maiúscula</key>
<key alias="urlEncode">Codificar URL</key>
<key alias="urlEncodeHelp">Vai formatar caracteres especiais em URLs</key>

View File

@@ -356,7 +356,8 @@
<key alias="theFriendliestCommunity">Самое дружелюбное сообщество</key>
<key alias="linkToPage">Ссылка на страницу</key>
<key alias="openInNewWindow">Открывать ссылку в новом окне или вкладке браузера</key>
<key alias="linkToMedia">Ссылка на медиа-файл</key>
<key alias="linkToMedia">Ссылка на медиа-элемент</key>
<key alias="linkToFile">Ссылка на файл</key>
<key alias="selectMedia">Выбрать медиа</key>
<key alias="selectIcon">Выбрать значок</key>
<key alias="selectItem">Выбрать элемент</key>
@@ -366,7 +367,7 @@
<key alias="selectMember">Выбрать участника</key>
<key alias="selectMemberGroup">Выбрать группу участников</key>
<key alias="noMacroParams">Это макрос без параметров</key>
<key alias="noMacros">Нет макросов доступных для вставки в редактор</key>
<key alias="noMacros">Нет макросов, доступных для вставки в редактор</key>
<key alias="externalLoginProviders">Провайдеры аутентификации</key>
<key alias="exceptionDetail">Подробное сообщение об ошибке</key>
<key alias="stacktrace">Трассировка стека</key>
@@ -487,6 +488,7 @@
<key alias="closewindow">Закрыть окно</key>
<key alias="comment">Примечание</key>
<key alias="confirm">Подтвердить</key>
<key alias="constrain">Сохранять пропорции</key>
<key alias="constrainProportions">Сохранять пропорции</key>
<key alias="continue">Далее</key>
<key alias="copy">Копировать</key>
@@ -554,6 +556,7 @@
<key alias="rename">Переименовать</key>
<key alias="renew">Обновить</key>
<key alias="required" version="7.0">Обязательное</key>
<key alias="retrieve">Получить</key>
<key alias="retry">Повторить</key>
<key alias="rights">Разрешения</key>
<key alias="search">Поиск</key>
@@ -571,7 +574,7 @@
<key alias="update">Обновить</key>
<key alias="upgrade">Обновление</key>
<key alias="upload">Загрузить</key>
<key alias="url">URL</key>
<key alias="url">Интернет-ссылка</key>
<key alias="user">Пользователь</key>
<key alias="username">Имя пользователя</key>
<key alias="value">Значение</key>
@@ -990,7 +993,7 @@
<key alias="dropHere">Перетащите сюда</key>
<key alias="orClickHereToUpload">или нажмите здесь для выбора файла пакета</key>
<key alias="uploadPackage">Загрузить пакет</key>
<key alias="localPackageDescr">Установите локальный пакет из файла, расположенного на Вашем компьютере. Остерегайтесь устанавливать пакеты из непроверенных источников!</key>
<key alias="localPackageDescription">Установите локальный пакет из файла, расположенного на Вашем компьютере. Остерегайтесь устанавливать пакеты из непроверенных источников!</key>
<key alias="uploadAnother">Загрузить еще один пакет</key>
<key alias="cancelAndUploadAnother">Отменить и загрузить другой пакет</key>
<key alias="packageLicense">Лицензия</key>
@@ -1422,12 +1425,15 @@
</area>
<area alias="templateEditor">
<key alias="addFallbackField">Добавить поле замены</key>
<key alias="fallbackField">Поле замены</key>
<key alias="addDefaultValue">Добавить значение по-умолчанию</key>
<key alias="defaultValue">Значение по-умолчанию</key>
<key alias="alternativeField">Альтернативное поле</key>
<key alias="alternativeText">Текст по-умолчанию</key>
<key alias="casing">Регистр</key>
<key alias="chooseField">Выбрать поле</key>
<key alias="convertLineBreaks">Преобразовать переводы строк</key>
<key alias="convertLineBreaksDescription">Да, преобразовывать</key>
<key alias="convertLineBreaksHelp">Заменяет переводы строк на тэг html 'br'</key>
<key alias="customFields">Пользовательские</key>
<key alias="dateOnly">Только дата</key>
@@ -1447,8 +1453,6 @@
<key alias="preContent">Вставить перед полем</key>
<key alias="recursive">Рекурсивно</key>
<key alias="recursiveDescr">Да, использовать рекурсию</key>
<key alias="removeParagraph">Удалить тэги параграфов</key>
<key alias="removeParagraphHelp">Удаляются тэги параграфа ('p') в начале и в конце абзацев</key>
<key alias="separator">Разделитель</key>
<key alias="standardFields">Стандартные</key>
<key alias="uppercase">В верхнем регистре</key>
@@ -1528,6 +1532,8 @@
<key alias="documentTypes">Типы документов</key>
<key alias="packager">Пакеты дополнений</key>
<key alias="packages">Пакеты дополнений</key>
<key alias="partialViews">Частичные представления</key>
<key alias="partialViewMacros">Файлы макросов</key>
<key alias="python">Скрипты Python</key>
<key alias="relationTypes">Типы связей</key>
<key alias="repositories">Установить из репозитория</key>

View File

@@ -820,8 +820,6 @@
<key alias="postContent">Infoga efter fält</key>
<key alias="preContent">Infoga före fält</key>
<key alias="recursive">Rekursiv</key>
<key alias="removeParagraph">Avlägsna stycke-taggar</key>
<key alias="removeParagraphHelp">Kommer att avlägsna alla &amp;lt;P&amp;gt; i början och slutet av texten</key>
<key alias="standardFields">Standardfält</key>
<key alias="uppercase">Versaler</key>
<key alias="urlEncode">URL-koda</key>

File diff suppressed because it is too large Load Diff

View File

@@ -113,7 +113,7 @@ namespace Umbraco.Web.Cache
}
TagsValueConverter.ClearCaches();
MultipleMediaPickerPropertyConverter.ClearCaches();
LegacyMediaPickerPropertyConverter.ClearCaches();
SliderValueConverter.ClearCaches();
MediaPickerPropertyConverter.ClearCaches();

View File

@@ -230,7 +230,7 @@ namespace Umbraco.Web.Editors
basic.Description = TranslateItem(basic.Description);
}
return basics;
return basics.OrderBy(x => x.Name);
}
/// <summary>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Web;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
@@ -53,7 +54,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
private HealthCheckStatus CheckForValidCertificate()
{
var message = string.Empty;
var success = false;
StatusResultType result;
var url = HealthCheckContext.HttpContext.Request.Url;
// Attempt to access the site over HTTPS to see if it HTTPS is supported
@@ -65,7 +66,37 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
try
{
var response = (HttpWebResponse)request.GetResponse();
success = response.StatusCode == HttpStatusCode.OK;
if (response.StatusCode == HttpStatusCode.OK)
{
// Got a valid response, check now for if certificate expiring within 14 days
// Hat-tip: https://stackoverflow.com/a/15343898/489433
const int NumberOfDaysForExpiryWarning = 14;
var cert = request.ServicePoint.Certificate;
var cert2 = new X509Certificate2(cert);
var expirationDate = cert2.NotAfter;
var daysToExpiry = (int)Math.Floor((cert2.NotAfter - DateTime.Now).TotalDays);
if (daysToExpiry <= 0)
{
result = StatusResultType.Error;
message = _textService.Localize("healthcheck/httpsCheckExpiredCertificate");
}
else if (daysToExpiry < NumberOfDaysForExpiryWarning)
{
result = StatusResultType.Warning;
message = _textService.Localize("healthcheck/httpsCheckExpiringCertificate", new[] { daysToExpiry.ToString() });
}
else
{
result = StatusResultType.Success;
message = _textService.Localize("healthcheck/httpsCheckValidCertificate");
}
}
else
{
result = StatusResultType.Error;
message = _textService.Localize("healthcheck/httpsCheckInvalidUrl", new[] { address, response.StatusDescription });
}
}
catch (Exception ex)
{
@@ -80,17 +111,16 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
{
message = _textService.Localize("healthcheck/httpsCheckInvalidUrl", new[] { address, ex.Message });
}
result = StatusResultType.Error;
}
var actions = new List<HealthCheckAction>();
if (success)
message = _textService.Localize("healthcheck/httpsCheckValidCertificate");
return
new HealthCheckStatus(message)
{
ResultType = success ? StatusResultType.Success : StatusResultType.Error,
ResultType = result,
Actions = actions
};
}

View File

@@ -76,7 +76,7 @@ namespace Umbraco.Web.Models
public bool HasImage()
{
return string.IsNullOrEmpty(Src);
return ! string.IsNullOrEmpty(Src);
}
public string ToHtmlString()

View File

@@ -165,12 +165,20 @@ namespace Umbraco.Web.Models.Mapping
var listViewTab = new Tab<ContentPropertyDisplay>();
listViewTab.Alias = Constants.Conventions.PropertyGroups.ListViewGroupName;
listViewTab.Label = localizedTextService.Localize("content/childItems");
listViewTab.Id = 25;
listViewTab.Id = display.Tabs.Count() + 1;
listViewTab.IsActive = true;
var listViewConfig = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals);
//add the entity type to the config
listViewConfig["entityType"] = entityType;
listViewConfig["entityType"] = entityType;
//Override Tab Label if tabName is provided
if (listViewConfig.ContainsKey("tabName"))
{
var configTabName = listViewConfig["tabName"];
if (configTabName != null && string.IsNullOrWhiteSpace(configTabName.ToString()) == false)
listViewTab.Label = configTabName.ToString();
}
var listViewProperties = new List<ContentPropertyDisplay>();
listViewProperties.Add(new ContentPropertyDisplay

View File

@@ -42,7 +42,7 @@ namespace Umbraco.Web.PropertyEditors
allowBulkPublish = true,
allowBulkUnpublish = true,
allowBulkCopy = true,
allowBulkMove = true,
allowBulkMove = false,
allowBulkDelete = true
}}
};
@@ -50,7 +50,10 @@ namespace Umbraco.Web.PropertyEditors
}
internal class ListViewPreValueEditor : PreValueEditor
{
{
[PreValueField("tabName", "Tab Name", "textstring", Description = "The name of the listview tab (default if empty: 'Child Items')")]
public int TabName { get; set; }
[PreValueField("displayAtTabNumber", "Display At Tab Number", "number", Description = "Which tab position that the list of child items will be displayed")]
public int DisplayAtTabNumber { get; set; }

View File

@@ -6,5 +6,15 @@ namespace Umbraco.Web.PropertyEditors
[PropertyEditor(Constants.PropertyEditors.TextboxMultipleAlias, "Textarea", "textarea", IsParameterEditor = true, ValueType = PropertyEditorValueTypes.Text, Icon="icon-application-window-alt")]
public class TextAreaPropertyEditor : PropertyEditor
{
protected override PreValueEditor CreatePreValueEditor()
{
return new TextAreaPreValueEditor();
}
internal class TextAreaPreValueEditor : PreValueEditor
{
[PreValueField("maxChars", "Maximum allowed characters", "number", Description = "If empty - no character limit")]
public bool MaxChars { get; set; }
}
}
}

View File

@@ -12,6 +12,19 @@ namespace Umbraco.Web.PropertyEditors
{
[PropertyEditor(Constants.PropertyEditors.TextboxAlias, "Textbox", "textbox", IsParameterEditor = true, Group = "Common")]
public class TextboxPropertyEditor : PropertyEditor
{
{
protected override PreValueEditor CreatePreValueEditor()
{
return new TextboxPreValueEditor();
}
internal class TextboxPreValueEditor : PreValueEditor
{
[PreValueField("maxChars", "Maximum allowed characters", "number", Description = "If empty - no character limit")]
public bool MaxChars { get; set; }
}
}
}

View File

@@ -1,5 +1,5 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MultipleMediaPickerPropertyConverter.cs" company="Umbraco">
// <copyright file="LegacyMediaPickerPropertyConverter.cs" company="Umbraco">
// Umbraco
// </copyright>
// <summary>
@@ -24,20 +24,20 @@ using Umbraco.Core.Services;
namespace Umbraco.Web.PropertyEditors.ValueConverters
{
/// <summary>
/// The multiple media picker property value converter.
/// The multiple media picker and double legacy media picker property value converter, should be deleted for v8
/// </summary>
[DefaultPropertyValueConverter(typeof(MustBeStringValueConverter))]
public class MultipleMediaPickerPropertyConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
public class LegacyMediaPickerPropertyConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
{
private readonly IDataTypeService _dataTypeService;
//TODO: Remove this ctor in v8 since the other one will use IoC
public MultipleMediaPickerPropertyConverter()
public LegacyMediaPickerPropertyConverter()
: this(ApplicationContext.Current.Services.DataTypeService)
{
}
public MultipleMediaPickerPropertyConverter(IDataTypeService dataTypeService)
public LegacyMediaPickerPropertyConverter(IDataTypeService dataTypeService)
{
if (dataTypeService == null) throw new ArgumentNullException("dataTypeService");
_dataTypeService = dataTypeService;
@@ -54,10 +54,17 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
/// </returns>
public override bool IsConverter(PublishedPropertyType propertyType)
{
if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters)
if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters && propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultipleMediaPickerAlias))
{
return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultipleMediaPickerAlias);
return true;
}
if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters && propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MediaPickerAlias))
{
// this is the double legacy media picker, it can pick only single media items
return true;
}
return false;
}
@@ -109,7 +116,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
ApplicationContext.Current.Services.DataTypeService.GetDataTypeDefinitionById(
propertyType.DataTypeId).Name);
LogHelper.Warn<MultipleMediaPickerPropertyConverter>(error);
LogHelper.Warn<LegacyMediaPickerPropertyConverter>(error);
throw new Exception(error);
}
}
@@ -264,4 +271,4 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
Storages.Clear();
}
}
}
}

View File

@@ -126,10 +126,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
if (propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MediaPicker2Alias))
return true;
if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters)
{
return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MediaPickerAlias);
}
return false;
}

View File

@@ -10,6 +10,7 @@ using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
using Umbraco.Web.Models;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Web.Routing;
using ContentType = umbraco.cms.businesslogic.ContentType;
@@ -24,8 +25,24 @@ namespace Umbraco.Web
public static Guid GetKey(this IPublishedContent content)
{
// fast
var contentWithKey = content as IPublishedContentWithKey;
return contentWithKey == null ? Guid.Empty : contentWithKey.Key;
if (contentWithKey != null) return contentWithKey.Key;
// try to unwrap (models...)
var contentWrapped = content as PublishedContentWrapped;
while (contentWrapped != null)
{
content = contentWrapped.Unwrap();
contentWrapped = content as PublishedContentWrapped;
}
// again
contentWithKey = content as IPublishedContentWithKey;
if (contentWithKey != null) return contentWithKey.Key;
LogHelper.Debug(typeof(PublishedContentExtensions), string.Format("Could not get key for IPublishedContent with id {0} of type {1}.", content.Id, content.GetType().FullName));
return Guid.Empty;
}
#endregion
@@ -634,14 +651,10 @@ namespace Umbraco.Web
private static bool IsDocumentTypeRecursive(IPublishedContent content, string docTypeAlias)
{
var contentTypeService = UmbracoContext.Current.Application.Services.ContentTypeService;
var type = contentTypeService.GetContentType(content.DocumentTypeAlias);
while (type != null && type.ParentId > 0)
{
type = contentTypeService.GetContentType(type.ParentId);
if (type.Alias.InvariantEquals(docTypeAlias))
return true;
}
return false;
var type = contentTypeService.GetContentType(content.DocumentTypeAlias);
if (type.Alias.InvariantEquals(docTypeAlias) || content.IsComposedOf(docTypeAlias))
return true;
return false;
}
public static bool IsNull(this IPublishedContent content, string alias, bool recurse)
@@ -912,14 +925,14 @@ namespace Umbraco.Web
#region Axes: ancestors, ancestors-or-self
// as per XPath 1.0 specs §2.2,
// as per XPath 1.0 specs §2.2,
// - the ancestor axis contains the ancestors of the context node; the ancestors of the context node consist
// of the parent of context node and the parent's parent and so on; thus, the ancestor axis will always
// include the root node, unless the context node is the root node.
// - the ancestor-or-self axis contains the context node and the ancestors of the context node; thus,
// the ancestor axis will always include the root node.
//
// as per XPath 2.0 specs §3.2.1.1,
// as per XPath 2.0 specs §3.2.1.1,
// - the ancestor axis is defined as the transitive closure of the parent axis; it contains the ancestors
// of the context node (the parent, the parent of the parent, and so on) - The ancestor axis includes the
// root node of the tree in which the context node is found, unless the context node is the root node.
@@ -929,7 +942,7 @@ namespace Umbraco.Web
// the ancestor and ancestor-or-self axis are reverse axes ie they contain the context node or nodes that
// are before the context node in document order.
//
// document order is defined by §2.4.1 as:
// document order is defined by §2.4.1 as:
// - the root node is the first node.
// - every node occurs before all of its children and descendants.
// - the relative order of siblings is the order in which they occur in the children property of their parent node.
@@ -1240,12 +1253,12 @@ namespace Umbraco.Web
}
// as per XPath 1.0 specs §2.2,
// as per XPath 1.0 specs §2.2,
// - the descendant axis contains the descendants of the context node; a descendant is a child or a child of a child and so on; thus
// the descendant axis never contains attribute or namespace nodes.
// - the descendant-or-self axis contains the context node and the descendants of the context node.
//
// as per XPath 2.0 specs §3.2.1.1,
// as per XPath 2.0 specs §3.2.1.1,
// - the descendant axis is defined as the transitive closure of the child axis; it contains the descendants of the context node (the
// children, the children of the children, and so on).
// - the descendant-or-self axis contains the context node and the descendants of the context node.
@@ -1253,7 +1266,7 @@ namespace Umbraco.Web
// the descendant and descendant-or-self axis are forward axes ie they contain the context node or nodes that are after the context
// node in document order.
//
// document order is defined by §2.4.1 as:
// document order is defined by §2.4.1 as:
// - the root node is the first node.
// - every node occurs before all of its children and descendants.
// - the relative order of siblings is the order in which they occur in the children property of their parent node.

View File

@@ -70,6 +70,7 @@ namespace Umbraco.Web.Routing
// test for collisions
var uri = new Uri(url.TrimEnd('/'), UriKind.RelativeOrAbsolute);
if (uri.IsAbsoluteUri == false) uri = uri.MakeAbsolute(UmbracoContext.Current.CleanedUmbracoUrl);
uri = UriUtility.UriToUmbraco(uri);
var pcr = new PublishedContentRequest(uri, UmbracoContext.Current.RoutingContext, UmbracoConfig.For.UmbracoSettings().WebRouting, s => Roles.Provider.GetRolesForUser(s));
pcr.Engine.TryRouteRequest();

View File

@@ -166,11 +166,11 @@ namespace Umbraco.Web.Security.Providers
username,
email,
FormatPasswordForStorage(encodedPassword, salt),
memberTypeAlias);
memberTypeAlias,
isApproved);
member.PasswordQuestion = passwordQuestion;
member.RawPasswordAnswerValue = EncryptString(passwordAnswer);
member.IsApproved = isApproved;
member.LastLoginDate = DateTime.Now;
member.LastPasswordChangeDate = DateTime.Now;

View File

@@ -1,21 +1,18 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http.Formatting;
using umbraco.BusinessLogic.Actions;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Web.Models.Trees;
using System.Web;
using Umbraco.Core.Services;
namespace Umbraco.Web.Trees
{
public abstract class FileSystemTreeController : TreeController
{
protected abstract IFileSystem2 FileSystem { get; }
protected abstract string[] Extensions { get; }
protected abstract string FileIcon { get; }
protected abstract string FilePath { get; }
protected abstract string FileSearchPattern { get; }
/// <summary>
/// Inheritors can override this method to modify the file node that is created.
@@ -31,43 +28,67 @@ namespace Umbraco.Web.Trees
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.Root.ToInvariantString()
? HttpUtility.UrlDecode(id).TrimStart("/")
: "";
var orgPath = "";
string path;
if (string.IsNullOrEmpty(id) == false && id != "-1")
{
orgPath = id;
path = IOHelper.MapPath(FilePath + "/" + orgPath);
orgPath += "/";
}
else
{
path = IOHelper.MapPath(FilePath);
}
var directories = FileSystem.GetDirectories(path);
var dirInfo = new DirectoryInfo(path);
var dirInfos = dirInfo.GetDirectories();
var nodes = new TreeNodeCollection();
foreach (var directory in directories)
foreach (var dir in dirInfos)
{
var hasChildren = FileSystem.GetFiles(directory).Any() || FileSystem.GetDirectories(directory).Any();
if ((dir.Attributes & FileAttributes.Hidden) != 0)
continue;
var hasChildren = dir.GetFiles().Length > 0 || dir.GetDirectories().Length > 0;
var node = CreateTreeNode(orgPath + dir.Name, orgPath, queryStrings, dir.Name, "icon-folder", hasChildren);
var name = Path.GetFileName(directory);
var node = CreateTreeNode(HttpUtility.UrlEncode(directory), path, queryStrings, name, "icon-folder", hasChildren);
OnRenderFolderNode(ref node);
if(node != null)
if (node != null)
nodes.Add(node);
}
//this is a hack to enable file system tree to support multiple file extension look-up
//so the pattern both support *.* *.xml and xml,js,vb for lookups
var files = FileSystem.GetFiles(path).Where(x =>
{
var extension = Path.GetExtension(x);
return extension != null && Extensions.Contains(extension.Trim('.'), StringComparer.InvariantCultureIgnoreCase);
});
var allowedExtensions = new string[0];
var filterByMultipleExtensions = FileSearchPattern.Contains(",");
FileInfo[] fileInfo;
foreach (var file in files)
{
var withoutExt = Path.GetFileNameWithoutExtension(file);
if (withoutExt.IsNullOrWhiteSpace() == false)
{
var name = Path.GetFileName(file);
var node = CreateTreeNode(HttpUtility.UrlEncode(file), path, queryStrings, name, FileIcon, false);
OnRenderFileNode(ref node);
if (node != null)
nodes.Add(node);
}
if (filterByMultipleExtensions)
{
fileInfo = dirInfo.GetFiles();
allowedExtensions = FileSearchPattern.ToLower().Split(',');
}
else
fileInfo = dirInfo.GetFiles(FileSearchPattern);
foreach (var file in fileInfo)
{
if ((file.Attributes & FileAttributes.Hidden) != 0)
continue;
if (filterByMultipleExtensions && Array.IndexOf(allowedExtensions, file.Extension.ToLower().Trim('.')) < 0)
continue;
var withoutExt = Path.GetFileNameWithoutExtension(file.Name);
if (withoutExt.IsNullOrWhiteSpace())
continue;
var node = CreateTreeNode(orgPath + file.Name, orgPath, queryStrings, file.Name, "icon-file", false);
OnRenderFileNode(ref node);
if (node != null)
nodes.Add(node);
}
return nodes;
@@ -90,22 +111,27 @@ namespace Umbraco.Web.Trees
return menu;
}
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.Root.ToInvariantString()
? System.Web.HttpUtility.UrlDecode(id).TrimStart("/")
: "";
string path;
if (string.IsNullOrEmpty(id) == false)
{
var orgPath = System.Web.HttpUtility.UrlDecode(id);
path = IOHelper.MapPath(FilePath + "/" + orgPath);
}
else
{
path = IOHelper.MapPath(FilePath);
}
var isFile = FileSystem.FileExists(path);
var isDirectory = FileSystem.DirectoryExists(path);
if (isDirectory)
var dirInfo = new DirectoryInfo(path);
//check if it's a directory
if (dirInfo.Attributes == FileAttributes.Directory)
{
//set the default to create
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
//create action
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
var hasChildren = FileSystem.GetFiles(path).Any() || FileSystem.GetDirectories(path).Any();
var hasChildren = dirInfo.GetFiles().Length > 0 || dirInfo.GetDirectories().Length > 0;
//We can only delete folders if it doesn't have any children (folders or files)
if (hasChildren == false)
{
@@ -116,9 +142,10 @@ namespace Umbraco.Web.Trees
//refresh action
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
}
else if (isFile)
//if it's not a directory then we only allow to delete the item
else
{
//if it's not a directory then we only allow to delete the item
//delete action
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)));
}

View File

@@ -0,0 +1,127 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http.Formatting;
using umbraco.BusinessLogic.Actions;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Web.Models.Trees;
using System.Web;
namespace Umbraco.Web.Trees
{
public abstract class FileSystemTreeController2 : TreeController
{
protected abstract IFileSystem2 FileSystem { get; }
protected abstract string[] Extensions { get; }
protected abstract string FileIcon { get; }
/// <summary>
/// Inheritors can override this method to modify the file node that is created.
/// </summary>
/// <param name="treeNode"></param>
protected virtual void OnRenderFileNode(ref TreeNode treeNode) { }
/// <summary>
/// Inheritors can override this method to modify the folder node that is created.
/// </summary>
/// <param name="treeNode"></param>
protected virtual void OnRenderFolderNode(ref TreeNode treeNode) { }
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.Root.ToInvariantString()
? HttpUtility.UrlDecode(id).TrimStart("/")
: "";
var directories = FileSystem.GetDirectories(path);
var nodes = new TreeNodeCollection();
foreach (var directory in directories)
{
var hasChildren = FileSystem.GetFiles(directory).Any() || FileSystem.GetDirectories(directory).Any();
var name = Path.GetFileName(directory);
var node = CreateTreeNode(HttpUtility.UrlEncode(directory), path, queryStrings, name, "icon-folder", hasChildren);
OnRenderFolderNode(ref node);
if(node != null)
nodes.Add(node);
}
//this is a hack to enable file system tree to support multiple file extension look-up
//so the pattern both support *.* *.xml and xml,js,vb for lookups
var files = FileSystem.GetFiles(path).Where(x =>
{
var extension = Path.GetExtension(x);
return extension != null && Extensions.Contains(extension.Trim('.'), StringComparer.InvariantCultureIgnoreCase);
});
foreach (var file in files)
{
var withoutExt = Path.GetFileNameWithoutExtension(file);
if (withoutExt.IsNullOrWhiteSpace()) continue;
var name = Path.GetFileName(file);
var node = CreateTreeNode(HttpUtility.UrlEncode(file), path, queryStrings, name, FileIcon, false);
OnRenderFileNode(ref node);
if (node != null)
nodes.Add(node);
}
return nodes;
}
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
{
var menu = new MenuItemCollection();
//if root node no need to visit the filesystem so lets just create the menu and return it
if (id == Constants.System.Root.ToInvariantString())
{
//set the default to create
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
//create action
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
//refresh action
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
return menu;
}
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.Root.ToInvariantString()
? HttpUtility.UrlDecode(id).TrimStart("/")
: "";
var isFile = FileSystem.FileExists(path);
var isDirectory = FileSystem.DirectoryExists(path);
if (isDirectory)
{
//set the default to create
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
//create action
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
var hasChildren = FileSystem.GetFiles(path).Any() || FileSystem.GetDirectories(path).Any();
//We can only delete folders if it doesn't have any children (folders or files)
if (hasChildren == false)
{
//delete action
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)), true);
}
//refresh action
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
}
else if (isFile)
{
//if it's not a directory then we only allow to delete the item
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)));
}
return menu;
}
}
}

View File

@@ -7,15 +7,15 @@ namespace Umbraco.Web.Trees
/// <summary>
/// Tree for displaying partial view macros in the developer app
/// </summary>
[Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)]
public class PartialViewMacrosTreeController : FileSystemTreeController
[Tree(Constants.Applications.Developer, "partialViewMacros", null, sortOrder: 6)]
public class PartialViewMacrosTreeController : FileSystemTreeController2
{
protected override IFileSystem2 FileSystem
{
get { return FileSystemProviderManager.Current.MacroPartialsFileSystem; }
}
private static readonly string[] ExtensionsStatic = { "cshtml" };
private static readonly string[] ExtensionsStatic = {"cshtml"};
protected override string[] Extensions
{

View File

@@ -7,15 +7,15 @@ namespace Umbraco.Web.Trees
/// <summary>
/// Tree for displaying partial views in the settings app
/// </summary>
[Tree(Constants.Applications.Settings, "partialViews", "Partial Views", sortOrder: 2)]
public class PartialViewsTreeController : FileSystemTreeController
[Tree(Constants.Applications.Settings, "partialViews", null, sortOrder: 2)]
public class PartialViewsTreeController : FileSystemTreeController2
{
protected override IFileSystem2 FileSystem
{
get { return FileSystemProviderManager.Current.PartialViewsFileSystem; }
}
protected override IFileSystem2 FileSystem
{
get { return FileSystemProviderManager.Current.PartialViewsFileSystem; }
}
private static readonly string[] ExtensionsStatic = { "cshtml" };
private static readonly string[] ExtensionsStatic = {"cshtml"};
protected override string[] Extensions
{

View File

@@ -4,8 +4,8 @@ using Umbraco.Web.Models.Trees;
namespace Umbraco.Web.Trees
{
[Tree(Constants.Applications.Settings, "scripts", "Scripts", sortOrder: 4)]
public class ScriptTreeController : FileSystemTreeController
[Tree(Constants.Applications.Settings, "scripts", null, sortOrder: 4)]
public class ScriptTreeController : FileSystemTreeController2
{
protected override IFileSystem2 FileSystem
{

View File

@@ -411,7 +411,7 @@
<Compile Include="PropertyEditors\ValueConverters\MediaPickerPropertyConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\MemberPickerPropertyConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\MultiNodeTreePickerPropertyConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\MultipleMediaPickerPropertyConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\LegacyMediaPickerPropertyConverter.cs" />
<Compile Include="PublishedContentQueryExtensions.cs" />
<Compile Include="Routing\RedirectTrackingEventHandler.cs" />
<Compile Include="Editors\RedirectUrlManagementController.cs" />
@@ -435,6 +435,7 @@
<Compile Include="Security\Identity\PreviewAuthenticationMiddleware.cs" />
<Compile Include="SingletonHttpContextAccessor.cs" />
<Compile Include="Trees\ContentTypeTreeController.cs" />
<Compile Include="Trees\FileSystemTreeController.cs" />
<Compile Include="Trees\PackagesTreeController.cs" />
<Compile Include="Trees\MediaTypeTreeController.cs" />
<Compile Include="Trees\MemberTypeTreeController.cs" />
@@ -712,7 +713,7 @@
<Compile Include="TagQuery.cs" />
<Compile Include="Trees\CoreTreeAttribute.cs" />
<Compile Include="Trees\DataTypeTreeController.cs" />
<Compile Include="Trees\FileSystemTreeController.cs" />
<Compile Include="Trees\FileSystemTreeController2.cs" />
<Compile Include="Trees\LanguageTreeController.cs" />
<Compile Include="Trees\LegacyBaseTreeAttribute.cs" />
<Compile Include="Trees\MemberTreeController.cs" />

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