Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7

# Conflicts:
#	build/UmbracoVersion.txt
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoVersion.cs
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
#	src/Umbraco.Web/Umbraco.Web.csproj
This commit is contained in:
Shannon
2017-07-18 13:27:03 +10:00
56 changed files with 905 additions and 205 deletions
@@ -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;
+7
View File
@@ -0,0 +1,7 @@
namespace Umbraco.Core.Models
{
public interface IUserControl : IFile
{
}
}
+2 -1
View File
@@ -4,6 +4,7 @@
{
Unknown = 0, // default
PartialView = 1,
PartialViewMacro = 2
PartialViewMacro = 2,
MacroScript = 3
}
}
+32
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; }
}
}
}
@@ -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()
{
}
{ }
}
}
@@ -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);
}
}
@@ -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; } }
}
}
@@ -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);
}
}
}
@@ -248,6 +248,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);
+76
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))
+46
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>
+6 -2
View File
@@ -73,6 +73,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)]
@@ -86,9 +90,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)
{
+11
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>
+5
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" />
@@ -578,12 +580,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" />
@@ -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;
}
}
@@ -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
});
}
};
});
});
@@ -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>
+20 -25
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
// ---------------------------
+18 -3
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
}
@@ -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;
@@ -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 {
@@ -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;
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -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;
@@ -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);
@@ -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>
@@ -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);
@@ -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>
+1
View File
@@ -568,6 +568,7 @@
<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" />
@@ -1291,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>
@@ -523,6 +523,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>
@@ -1556,8 +1557,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>
@@ -1637,4 +1640,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>
@@ -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>
@@ -1543,8 +1544,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>
@@ -1624,4 +1627,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>
+12 -4
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>
@@ -1526,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>
@@ -115,7 +115,7 @@ namespace Umbraco.Web.Cache
}
TagsValueConverter.ClearCaches();
MultipleMediaPickerPropertyConverter.ClearCaches();
LegacyMediaPickerPropertyConverter.ClearCaches();
SliderValueConverter.ClearCaches();
MediaPickerPropertyConverter.ClearCaches();
@@ -230,7 +230,7 @@ namespace Umbraco.Web.Editors
basic.Description = TranslateItem(basic.Description);
}
return basics;
return basics.OrderBy(x => x.Name);
}
/// <summary>
@@ -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
};
}
@@ -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
@@ -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; }
@@ -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; }
}
}
}
@@ -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; }
}
}
}
@@ -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;
@@ -58,6 +58,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
{
return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultipleMediaPickerAlias);
}
if (UmbracoConfig.For.UmbracoSettings().Content.EnablePropertyValueConverters)
{
// this is the double legacy media picker, it can pick only single media items
return propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MediaPickerAlias);
}
return false;
}
@@ -109,7 +115,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);
}
}
@@ -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;
}
+10 -14
View File
@@ -640,14 +640,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)
@@ -918,14 +914,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.
@@ -935,7 +931,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.
@@ -1246,12 +1242,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.
@@ -1259,7 +1255,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.
@@ -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)));
}
@@ -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;
}
}
}
@@ -8,14 +8,14 @@ namespace Umbraco.Web.Trees
/// Tree for displaying partial view macros in the developer app
/// </summary>
[Tree(Constants.Applications.Developer, "partialViewMacros", null, sortOrder: 6)]
public class PartialViewMacrosTreeController : FileSystemTreeController
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
{
@@ -8,14 +8,14 @@ namespace Umbraco.Web.Trees
/// Tree for displaying partial views in the settings app
/// </summary>
[Tree(Constants.Applications.Settings, "partialViews", null, sortOrder: 2)]
public class PartialViewsTreeController : FileSystemTreeController
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
{
@@ -5,7 +5,7 @@ using Umbraco.Web.Models.Trees;
namespace Umbraco.Web.Trees
{
[Tree(Constants.Applications.Settings, "scripts", null, sortOrder: 4)]
public class ScriptTreeController : FileSystemTreeController
public class ScriptTreeController : FileSystemTreeController2
{
protected override IFileSystem2 FileSystem
{
+3 -2
View File
@@ -439,7 +439,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="PropertyEditors\ValueConverters\NestedContentSingleValueConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\NestedContentManyValueConverter.cs" />
<Compile Include="PropertyEditors\ValueConverters\NestedContentPublishedPropertyTypeExtensions.cs" />
@@ -467,6 +467,7 @@
<Compile Include="SingletonHttpContextAccessor.cs" />
<Compile Include="Trees\ContentBlueprintTreeController.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" />
@@ -721,7 +722,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" />
@@ -1393,15 +1393,25 @@ namespace umbraco
/// <returns>An XpathNodeIterator containing the current page as Xml.</returns>
public static XPathNodeIterator GetXmlNodeCurrent()
{
var pageId = "";
try
{
var nav = Umbraco.Web.UmbracoContext.Current.ContentCache.GetXPathNavigator();
nav.MoveToId(HttpContext.Current.Items["pageID"].ToString());
var pageIdItem = HttpContext.Current.Items["pageID"];
if (pageIdItem == null)
{
throw new NullReferenceException("pageID not found in the current HTTP context");
}
pageId = pageIdItem.ToString();
nav.MoveToId(pageId);
return nav.Select(".");
}
catch (Exception ex)
{
LogHelper.Error<library>("Could not retrieve current xml node", ex);
LogHelper.Error<library>(string.Concat("Could not retrieve current xml node for page Id ",pageId), ex);
}
XmlDocument xd = new XmlDocument();
@@ -46,6 +46,7 @@ using System.Linq;
using File = System.IO.File;
using MacroTypes = umbraco.cms.businesslogic.macro.MacroTypes;
using Member = umbraco.cms.businesslogic.member.Member;
using UserControl = System.Web.UI.UserControl;
namespace umbraco
{
@@ -19,6 +19,7 @@ using Umbraco.Core.PropertyEditors;
using umbraco.BusinessLogic;
using System.Collections.Generic;
using MacroProperty = umbraco.cms.businesslogic.macro.MacroProperty;
using UserControl = System.Web.UI.UserControl;
namespace umbraco.developer
{