Compare commits

..

72 Commits

Author SHA1 Message Date
Sebastiaan Janssen 85b00f60ab Update broken unit test 2016-06-14 14:38:19 +02:00
Sebastiaan Janssen 2ae2047b7c Temporarily ignore two failing tests 2016-06-14 14:18:24 +02:00
Shannon c5aa97e23a Merge branch 'temp-packages' into dev-v7
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/less/components/editor/subheader/umb-editor-sub-header.less
2016-06-14 14:15:47 +02:00
Shannon 017b925fe7 Merge branch 'temp-packages' of https://github.com/umbraco/Umbraco-CMS into temp-packages 2016-06-14 14:12:41 +02:00
Shannon 3fcb2a7840 Installer is now 'fully' working 2016-06-14 14:12:10 +02:00
Mads Rasmussen 5f506a8bbc fix dropzone width 2016-06-13 23:50:36 +02:00
Sebastiaan Janssen 8539de08d0 IRedirectUrlRepository needs to be public 2016-06-13 23:41:19 +02:00
Shannon a8c71e52ec Ensures the min umbraco version is checked when installing packages that have the strict flag set. 2016-06-13 23:35:09 +02:00
Sebastiaan Janssen 7cddeca730 New installer background 2016-06-13 23:24:58 +02:00
Sebastiaan Janssen 7f154e28de Add the beta label 2016-06-13 23:24:57 +02:00
Sebastiaan Janssen b2cd4820be Updates Examine 2016-06-13 23:24:19 +02:00
Sebastiaan Janssen 82954b6b6f Merge pull request #1325 from umbraco/temp-u4-8361
U4-8361 Basic 301 Support In Core
2016-06-13 23:22:34 +02:00
Stephan ef181b7230 U4-8361 - internal some things 2016-06-13 23:20:27 +02:00
Mads Rasmussen 67d71908bf add back button when uninstalling package 2016-06-13 22:45:42 +02:00
Mads Rasmussen e38d4b7e88 increase icon font size 2016-06-13 20:57:00 +02:00
Sebastiaan Janssen 2407517fd5 Merge pull request #1326 from AndyButland/u4-3805f
u4-3805 - Set fixed value for click-jacking protection to sameorigin...
2016-06-13 20:54:26 +02:00
AndyButland 0a00be7cfe Set fixed value for click-jacking protection to sameorigin instead of deny 2016-06-13 20:48:37 +02:00
Mads Rasmussen 1a7993b9d7 set bigger font icon on uninstall dashboard 2016-06-13 20:46:40 +02:00
Shannon c0625a7853 adds small icon to packages without them 2016-06-13 20:15:11 +02:00
Shannon 3d2fe3c9da Install/uninstall working - just need a few ui updates and then get the repo install working 2016-06-13 20:10:31 +02:00
Stephan 839603342b U4-8361 - bugfix, get events to work 2016-06-13 19:45:25 +02:00
Andy Butland 203e9e3394 Ensured click-jacking meta-tag health check searches case insensitively (#1324) 2016-06-13 19:44:01 +02:00
Shannon 2d9010caf4 I can haz package uninstallation 2016-06-13 19:43:04 +02:00
Shannon c2b016d7d0 Gets local package installation 'working', now needs a little UI work. 2016-06-13 18:28:56 +02:00
Sebastiaan Janssen d83d7956bb Dev v7 health check (#1220)
Adds health check feature including some initial checks
2016-06-13 17:42:05 +02:00
Shannon f5d3f9087f updates to package filtering to unselect a category 2016-06-13 13:29:51 +02:00
Shannon 441ea0cfaf Got the package search/filter/etc.. all working 2016-06-13 13:10:26 +02:00
Shannon 5a32e1a294 package tree is basically done, got the editor updated to work with the server data to display the initial screen and details 2016-06-12 21:39:29 +02:00
Stephan 795e6945e3 Merge pull request #1313 from marcemarc/temp-u4-8361
Create 301 Redirects When Urls Change - merging for review
2016-06-12 19:54:00 +02:00
Stephan e231206555 U4-8361 - bugfix, get finder to work 2016-06-12 19:47:11 +02:00
Marc Goodson 7c49bb4aca Event Handler to wire up to publishing and moving events to track Url changes, and then create appropriate 301 redirects using the new RedirectUrlService to maintain google juice 2016-06-12 16:54:13 +01:00
Stephan 0ad25ff7b9 Merge pull request #1310 from JimBobSquarePants/temp-u4-8361
Add IContentFinder implementation.
2016-06-12 16:47:44 +02:00
Stephan 2570a9139c U4-8361 - service & repository 2016-06-12 16:22:06 +02:00
James Jackson-South c916c09dee Add IContentFinder implementation. 2016-06-12 23:51:22 +10:00
Stephan 79bbd6a70c U4-8361 - implement repository 2016-06-12 15:27:54 +02:00
Shannon ba9315079b Merge branch 'temp-packages' of https://github.com/umbraco/Umbraco-CMS into temp-packages 2016-06-12 15:20:50 +02:00
Shannon cba91145ee Updates new packages tree, obsoletes old one, have the package UI running for root node and sub nodes only for created packages. 2016-06-12 15:19:42 +02:00
Stephan a7759c6175 U4-8361 - renaming 2016-06-12 13:07:00 +02:00
Stephan 36fbb9df86 U4-8361 - initial work 2016-06-12 12:38:35 +02:00
Mads Rasmussen ae7c145fcd add scrollable header to details page 2016-06-12 11:45:57 +02:00
Mads Rasmussen 08ae541bd8 center icons in header 2016-06-01 13:47:03 +02:00
Mads Rasmussen e3b8ad5e23 fake loading state 2016-06-01 13:27:33 +02:00
Mads Rasmussen 6dab9d2966 clean up overview controller and view 2016-06-01 13:19:02 +02:00
Mads Rasmussen d435c8c4c8 hide footer 2016-06-01 13:16:49 +02:00
Simon Busborg 2ce2a03072 Local installer 2016-06-01 13:03:19 +02:00
Mads Rasmussen a39150a98c added auto focus to search field 2016-06-01 12:57:53 +02:00
Mads Rasmussen 0d99e1d9a2 added pagination 2016-06-01 11:12:34 +02:00
Mads Rasmussen 43f7a18eb6 move details view into repo sub view 2016-06-01 10:06:09 +02:00
Mads Rasmussen 41bef87365 add description to package 2016-05-31 21:56:33 +02:00
Mads Rasmussen 05cdc066ab do not redirect when changing category 2016-05-31 21:29:23 +02:00
Mads Rasmussen 5858823252 add even spacing to sections + a bit more padding to category nav 2016-05-31 21:29:00 +02:00
Mads Rasmussen 86eaacc5d2 tighten repo overview a bit 2016-05-31 21:10:52 +02:00
Simon Busborg c66de2298b Added local installer 2016-05-31 16:18:41 +02:00
Mads Rasmussen 4545e83534 added list for installed packages 2016-05-31 16:00:23 +02:00
Mads Rasmussen c36db267ce add val-form-manager to fix js errors 2016-05-31 15:57:34 +02:00
Mads Rasmussen 8b3160cd79 remove unused model 2016-05-31 15:56:56 +02:00
Mads Rasmussen 5ba2967c15 added design for package details 2016-05-31 14:02:07 +02:00
Simon Busborg c3e4b611a6 added subviews 2016-05-31 11:18:34 +02:00
Mads Rasmussen 521329feac send id to category and details view 2016-05-30 09:15:28 +02:00
Simon Busborg 5b9d063808 Changed categories 2016-05-27 14:52:35 +02:00
Mads Rasmussen a04cb1d970 added details view 2016-05-27 14:29:30 +02:00
Mads Rasmussen d77986fb18 open overview html instead of edit html.. add category html and link the two views 2016-05-27 13:11:03 +02:00
Simon Busborg db21aef150 added icons and versions 2016-05-27 11:59:19 +02:00
Mads Rasmussen 140218b0ed remove install button + make categories smaller 2016-05-27 10:32:55 +02:00
Simon Busborg 3cc8a71a60 added button back 2016-05-26 16:31:56 +02:00
Simon Busborg 8a744b52fe Removed install button, better spacing 2016-05-26 15:21:12 +02:00
Mads Rasmussen 098eb32ef6 added categories 2016-05-26 14:57:38 +02:00
Simon Busborg 7bb8430022 Packages 📦 2016-05-26 14:50:11 +02:00
Simon Busborg 259ccd653e Added dummy data to packages 2016-05-26 14:13:55 +02:00
Simon Busborg f876b6b728 added umb-packages.less, added repeater for packages + minimal styling 2016-05-26 13:39:01 +02:00
Mads Rasmussen 05746c7b7c added editor scaffold 2016-05-26 13:12:41 +02:00
Mads Rasmussen 30ac4e94be hacked new packages tree 2016-05-26 13:12:05 +02:00
166 changed files with 7294 additions and 1222 deletions
+1
View File
@@ -23,6 +23,7 @@ ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\
CD %CD%\..\src\Umbraco.Web.UI.Client\
ECHO Do npm install and the grunt build of Belle
call npm cache clean --quiet
call npm install --quiet
call npm install -g grunt-cli --quiet
call npm install -g bower --quiet
+1 -1
View File
@@ -31,7 +31,7 @@
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.0.0, 3.1.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 9.0.0)" />
<dependency id="Examine" version="[0.1.68, 1.0.0)" />
<dependency id="Examine" version="[0.1.69-beta, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.3.3, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.5.3, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 2.0.0)" />
+2 -1
View File
@@ -1,2 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.5.0
7.5.0
beta
+1 -1
View File
@@ -12,4 +12,4 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.5.0")]
[assembly: AssemblyInformationalVersion("7.5.0")]
[assembly: AssemblyInformationalVersion("7.5.0-beta")]
+8 -22
View File
@@ -1,22 +1,18 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Profiling;
using Umbraco.Core.Services;
using Umbraco.Core.Sync;
namespace Umbraco.Core
{
/// <summary>
/// <summary>
/// the Umbraco Application context
/// </summary>
/// <remarks>
@@ -187,7 +183,6 @@ namespace Umbraco.Core
readonly ManualResetEventSlim _isReadyEvent = new ManualResetEventSlim(false);
private DatabaseContext _databaseContext;
private ServiceContext _services;
private PackageMigrationsContext _packageMigrationsContext;
public bool IsReady
{
@@ -217,19 +212,17 @@ namespace Umbraco.Core
public bool IsConfigured
{
get { return _umbracoVersionConfigured.Value; }
get { return _configured.Value; }
}
/// <summary>
/// If the db is configured, there is a database context and there is an umbraco schema,
/// but we are: (not 'configured' OR there's pending migrations), then it means we are upgrading
/// If the db is configured, there is a database context and there is an umbraco schema, but we are not 'configured' , then it means we are upgrading
/// </summary>
public bool IsUpgrading
{
get
{
if ((IsConfigured == false || PackageMigrationsContext.HasPendingPackageMigrations)
if (IsConfigured == false
&& DatabaseContext != null
&& DatabaseContext.IsDatabaseConfigured)
{
@@ -275,8 +268,7 @@ namespace Umbraco.Core
// ReSharper disable once InconsistentNaming
internal string _umbracoApplicationUrl;
private Lazy<bool> _umbracoVersionConfigured;
private Lazy<bool> _configured;
internal MainDom MainDom { get; private set; }
private void Init()
@@ -285,7 +277,7 @@ namespace Umbraco.Core
MainDom.Acquire();
//Create the lazy value to resolve whether or not the application is 'configured'
_umbracoVersionConfigured = new Lazy<bool>(() =>
_configured = new Lazy<bool>(() =>
{
try
{
@@ -326,10 +318,9 @@ namespace Umbraco.Core
LogHelper.Error<ApplicationContext>("Error determining if application is configured, returning false", ex);
return false;
}
});
_packageMigrationsContext = new PackageMigrationsContext(DatabaseContext, Services.MigrationEntryService, ProfilingLogger.Logger);
}
});
}
private string ConfigurationStatus
{
@@ -352,11 +343,6 @@ namespace Umbraco.Core
throw new Exception("ApplicationContext has already been initialized.");
}
internal PackageMigrationsContext PackageMigrationsContext
{
get { return _packageMigrationsContext; }
}
/// <summary>
/// Gets the current DatabaseContext
/// </summary>
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return ""; } }
public static string CurrentComment { get { return "beta"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
@@ -32,7 +32,12 @@ namespace Umbraco.Core.Configuration
public static SemVersion GetSemanticVersion()
{
return Current.GetSemanticVersion();
return new SemVersion(
Current.Major,
Current.Minor,
Current.Build,
CurrentComment.IsNullOrWhiteSpace() ? null : CurrentComment,
Current.Revision > 0 ? Current.Revision.ToInvariantString() : null);
}
}
}
@@ -73,6 +73,11 @@
/// </summary>
public const string DataTypes = "dataTypes";
/// <summary>
/// alias for the packages tree
/// </summary>
public const string Packages = "packager";
/// <summary>
/// alias for the dictionary tree.
/// </summary>
+1 -6
View File
@@ -500,12 +500,7 @@ namespace Umbraco.Core
//the database migration objects
MigrationResolver.Current = new MigrationResolver(
ProfilingLogger.Logger,
() => PluginManager.ResolveTypes<IMigration>())
{
//This needs to be resolved before it's frozen because we need to find migrations to determine
// if Umbraco IsConfigured
CanResolveBeforeFrozen = true
};
() => PluginManager.ResolveTypes<IMigration>());
// todo: remove once we drop IPropertyEditorValueConverter support.
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
+10 -1
View File
@@ -69,7 +69,16 @@ namespace Umbraco.Core.IO
}
}
public static string AppPlugins
public static string AppCode
{
get
{
//NOTE: this is not configurable and shouldn't need to be
return "~/App_Code";
}
}
public static string AppPlugins
{
get
{
+2 -1
View File
@@ -1,4 +1,5 @@
using Semver;
using System;
using Semver;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
+18
View File
@@ -0,0 +1,18 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
public interface IRedirectUrl : IAggregateRoot, IRememberBeingDirty
{
[DataMember]
int ContentId { get; set; }
[DataMember]
DateTime CreateDateUtc { get; set; }
[DataMember]
string Url { get; set; }
}
}
@@ -0,0 +1,35 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoRedirectUrl")]
[PrimaryKey("id")]
[ExplicitColumns]
class RedirectUrlDto
{
public RedirectUrlDto()
{
CreateDateUtc = DateTime.UtcNow;
}
[Column("id")]
[PrimaryKeyColumn(IdentitySeed = 1, Name = "PK_umbracoRedirectUrl")]
public int Id { get; set; }
[Column("contentId")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[ForeignKey(typeof(NodeDto), Column = "id")]
public int ContentId { get; set; }
[Column("createDateUtc")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public DateTime CreateDateUtc { get; set; }
[Column("url")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRedirectUrl", ForColumns = "url, createDateUtc")]
public string Url { get; set; }
}
}
+61
View File
@@ -0,0 +1,61 @@
using System;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
[Serializable]
[DataContract(IsReference = true)]
public class RedirectUrl : Entity, IRedirectUrl
{
public RedirectUrl()
{
CreateDateUtc = DateTime.UtcNow;
}
private static readonly PropertyInfo ContentIdSelector = ExpressionHelper.GetPropertyInfo<RedirectUrl, int>(x => x.ContentId);
private static readonly PropertyInfo CreateDateUtcSelector = ExpressionHelper.GetPropertyInfo<RedirectUrl, DateTime>(x => x.CreateDateUtc);
private static readonly PropertyInfo UrlSelector = ExpressionHelper.GetPropertyInfo<RedirectUrl, string>(x => x.Url);
private int _contentId;
private DateTime _createDateUtc;
private string _url;
public int ContentId
{
get { return _contentId; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
return _contentId = value;
}, _contentId, ContentIdSelector);
}
}
public DateTime CreateDateUtc
{
get { return _createDateUtc; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
return _createDateUtc = value;
}, _createDateUtc, CreateDateUtcSelector);
}
}
public string Url
{
get { return _url; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
return _url = value;
}, _url, UrlSelector);
}
}
}
}
@@ -1,114 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Services;
namespace Umbraco.Core.Packaging
{
internal class PackageMigrationsContext
{
private readonly DatabaseContext _dbContext;
private readonly IMigrationEntryService _migrationEntryService;
private readonly ILogger _logger;
public PackageMigrationsContext(DatabaseContext dbContext,
IMigrationEntryService migrationEntryService,
ILogger logger)
{
_dbContext = dbContext;
_migrationEntryService = migrationEntryService;
_logger = logger;
ResetPendingPackageMigrations();
}
//List of product names and their maximum sem version registered in migrations
private Lazy<ReadOnlyDictionary<string, SemVersion>> _packageVersionsConfigured;
/// <summary>
/// Called to initialize or when package migrations have executed - since an app restart is mandatory
/// </summary>
internal void ResetPendingPackageMigrations()
{
_packageVersionsConfigured = new Lazy<ReadOnlyDictionary<string, SemVersion>>(() =>
{
var result = new Dictionary<string, SemVersion>();
//The versions are the same in config, but are they the same in the database. We can only check this
// if we have a db context available, if we don't then we are not installed anyways
if (_dbContext.IsDatabaseConfigured && _dbContext.CanConnect)
{
//Now we can check if there are any package migrations that haven't been executed.
//Find and group the packages that have migrations.
//Then we need to determine if there are outstanding migrations that need to be run based on the
// migration versions stored in the db compared to the available migrations.
var packageMigrations = MigrationResolver.Current.MigrationMetaData
.Where(x => x.ProductName != GlobalSettings.UmbracoMigrationName)
.GroupBy(x => x.ProductName)
.ToArray();
var packageMigrationProductNames = packageMigrations.Select(x => x.Key).Distinct().ToArray();
var allPackageDbEntries = _migrationEntryService.FindEntries(packageMigrationProductNames)
.ToArray();
foreach (var packageMigration in packageMigrations)
{
var packageDbVersions = allPackageDbEntries
.Where(x => x.MigrationName.InvariantEquals(packageMigration.Key))
.Select(x => x.Version);
var notExecuted = packageMigration
.Select(x => x.TargetVersion.GetSemanticVersion())
.Except(packageDbVersions)
.ToArray();
if (notExecuted.Any())
{
var maxVersion = notExecuted.Max();
result.Add(packageMigration.Key, maxVersion);
_logger.Debug<ApplicationContext>(
string.Format("The migration {0} for version: '{1} has not been executed, there is no record in the database",
packageMigration.Key,
maxVersion.ToSemanticString()));
}
}
}
return new ReadOnlyDictionary<string, SemVersion>(result);
});
}
/// <summary>
/// Returns true if there are pending package migrations that need to be executed
/// </summary>
internal bool HasPendingPackageMigrations
{
get { return _packageVersionsConfigured.Value.Any(); }
}
/// <summary>
/// Returns the list of package migration names that need to be executed
/// </summary>
internal ReadOnlyDictionary<string, SemVersion> GetPendingPackageMigrations()
{
return _packageVersionsConfigured.Value;
}
/// <summary>
/// Returns the list of package migration names and versions that need to be executed
/// with the following format: PackageProductName (2.0.0)
/// </summary>
/// <returns></returns>
internal IEnumerable<string> GetPendingPackageMigrationFriendlyNames()
{
return _packageVersionsConfigured.Value.Select(x => string.Format("{0} ({1})", x.Key, x.Value));
}
}
}
@@ -80,11 +80,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{40, typeof (ServerRegistrationDto)},
{41, typeof (AccessDto)},
{42, typeof (AccessRuleDto)},
{43, typeof(CacheInstructionDto)},
{43, typeof (CacheInstructionDto)},
{44, typeof (ExternalLoginDto)},
{45, typeof (MigrationDto)},
{46, typeof (UmbracoDeployChecksumDto)},
{47, typeof (UmbracoDeployDependencyDto)}
{47, typeof (UmbracoDeployDependencyDto)},
{48, typeof (RedirectUrlDto) }
};
#endregion
@@ -33,11 +33,6 @@ namespace Umbraco.Core.Persistence.Migrations
get { return Values; }
}
public IEnumerable<MigrationAttribute> MigrationMetaData
{
get { return Values.SelectMany(x => x.GetType().GetCustomAttributes<MigrationAttribute>(false)).WhereNotNull(); }
}
/// <summary>
/// This will ctor the IMigration instances
@@ -94,6 +94,7 @@ namespace Umbraco.Core.Persistence.Migrations
var migrations = isUpgrade
? OrderedUpgradeMigrations(foundMigrations).ToList()
: OrderedDowngradeMigrations(foundMigrations).ToList();
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, _productName, true), this))
{
@@ -297,6 +298,7 @@ namespace Umbraco.Core.Persistence.Migrations
{
_migrationEntryService.CreateEntry(_productName, _targetVersion);
}
}
}
@@ -0,0 +1,40 @@
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZero
{
[Migration("7.5.0", 100, GlobalSettings.UmbracoMigrationName)]
public class AddRedirectUrlTable : MigrationBase
{
public AddRedirectUrlTable(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
// don't exeucte if the table is already there
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
if (tables.InvariantContains("umbracoRedirectUrl")) return;
Create.Table("umbracoRedirectUrl")
.WithColumn("id").AsInt32().Identity().PrimaryKey("PK_umbracoRedirectUrl")
.WithColumn("contentId").AsInt32().NotNullable()
.WithColumn("createDateUtc").AsDateTime().NotNullable()
.WithColumn("url").AsString(2048).NotNullable();
//Create.PrimaryKey("PK_umbracoRedirectUrl").OnTable("umbracoRedirectUrl").Columns(new[] { "id" });
Create.Index("IX_umbracoRedirectUrl").OnTable("umbracoRedirectUrl")
.OnColumn("url")
.Ascending()
.OnColumn("createDateUtc")
.Ascending()
.WithOptions().NonClustered();
}
public override void Down()
{ }
}
}
@@ -143,6 +143,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var list = new List<string>
{
"DELETE FROM umbracoRedirectUrl WHERE contentId = @Id",
"DELETE FROM cmsTask WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using Semver;
using Umbraco.Core.Models;
@@ -8,9 +7,5 @@ namespace Umbraco.Core.Persistence.Repositories
public interface IMigrationEntryRepository : IRepositoryQueryable<int, IMigrationEntry>
{
IMigrationEntry FindEntry(string migrationName, SemVersion version);
IEnumerable<IMigrationEntry> FindEntries(SemVersion version, IEnumerable<string> migrationNames);
IEnumerable<IMigrationEntry> FindEntries(IEnumerable<string> migrationNames);
IEnumerable<IMigrationEntry> FindEntries(string migrationName);
}
}
@@ -0,0 +1,17 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IRedirectUrlRepository : IRepositoryQueryable<int, IRedirectUrl>
{
IRedirectUrl Get(string url, int contentId);
void Delete(int id);
void DeleteAll();
void DeleteContentUrls(int contentId);
IRedirectUrl GetMostRecentUrl(string url);
IEnumerable<RedirectUrl> GetContentUrls(int contentId);
IEnumerable<RedirectUrl> GetAllUrls(long pageIndex, int pageSize, out long total);
IEnumerable<RedirectUrl> GetAllUrls(int rootContentId, long pageIndex, int pageSize, out long total);
}
}
@@ -128,53 +128,5 @@ namespace Umbraco.Core.Persistence.Repositories
return result == null ? null : factory.BuildEntity(result);
}
public IEnumerable<IMigrationEntry> FindEntries(SemVersion version, IEnumerable<string> migrationNames)
{
if (migrationNames.Any() == false) return Enumerable.Empty<IMigrationEntry>();
var versionString = version.ToString();
var sql = new Sql().Select("*")
.From<MigrationDto>(SqlSyntax)
.Where(
SqlSyntax.GetQuotedColumnName("version") + "=@versionString AND " +
SqlSyntax.GetQuotedColumnName("name") + " IN (@names)", new {names = migrationNames, versionString});
var result = Database.Fetch<MigrationDto>(sql);
var factory = new MigrationEntryFactory();
return result.Select(factory.BuildEntity);
}
public IEnumerable<IMigrationEntry> FindEntries(IEnumerable<string> migrationNames)
{
if (migrationNames.Any() == false) return Enumerable.Empty<IMigrationEntry>();
var sql = new Sql().Select("*")
.From<MigrationDto>(SqlSyntax)
.Where(
SqlSyntax.GetQuotedColumnName("name") + " IN (@names)", new { names = migrationNames });
var result = Database.Fetch<MigrationDto>(sql);
var factory = new MigrationEntryFactory();
return result.Select(factory.BuildEntity);
}
public IEnumerable<IMigrationEntry> FindEntries(string migrationName)
{
var sql = new Sql().Select("*")
.From<MigrationDto>(SqlSyntax)
.Where<MigrationDto>(x => x.Name.InvariantEquals(migrationName));
var result = Database.Fetch<MigrationDto>(sql);
var factory = new MigrationEntryFactory();
return result.Select(factory.BuildEntity);
}
}
}
@@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
internal class RedirectUrlRepository : PetaPocoRepositoryBase<int, IRedirectUrl>, IRedirectUrlRepository
{
public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
: base(work, cache, logger, sqlSyntax)
{ }
protected override int PerformCount(IQuery<IRedirectUrl> query)
{
throw new NotSupportedException("This repository does not support this method.");
}
protected override bool PerformExists(int id)
{
return PerformGet(id) != null;
}
protected override IRedirectUrl PerformGet(int id)
{
var sql = GetBaseQuery(false).Where<RedirectUrlDto>(x => x.Id == id);
var dto = Database.Fetch<RedirectUrlDto>(sql).FirstOrDefault();
return dto == null ? null : Map(dto);
}
protected override IEnumerable<IRedirectUrl> PerformGetAll(params int[] ids)
{
if (ids.Length > 2000)
throw new NotSupportedException("This repository does not support more than 2000 ids.");
var sql = GetBaseQuery(false).WhereIn<RedirectUrlDto>(x => x.Id, ids);
var dtos = Database.Fetch<RedirectUrlDto>(sql);
return dtos.WhereNotNull().Select(Map);
}
protected override IEnumerable<IRedirectUrl> PerformGetByQuery(IQuery<IRedirectUrl> query)
{
throw new NotSupportedException("This repository does not support this method.");
}
protected override Sql GetBaseQuery(bool isCount)
{
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*").From<RedirectUrlDto>(SqlSyntax);
return sql;
}
protected override string GetBaseWhereClause()
{
return "id = @Id";
}
protected override IEnumerable<string> GetDeleteClauses()
{
var list = new List<string>
{
"DELETE FROM umbracoRedirectUrl WHERE id = @Id"
};
return list;
}
protected override Guid NodeObjectTypeId
{
get { throw new NotImplementedException(); }
}
protected override void PersistNewItem(IRedirectUrl entity)
{
var dto = Map(entity);
Database.Insert(dto);
entity.Id = dto.Id;
}
protected override void PersistUpdatedItem(IRedirectUrl entity)
{
var dto = Map(entity);
Database.Update(dto);
}
private static RedirectUrlDto Map(IRedirectUrl redirectUrl)
{
if (redirectUrl == null) return null;
return new RedirectUrlDto
{
Id = redirectUrl.Id,
ContentId = redirectUrl.ContentId,
CreateDateUtc = redirectUrl.CreateDateUtc,
Url = redirectUrl.Url
};
}
private static IRedirectUrl Map(RedirectUrlDto dto)
{
if (dto == null) return null;
return new RedirectUrl
{
Id = dto.Id,
ContentId = dto.ContentId,
CreateDateUtc = dto.CreateDateUtc,
Url = dto.Url
};
}
public IRedirectUrl Get(string url, int contentId)
{
var sql = GetBaseQuery(false).Where<RedirectUrlDto>(x => x.Url == url && x.ContentId == contentId);
var dto = Database.Fetch<RedirectUrlDto>(sql).FirstOrDefault();
return dto == null ? null : Map(dto);
}
public void DeleteAll()
{
Database.Execute("DELETE FROM umbracoRedirectUrl");
}
public void DeleteContentUrls(int contentId)
{
Database.Execute("DELETE FROM umbracoRedirectUrl WHERE contentId=@contentId", new { contentId });
}
public void Delete(int id)
{
Database.Delete<RedirectUrlDto>(id);
}
public IRedirectUrl GetMostRecentUrl(string url)
{
var dtos = Database.Fetch<RedirectUrlDto>("SELECT * FROM umbracoRedirectUrl WHERE url=@url ORDER BY createDateUtc DESC;",
new { url });
var dto = dtos.FirstOrDefault();
return dto == null ? null : Map(dto);
}
public IEnumerable<RedirectUrl> GetContentUrls(int contentId)
{
var dtos = Database.Fetch<RedirectUrlDto>("SELECT * FROM umbracoRedirectUrl WHERE contentId=@id ORDER BY createDateUtc DESC;",
new { id = contentId });
return dtos.Select(x => new RedirectUrl
{
Id = x.Id,
ContentId = x.ContentId,
CreateDateUtc = x.CreateDateUtc,
Url = x.Url
});
}
public IEnumerable<RedirectUrl> GetAllUrls(long pageIndex, int pageSize, out long total)
{
var sql = GetBaseQuery(false).OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
var result = Database.Page<RedirectUrlDto>(pageIndex + 1, pageSize, sql);
total = Convert.ToInt32(result.TotalItems);
var rules = result.Items.Select(x => new RedirectUrl
{
Id = x.Id,
ContentId = x.ContentId,
CreateDateUtc = x.CreateDateUtc,
Url = x.Url
});
return rules;
}
public IEnumerable<RedirectUrl> GetAllUrls(int rootContentId, long pageIndex, int pageSize, out long total)
{
var sql = GetBaseQuery(false)
.InnerJoin<NodeDto>(SqlSyntax).On<NodeDto, RedirectUrlDto>(SqlSyntax, left => left.NodeId, right => right.ContentId)
.Where("umbracoNode.path LIKE @path", new { path = "%," + rootContentId + ",%" })
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
var result = Database.Page<RedirectUrlDto>(pageIndex + 1, pageSize, sql);
total = Convert.ToInt32(result.TotalItems);
var rules = result.Items.Select(x => new RedirectUrl
{
Id = x.Id,
ContentId = x.ContentId,
CreateDateUtc = x.CreateDateUtc,
Url = x.Url
});
return rules;
}
}
}
@@ -25,7 +25,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override int PerformCount(IQuery<IServerRegistration> query)
{
throw new NotSupportedException("This repository does not support this method");
throw new NotSupportedException("This repository does not support this method.");
}
protected override bool PerformExists(int id)
@@ -55,7 +55,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<IServerRegistration> PerformGetByQuery(IQuery<IServerRegistration> query)
{
throw new NotSupportedException("This repository does not support this method");
throw new NotSupportedException("This repository does not support this method.");
}
protected override Sql GetBaseQuery(bool isCount)
@@ -333,5 +333,14 @@ namespace Umbraco.Core.Persistence
_logger, _sqlSyntax,
containerObjectType);
}
public IRedirectUrlRepository CreateRedirectUrlRepository(IDatabaseUnitOfWork uow)
{
return new RedirectUrlRepository(
uow,
_cacheHelper,
_logger,
_sqlSyntax);
}
}
}
+12
View File
@@ -0,0 +1,12 @@
using Semver;
namespace Umbraco.Core
{
public static class SemVersionExtensions
{
public static string ToSemanticString(this SemVersion semVersion)
{
return semVersion.ToString().Replace("--", "-").Replace("-+", "+");
}
}
}
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Semver;
using Umbraco.Core.Models;
@@ -22,28 +23,6 @@ namespace Umbraco.Core.Services
/// <returns></returns>
IMigrationEntry FindEntry(string migrationName, SemVersion version);
/// <summary>
/// Finds migration entries by name
/// </summary>
/// <param name="migrationName"></param>
/// <returns></returns>
IEnumerable<IMigrationEntry> FindEntries(string migrationName);
/// <summary>
/// Finds migration entries by list of product names
/// </summary>
/// <param name="migrationNames"></param>
/// <returns></returns>
IEnumerable<IMigrationEntry> FindEntries(IEnumerable<string> migrationNames);
/// <summary>
/// Finds migrations by version and list of product names
/// </summary>
/// <param name="version"></param>
/// <param name="migrationNames"></param>
/// <returns></returns>
IEnumerable<IMigrationEntry> FindEntries(SemVersion version, IEnumerable<string> migrationNames);
/// <summary>
/// Gets all entries for a given migration name
/// </summary>
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Services
{
public interface IRedirectUrlService : IService
{
void Register(string url, int contentId);
void DeleteContentRedirectUrls(int contentId);
void Delete(IRedirectUrl redirectUrl);
void Delete(int id);
void DeleteAll();
IRedirectUrl GetMostRecentRedirectUrl(string url);
IEnumerable<IRedirectUrl> GetContentRedirectUrls(int contentId);
IEnumerable<IRedirectUrl> GetAllRedirectUrls(long pageIndex, int pageSize, out long total);
IEnumerable<IRedirectUrl> GetAllRedirectUrls(int rootContentId, long pageIndex, int pageSize, out long total);
}
}
@@ -60,33 +60,6 @@ namespace Umbraco.Core.Services
}
}
public IEnumerable<IMigrationEntry> FindEntries(string migrationName)
{
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateMigrationEntryRepository(uow))
{
return repo.FindEntries(migrationName);
}
}
public IEnumerable<IMigrationEntry> FindEntries(IEnumerable<string> migrationNames)
{
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateMigrationEntryRepository(uow))
{
return repo.FindEntries(migrationNames);
}
}
public IEnumerable<IMigrationEntry> FindEntries(SemVersion version, IEnumerable<string> migrationNames)
{
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateMigrationEntryRepository(uow))
{
return repo.FindEntries(version, migrationNames);
}
}
/// <summary>
/// Gets all entries for a given migration name
/// </summary>
@@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
{
internal class RedirectUrlService : RepositoryService, IRedirectUrlService
{
public RedirectUrlService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory)
: base(provider, repositoryFactory, logger, eventMessagesFactory)
{ }
public void Register(string url, int contentId)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var repo = RepositoryFactory.CreateRedirectUrlRepository(uow))
{
var redir = repo.Get(url, contentId);
if (redir != null)
redir.CreateDateUtc = DateTime.UtcNow;
else
redir = new RedirectUrl { Url = url, ContentId = contentId };
repo.AddOrUpdate(redir);
uow.Commit();
}
}
public void Delete(IRedirectUrl redirectUrl)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var repo = RepositoryFactory.CreateRedirectUrlRepository(uow))
{
repo.Delete(redirectUrl);
uow.Commit();
}
}
public void Delete(int id)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var repo = RepositoryFactory.CreateRedirectUrlRepository(uow))
{
repo.Delete(id);
uow.Commit();
}
}
public void DeleteContentRedirectUrls(int contentId)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var repo = RepositoryFactory.CreateRedirectUrlRepository(uow))
{
repo.DeleteContentUrls(contentId);
uow.Commit();
}
}
public void DeleteAll()
{
using (var uow = UowProvider.GetUnitOfWork())
using (var repo = RepositoryFactory.CreateRedirectUrlRepository(uow))
{
repo.DeleteAll();
uow.Commit();
}
}
public IRedirectUrl GetMostRecentRedirectUrl(string url)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var repo = RepositoryFactory.CreateRedirectUrlRepository(uow))
{
var rule = repo.GetMostRecentUrl(url);
uow.Commit();
return rule;
}
}
public IEnumerable<IRedirectUrl> GetContentRedirectUrls(int contentId)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var repo = RepositoryFactory.CreateRedirectUrlRepository(uow))
{
var rules = repo.GetContentUrls(contentId);
uow.Commit();
return rules;
}
}
public IEnumerable<IRedirectUrl> GetAllRedirectUrls(long pageIndex, int pageSize, out long total)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var repo = RepositoryFactory.CreateRedirectUrlRepository(uow))
{
var rules = repo.GetAllUrls(pageIndex, pageSize, out total);
uow.Commit();
return rules;
}
}
public IEnumerable<IRedirectUrl> GetAllRedirectUrls(int rootContentId, long pageIndex, int pageSize, out long total)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var repo = RepositoryFactory.CreateRedirectUrlRepository(uow))
{
var rules = repo.GetAllUrls(rootContentId, pageIndex, pageSize, out total);
uow.Commit();
return rules;
}
}
}
}
+15 -3
View File
@@ -1,5 +1,4 @@
using System;
using log4net;
using Umbraco.Core.Logging;
using System.IO;
using System.Linq;
@@ -7,7 +6,6 @@ using Umbraco.Core.IO;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Publishing;
using umbraco.interfaces;
using Umbraco.Core.Events;
namespace Umbraco.Core.Services
@@ -64,6 +62,7 @@ namespace Umbraco.Core.Services
private Lazy<IMemberGroupService> _memberGroupService;
private Lazy<INotificationService> _notificationService;
private Lazy<IExternalLoginService> _externalLoginService;
private Lazy<IRedirectUrlService> _redirectUrlService;
/// <summary>
/// public ctor - will generally just be used for unit testing all items are optional and if not specified, the defaults will be used
@@ -93,6 +92,7 @@ namespace Umbraco.Core.Services
/// <param name="publicAccessService"></param>
/// <param name="externalLoginService"></param>
/// <param name="migrationEntryService"></param>
/// <param name="redirectUrlService"></param>
public ServiceContext(
IContentService contentService = null,
IMediaService mediaService = null,
@@ -118,7 +118,8 @@ namespace Umbraco.Core.Services
IMacroService macroService = null,
IPublicAccessService publicAccessService = null,
IExternalLoginService externalLoginService = null,
IMigrationEntryService migrationEntryService = null)
IMigrationEntryService migrationEntryService = null,
IRedirectUrlService redirectUrlService = null)
{
if (migrationEntryService != null) _migrationEntryService = new Lazy<IMigrationEntryService>(() => migrationEntryService);
if (externalLoginService != null) _externalLoginService = new Lazy<IExternalLoginService>(() => externalLoginService);
@@ -145,6 +146,7 @@ namespace Umbraco.Core.Services
if (taskService != null) _taskService = new Lazy<ITaskService>(() => taskService);
if (macroService != null) _macroService = new Lazy<IMacroService>(() => macroService);
if (publicAccessService != null) _publicAccessService = new Lazy<IPublicAccessService>(() => publicAccessService);
if (redirectUrlService != null) _redirectUrlService = new Lazy<IRedirectUrlService>(() => redirectUrlService);
}
/// <summary>
@@ -310,6 +312,8 @@ namespace Umbraco.Core.Services
if (_memberGroupService == null)
_memberGroupService = new Lazy<IMemberGroupService>(() => new MemberGroupService(provider, repositoryFactory, logger, eventMessagesFactory));
if (_redirectUrlService == null)
_redirectUrlService = new Lazy<IRedirectUrlService>(() => new RedirectUrlService(provider, repositoryFactory, logger, eventMessagesFactory));
}
/// <summary>
@@ -516,5 +520,13 @@ namespace Umbraco.Core.Services
{
get { return _externalLoginService.Value; }
}
/// <summary>
/// Gets the RedirectUrlService.
/// </summary>
public IRedirectUrlService RedirectUrlService
{
get { return _redirectUrlService.Value; }
}
}
}
+9 -1
View File
@@ -373,6 +373,7 @@
<Compile Include="Models\Identity\IIdentityUserLogin.cs" />
<Compile Include="Models\IMigrationEntry.cs" />
<Compile Include="Models\IPublishedContentWithKey.cs" />
<Compile Include="Models\IRedirectUrl.cs" />
<Compile Include="Models\IServerRegistration.cs" />
<Compile Include="Models\Mapping\MappingExpressionExtensions.cs" />
<Compile Include="Models\MigrationEntry.cs" />
@@ -396,6 +397,7 @@
<Compile Include="Media\Exif\ExifTag.cs" />
<Compile Include="Media\Exif\ExifTagFactory.cs" />
<Compile Include="Models\Rdbms\AccessRuleDto.cs" />
<Compile Include="Models\Rdbms\RedirectUrlDto.cs" />
<Compile Include="Models\Rdbms\DocumentPublishedReadOnlyDto.cs" />
<Compile Include="Models\Rdbms\ExternalLoginDto.cs" />
<Compile Include="Models\Rdbms\MigrationDto.cs" />
@@ -404,7 +406,6 @@
<Compile Include="Models\UmbracoDomain.cs" />
<Compile Include="Models\DoNotCloneAttribute.cs" />
<Compile Include="Models\IDomain.cs" />
<Compile Include="Packaging\PackageMigrationsContext.cs" />
<Compile Include="Persistence\DatabaseNodeLockExtensions.cs" />
<Compile Include="Persistence\Factories\ExternalLoginFactory.cs" />
<Compile Include="Persistence\Factories\MigrationEntryFactory.cs" />
@@ -418,6 +419,7 @@
<Compile Include="Persistence\Mappers\MigrationEntryMapper.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionFourOneZero\AddPreviewXmlTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveZero\EnsureServersLockObject.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveZero\AddRedirectUrlTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\FixListViewMediaSortOrder.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\AddDataDecimalColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\AddUmbracoDeployTables.cs" />
@@ -470,12 +472,14 @@
<Compile Include="Persistence\Repositories\Interfaces\IExternalLoginRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IMigrationEntryRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\IPublicAccessRepository.cs" />
<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\ITaskRepository.cs" />
<Compile Include="Persistence\Repositories\Interfaces\ITaskTypeRepository.cs" />
<Compile Include="Persistence\Repositories\MigrationEntryRepository.cs" />
<Compile Include="Persistence\Repositories\PublicAccessRepository.cs" />
<Compile Include="Persistence\Repositories\RedirectUrlRepository.cs" />
<Compile Include="Persistence\Repositories\TaskRepository.cs" />
<Compile Include="Persistence\Repositories\TaskTypeRepository.cs" />
<Compile Include="PropertyEditors\DecimalValidator.cs" />
@@ -495,6 +499,7 @@
<Compile Include="Security\IBackOfficeUserPasswordChecker.cs" />
<Compile Include="Security\MembershipPasswordHasher.cs" />
<Compile Include="Security\EmailService.cs" />
<Compile Include="SemVersionExtensions.cs" />
<Compile Include="Serialization\NoTypeConverterJsonConverter.cs" />
<Compile Include="ServiceProviderExtensions.cs" />
<Compile Include="IO\ResizedImage.cs" />
@@ -508,9 +513,12 @@
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\MigrateStylesheetDataToFile.cs" />
<Compile Include="Services\AuditService.cs" />
<Compile Include="Services\ContentTypeServiceExtensions.cs" />
<Compile Include="Models\RedirectUrl.cs" />
<Compile Include="Services\RedirectUrlService.cs" />
<Compile Include="Services\DomainService.cs" />
<Compile Include="Services\ExternalLoginService.cs" />
<Compile Include="Services\IAuditService.cs" />
<Compile Include="Services\IRedirectUrlService.cs" />
<Compile Include="Services\IDomainService.cs" />
<Compile Include="Services\IExternalLoginService.cs" />
<Compile Include="Services\IMigrationEntryService.cs" />
+3 -19
View File
@@ -5,25 +5,9 @@ using Semver;
namespace Umbraco.Core
{
public static class VersionExtensions
internal static class VersionExtensions
{
public static string ToSemanticString(this SemVersion semVersion)
{
return semVersion.ToString().Replace("--", "-").Replace("-+", "+");
}
internal static SemVersion GetSemanticVersion(this Version version)
{
return new SemVersion(
version.Major,
version.Minor,
version.Build,
null,
version.Revision > 0 ? version.Revision.ToInvariantString() : null);
}
internal static Version GetVersion(this SemVersion semVersion, int maxParts = 4)
public static Version GetVersion(this SemVersion semVersion, int maxParts = 4)
{
int build = 0;
int.TryParse(semVersion.Build, out build);
@@ -40,7 +24,7 @@ namespace Umbraco.Core
return new Version(semVersion.Major, semVersion.Minor);
}
internal static Version SubtractRevision(this Version version)
public static Version SubtractRevision(this Version version)
{
var parts = new List<int>(new[] {version.Major, version.Minor, version.Build, version.Revision});
@@ -26,7 +26,7 @@ namespace Umbraco.Tests.Migrations
() => new List<Type>
{
typeof (AlterUserTableMigrationStub),
typeof (Dummy),
typeof(Dummy),
typeof (SixZeroMigration1),
typeof (SixZeroMigration2),
typeof (FourElevenMigration),
@@ -51,6 +51,7 @@ namespace Umbraco.Tests.Migrations
};
Resolution.Freeze();
}
[Test]
@@ -1,134 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using NUnit.Framework;
using Semver;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Profiling;
using Umbraco.Core.Services;
using Umbraco.Tests.Migrations.Stubs;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web.Install.MigrationSteps;
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
namespace Umbraco.Tests.Migrations
{
[DatabaseTestBehavior(DatabaseBehavior.EmptyDbFilePerTest)]
[TestFixture]
public class PackageMigrationsTests : BaseDatabaseFactoryTest
{
private DatabaseContext _databaseContext;
private ILogger _logger;
private IMigrationEntryService _migrationEntryService;
/// <summary>
/// sets up resolvers before resolution is frozen
/// </summary>
protected override void FreezeResolution()
{
MigrationResolver.Current = new MigrationResolver(
Mock.Of<ILogger>(),
() => new List<Type>
{
typeof (PackageMigration1),
typeof (PackageMigration2)
});
//This is needed because the Migration resolver is creating the migration instances with their full ctors
_logger = Mock.Of<ILogger>();
LoggerResolver.Current = new LoggerResolver(_logger)
{
CanResolveBeforeFrozen = true
};
base.FreezeResolution();
}
protected override ApplicationContext CreateApplicationContext()
{
var sqlSyntax = new SqlCeSyntaxProvider();
var db = GetConfiguredDatabase();
var databaseContext = new Mock<DatabaseContext>(Mock.Of<IDatabaseFactory>(), Mock.Of<ILogger>(), sqlSyntax, "test");
databaseContext.SetupGet(x => x.CanConnect).Returns(true);
databaseContext.SetupGet(x => x.IsDatabaseConfigured).Returns(true);
databaseContext.Setup(x => x.Database).Returns(db);
_databaseContext = databaseContext.Object;
var umbracoMigrationEntry = new MigrationEntry(1, DateTime.UtcNow, GlobalSettings.UmbracoMigrationName,
UmbracoVersion.GetSemanticVersion());
var migrationEntryService = new Mock<IMigrationEntryService>();
migrationEntryService.Setup(x => x.FindEntry(It.IsAny<string>(), It.IsAny<SemVersion>()))
.Returns(umbracoMigrationEntry);
migrationEntryService.Setup(x => x.FindEntries(It.IsAny<string>())).Returns(Enumerable.Empty<IMigrationEntry>());
migrationEntryService.Setup(x => x.FindEntries(It.IsAny<string[]>())).Returns(Enumerable.Empty<IMigrationEntry>());
_migrationEntryService = migrationEntryService.Object;
//This is needed because the Migration resolver is creating migration instances with their full ctors
var applicationContext = new ApplicationContext(
//assign the db context
_databaseContext,
//assign the service context
new ServiceContext(migrationEntryService: _migrationEntryService),
CacheHelper.CreateDisabledCacheHelper(),
ProfilingLogger)
{
IsReady = true
};
return applicationContext;
}
[Test]
public void PackageMigrationsContext_HasPendingPackageMigrations()
{
var packageMigrationsContext = new PackageMigrationsContext(_databaseContext,
_migrationEntryService, _logger);
var hasPackageMigrations = packageMigrationsContext.HasPendingPackageMigrations;
Assert.True(hasPackageMigrations);
var result = packageMigrationsContext.GetPendingPackageMigrations();
Assert.AreEqual(1, result.Count);
Assert.True(result.ContainsKey("com.company.package"));
Assert.AreEqual(new SemVersion(2, 0, 0), result.First().Value);
var names = packageMigrationsContext.GetPendingPackageMigrationFriendlyNames();
var friendlyName = names.First();
Assert.AreEqual("com.company.package (2.0.0)", friendlyName);
}
[Test]
public void ExecutionMigrationStep_Can_Execute()
{
var packageMigrationsContext = new PackageMigrationsContext(_databaseContext,
_migrationEntryService, _logger);
var migrationStep = new ExecutionMigrationStep(_migrationEntryService, packageMigrationsContext,
_databaseContext.Database, _databaseContext.DatabaseProvider, _logger);
//Note: model isn't used here, so we just pass in an empty string
var executed = migrationStep.Execute(string.Empty);
Assert.Null(executed);
}
public UmbracoDatabase GetConfiguredDatabase()
{
return new UmbracoDatabase(GetDbConnectionString(), GetDbProviderName(), Mock.Of<ILogger>());
}
[TearDown]
public override void TearDown()
{
base.TearDown();
LoggerResolver.Reset();
MigrationResolver.Reset();
}
}
}
@@ -1,29 +0,0 @@
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Tests.Migrations.Stubs
{
[Migration("1.0.0", 0, "com.company.package")]
public class PackageMigration1 : MigrationBase
{
private const string TableName = "RandomTableTest";
public PackageMigration1(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
{
}
public override void Up()
{
Create.Table(TableName)
.WithColumn("Id").AsGuid().Unique().NotNullable().PrimaryKey("awesome_id")
.WithColumn("Content").AsString(500).NotNullable()
.WithColumn("TimeStamp").AsDateTime().NotNullable();
}
public override void Down()
{
Delete.Table(TableName);
}
}
}
@@ -1,27 +0,0 @@
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Tests.Migrations.Stubs
{
[Migration("2.0.0", 0, "com.company.package")]
public class PackageMigration2 : MigrationBase
{
private const string TableName = "RandomTableTest";
private const string ColumnName = "MoreContent";
public PackageMigration2(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
{
}
public override void Up()
{
Alter.Table(TableName).AddColumn(ColumnName).AsString(200).Nullable();
}
public override void Down()
{
Delete.Column(ColumnName).FromTable(TableName);
}
}
}
@@ -1,14 +1,19 @@
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.Linq;
using System.Text.RegularExpressions;
using Moq;
using NUnit.Framework;
using Semver;
using SQLCE4Umbraco;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings;
@@ -0,0 +1,216 @@
using System;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Persistence.Repositories
{
[DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)]
[TestFixture]
public class RedirectUrlRepositoryTests : BaseDatabaseFactoryTest
{
[SetUp]
public override void Initialize()
{
base.Initialize();
CreateTestData();
}
[TearDown]
public override void TearDown()
{
base.TearDown();
}
[Test]
public void CanSaveAndGet()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
using (var uow = provider.GetUnitOfWork())
using (var repo = CreateRepository(uow))
{
var rurl = new RedirectUrl
{
ContentId = _textpage.Id,
Url = "blah"
};
repo.AddOrUpdate(rurl);
uow.Commit();
Assert.AreNotEqual(0, rurl.Id);
}
using (var uow = provider.GetUnitOfWork())
using (var repo = CreateRepository(uow))
{
var rurl = repo.GetMostRecentUrl("blah");
uow.Commit();
Assert.IsNotNull(rurl);
Assert.AreEqual(_textpage.Id, rurl.ContentId);
}
}
[Test]
public void CanSaveAndGetMostRecent()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
using (var uow = provider.GetUnitOfWork())
using (var repo = CreateRepository(uow))
{
var rurl = new RedirectUrl
{
ContentId = _textpage.Id,
Url = "blah"
};
repo.AddOrUpdate(rurl);
uow.Commit();
Assert.AreNotEqual(0, rurl.Id);
rurl = new RedirectUrl
{
ContentId = _otherpage.Id,
Url = "blah"
};
repo.AddOrUpdate(rurl);
uow.Commit();
Assert.AreNotEqual(0, rurl.Id);
}
using (var uow = provider.GetUnitOfWork())
using (var repo = CreateRepository(uow))
{
var rurl = repo.GetMostRecentUrl("blah");
uow.Commit();
Assert.IsNotNull(rurl);
Assert.AreEqual(_otherpage.Id, rurl.ContentId);
}
}
[Test]
public void CanSaveAndGetByContent()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
using (var uow = provider.GetUnitOfWork())
using (var repo = CreateRepository(uow))
{
var rurl = new RedirectUrl
{
ContentId = _textpage.Id,
Url = "blah"
};
repo.AddOrUpdate(rurl);
uow.Commit();
Assert.AreNotEqual(0, rurl.Id);
rurl = new RedirectUrl
{
ContentId = _textpage.Id,
Url = "durg"
};
repo.AddOrUpdate(rurl);
uow.Commit();
Assert.AreNotEqual(0, rurl.Id);
}
using (var uow = provider.GetUnitOfWork())
using (var repo = CreateRepository(uow))
{
var rurls = repo.GetContentUrls(_textpage.Id).ToArray();
uow.Commit();
Assert.AreEqual(2, rurls.Length);
Assert.AreEqual("durg", rurls[0].Url);
Assert.AreEqual("blah", rurls[1].Url);
}
}
[Test]
public void CanSaveAndDelete()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
using (var uow = provider.GetUnitOfWork())
using (var repo = CreateRepository(uow))
{
var rurl = new RedirectUrl
{
ContentId = _textpage.Id,
Url = "blah"
};
repo.AddOrUpdate(rurl);
uow.Commit();
Assert.AreNotEqual(0, rurl.Id);
rurl = new RedirectUrl
{
ContentId = _otherpage.Id,
Url = "durg"
};
repo.AddOrUpdate(rurl);
uow.Commit();
Assert.AreNotEqual(0, rurl.Id);
}
using (var uow = provider.GetUnitOfWork())
using (var repo = CreateRepository(uow))
{
repo.DeleteContentUrls(_textpage.Id);
uow.Commit();
var rurls = repo.GetContentUrls(_textpage.Id);
Assert.AreEqual(0, rurls.Count());
}
}
private IRedirectUrlRepository CreateRepository(IDatabaseUnitOfWork uow)
{
return new RedirectUrlRepository(uow, CacheHelper.CreateDisabledCacheHelper(), Logger, SqlSyntax);
}
private IContent _textpage, _subpage, _otherpage, _trashed;
public void CreateTestData()
{
//Create and Save ContentType "umbTextpage" -> (NodeDto.NodeIdSeed)
var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage");
contentType.Key = new Guid("1D3A8E6E-2EA9-4CC1-B229-1AEE19821522");
ServiceContext.ContentTypeService.Save(contentType);
//Create and Save Content "Homepage" based on "umbTextpage" -> (NodeDto.NodeIdSeed + 1)
_textpage = MockedContent.CreateSimpleContent(contentType);
_textpage.Key = new Guid("B58B3AD4-62C2-4E27-B1BE-837BD7C533E0");
ServiceContext.ContentService.Save(_textpage, 0);
//Create and Save Content "Text Page 1" based on "umbTextpage" -> (NodeDto.NodeIdSeed + 2)
_subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", _textpage.Id);
_subpage.Key = new Guid("FF11402B-7E53-4654-81A7-462AC2108059");
ServiceContext.ContentService.Save(_subpage, 0);
//Create and Save Content "Text Page 1" based on "umbTextpage" -> (NodeDto.NodeIdSeed + 3)
_otherpage = MockedContent.CreateSimpleContent(contentType, "Text Page 2", _textpage.Id);
ServiceContext.ContentService.Save(_otherpage, 0);
//Create and Save Content "Text Page Deleted" based on "umbTextpage" -> (NodeDto.NodeIdSeed + 4)
_trashed = MockedContent.CreateSimpleContent(contentType, "Text Page Deleted", -20);
((Content) _trashed).Trashed = true;
ServiceContext.ContentService.Save(_trashed, 0);
}
}
}
@@ -283,7 +283,7 @@ namespace Umbraco.Tests.Plugins
{
var trees = _manager.ResolveAttributedTrees();
// commit 6c5e35ec2cbfa31be6790d1228e0c2faf5f55bc8 brings the count down to 14
Assert.AreEqual(14, trees.Count());
Assert.AreEqual(12, trees.Count());
}
[Test]
@@ -193,7 +193,9 @@ namespace Umbraco.Tests.TestHelpers
engine.CreateDatabase();
}
}
}
}
/// <summary>
+3 -5
View File
@@ -59,8 +59,8 @@
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Examine, Version=0.1.68.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.68.0\lib\Examine.dll</HintPath>
<Reference Include="Examine, Version=0.1.69.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.69.0-beta\lib\Examine.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
@@ -176,10 +176,8 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Migrations\MigrationIssuesTests.cs" />
<Compile Include="Migrations\PackageMigrationsTests.cs" />
<Compile Include="Migrations\Stubs\PackageMigration1.cs" />
<Compile Include="Migrations\Stubs\PackageMigration2.cs" />
<Compile Include="Persistence\Migrations\MigrationStartupHandlerTests.cs" />
<Compile Include="Persistence\Repositories\RedirectUrlRepositoryTests.cs" />
<Compile Include="Web\AngularIntegration\AngularAntiForgeryTests.cs" />
<Compile Include="Web\AngularIntegration\ContentModelSerializationTests.cs" />
<Compile Include="Web\AngularIntegration\JsInitializationTests.cs" />
@@ -94,6 +94,7 @@ namespace Umbraco.Tests.UmbracoExamine
}
[Test]
[Ignore]
public void Index_Move_Media_To_Non_Indexable_ParentID()
{
//get a node from the data repo (this one exists underneath 2222)
@@ -177,6 +178,7 @@ namespace Umbraco.Tests.UmbracoExamine
/// This will delete an item from the index and ensure that all children of the node are deleted too!
/// </summary>
[Test]
[Ignore]
public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed()
{
@@ -1,6 +1,5 @@
using System;
using NUnit.Framework;
using Semver;
using Umbraco.Core;
namespace Umbraco.Tests
@@ -27,6 +26,5 @@ namespace Umbraco.Tests
Assert.AreEqual(new Version(outcome), result);
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
<packages>
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="Examine" version="0.1.68.0" targetFramework="net45" />
<package id="Examine" version="0.1.69.0-beta" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

After

Width:  |  Height:  |  Size: 401 KiB

@@ -0,0 +1,76 @@
/**
* @ngdoc service
* @name umbraco.resources.healthCheckResource
* @function
*
* @description
* Used by the health check dashboard to get checks and send requests to fix checks.
*/
(function () {
'use strict';
function healthCheckResource($http, umbRequestHelper) {
/**
* @ngdoc function
* @name umbraco.services.healthCheckService#getAllChecks
* @methodOf umbraco.services.healthCheckService
* @function
*
* @description
* Called to get all available health checks
*/
function getAllChecks() {
return umbRequestHelper.resourcePromise(
$http.get(Umbraco.Sys.ServerVariables.umbracoUrls.healthCheckBaseUrl + "GetAllHealthChecks"),
"Failed to retrieve health checks"
);
}
/**
* @ngdoc function
* @name umbraco.services.healthCheckService#getStatus
* @methodOf umbraco.services.healthCheckService
* @function
*
* @description
* Called to get execute a health check and return the check status
*/
function getStatus(id) {
return umbRequestHelper.resourcePromise(
$http.get(Umbraco.Sys.ServerVariables.umbracoUrls.healthCheckBaseUrl + 'GetStatus?id=' + id),
'Failed to retrieve status for health check with ID ' + id
);
}
/**
* @ngdoc function
* @name umbraco.services.healthCheckService#executeAction
* @methodOf umbraco.services.healthCheckService
* @function
*
* @description
* Called to execute a health check action (rectifying an issue)
*/
function executeAction(action) {
return umbRequestHelper.resourcePromise(
$http.post(Umbraco.Sys.ServerVariables.umbracoUrls.healthCheckBaseUrl + 'ExecuteAction', action),
'Failed to execute action with alias ' + action.alias + ' and healthCheckId + ' + action.healthCheckId
);
}
var resource = {
getAllChecks: getAllChecks,
getStatus: getStatus,
executeAction: executeAction
};
return resource;
}
angular.module('umbraco.resources').factory('healthCheckResource', healthCheckResource);
})();
@@ -0,0 +1,64 @@
/**
* @ngdoc service
* @name umbraco.resources.ourPackageRepositoryResource
* @description handles data for package installations
**/
function ourPackageRepositoryResource($q, $http, umbDataFormatter, umbRequestHelper) {
var baseurl = "https://our.umbraco.org/webapi/packages/v1";
return {
getDetails: function (packageId) {
return umbRequestHelper.resourcePromise(
$http.get(baseurl + "/" + packageId),
'Failed to get package details');
},
getCategories: function () {
return umbRequestHelper.resourcePromise(
$http.get(baseurl),
'Failed to query packages');
},
getPopular: function (maxResults, category) {
if (maxResults === undefined) {
maxResults = 10;
}
if (category === undefined) {
category = "";
}
return umbRequestHelper.resourcePromise(
$http.get(baseurl + "?pageIndex=0&pageSize=" + maxResults + "&category=" + category + "&order=Popular"),
'Failed to query packages');
},
search: function (pageIndex, pageSize, category, query, canceler) {
var httpConfig = {};
if (canceler) {
httpConfig["timeout"] = canceler;
}
if (category === undefined) {
category = "";
}
if (query === undefined) {
query = "";
}
return umbRequestHelper.resourcePromise(
$http.get(baseurl + "?pageIndex=" + pageIndex + "&pageSize=" + pageSize + "&category=" + category + "&query=" + query),
httpConfig,
'Failed to query packages');
}
};
}
angular.module('umbraco.resources').factory('ourPackageRepositoryResource', ourPackageRepositoryResource);
@@ -7,6 +7,31 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) {
return {
/**
* @ngdoc method
* @name umbraco.resources.packageInstallResource#getInstalled
* @methodOf umbraco.resources.packageInstallResource
*
* @description
* Gets a list of installed packages
*/
getInstalled: function() {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"packageInstallApiBaseUrl",
"GetInstalled")),
'Failed to get installed packages');
},
uninstall: function(packageId) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"packageInstallApiBaseUrl",
"Uninstall", { packageId: packageId })),
'Failed to uninstall package');
},
/**
* @ngdoc method
@@ -1,4 +1,62 @@
/*Contains multiple services for various helper tasks */
function versionHelper() {
return {
//see: https://gist.github.com/TheDistantSea/8021359
versionCompare: function(v1, v2, options) {
var lexicographical = options && options.lexicographical,
zeroExtend = options && options.zeroExtend,
v1parts = v1.split('.'),
v2parts = v2.split('.');
function isValidPart(x) {
return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x);
}
if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
return NaN;
}
if (zeroExtend) {
while (v1parts.length < v2parts.length) {
v1parts.push("0");
}
while (v2parts.length < v1parts.length) {
v2parts.push("0");
}
}
if (!lexicographical) {
v1parts = v1parts.map(Number);
v2parts = v2parts.map(Number);
}
for (var i = 0; i < v1parts.length; ++i) {
if (v2parts.length === i) {
return 1;
}
if (v1parts[i] === v2parts[i]) {
continue;
}
else if (v1parts[i] > v2parts[i]) {
return 1;
}
else {
return -1;
}
}
if (v1parts.length !== v2parts.length) {
return -1;
}
return 0;
}
};
}
angular.module('umbraco.services').factory('versionHelper', versionHelper);
function packageHelper(assetsService, treeService, eventsService, $templateCache) {
@@ -12,8 +12,7 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
var _installerModel = {
installId: undefined,
instructions: {
},
installType: Umbraco.Sys.ServerVariables.installType
}
};
//add to umbraco installer facts here
@@ -108,7 +107,7 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
init : function(){
service.status.loading = true;
if(!_status.all){
service.getSteps(Umbraco.Sys.ServerVariables.installType).then(function (response) {
service.getSteps().then(function(response){
service.status.steps = response.data.steps;
service.status.index = 0;
_installerModel.installId = response.data.installId;
@@ -127,8 +126,8 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetPackages");
},
getSteps : function(installType){
return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetSetup?installType=" + installType);
getSteps : function(){
return $http.get(Umbraco.Sys.ServerVariables.installApiBaseUrl + "GetSetup");
},
gotoStep : function(index){
@@ -1,12 +0,0 @@
<div>
<h1>Package migration failed</h1>
<p>
The package migration failed for product <strong>{{installer.current.model}}</strong>.
Package migrations normally only fail due to a 3rd party library that has cancelled the migration event.
</p>
<p>
See the log for full details
</p>
</div>
@@ -1,17 +0,0 @@
<div>
<h1>Package migrations installation</h1>
<p>
The following packages have migrations that need to be executed:
</p>
<ul>
<li ng-repeat="item in installer.current.model">
{{item}}
</li>
</ul>
<p>
<button class="btn btn-success" ng-click="forward()">Continue</button>
</p>
</div>
@@ -108,6 +108,8 @@
@import "components/umb-empty-state.less";
@import "components/umb-property-editor.less";
@import "components/umb-iconpicker.less";
@import "components/umb-packages.less";
@import "components/umb-package-local-install.less";
@import "components/buttons/umb-button.less";
@import "components/buttons/umb-button-group.less";
@@ -126,3 +128,5 @@
@import "typeahead.less";
@import "hacks.less";
@import "healthcheck.less";
@@ -13,8 +13,8 @@
box-shadow: 0 5px 0 rgba(0, 0, 0, 0.08), 0 1px 0 rgba(0, 0, 0, 0.16);
transition: box-shadow 1s;
top: 101px; /* height of header: 100px + its bottom-border: 1px */
transform: translate(0, 50%);
margin-top: 0;
margin-bottom: 0;
}
.umb-group-builder__property-preview .umb-editor-sub-header {
@@ -7,6 +7,8 @@
left: 50%;
transform: translate(-50%, -50%);
font-size: 0;
margin-left: -6px; // hack to center it
margin-top: -6px; // hack to center it
}
.umb-load-indicator__bubble {
@@ -0,0 +1,153 @@
//
// Install local package
//
// Helpers
.faded {
color: @grayMed;
}
.inline-flex {
display: inline-flex;
}
.mt-3 {
margin-top: 30px;
}
// Upload state
.umb-upload-local,
.umb-info-local {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.umb-upload-local form,
.umb-info-local-items {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 0;
max-width: 640px;
}
.umb-upload-local__dropzone {
position: relative;
width: 500px;
height: 300px;
border: 2px dashed @grayLight;
border-radius: 3px;
background: @grayLighter;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: 30px;
transition: 100ms box-shadow ease, 100ms border ease;
&:hover,
&.drag-over {
border-color: @blue;
border-style: solid;
box-shadow: 0 3px 8px rgba(0,0,0, .1);
transition: 100ms box-shadow ease, 100ms border ease;
}
}
.umb-upload-local__dropzone i {
display: block;
color: @grayLight;
font-size: 110px;
line-height: 1;
}
.umb-upload-local__select-file {
font-weight: bold;
color: @blue;
cursor: pointer;
}
// Accept terms
.umb-accept-terms {
display: flex;
align-items: center;
font-size: 13px;
}
.umb-package-installer-label .label-text {
margin-left: 5px;
}
.umb-package-installer-label input[type="radio"],
.umb-package-installer-label input[type="checkbox"] {
margin-top: 0px;
}
.umb-package-installer-label {
display: inline-flex;
align-items: center;
font-size: 13px;
user-select: none;
}
// Info state
.umb-info-local-items {
border: 2px solid @grayLight;
border-radius: 3px;
background: @grayLighter;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 0 20px;
max-width: 540px;
}
.umb-info-local-items a {
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
.umb-info-local-item {
margin-bottom: 20px;
}
.umb-info-local-items .umb-package-icon {
width: 100%;
box-sizing: border-box;
min-height: 150px;
font-size: 60px;
}
.umb-info-local-items .umb-package-icon img {
max-width: 100px;
}
.umb-info-local-items .umb-package-info {
width: 100%;
box-sizing: border-box;
padding: 20px 40px;
}
.umb-info-local-items .umb-package-installer-label {
margin-left: 10px;
}
@@ -0,0 +1,520 @@
.umb-packages-view-title {
font-size: 20px;
font-weight: bold;
color: @black;
margin-bottom: 30px;
}
.umb-packages-view-wrapper {
padding: 20px 60px;
}
.umb-packages-section {
margin-bottom: 40px;
}
.umb-packages-search {
width: 100%;
background: @grayLighter;
border-radius: 3px;
padding: 30px;
box-sizing: border-box;
}
.umb-packages-search input {
border-width: 2px;
border-radius: 3px;
min-height: 44px;
padding: 4px 10px;
font-size: 16px;
border-color: #ececec;
margin-bottom: 0;
border-color: @grayLight;
&:hover, &:focus {
border-color: @grayLight;
}
}
.umb-packages__pagination {
display: flex;
justify-content: center;
}
.umb-packages {
margin: 0 -10px;
display: flex;
flex-wrap: wrap;
}
// Cards
.umb-package {
padding: 10px;
box-sizing: border-box;
width: 25%;
}
.umb-package-link {
display: flex;
flex-wrap: wrap;
flex-direction: column;
justify-content: center;
position: relative;
box-sizing: border-box;
height: 100%;
width: 100%;
border: 1px solid #ececec;
border-radius: 3px;
text-decoration: none !important;
transition: border-color 100ms ease;
&:hover {
border-color: @blue;
}
}
// Icon
.umb-package-icon {
display: flex;
justify-content: center;
align-items: center;
padding-top: 10px;
padding-right: 10px;
padding-left: 10px;
padding-bottom: 10px;
text-align: center;
background-color: white;
min-height: 60px;
}
.umb-package-icon img {
max-width: 70px;
height: auto;
}
// Info
.umb-package-info {
padding-right: 15px;
padding-bottom: 15px;
padding-left: 15px;
padding-top: 15px;
text-align: center;
background: @grayLighter;
border-top: 1px solid #ececec;
}
// Name
.umb-package-name {
font-size: 14px;
max-width: 250px;
margin-bottom: 5px;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: normal;
margin-left: auto;
margin-right: auto;
}
.umb-package-description {
font-size: 11px;
color: @grayMed;
word-wrap: break-word;
line-height: 1.1rem;
}
// Numbers
.umb-package-numbers {
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: center;
opacity: .6;
margin-top: 10px;
}
.umb-package-numbers small {
padding: 0 5px;
display: flex;
align-items: center;
justify-content: center;
}
.umb-package-numbers i {
font-size: 14px;
}
.umb-package-link:hover .umb-package-numbers {
opacity: 1;
}
.umb-package-link:hover .umb-package-numbers .icon-hearts {
color: red !important;
}
// Version
.umb-package-version {
display: inline-flex;
font-size: 11px;
font-weight: bold;
padding: 1px 5px;
background: #ececec;
border-radius: 3px;
color: black;
}
/* umb-buttons-era */
.umb-era-button {
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
font-weight: bold;
height: 38px;
line-height: 1;
max-width: 100%;
padding: 0 18px;
color: #484848;
background-color: #e0e0e0;
text-decoration: none !important;
user-select: none;
white-space: nowrap;
overflow: hidden;
border-radius: 3px;
border: 0 none;
box-sizing: border-box;
cursor: pointer;
transition: background-color 80ms ease, color 80ms ease;
}
.umb-era-button:hover,
.umb-era-button:active {
color: #484848;
background-color: #d3d3d3;
outline: none;
text-decoration: none;
}
.umb-era-button:focus {
outline: none;
}
.umb-era-button.-blue {
background: @blue;
color: white;
}
.umb-era-button.-blue:hover {
background-color: @blueDark;
}
.umb-era-button.-link {
padding: 0;
background: transparent;
}
.umb-era-button.-link:hover {
background-color: transparent;
opacity: .6;
}
.umb-era-button.-inactive {
cursor: not-allowed;
color: #BBB;
background: #EAE7E7;
}
.umb-era-button.-inactive:hover {
color: #BBB;
background: #EAE7E7;
}
.umb-era-button.-full-width {
display: block;
width: 100%;
}
/* CATEGORIES */
.umb-packages-categories {
display: flex;
user-select: center;
flex-wrap: wrap;
}
.umb-packages-category {
display: flex;
align-items: center;
flex: 1 0 auto;
justify-content: center;
max-width: 25%;
font-size: 14px;
font-weight: bold;
color: @black;
box-sizing: border-box;
justify-content: center;
border-top: 1px solid @grayLight;
border-bottom: 1px solid @grayLight;
border-right: 1px solid @grayLight;
padding: 10px 0;
}
.umb-packages-category:hover,
.umb-packages-category.-active {
text-decoration: none;
color: @blue;
}
.umb-packages-category.-first {
border-left: 1px solid @grayLight;
border-radius: 3px 0 0 3px;
}
.umb-packages-category.-last {
border-right: 1px solid @grayLight;
border-radius: 0 3px 3px 0;
}
/* PACKAGE DETAILS */
.umb-package-details {
display: flex;
}
.umb-package-details__back-link {
font-weight: bold;
color: @grayMed;
}
.umb-package-details__back-link:hover {
color: @black;
text-decoration: none;
}
.umb-package-details__main-content {
flex: 1 1 auto;
margin-right: 40px;
}
.umb-package-details__sidebar {
flex: 0 0 350px;
}
.umb-package-details__section {
background: @grayLighter;
padding: 20px;
margin-bottom: 20px;
border-radius: 3px;
}
.umb-package-details__section-title {
font-size: 17px;
font-weight: bold;
color: black;
margin-top: 0;
margin-bottom: 15px;
}
.umb-package-details__section-description {
font-size: 12px;
line-height: 1.6em;
margin-bottom: 15px;
}
.umb-package-details__information-item {
display: flex;
margin-bottom: 5px;
font-size: 13px;
}
.umb-package-details__information-item-label {
color: black;
font-weight: bold;
margin-right: 3px;
}
.umb-package-details__information-item-label-2 {
font-size: 12px;
color: @grayMed;
}
.umb-package-details__compatability {
margin-bottom: 15px;
}
.umb-package-details__compatability-label {
margin-bottom: 3px;
}
.umb-package-details__description {
margin-bottom: 20px;
line-height: 1.6em;
}
.umb-package-details__description p {
margin-bottom: 20px;
}
/* Links */
.umb-package-details__link {
color: #DA3287;
}
.umb-package-details__link:hover {
color: #B32D71;
text-decoration: none;
}
/* Owner profile */
.umb-package-details__owner-profile {
display: flex;
align-items: center;
}
.umb-package-details__owner-profile-avatar {
margin-right: 15px;
}
.umb-package-details__owner-profile-name {
font-size: 15px;
color: #000000;
font-weight: bold;
}
.umb-package-details__owner-profile-karma {
font-size: 12px;
color: @grayMed;
}
/* gallery */
.umb-gallery__thumbnails {
display: flex;
flex-wrap: wrap;
}
.umb-gallery__thumbnail {
flex: 1 1 100px;
border: 1px solid @grayLight;
border-radius: 3px;
margin: 5px;
padding: 10px;
box-sizing: border-box;
}
.umb-gallery__thumbnail:hover {
cursor: pointer;
border-color: @blue;
}
/* Avatar */
.umb-avatar {
border-radius: 50%;
width: 50px;
}
/* Progress bar */
.umb-progress-bar {
background: @grayLight;
width: 100%;
display: block;
height: 10px;
border-radius: 10px;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.umb-progress-bar__progress {
background: #50C878;
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 100%;
border-radius: 10px;
}
/* PACKAGE LIST */
.umb-package-list {
display: flex;
flex-direction: column;
}
.umb-package-list__item {
display: flex;
flex-direction: row;
background: @grayLighter;
margin-bottom: 10px;
border-radius: 3px;
padding: 20px;
align-items: center;
}
.umb-package-list__item-icon {
flex: 0 0 50px;
margin-right: 20px;
font-size: 30px;
text-align: center;
}
.umb-package-list__item-content {
flex: 1 1 auto;
margin-right: 20px;
}
.umb-package-list__item-name {
font-size: 16px;
margin-bottom: 5px;
color: @black;
font-weight: bold;
}
.umb-package-list__item-description {
font-size: 14px;
color: @grayMed;
}
.umb-package-list__item-actions {
flex: 1 1 auto;
display: flex;
justify-content: flex-end;
}
@@ -0,0 +1,333 @@
/* Vars */
@red-orange: #FF3F34;
@sunrise: #F5D226;
@emerald: #50C878;
.umb-healthcheck {
display: flex;
flex-wrap: wrap;
}
/* Group and states */
.umb-healthcheck-group {
display: flex;
flex-wrap: wrap;
flex-direction: column;
background: @grayLighter;
border-radius: 3px;
padding: 15px 10px;
box-sizing: border-box;
text-align: center;
border: 2px solid transparent;
height: 100%;
}
.umb-healthcheck-group:hover {
border: 2px solid @blue;
cursor: pointer;
}
.umb-healthcheck-group__load-container {
position: relative;
height: 30px;
margin-top: 15px;
margin-bottom: 16px;
}
/* Title */
.umb-healthcheck-title {
margin-bottom: 15px;
font-size: 14px;
font-weight: bold;
}
/* Messages */
.umb-healthcheck-messages {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.umb-healthcheck-message {
position: relative;
background: #fff;
border-radius: 50px;
display: flex;
align-items: center;
flex: 1 1 auto;
padding: 5px 10px;
margin-bottom: 5px;
color: #000;
font-weight: bold;
}
.umb-healthcheck-message i {
font-size: 20px;
margin-right: 5px;
}
.umb-healthcheck-details-link {
color: @blue;
}
.umb-healthcheck-details-link:hover {
text-decoration: none;
color: @blue;
}
/* Helpers */
.align-self-center {
align-self: center;
}
/* umb-buttons-era */
.umb-era-button {
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
font-weight: bold;
height: 38px;
line-height: 1;
max-width: 100%;
padding: 0 18px;
color: #484848;
background-color: #e0e0e0;
text-decoration: none !important;
user-select: none;
white-space: nowrap;
overflow: hidden;
border-radius: 3px;
border: 0 none;
box-sizing: border-box;
cursor: pointer;
transition: background-color 80ms ease, color 80ms ease, opacity 80ms ease;
}
.umb-era-button:hover,
.umb-era-button:active {
color: #484848;
background-color: #d3d3d3;
outline: none;
text-decoration: none;
}
.umb-era-button:focus {
outline: none;
}
.umb-era-button.-blue {
background: @blue;
color: white;
}
.umb-era-button.-blue:hover {
background-color: @blueDark;
}
.umb-era-button.-link {
padding: 0;
background: transparent;
}
.umb-era-button.-link:hover {
background-color: transparent;
opacity: .6;
}
.umb-era-button.-inactive {
cursor: not-allowed;
color: #BBB;
background: #EAE7E7;
}
.umb-era-button.-inactive:hover {
color: #BBB;
background: #EAE7E7;
}
.umb-era-button.-full-width {
display: block;
width: 100%;
}
.umb-era-button.-white {
background-color: @white;
&:hover {
opacity: .9;
}
}
.umb-era-button.-text-black {
color: @black;
}
/* Spacing for boxes */
.umb-air {
flex: 0 0 auto;
flex-basis: 100%;
max-width: 100%;
padding: 10px;
box-sizing: border-box;
@media (min-width: 500px) {
flex-basis: 50%;
max-width: 50%;
}
@media (min-width: 768px) {
flex-basis: 20%;
max-width: 20%;
}
}
/* DETAILS */
.umb-healthcheck-back-link {
font-weight: bold;
color: @black;
}
.umb-healthcheck-group__details {
border-radius: 3px;
margin-bottom: 40px;
}
.umb-healthcheck-group__details-group-title {
background-color: @blue;
padding: 10px 20px;
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 3px 3px 0 0;
}
.umb-healthcheck-group__details-group-name {
font-size: 16px;
color: @white;
font-weight: bold;
}
.umb-healthcheck-group__details-checks {
border: 2px solid @grayLight;
border-top: none;
border-radius: 0 0 3px 3px;
}
.umb-healthcheck-group__details-check {
}
.umb-healthcheck-group__details-check-title {
padding: 15px 20px;
background-color: @grayLighter;
}
.umb-healthcheck-group__details-check-name {
font-size: 14px;
color: @black;
font-weight: bold;
}
.umb-healthcheck-group__details-check-description {
font-size: 12px;
color: @grayMed;
line-height: 1.6rem;
//margin-top: 10px;
}
.umb-healthcheck-group__details-status {
padding: 15px 0;
display: flex;
border-bottom: 2px solid @grayLighter;
position: relative;
}
.umb-healthcheck-group__details-status-overlay {
background: @white;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 0.9;
}
.umb-healthcheck-group__details-status:last-child {
border-bottom: none;
}
.umb-healthcheck-group__details-status-icon-container {
flex: 0 0 50px;
display: flex;
justify-content: center;
padding: 0 20px;
}
.umb-healthcheck-status-icon {
font-size: 20px;
margin-top: 2px;
}
.umb-healthcheck-status-icon.-large {
width: 70px;
height: 70px;
font-size: 30px;
background-color: @white;
}
.umb-healthcheck-group__details-status-content {
padding: 0 20px;
flex: 1 1 auto;
}
.umb-healthcheck-group__details-status-text {
line-height: 1.6em;
}
.umb-healthcheck-group__details-status-actions {
display: flex;
flex-direction: column;
margin-top: 10px;
}
.umb-healthcheck-group__details-status-action {
background-color: @grayLighter;
padding: 10px;
margin-bottom: 10px;
border-radius: 3px;
}
.umb-healthcheck-group__details-status-action:last-child {
margin-bottom: 0;
}
.umb-healthcheck-group__details-status-action-description {
margin-top: 10px;
font-size: 13px;
}
@@ -366,17 +366,17 @@
flex: 1;
}
.umb-panel-header-content.-top-position {
position: relative;
top: -12px;
}
.umb-panel-header-left-side {
display: flex;
flex: 1;
flex-direction: row;
}
.umb-panel-header-left-side.-top-position {
position: relative;
top: -12px;
}
.umb-panel-header-icon {
cursor: pointer;
margin-right: 5px;
@@ -2,9 +2,9 @@
<div class="umb-panel-header-content-wrapper">
<div class="umb-panel-header-content" ng-class="{'-top-position': tabs || !icon }">
<div class="umb-panel-header-content">
<div class="umb-panel-header-left-side">
<div class="umb-panel-header-left-side" ng-class="{'-top-position': tabs || !icon }">
<div class="umb-panel-header-icon" ng-if="!hideIcon" ng-click="openIconPicker()" ng-class="{'-placeholder': $parent.icon==='' || $parent.icon===null}">
<i class="icon {{$parent.icon}}" ng-if="$parent.icon!=='' && $parent.icon!==null"></i>
@@ -31,7 +31,7 @@ function FormsController($scope, $route, $cookieStore, packageResource) {
return packageResource.installFiles(pack);
}, $scope.error)
.then(function(pack){
$scope.state = "Restarting, please hold...";
$scope.state = "Restarting, please wait...";
return packageResource.installData(pack);
}, $scope.error)
.then(function(pack){
@@ -0,0 +1,136 @@
(function () {
"use strict";
function HealthCheckController($scope, healthCheckResource) {
var SUCCESS = 0;
var WARNING = 1;
var ERROR = 2;
var INFO = 3;
var vm = this;
vm.viewState = "list";
vm.groups = [];
vm.selectedGroup = {};
vm.getStatus = getStatus;
vm.executeAction = executeAction;
vm.checkAllInGroup = checkAllInGroup;
vm.openGroup = openGroup;
vm.setViewState = setViewState;
// Get a (grouped) list of all health checks
healthCheckResource.getAllChecks().then(
function(response) {
// set number of checks which has been executed
for (var i = 0; i < response.length; i++) {
var group = response[i];
group.checkCounter = 0;
checkAllInGroup(group, group.checks);
}
vm.groups = response;
}
);
function setGroupGlobalResultType(group) {
var totalSuccess = 0;
var totalError = 0;
var totalWarning = 0;
var totalInfo = 0;
// count total number of statusses
angular.forEach(group.checks, function(check){
angular.forEach(check.status, function(status){
switch(status.resultType) {
case SUCCESS:
totalSuccess = totalSuccess + 1;
break;
case WARNING:
totalWarning = totalWarning + 1;
break;
case ERROR:
totalError = totalError + 1;
break;
case INFO:
totalInfo = totalInfo + 1;
break;
}
});
});
group.totalSuccess = totalSuccess;
group.totalError = totalError;
group.totalWarning = totalWarning;
group.totalInfo = totalInfo;
}
// Get the status of an individual check
function getStatus(check) {
check.loading = true;
check.status = null;
healthCheckResource.getStatus(check.id).then(function(response) {
check.loading = false;
check.status = response;
});
}
function executeAction(check, index, action) {
healthCheckResource.executeAction(action).then(function (response) {
check.status[index] = response;
});
}
function checkAllInGroup(group, checks) {
group.checkCounter = 0;
group.loading = true;
angular.forEach(checks, function(check) {
check.loading = true;
healthCheckResource.getStatus(check.id).then(function(response) {
check.status = response;
group.checkCounter = group.checkCounter + 1;
check.loading = false;
// when all checks are done, set global group result
if (group.checkCounter === checks.length) {
setGroupGlobalResultType(group);
group.loading = false;
}
});
});
}
function openGroup(group) {
vm.selectedGroup = group;
vm.viewState = "details";
}
function setViewState(state) {
vm.viewState = state;
if(state === 'list') {
for (var i = 0; i < vm.groups.length; i++) {
var group = vm.groups[i];
setGroupGlobalResultType(group);
}
}
}
}
angular.module("umbraco").controller("Umbraco.Dashboard.HealthCheckController", HealthCheckController);
})();
@@ -0,0 +1,114 @@
<div ng-controller="Umbraco.Dashboard.HealthCheckController as vm">
<h3>Health Check</h3>
<p>The health checker evaluates various areas of your site for best practice settings, configuration, potential problems, etc. You can easily fix problems by pressing a button. <br/>
You can add your own health checks, have a look at <a href="https://our.umbraco.org/documentation/Extending/Healthcheck/" class="btn-link -underline">the documentation for more information</a> about custom health checks.</p>
<div class="umb-healthcheck" ng-if="vm.viewState === 'list'">
<div class="umb-air" ng-repeat="group in vm.groups" ng-click="vm.openGroup(group);">
<div class="umb-healthcheck-group">
<div class="umb-healthcheck-title">{{group.name}}</div>
<div class="umb-healthcheck-group__load-container" ng-if="group.loading">
<umb-load-indicator></umb-load-indicator>
</div>
<div class="umb-healthcheck-messages" ng-if="!group.loading">
<div class="umb-healthcheck-message" ng-if="group.totalSuccess > 0">
<i class="icon-check color-green"></i>
{{ group.totalSuccess }}
</div>
<div class="umb-healthcheck-message" ng-if="group.totalWarning > 0">
<i class="icon-alert color-yellow"></i>
{{ group.totalWarning }}
</div>
<div class="umb-healthcheck-message" ng-if="group.totalError > 0">
<i class="icon-delete color-red"></i>
{{ group.totalError }}
</div>
<div class="umb-healthcheck-message" ng-if="group.totalInfo > 0">
<i class="umb-healthcheck-status-icon icon-info"></i>
{{ group.totalInfo }}
</div>
</div>
</div>
</div>
</div>
<div ng-if="vm.viewState === 'details'">
<umb-editor-sub-header>
<umb-editor-sub-header-content-left>
<a class="umb-healthcheck-back-link" href="" ng-click="vm.setViewState('list');">&larr; Take me back</a>
</umb-editor-sub-header-content-left>
</umb-editor-sub-header>
<div class="umb-healthcheck-group__details">
<div class="umb-healthcheck-group__details-group-title">
<div class="umb-healthcheck-group__details-group-name">{{ vm.selectedGroup.name }}</div>
<button class="umb-era-button -white -text-black" type="button" ng-click="vm.checkAllInGroup(vm.selectedGroup, vm.selectedGroup.checks);">Check all</button>
</div>
<div class="umb-healthcheck-group__details-checks">
<div class="umb-healthcheck-group__details-check" ng-repeat="check in vm.selectedGroup.checks">
<div class="umb-healthcheck-group__details-check-title">
<div class="umb-healthcheck-group__details-check-name">{{ check.name }}</div>
<div class="umb-healthcheck-group__details-check-description">{{ check.description }}</div>
</div>
<div class="umb-healthcheck-group__details-status" ng-repeat="status in check.status">
<div class="umb-healthcheck-group__details-status-icon-container">
<i class="umb-healthcheck-status-icon icon-check color-green" ng-if="status.resultType == 0"></i>
<i class="umb-healthcheck-status-icon icon-alert icon-alert color-yellow" ng-if="status.resultType == 1"></i>
<i class="umb-healthcheck-status-icon icon-delete icon-delete color-red" ng-if="status.resultType == 2"></i>
<i class="umb-healthcheck-status-icon icon-info icon-info" ng-if="status.resultType == 3"></i>
</div>
<div class="umb-healthcheck-group__details-status-content">
<div class="umb-healthcheck-group__details-status-text">
<div ng-bind-html="status.message"></div>
<div ng-if="status.description" ng-bind-html="status.description"></div>
</div>
<div class="umb-healthcheck-group__details-status-actions">
<div class="umb-healthcheck-group__details-status-action" ng-repeat="action in status.actions">
<span ng-show="action.valueRequired">
Set new value:
<input type="text" ng-model="action.providedValue" required/>
</span>
<button type="button" class="umb-era-button" ng-click="vm.executeAction(check, $index, action);">{{action.name}}</button>
<div class="umb-healthcheck-group__details-status-action-description" ng-if="action.description" ng-bind-html="action.description"></div>
</div>
</div>
</div>
<div ng-show="check.loading">
<div class="umb-healthcheck-group__details-status-overlay"></div>
<umb-load-indicator></umb-load-indicator>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,100 @@
(function () {
"use strict";
function PackagesCategoryController($scope, $routeParams) {
var vm = this;
vm.page = {};
vm.page.name = "Category";
vm.selectCategory = selectCategory;
vm.categories = [
{
"icon": "icon-male-and-female",
"name": "All",
"active": false
},
{
"icon": "icon-male-and-female",
"name": "Collaboration",
"active": true
},
{
"icon": "icon-molecular-network",
"name": "Backoffice extensions"
},
{
"icon": "icon-brackets",
"name": "Developer tools"
},
{
"icon": "icon-wand",
"name": "Starter kits"
},
{
"icon": "icon-medal",
"name": "Umbraco Pro"
},
{
"icon": "icon-wrench",
"name": "Website utilities"
}
];
vm.packages = [
{
"name": "uSightly",
"description": "An HTML5 audio player based on jPlayer",
"karma": "1",
"downloads": "1672",
"icon":"https://our.umbraco.org/media/wiki/150283/635768313097111400_usightlylogopng.png?bgcolor=fff&height=154&width=281&format=png"
},
{
"name": "Kill IE6",
"description": "A simple port of the IE6 warning script (http://code.google.com/p/ie6-upgrade-warning/) to use in your Umbraco websites.",
"karma": "11",
"downloads": "688",
"icon":"https://our.umbraco.org/media/wiki/9138/634697622367666000_offroadcode-100x100.png?bgcolor=fff&height=154&width=281&format=png"
},
{
"name": "Examine Media Indexer",
"description": "CogUmbracoExamineMediaIndexer",
"karma": "3",
"downloads": "1329",
"icon":"https://our.umbraco.org/media/wiki/50703/634782902373558000_cogworks.jpg?bgcolor=fff&height=154&width=281&format=png"
},
{
"name": "SVG Icon Picker",
"description": "A picker, for picking icons from an SVG spritesheet.",
"karma": "5",
"downloads": "8",
"icon":"https://our.umbraco.org/media/wiki/154472/635997115126742822_logopng.png?bgcolor=fff&height=154&width=281&format=png"
},
{
"name": "Pipeline CRM",
"description": "Pipeline is a social CRM that lives in Umbraco back-office. It tracks opportunities and helps teams collaborate with timelines and tasks. It stores information about your customers and your interactions with them. It integrates with your website, capturing opportunities from forms and powering personal portals.",
"karma": "3",
"downloads": "105",
"icon":"https://our.umbraco.org/media/wiki/152476/635917291068518788_pipeline-crm-logopng.png?bgcolor=fff&height=154&width=281&format=png"
},
{
"name": "CodeMirror",
"description": "CodeMirror Editor for Umbraco",
"karma": "1",
"downloads": "70",
"icon":"https://our.umbraco.org/media/wiki/151028/635810233171153461_logopng.png?bgcolor=fff&height=154&width=281&format=png"
}
];
function selectCategory(category) {
}
}
angular.module("umbraco").controller("Umbraco.Editors.Packages.CategoryController", PackagesCategoryController);
})();
@@ -0,0 +1,109 @@
<div ng-controller="Umbraco.Editors.Packages.CategoryController as vm" class="clearfix">
<umb-load-indicator ng-if="page.loading"></umb-load-indicator>
<form name="packagesForm" ng-submit="save()" novalidate>
<umb-editor-view footer="false" ng-if="!page.loading">
<umb-editor-header
name="vm.page.name"
name-locked="true"
hide-icon="true"
hide-description="true"
hide-alias="true">
</umb-editor-header>
<umb-editor-container>
<umb-editor-sub-header>
<umb-editor-sub-header-content-left>
<umb-editor-sub-header-section>
<div class="umb-packages-categories -collapsed">
<a href="" class="umb-packages-category" ng-class="{'-active': category.active === true}" ng-click="vm.selectCategory(category)" ng-repeat="category in vm.categories">
<i class="umb-packages-category-icon" ng-class="category.icon"></i>
<div>{{ category.name }}</div>
</a>
</div>
</umb-editor-sub-header-section>
</umb-editor-sub-header-content-left>
</umb-editor-sub-header>
<h4><strong>Popular</strong></h4>
<div class="umb-packages clearfix">
<div class="umb-package" ng-repeat="package in vm.packages">
<a class="umb-package-link" href="#">
<div class="umb-package-icon">
<img ng-src="{{package.icon}}" alt="" />
</div>
<div class="umb-package-info">
<div class="umb-package-name">{{ package.name }}</div>
<div class="umb-package-numbers">
<small class="umb-package-downloads">
<i class="icon-download-alt"></i> <strong>{{ package.downloads }}</strong>
</small>
<small class="umb-package-karma">
<i class="icon-hearts"></i> <strong>{{ package.ownerInfo.karma }}</strong>
</small>
</div>
</div>
</a>
</div> <!-- end package -->
</div> <!-- end packages -->
<h4><strong>Latest</strong></h4>
<div class="umb-packages clearfix">
<div class="umb-package" ng-repeat="package in vm.packages">
<a class="umb-package-link" href="#">
<div class="umb-package-icon">
<img src="{{ package.icon }}" alt="" />
</div>
<div class="umb-package-info">
<h4 class="umb-package-name">
{{ package.name }}
</h4>
<div class="umb-package-numbers">
<small class="umb-package-downloads">
<i class="icon-download-alt"></i> <strong>{{ package.downloads }}</strong>
</small>
<small class="umb-package-karma">
<i class="icon-hearts"></i> <strong>{{ package.ownerInfo.karma }}</strong>
</small>
</div>
</div>
</a>
</div> <!-- end package -->
</div> <!-- end packages -->
</umb-editor-container>
<umb-editor-footer>
<umb-editor-footer-content-left>
Breadcrumbs here
</umb-editor-footer-content-left>
</umb-editor-footer>
</umb-editor-view>
</form>
</div>
@@ -0,0 +1,100 @@
(function () {
"use strict";
function PackageDetailsController($scope, $routeParams) {
var vm = this;
vm.page = {};
vm.package = {
"name": "Merchello",
"description": "&lt;p&gt;Merchello is a high performance, designer friendly, open source Umbraco ecommerce package built for the store owner.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;What Merchello does for you&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;In version 1, Merchello supports a large variety of products with options that can be attached to a single warehouse, processes orders, manages taxes and shipping, and sends out email notifications to your customers. The beauty of Merchello is that while it oversees all of your products, orders, and store settings, it allows Umbraco to maintain your content. This seamless integration gives you the flexibility to build your store in any way imagineable on a robust platform capable of handling a wide variety of store sizes.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Find out more on our website&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://merchello.com&quot;&gt;https://merchello.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Contribute&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;We would love and need your help. If you want to contribute to Merchello's core, the easiest way to get started is to fork the project on https://github.com/merchello/Merchello and open src/Merchello.sln in Visual Studio. We're excited to see what you do!&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Starter Kit&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;We have built a simple starter kit for Merchello called Bazaar, and you can download it below in the package files tab.&lt;/p&gt;",
"compatibility": [
{
"version": "7.4.x",
"percentage": "100"
},
{
"version": "7.3.x",
"percentage": "86"
},
{
"version": "7.2.x",
"percentage": "93"
},
{
"version": "7.1.x",
"percentage": "100"
}
],
"information": {
"owner": "Rusty Swayne",
"ownerAvatar": "https://our.umbraco.org/media/upload/d476d257-a494-46d9-9a00-56c2f94a55c8/our-profile.jpg?width=200&height=200&mode=crop",
"ownerKarma": "2673",
"contributors": [
{
"name": "Lee"
},
{
"name": "Jason Prothero"
}
],
"created": "18/12/2013",
"currentVersion": "2.0.0",
"netVersion": "4.5",
"license": "MIT",
"downloads": "4198",
"karma": "53"
},
"externalSources": [
{
"name": "Source code",
"url": "https://github.com/merchello/Merchello"
},
{
"name": "Issue tracker",
"url": "http://issues.merchello.com/youtrack/oauth?state=%2Fyoutrack%2FrootGo"
}
],
"images": [
{
"thumbnail": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png?bgcolor=fff&height=154&width=281&format=png",
"source": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png"
},
{
"thumbnail": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png?bgcolor=fff&height=154&width=281&format=png",
"source": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png"
},
{
"thumbnail": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png?bgcolor=fff&height=154&width=281&format=png",
"source": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png"
},
{
"thumbnail": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png?bgcolor=fff&height=154&width=281&format=png",
"source": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png"
},
{
"thumbnail": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png?bgcolor=fff&height=154&width=281&format=png",
"source": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png"
},
{
"thumbnail": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png?bgcolor=fff&height=154&width=281&format=png",
"source": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png"
},
{
"thumbnail": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png?bgcolor=fff&height=154&width=281&format=png",
"source": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png"
},
{
"thumbnail": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png?bgcolor=fff&height=154&width=281&format=png",
"source": "https://our.umbraco.org/media/wiki/104946/635591947547374885_Product-Listpng.png"
}
]
};
}
angular.module("umbraco").controller("Umbraco.Editors.Packages.DetailsController", PackageDetailsController);
})();
@@ -0,0 +1,141 @@
<div ng-controller="Umbraco.Editors.Packages.DetailsController as vm">
<umb-load-indicator ng-if="page.loading"></umb-load-indicator>
<form name="packagesForm" ng-submit="save()" novalidate>
<umb-editor-view ng-if="!page.loading">
<umb-editor-header
name="vm.package.name"
name-locked="true"
hide-icon="true"
hide-description="true"
hide-alias="true">
</umb-editor-header>
<umb-editor-container>
<div class="umb-package-details">
<div class="umb-package-details__main-content">
<div class="umb-package-details__description" ng-bind-html-unsafe="vm.package.description"></div>
<div class="umb-gallery">
<div class="umb-gallery__thumbnails">
<a class="umb-gallery__thumbnail" target="_blank" href="{{ image.source }}" ng-repeat="image in vm.package.images">
<img ng-src="{{ image.thumbnail }}" />
</a>
</div>
</div>
</div>
<div class="umb-package-details__sidebar">
<div class="umb-package-details__section">
<button class="umb-era-button -blue -full-width">Install package</button>
</div>
<div class="umb-package-details__section">
<div class="umb-package-details__owner-profile">
<div class="umb-package-details__owner-profile-avatar">
<img class="umb-avatar" ng-src="{{vm.package.information.ownerAvatar}}" />
</div>
<div>
<div class="umb-package-details__owner-profile-name">{{ vm.package.information.owner }}</div>
<div class="umb-package-details__owner-profile-karma">
{{ vm.package.information.owner }} has <strong>{{ vm.package.information.ownerKarma }}</strong> karma points
</div>
</div>
</div>
</div>
<div class="umb-package-details__section">
<div class="umb-package-details__section-title">Information</div>
<div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Owner:</div>
<div>{{vm.package.information.owner}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Contributors:</div>
<div ng-repeat="contributor in vm.package.information.contributors">
<a class="umb-package-details__link" href="{{ contributor.profileUrl }}" target="_blank">{{ contributor.name }}<span ng-if="!$last">,&nbsp;&nbsp;</span></a>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Created:</div>
<div>{{vm.package.information.created}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Current version:</div>
<div>{{vm.package.information.created}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">.Net Version:</div>
<div>{{vm.package.information.netVersion}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">License:</div>
<div>{{vm.package.information.license}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Downloads:</div>
<div>{{vm.package.information.downloads}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Karma:</div>
<div>{{vm.package.information.karma}}</div>
</div>
</div>
</div>
<div class="umb-package-details__section">
<div class="umb-package-details__section-title">Compatibility</div>
<div class="umb-package-details__section-description">This project is compatible with the following versions as reported by community members who have downloaded this package:</div>
<div class="umb-package-details__compatability" ng-repeat="compatibility in vm.package.compatibility | filter:percentage > 0">
<div class="umb-package-details__compatability-label">
<span class="umb-package-details__information-item-label">{{compatibility.version}}</span>
<span class="umb-package-details__information-item-label-2">({{compatibility.percentage}}%)</span>
</div>
<div class="umb-progress-bar">
<span class="umb-progress-bar__progress" style="width: {{compatibility.percentage}}%"></span>
</div>
</div>
</div>
<div class="umb-package-details__section">
<div class="umb-package-details__section-title">External sources</div>
<div>
<div class="umb-package-details__information-item" ng-repeat="externalSource in vm.package.externalSources">
<a class="umb-package-details__link" target="_blank" href="{{ externalSource.url }}">
<i class="icon-out"></i>
{{ externalSource.name }}
</a>
</div>
</div>
</div>
</div>
</div>
</umb-editor-container>
</umb-editor-view>
</form>
</div>
@@ -0,0 +1,50 @@
(function () {
"use strict";
function PackagesOverviewController($scope, $route, $location, navigationService, $timeout, $cookieStore) {
//Hack!
// if there is a cookie value for packageInstallUri then we need to redirect there,
// the issue is that we still have webforms and we cannot go to a hash location and then window.reload
// because it will double load it.
// we will refresh and then navigate there.
var installPackageUri = window.localStorage.getItem("packageInstallUri");
if (installPackageUri) {
window.localStorage.removeItem("packageInstallUri");
$location.path(installPackageUri).search("");
}
else {
var vm = this;
vm.page = {};
vm.page.name = "Packages";
vm.page.navigation = [
{
"name": "Packages",
"icon": "icon-cloud",
"view": "views/packager/views/repo.html",
"active": true
},
{
"name": "Installed",
"icon": "icon-box",
"view": "views/packager/views/installed.html"
},
{
"name": "Install local",
"icon": "icon-add",
"view": "views/packager/views/install-local.html"
}
];
$timeout(function () {
navigationService.syncTree({ tree: "packager", path: "-1" });
});
}
}
angular.module("umbraco").controller("Umbraco.Editors.Packages.OverviewController", PackagesOverviewController);
})();
@@ -0,0 +1,28 @@
<div ng-controller="Umbraco.Editors.Packages.OverviewController as vm" class="clearfix">
<form name="packagesForm" ng-submit="save()" novalidate val-form-manager>
<umb-editor-view footer="false">
<umb-editor-header
name="vm.page.name"
name-locked="true"
hide-icon="true"
hide-description="true"
navigation="vm.page.navigation"
hide-alias="true">
</umb-editor-header>
<umb-editor-container>
<umb-editor-sub-views
sub-views="vm.page.navigation">
</umb-editor-sub-views>
</umb-editor-container>
</umb-editor-view>
</form>
</div>
@@ -0,0 +1,146 @@
(function () {
"use strict";
function PackagesInstallLocalController($scope, $route, $location, Upload, umbRequestHelper, packageResource, $cookieStore, $timeout) {
var vm = this;
vm.state = "upload";
vm.localPackage = {};
vm.installPackage = installPackage;
vm.installState = {
status: ""
};
vm.zipFile = {
uploadStatus: "idle",
uploadProgress: 0,
serverErrorMessage: null
};
$scope.handleFiles = function (files, event) {
for (var i = 0; i < files.length; i++) {
upload(files[i]);
}
};
function upload(file) {
Upload.upload({
url: umbRequestHelper.getApiUrl("packageInstallApiBaseUrl", "UploadLocalPackage"),
fields: {},
file: file
}).progress(function (evt) {
// calculate progress in percentage
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
// set percentage property on file
vm.zipFile.uploadProgress = progressPercentage;
// set uploading status on file
vm.zipFile.uploadStatus = "uploading";
}).success(function (data, status, headers, config) {
if (data.notifications && data.notifications.length > 0) {
// set error status on file
vm.zipFile.uploadStatus = "error";
// Throw message back to user with the cause of the error
vm.zipFile.serverErrorMessage = data.notifications[0].message;
//TODO: Handle the error in UI
} else {
// set done status on file
vm.zipFile.uploadStatus = "done";
loadPackage();
vm.localPackage = data;
}
}).error(function (evt, status, headers, config) {
//TODO: Handle the error in UI
// set status done
vm.zipFile.uploadStatus = "error";
//if the service returns a detailed error
if (evt.InnerException) {
vm.zipFile.serverErrorMessage = evt.InnerException.ExceptionMessage;
//Check if its the common "too large file" exception
if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) {
vm.zipFile.serverErrorMessage = "File too large to upload";
}
} else if (evt.Message) {
file.serverErrorMessage = evt.Message;
}
// If file not found, server will return a 404 and display this message
if (status === 404) {
vm.zipFile.serverErrorMessage = "File not found";
}
});
}
function loadPackage() {
if (vm.zipFile.uploadStatus === "done") {
vm.state = "packageDetails";
}
}
function installPackage() {
vm.installState.status = "Installing";
//TODO: If any of these fail, will they keep calling the next one?
packageResource
.installFiles(vm.localPackage)
.then(function(pack) {
vm.installState.status = "Importing...";
return packageResource.import(pack);
},
installError)
.then(function(pack) {
vm.installState.status = "Installing...";
return packageResource.installFiles(pack);
},
installError)
.then(function(pack) {
vm.installState.status = "Restarting, please wait...";
return packageResource.installData(pack);
},
installError)
.then(function(pack) {
vm.installState.status = "All done, your browser will now refresh";
return packageResource.cleanUp(pack);
},
installError)
.then(function(result) {
if (result.postInstallationPath) {
//Put the redirect Uri in a cookie so we can use after reloading
window.localStorage.setItem("packageInstallUri", result.postInstallationPath);
}
//reload on next digest (after cookie)
$timeout(function() {
window.location.reload(true);
});
},
installError);
}
function installError() {
//TODO: Need to do something about this?
}
}
angular.module("umbraco").controller("Umbraco.Editors.Packages.InstallLocalController", PackagesInstallLocalController);
})();
@@ -0,0 +1,112 @@
<div class="umb-packages-view-wrapper" ng-controller="Umbraco.Editors.Packages.InstallLocalController as vm">
<!-- Upload -->
<div class="umb-upload-local" ng-if="vm.state === 'upload'">
<form novalidate name="localPackageForm">
<!-- Drag and drop files area -->
<div ngf-drop
ng-hide="hideDropzone"
ng-model="filesHolder"
ngf-change="handleFiles($files, $event)"
class="umb-upload-local__dropzone"
ngf-drag-over-class="drag-over"
ngf-multiple="false"
ngf-allow-dir="false"
ngf-pattern="*.zip"
ngf-max-size="{{ maxFileSize }}"
ng-class="{'is-small': compact!=='false' || (done.length+queue.length) > 0 }">
<div class="content">
<!-- Drag and drop illustration -->
<i class="icon-box" draggable="false"></i>
<small class="faded" draggable="false"><strong>Drop to upload</strong></small>
<!-- Select files -->
<div class="umb-upload-local__select-file"
ngf-select
ng-model="filesHolder"
ngf-change="handleFiles($files, $event)"
ngf-multiple="true"
ngf-pattern="*.zip"
ngf-max-size="{{ maxFileSize }}">
- <localize key="media_orClickHereToUpload">or click here to choose files</localize>
</div>
</div>
</div>
<h3><strong>Upload package</strong></h3>
<p class="faded">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam
</p>
</form>
</div>
<!-- Package details -->
<div class="umb-info-local" ng-if="vm.state === 'packageDetails'">
<div class="umb-info-local-items">
<form novalidate name="localPackageForm">
<div class="umb-package-icon">
<i ng-if="!vm.localPackage.icon" class="icon-box"></i>
<img ng-if="vm.localPackage.icon" ng-src="{{vm.localPackage.icon}}" alt="" />
</div>
<div class="umb-package-info">
<h4 class="umb-info-local-item"><strong>{{ vm.localPackage.name }}</strong></h4>
<div class="umb-info-local-item">
<strong>Author</strong>
<a href="{{ vm.localPackage.authorLink }}" target="_blank">{{ vm.localPackage.author }}</a>
</div>
<div class="umb-info-local-item">
<strong>Version</strong>
{{ vm.localPackage.version }}
</div>
<div class="umb-info-local-item">
<strong>License</strong>
<a href="{{ vm.localPackage.licenseUrl }}">{{ vm.localPackage.license }}</a>
</div>
<div class="umb-info-local-item">
<strong>Read me</strong>
<br>
<small ng-bind-html-unsafe="vm.localPackage.readMe"></small>
</div>
<div class="umb-info-local-item mt-3" ng-if="vm.installState.status == '' && vm.localPackage.isCompatible">
<button type="button"
ng-class="{'-inactive' : localPackageForm.$invalid}"
ng-disabled="localPackageForm.$invalid"
class="umb-era-button -blue inline-flex"
ng-click="vm.installPackage()">
Install package
</button>
<label for="license-accept" class="umb-package-installer-label">
<input type="checkbox" id="license-accept" ng-model="vm.localPackage.packageLicenseAccept" required no-dirty-check>
<strong class="label-text">I accept <a href="{{ vm.localPackage.licenseUrl }}">terms of use</a></strong>
</label>
</div>
<div class="umb-info-local-item text-error" ng-if="!vm.localPackage.isCompatible">
This package cannot be installed, it requires a minimum Umbraco version of {{vm.localPackage.umbracoVersion}}
</div>
<div class="umb-info-local-item text-info">
<p>{{vm.installState.status}}</p>
</div>
</div>
</form>
</div>
</div>
</div>
@@ -0,0 +1,52 @@
(function () {
"use strict";
function PackagesInstalledController($scope, $route, $location, packageResource) {
var vm = this;
vm.confirmUninstall = confirmUninstall;
vm.uninstallPackage = uninstallPackage;
vm.state = "list";
vm.installState = {
status: ""
};
vm.package = {};
function init() {
packageResource.getInstalled()
.then(function (packs) {
vm.installedPackages = packs;
});
vm.installState.status = "";
vm.state = "list";
}
function confirmUninstall(pck) {
vm.state = "packageDetails";
vm.package = pck;
}
function uninstallPackage(installedPackage) {
vm.installState.status = "Uninstalling package...";
packageResource.uninstall(installedPackage.id)
.then(function () {
if (installedPackage.files.length > 0) {
vm.installState.status = "All done, your browser will now refresh";
var url = window.location.href + "?uninstalled=" + vm.package.packageGuid;
window.location.reload(true);
}
else {
init();
}
});
}
init();
}
angular.module("umbraco").controller("Umbraco.Editors.Packages.InstalledController", PackagesInstalledController);
})();
@@ -0,0 +1,102 @@
<div ng-controller="Umbraco.Editors.Packages.InstalledController as vm">
<!-- Package list-->
<div class="umb-packages-view-wrapper" ng-if="vm.state == 'list'">
<div class="umb-packages-view-title">Installed packages</div>
<div class="umb-package-list">
<div class="umb-package-list__item" ng-repeat="installedPackage in vm.installedPackages">
<div class="umb-package-list__item-icon">
<i ng-if="!installedPackage.icon" class="icon-box"></i>
<img ng-if="installedPackage.icon" ng-src="{{installedPackage.icon}}" />
</div>
<div class="umb-package-list__item-content">
<div class="umb-package-list__item-name">{{ installedPackage.name }}</div>
<div class="umb-package-list__item-description">
{{ installedPackage.version }} | <a href="{{ installedPackage.url }}">{{ installedPackage.url }}</a>| {{ installedPackage.author }}
</div>
</div>
<div class="umb-package-list__item-actions">
<button type="button" class="umb-era-button" ng-click="vm.confirmUninstall(installedPackage)">Uninstall</button>
</div>
</div>
</div>
</div>
<!-- Package details -->
<div ng-if="vm.state === 'packageDetails'">
<umb-editor-sub-header>
<umb-editor-sub-header-content-left>
<a class="umb-package-details__back-link" href="" ng-click="vm.state = 'list'">&larr; Take me back</a>
</umb-editor-sub-header-content-left>
</umb-editor-sub-header>
<div class="umb-info-local">
<div class="umb-info-local-items">
<form novalidate name="uninstallForm">
<div class="umb-package-icon">
<i ng-if="!vm.package.icon" class="icon-box"></i>
<img ng-if="vm.package.icon" ng-src="{{vm.package.icon}}" alt="" />
</div>
<div class="umb-package-info">
<h4 class="umb-info-local-item"><strong>{{ vm.package.name }}</strong></h4>
<div class="umb-info-local-item">
<strong>Author</strong>
<a href="{{ vm.package.authorLink }}" target="_blank">{{ vm.package.author }}</a>
</div>
<div class="umb-info-local-item">
<strong>Version</strong>
{{ vm.package.version }}
</div>
<div class="umb-info-local-item">
<strong>License</strong>
<a href="{{ vm.package.licenseUrl }}">{{ vm.package.license }}</a>
</div>
<div class="umb-info-local-item">
<strong>Read me</strong>
<br>
<small> {{ vm.package.readme }} </small>
</div>
<div class="umb-info-local-item mt-3" ng-if="vm.installState.status == ''">
<button type="button"
ng-class="{'-inactive' : uninstallForm.$invalid}"
ng-disabled="uninstallForm.$invalid"
class="umb-era-button -blue inline-flex"
ng-click="vm.uninstallPackage(vm.package)">
Uninstall package
</button>
<label for="confirm-uninstall" class="umb-package-installer-label">
<input type="checkbox" id="confirm-uninstall" ng-model="vm.package.confirmUninstall" required no-dirty-check>
<strong class="label-text">Confirm package uninstall</strong>
</label>
</div>
<div class="umb-info-local-item text-info">
<p>{{vm.installState.status}}</p>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@@ -0,0 +1,240 @@
(function () {
"use strict";
function PackagesRepoController($scope, $route, $location, $timeout, ourPackageRepositoryResource, $q, packageResource, $cookieStore) {
var vm = this;
vm.packageViewState = "packageList";
vm.categories = [];
vm.loading = true;
vm.pagination = {
pageNumber: 1,
totalPages: 10,
pageSize: 8
};
vm.searchQuery = "";
vm.installState = {
status: ""
};
vm.selectCategory = selectCategory;
vm.showPackageDetails = showPackageDetails;
vm.setPackageViewState = setPackageViewState;
vm.nextPage = nextPage;
vm.prevPage = prevPage;
vm.goToPage = goToPage;
vm.installPackage = installPackage;
vm.downloadPackage = downloadPackage;
//used to cancel any request in progress if another one needs to take it's place
var canceler = null;
function getActiveCategory() {
if (vm.searchQuery !== "") {
return "";
}
for (var i = 0; i < vm.categories.length; i++) {
if (vm.categories[i].active === true) {
return vm.categories[i].name;
}
}
return "";
}
function init() {
vm.loading = true;
$q.all([
ourPackageRepositoryResource.getCategories()
.then(function(cats) {
vm.categories = cats;
}),
ourPackageRepositoryResource.getPopular(8)
.then(function(pack) {
vm.popular = pack.packages;
}),
ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize)
.then(function(pack) {
vm.packages = pack.packages;
vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize);
})
])
.then(function() {
vm.loading = false;
});
$scope.$watch(function() {
return vm.searchQuery;
}, _.debounce(function (newVal, oldVal) {
$scope.$apply(function () {
if (vm.searchQuery) {
if (newVal !== null && newVal !== undefined && newVal !== oldVal) {
vm.loading = true;
//a canceler exists, so perform the cancelation operation and reset
if (canceler) {
canceler.resolve();
canceler = $q.defer();
}
else {
canceler = $q.defer();
}
ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1,
vm.pagination.pageSize,
"",
vm.searchQuery,
canceler)
.then(function(pack) {
vm.packages = pack.packages;
vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize);
vm.pagination.pageNumber = 1;
vm.loading = false;
//set back to null so it can be re-created
canceler = null;
});
}
}
});
}, 200));
}
function selectCategory(selectedCategory, categories) {
var reset = false;
for (var i = 0; i < categories.length; i++) {
var category = categories[i];
if (category.name === selectedCategory.name && category.active === true) {
//it's already selected, let's unselect to show all again
reset = true;
}
category.active = false;
}
vm.loading = true;
vm.searchQuery = "";
var searchCategory = selectedCategory.name;
if (reset === true) {
searchCategory = "";
}
$q.all([
ourPackageRepositoryResource.getPopular(8, searchCategory)
.then(function(pack) {
vm.popular = pack.packages;
}),
ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize, searchCategory, vm.searchQuery)
.then(function(pack) {
vm.packages = pack.packages;
vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize);
vm.pagination.pageNumber = 1;
})
])
.then(function() {
vm.loading = false;
selectedCategory.active = reset === false;
});
}
function showPackageDetails(selectedPackage) {
ourPackageRepositoryResource.getDetails(selectedPackage.id)
.then(function(pack) {
vm.package = pack;
vm.packageViewState = "packageDetails";
});
}
function setPackageViewState(state) {
if(state) {
vm.packageViewState = state;
}
}
function nextPage(pageNumber) {
ourPackageRepositoryResource.search(pageNumber - 1, vm.pagination.pageSize, getActiveCategory(), vm.searchQuery)
.then(function (pack) {
vm.packages = pack.packages;
vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize);
});
}
function prevPage(pageNumber) {
ourPackageRepositoryResource.search(pageNumber - 1, vm.pagination.pageSize, getActiveCategory(), vm.searchQuery)
.then(function (pack) {
vm.packages = pack.packages;
vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize);
});
}
function goToPage(pageNumber) {
ourPackageRepositoryResource.search(pageNumber - 1, vm.pagination.pageSize, getActiveCategory(), vm.searchQuery)
.then(function (pack) {
vm.packages = pack.packages;
vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize);
});
}
function downloadPackage(selectedPackage) {
vm.loading = true;
packageResource
.fetch(selectedPackage.id)
.then(function(pack) {
vm.packageViewState = "packageInstall";
vm.loading = false;
vm.localPackage = pack;
vm.localPackage.allowed = true;
},
error);
}
function error(e, args) {
}
function installPackage(selectedPackage) {
vm.installState.status = "importing...";
packageResource
.import(selectedPackage)
.then(function(pack) {
vm.installState.status = "Installing...";
return packageResource.installFiles(pack);
},
error)
.then(function(pack) {
vm.installState.status = "Restarting, please wait...";
return packageResource.installData(pack);
},
error)
.then(function(pack) {
vm.installState.status = "All done, your browser will now refresh";
return packageResource.cleanUp(pack);
},
error)
.then(function(result) {
if (result.postInstallationPath) {
//Put the redirect Uri in a cookie so we can use after reloading
window.localStorage.setItem("packageInstallUri", result.postInstallationPath);
}
//reload on next digest (after cookie)
$timeout(function () {
window.location.reload(true);
});
},
error);
}
init();
}
angular.module("umbraco").controller("Umbraco.Editors.Packages.RepoController", PackagesRepoController);
})();
@@ -0,0 +1,293 @@
<div ng-controller="Umbraco.Editors.Packages.RepoController as vm" class="clearfix">
<umb-load-indicator ng-show="vm.loading"></umb-load-indicator>
<!-- LIST -->
<div class="umb-packages-view-wrapper" ng-if="vm.packageViewState === 'packageList'">
<div class="umb-packages-section">
<div class="umb-packages-search">
<input class="-full-width-input" type="text" name="query" placeholder="Search for packages" umb-auto-focus ng-model="vm.searchQuery" no-dirty-check>
</div>
</div>
<div ng-show="vm.loading === false">
<div class="umb-packages-section" ng-if="vm.searchQuery == ''">
<div class="umb-packages-categories">
<a href=""
class="umb-packages-category"
ng-click="vm.selectCategory(category, vm.categories)"
ng-repeat="category in vm.categories"
ng-class="{'-active': category.active, '-first': $first, '-last': $last}">
<div>{{ category.name }}</div>
</a>
</div>
</div>
<div class="umb-packages-section" ng-if="vm.searchQuery == ''">
<h4><strong>Popular</strong></h4>
<div class="umb-packages clearfix">
<div class="umb-package" ng-repeat="package in vm.popular">
<a class="umb-package-link" href="" ng-click="vm.showPackageDetails(package)">
<div class="umb-package-icon">
<img ng-src="{{package.icon}}" alt="" />
</div>
<div class="umb-package-info">
<div class="umb-package-name">{{ package.name }}</div>
<div class="umb-package-description">{{ package.excerpt | limitTo: 40 }}<span ng-if="package.excerpt > (package.excerpt | limitTo: 40)">...</span></div>
<div class="umb-package-numbers">
<small class="umb-package-downloads">
<i class="icon-download-alt"></i> <strong>{{ package.downloads }}</strong>
</small>
<small class="umb-package-karma">
<i class="icon-hearts"></i> <strong>{{ package.ownerInfo.karma }}</strong>
</small>
</div>
</div>
</a>
</div> <!-- end package -->
</div> <!-- end packages -->
</div>
<div class="umb-packages-section">
<h4><strong>Latest</strong></h4>
<div class="umb-packages clearfix">
<div class="umb-package" ng-repeat="package in vm.packages">
<a class="umb-package-link" href="" ng-click="vm.showPackageDetails(package)">
<div class="umb-package-icon">
<img src="{{ package.icon }}" alt="" />
</div>
<div class="umb-package-info">
<div class="umb-package-name">{{ package.name }}</div>
<div class="umb-package-description">{{ package.excerpt | limitTo: 40 }}<span ng-if="package.excerpt > (package.excerpt | limitTo: 40)">...</span></div>
<div class="umb-package-numbers">
<small class="umb-package-downloads">
<i class="icon-download-alt"></i> <strong>{{ package.downloads }}</strong>
</small>
<small class="umb-package-karma">
<i class="icon-hearts"></i> <strong>{{ package.ownerInfo.karma }}</strong>
</small>
</div>
</div>
</a>
</div> <!-- end package -->
</div> <!-- end packages -->
</div>
<div class="umb-packages__pagination" ng-if="vm.pagination.totalPages > 1 && vm.loading === false">
<umb-pagination page-number="vm.pagination.pageNumber"
total-pages="vm.pagination.totalPages"
on-next="vm.nextPage"
on-prev="vm.prevPage"
on-go-to-page="vm.goToPage">
</umb-pagination>
</div>
</div>
</div>
<!-- DETAILS --->
<div class="umb-packages-view-wrapper" ng-if="vm.packageViewState === 'packageDetails' && vm.loading === false">
<umb-editor-sub-header>
<umb-editor-sub-header-content-left>
<a class="umb-package-details__back-link" href="" ng-click="vm.setPackageViewState('packageList');">&larr; Take me back</a>
</umb-editor-sub-header-content-left>
</umb-editor-sub-header>
<div class="umb-package-details">
<div class="umb-package-details__main-content">
<div class="umb-packages-view-title">{{ vm.package.name }}</div>
<div class="umb-package-details__description" ng-bind-html-unsafe="vm.package.description"></div>
<div class="umb-gallery">
<div class="umb-gallery__thumbnails">
<a class="umb-gallery__thumbnail" target="_blank" href="{{ image.source }}" ng-repeat="image in vm.package.images">
<img ng-src="{{ image.thumbnail }}" />
</a>
</div>
</div>
</div>
<div class="umb-package-details__sidebar">
<div class="umb-package-details__section">
<button class="umb-era-button -blue -full-width" ng-click="vm.downloadPackage(vm.package)">Install package</button>
</div>
<div class="umb-package-details__section">
<div class="umb-package-details__owner-profile">
<div class="umb-package-details__owner-profile-avatar">
<img class="umb-avatar" ng-src="{{ 'https://our.umbraco.org' + vm.package.ownerInfo.ownerAvatar }}" />
</div>
<div>
<div class="umb-package-details__owner-profile-name">{{ vm.package.ownerInfo.owner }}</div>
<div class="umb-package-details__owner-profile-karma">
{{ vm.package.ownerInfo.owner }} has <strong>{{ vm.package.ownerInfo.karma }}</strong> karma points
</div>
</div>
</div>
</div>
<div class="umb-package-details__section">
<div class="umb-package-details__section-title">Information</div>
<div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Owner:</div>
<div>{{vm.package.ownerInfo.owner}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Contributors:</div>
<div ng-repeat="contributor in vm.package.ownerInfo.contributors">
<a class="umb-package-details__link" href="{{ contributor.profileUrl }}" target="_blank">{{ contributor.name }}<span ng-if="!$last">,&nbsp;&nbsp;</span></a>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Created:</div>
<div>{{vm.package.created}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Current version:</div>
<div>{{vm.package.latestVersion}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">.Net Version:</div>
<div>{{vm.package.information.netVersion}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">License:</div>
<div>{{vm.package.licenseName}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Downloads:</div>
<div>{{vm.package.downloads}}</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">Karma:</div>
<div>{{vm.package.ownerInfo.karma}}</div>
</div>
</div>
</div>
<div class="umb-package-details__section">
<div class="umb-package-details__section-title">Compatibility</div>
<div class="umb-package-details__section-description">This project is compatible with the following versions as reported by community members who have downloaded this package:</div>
<div class="umb-package-details__compatability" ng-repeat="compatibility in vm.package.compatibility | filter:percentage > 0">
<div class="umb-package-details__compatability-label">
<span class="umb-package-details__information-item-label">{{compatibility.version}}</span>
<span class="umb-package-details__information-item-label-2">({{compatibility.percentage}}%)</span>
</div>
<div class="umb-progress-bar">
<span class="umb-progress-bar__progress" style="width: {{compatibility.percentage}}%"></span>
</div>
</div>
</div>
<div class="umb-package-details__section">
<div class="umb-package-details__section-title">External sources</div>
<div>
<div class="umb-package-details__information-item" ng-repeat="externalSource in vm.package.externalSources">
<a class="umb-package-details__link" target="_blank" href="{{ externalSource.url }}">
<i class="icon-out"></i>
{{ externalSource.name }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Package details -->
<div class="umb-info-local" ng-if="vm.packageViewState === 'packageInstall' && vm.loading === false">
<div class="umb-info-local-items">
<form novalidate name="localPackageForm">
<div class="umb-package-icon">
<i ng-if="!vm.localPackage.icon" class="icon-box"></i>
<img ng-if="vm.localPackage.icon" ng-src="{{vm.localPackage.icon}}" alt="" />
</div>
<div class="umb-package-info">
<h4 class="umb-info-local-item"><strong>{{ vm.localPackage.name }}</strong></h4>
<div class="umb-info-local-item">
<strong>Author</strong>
<a href="{{ vm.localPackage.authorLink }}" target="_blank">{{ vm.localPackage.author }}</a>
</div>
<div class="umb-info-local-item">
<strong>Version</strong>
{{ vm.localPackage.version }}
</div>
<div class="umb-info-local-item">
<strong>License</strong>
<a href="{{ vm.localPackage.licenseUrl }}">{{ vm.localPackage.license }}</a>
</div>
<div class="umb-info-local-item">
<strong>Read me</strong>
<br>
<small ng-bind-html-unsafe="vm.localPackage.readMe"></small>
</div>
<div class="umb-info-local-item mt-3" ng-if="vm.installState.status == '' && vm.localPackage.isCompatible">
<button type="button"
ng-class="{'-inactive' : localPackageForm.$invalid}"
ng-disabled="localPackageForm.$invalid"
class="umb-era-button -blue inline-flex"
ng-click="vm.installPackage(vm.localPackage)">
Install package
</button>
<label for="license-accept" class="umb-package-installer-label">
<input type="checkbox" id="license-accept" ng-model="vm.localPackage.packageLicenseAccept" required no-dirty-check>
<strong class="label-text">I accept <a href="{{ vm.localPackage.licenseUrl }}">terms of use</a></strong>
</label>
</div>
<div class="umb-info-local-item text-error" ng-if="!vm.localPackage.isCompatible">
This package cannot be installed, it requires a minimum Umbraco version of {{vm.localPackage.umbracoVersion}}
</div>
<div class="umb-info-local-item text-info">
<p>{{vm.installState.status}}</p>
</div>
</div>
</form>
</div>
</div>
</div>
+2 -2
View File
@@ -128,8 +128,8 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\dotless.1.4.1.0\lib\dotless.Core.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.68.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.68.0\lib\Examine.dll</HintPath>
<Reference Include="Examine, Version=0.1.69.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.69.0-beta\lib\Examine.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
@@ -10,7 +10,7 @@ NOTES:
* Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config
* A new version will invalidate both client and server cache and create new persisted files
-->
<clientDependency version="745538204" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<clientDependency version="427986118" fileDependencyExtensions=".js,.css" loggerType="Umbraco.Web.UI.CdfLogger, umbraco">
<!--
This section is used for Web Forms only, the enableCompositeFiles="true" is optional and by default is set to true.
@@ -37,11 +37,6 @@
views/dashboard/developer/examinemanagement.html
</control>
</tab>
<tab caption="Xml Data Integrity Report">
<control>
views/dashboard/developer/xmldataintegrityreport.html
</control>
</tab>
</section>
<section alias="StartupMediaDashboardSection">
@@ -97,4 +92,15 @@
</tab>
</section>
<section alias="UmbracoHealthCheck">
<areas>
<area>developer</area>
</areas>
<tab caption="Health Check">
<control>
views/dashboard/developer/healthcheck.html
</control>
</tab>
</section>
</dashBoard>
+10 -5
View File
@@ -34,11 +34,6 @@
views/dashboard/developer/examinemanagement.html
</control>
</tab>
<tab caption="Xml Data Integrity Report">
<control>
views/dashboard/developer/xmldataintegrityreport.html
</control>
</tab>
</section>
<section alias="StartupMediaDashboardSection">
<areas>
@@ -104,4 +99,14 @@
</control>
</tab>
</section>
<section alias="UmbracoHealthCheck">
<areas>
<area>developer</area>
</areas>
<tab caption="Health Check">
<control>
views/dashboard/developer/healthcheck.html
</control>
</tab>
</section>
</dashBoard>
@@ -20,10 +20,9 @@
<add initialize="true" sortOrder="7" alias="mediaTypes" application="settings" title="Media Types" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MediaTypeTreeController, umbraco" />
<!--Developer-->
<add initialize="true" sortOrder="0" alias="dataTypes" application="developer" title="Data Types" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.DataTypeTreeController, umbraco" />
<add application="developer" alias="macros" title="Macros" type="umbraco.loadMacros, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="2" />
<add application="developer" alias="packager" title="Packages" type="umbraco.loadPackager, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="3" />
<add application="developer" alias="packagerPackages" title="Packager Packages" type="umbraco.loadPackages, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" initialize="false" sortOrder="3" />
<add initialize="true" sortOrder="0" alias="packager" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.PackagesTreeController, umbraco" />
<add initialize="true" sortOrder="1" alias="dataTypes" application="developer" title="Data Types" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.DataTypeTreeController, umbraco" />
<add application="developer" alias="macros" title="Macros" type="umbraco.loadMacros, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="2" />
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4" />
<add application="developer" alias="xslt" title="XSLT Files" type="umbraco.loadXslt, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="5" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder" />
+3 -3
View File
@@ -17,10 +17,9 @@
<add application="settings" alias="dictionary" title="Dictionary" type="umbraco.loadDictionary, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="6" />
<add initialize="true" sortOrder="7" alias="mediaTypes" application="settings" title="Media Types" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MediaTypeTreeController, umbraco" />
<!--Developer-->
<add initialize="true" sortOrder="0" alias="dataTypes" application="developer" title="Data Types" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.DataTypeTreeController, umbraco" />
<add initialize="true" sortOrder="0" alias="packager" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.PackagesTreeController, umbraco" />
<add initialize="true" sortOrder="1" alias="dataTypes" application="developer" title="Data Types" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.DataTypeTreeController, umbraco" />
<add application="developer" alias="macros" title="Macros" type="umbraco.loadMacros, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="2" />
<add application="developer" alias="packager" title="Packages" type="umbraco.loadPackager, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="3" />
<add application="developer" alias="packagerPackages" title="Packager Packages" type="umbraco.loadPackages, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" initialize="false" sortOrder="3" />
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4" />
<add application="developer" alias="xslt" title="XSLT Files" type="umbraco.loadXslt, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="5" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder" />
@@ -42,4 +41,5 @@
<add initialize="true" sortOrder="0" alias="form" application="forms" title="Forms" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Forms.Web.Trees.FormTreeController, Umbraco.Forms.Web" />
<add initialize="true" sortOrder="3" alias="prevaluesource" application="forms" title="Prevalue sources" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Forms.Web.Trees.PreValueSourceTreeController, Umbraco.Forms.Web" />
<add initialize="true" sortOrder="3" alias="formsecurity" application="users" title="Forms Security" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Forms.Web.Trees.FormSecurityTreeController, Umbraco.Forms.Web" />
</trees>
+1 -1
View File
@@ -4,7 +4,7 @@
<package id="ClientDependency" version="1.8.4" targetFramework="net45" />
<package id="ClientDependency-Mvc5" version="1.8.0.0" targetFramework="net45" />
<package id="dotless" version="1.4.1.0" targetFramework="net45" />
<package id="Examine" version="0.1.68.0" targetFramework="net45" />
<package id="Examine" version="0.1.69.0-beta" targetFramework="net45" />
<package id="ImageProcessor" version="2.3.3.0" targetFramework="net45" />
<package id="ImageProcessor.Web" version="4.5.3.0" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
@@ -64,8 +64,7 @@
Umbraco.Sys = {};
Umbraco.Sys.ServerVariables = {
"installApiBaseUrl": "@ViewBag.InstallApiBaseUrl",
"umbracoBaseUrl": "@ViewBag.UmbracoBaseFolder",
"installType": "@ViewBag.InstallType"
"umbracoBaseUrl": "@ViewBag.UmbracoBaseFolder"
};
</script>
<script src="lib/rgrove-lazyload/lazyload.js"></script>
@@ -1271,4 +1271,106 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="enterCustomValidation">...or enter a custom validation</key>
<key alias="fieldIsMandatory">Field is mandatory</key>
</area>
<area alias="healthcheck">
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
2: XPath
3: Configuration file path
-->
<key alias="checkSuccessMessage">Value is set to the recommended value: '%0%'.</key>
<key alias="rectifySuccessMessage">Value was set to '%1%' for XPath '%2%' in configuration file '%3%'.</key>
<key alias="checkErrorMessageDifferentExpectedValue">Expected value '%1%' for '%2%' in configuration file '%3%', but found '%0%'.</key>
<key alias="checkErrorMessageUnexpectedValue">Found unexpected value '%0%' for '%2%' in configuration file '%3%'.</key>
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
-->
<key alias="customErrorsCheckSuccessMessage">Custom errors are set to '%0%'.</key>
<key alias="customErrorsCheckErrorMessage">Custom errors are currently set to '%0%'. It is recommended to set this to '%1%' before go live.</key>
<key alias="customErrorsCheckRectifySuccessMessage">Custom errors successfully set to '%0%'.</key>
<key alias="macroErrorModeCheckSuccessMessage">MacroErrors are set to '%0%'.</key>
<key alias="macroErrorModeCheckErrorMessage">MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely when there's any errors in macros. Rectifying this will set the value to '%1%'.</key>
<key alias="macroErrorModeCheckRectifySuccessMessage">MacroErrors are now set to '%0%'.</key>
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
2: Server version
-->
<key alias="trySkipIisCustomErrorsCheckSuccessMessage">Try Skip IIS Custom Errors is set to '%0%' and you're using IIS version '%1%'.</key>
<key alias="trySkipIisCustomErrorsCheckErrorMessage">Try Skip IIS Custom Errors is currently '%0%'. It is recommended to set this to '%1%' for your IIS version (%2%).</key>
<key alias="trySkipIisCustomErrorsCheckRectifySuccessMessage">Try Skip IIS Custom Errors successfully set to '%0%'.</key>
<!-- The following keys get predefined tokens passed in that are not all the same, like above -->
<key alias="configurationServiceFileNotFound">File does not exist: '%0%'.</key>
<key alias="configurationServiceNodeNotFound"><![CDATA[Unable to find <strong>'%0%'</strong> in config file <strong>'%1%'</strong>.]]></key>
<key alias="configurationServiceError">There was an error, check log for full error: %0%.</key>
<key alias="xmlDataIntegrityCheckMembers">Total XML: %0%, Total: %1%</key>
<key alias="xmlDataIntegrityCheckMedia">Total XML: %0%, Total: %1%</key>
<key alias="xmlDataIntegrityCheckContent">Total XML: %0%, Total published: %1%</key>
<key alias="httpsCheckInvalidCertificate">Certificate validation error: '%0%'</key>
<key alias="httpsCheckInvalidUrl">Error pinging the URL %0% - '%1%'</key>
<key alias="httpsCheckIsCurrentSchemeHttps">You are currently %0% viewing the site using the HTTPS scheme.</key>
<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>
<key alias="httpsCheckConfigurationCheckResult">The appSetting 'umbracoUseSSL' is set to '%0%' in your web.config file, your cookies are %1% marked as secure.</key>
<key alias="httpsCheckEnableHttpsError">Could not update the 'umbracoUseSSL' setting in your web.config file. Error: %0%</key>
<!-- The following keys don't get tokens passed in -->
<key alias="httpsCheckEnableHttpsButton">Enable HTTPS</key>
<key alias="httpsCheckEnableHttpsDescription">Sets umbracoSSL setting to true in the appSettings of the web.config file.</key>
<key alias="httpsCheckEnableHttpsSuccess">The appSetting 'umbracoUseSSL' is now set to 'true' in your web.config file, your cookies will be marked as secure.</key>
<key alias="rectifyButton">Fix</key>
<key alias="cannotRectifyShouldNotEqual">Cannot fix a check with a value comparison type of 'ShouldNotEqual'.</key>
<key alias="cannotRectifyShouldEqualWithValue">Cannot fix a check with a value comparison type of 'ShouldEqual' with a provided value.</key>
<key alias="valueToRectifyNotProvided">Value to fix check not provided.</key>
<key alias="compilationDebugCheckSuccessMessage">Debug compilation mode is disabled.</key>
<key alias="compilationDebugCheckErrorMessage">Debug compilation mode is currently enabled. It is recommended to disable this setting before go live.</key>
<key alias="compilationDebugCheckRectifySuccessMessage">Debug compilation mode successfully disabled.</key>
<key alias="traceModeCheckSuccessMessage">Trace mode is disabled.</key>
<key alias="traceModeCheckErrorMessage">Trace mode is currently enabled. It is recommended to disable this setting before go live.</key>
<key alias="traceModeCheckRectifySuccessMessage">Trace mode successfully disabled.</key>
<key alias="folderPermissionsCheckMessage">All folders have the correct permissions set.</key>
<!-- The following keys get these tokens passed in:
0: Comma delimitted list of failed folder paths
-->
<key alias="requiredFolderPermissionFailed"><![CDATA[The following folders must be set up with modify permissions but could not be acccessed: <strong>%0%</strong>.]]></key>
<key alias="optionalFolderPermissionFailed"><![CDATA[The following folders must be set up with modify permissions for certain Umbraco operations to function but could not be acccessed: <strong>%0%</strong>. If they aren't being written to no action need be taken.]]></key>
<key alias="filePermissionsCheckMessage">All files have the correct permissions set.</key>
<!-- The following keys get these tokens passed in:
0: Comma delimitted list of failed folder paths
-->
<key alias="requiredFilePermissionFailed"><![CDATA[The following files must be set up with write permissions but could not be acccessed: <strong>%0%</strong>.]]></key>
<key alias="optionalFilePermissionFailed"><![CDATA[The following files must be set up with write permissions for certain Umbraco operations to function but could not be acccessed: <strong>%0%</strong>. If they aren't being written to no action need be taken.]]></key>
<key alias="clickJackingCheckHeaderFound"><![CDATA[The header or meta-tag <strong>X-Frame-Options</strong> used to control whether a site can be IFRAMed by another was found.]]></key>
<key alias="clickJackingCheckHeaderNotFound"><![CDATA[The header or meta-tag <strong>X-Frame-Options</strong> used to control whether a site can be IFRAMed by another was not found.]]></key>
<key alias="clickJackingSetHeaderInConfig">Set Header in Config</key>
<key alias="clickJackingSetHeaderInConfigDescription">Adds a value to the httpProtocol/customHeaders section of web.config to prevent the site being IFRAMed by other websites.</key>
<key alias="clickJackingSetHeaderInConfigSuccess">A setting to create a header preventing IFRAMing of the site by other websites has been added to your web.config file.</key>
<key alias="clickJackingSetHeaderInConfigError">Could not update web.config file. Error: %0%</key>
<!-- The following key get these tokens passed in:
0: Comma delimitted list of headers found
-->
<key alias="excessiveHeadersFound"><![CDATA[The following headers revealing information about the website technology were found: <strong>%0%</strong>.]]></key>
<key alias="excessiveHeadersNotFound">No headers revealing information about the website technology were found.</key>
<key alias="smtpMailSettingsNotFound">In the Web.config file, system.net/mailsettings could not be found.</key>
<key alias="smtpMailSettingsHostNotConfigured">In the Web.config file system.net/mailsettings section, the host is not configured.</key>
<key alias="smtpMailSettingsConnectionSuccess">SMTP settings are configured correctly and the service is operating as expected.</key>
<key alias="smtpMailSettingsConnectionFail">The SMTP server configured with host '%0%' and port '%1%' could not be reached. Please check to ensure the SMTP settings in the Web.config file system.net/mailsettings are correct.</key>
<key alias="notificationEmailsCheckSuccessMessage"><![CDATA[Notification email has been set to <strong>%0%</strong>.]]></key>
<key alias="notificationEmailsCheckErrorMessage"><![CDATA[Notification email is still set to the default value of <strong>%0%</strong>.]]></key>
</area>
</language>
@@ -1276,4 +1276,106 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="enterCustomValidation">...or enter a custom validation</key>
<key alias="fieldIsMandatory">Field is mandatory</key>
</area>
<area alias="healthcheck">
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
2: XPath
3: Configuration file path
-->
<key alias="checkSuccessMessage">Value is set to the recommended value: '%0%'.</key>
<key alias="rectifySuccessMessage">Value was set to '%1%' for XPath '%2%' in configuration file '%3%'.</key>
<key alias="checkErrorMessageDifferentExpectedValue">Expected value '%1%' for '%2%' in configuration file '%3%', but found '%0%'.</key>
<key alias="checkErrorMessageUnexpectedValue">Found unexpected value '%0%' for '%2%' in configuration file '%3%'.</key>
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
-->
<key alias="customErrorsCheckSuccessMessage">Custom errors are set to '%0%'.</key>
<key alias="customErrorsCheckErrorMessage">Custom errors are currently set to '%0%'. It is recommended to set this to '%1%' before go live.</key>
<key alias="customErrorsCheckRectifySuccessMessage">Custom errors successfully set to '%0%'.</key>
<key alias="macroErrorModeCheckSuccessMessage">MacroErrors are set to '%0%'.</key>
<key alias="macroErrorModeCheckErrorMessage">MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely when there's any errors in macros. Rectifying this will set the value to '%1%'.</key>
<key alias="macroErrorModeCheckRectifySuccessMessage">MacroErrors are now set to '%0%'.</key>
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
2: Server version
-->
<key alias="trySkipIisCustomErrorsCheckSuccessMessage">Try Skip IIS Custom Errors is set to '%0%' and you're using IIS version '%1%'.</key>
<key alias="trySkipIisCustomErrorsCheckErrorMessage">Try Skip IIS Custom Errors is currently '%0%'. It is recommended to set this to '%1%' for your IIS version (%2%).</key>
<key alias="trySkipIisCustomErrorsCheckRectifySuccessMessage">Try Skip IIS Custom Errors successfully set to '%0%'.</key>
<!-- The following keys get predefined tokens passed in that are not all the same, like above -->
<key alias="configurationServiceFileNotFound">File does not exist: '%0%'.</key>
<key alias="configurationServiceNodeNotFound"><![CDATA[Unable to find <strong>'%0%'</strong> in config file <strong>'%1%'</strong>.]]></key>
<key alias="configurationServiceError">There was an error, check log for full error: %0%.</key>
<key alias="xmlDataIntegrityCheckMembers">Total XML: %0%, Total: %1%</key>
<key alias="xmlDataIntegrityCheckMedia">Total XML: %0%, Total: %1%</key>
<key alias="xmlDataIntegrityCheckContent">Total XML: %0%, Total published: %1%</key>
<key alias="httpsCheckInvalidCertificate">Certificate validation error: '%0%'</key>
<key alias="httpsCheckInvalidUrl">Error pinging the URL %0% - '%1%'</key>
<key alias="httpsCheckIsCurrentSchemeHttps">You are currently %0% viewing the site using the HTTPS scheme.</key>
<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>
<key alias="httpsCheckConfigurationCheckResult">The appSetting 'umbracoUseSSL' is set to '%0%' in your web.config file, your cookies are %1% marked as secure.</key>
<key alias="httpsCheckEnableHttpsError">Could not update the 'umbracoUseSSL' setting in your web.config file. Error: %0%</key>
<!-- The following keys don't get tokens passed in -->
<key alias="httpsCheckEnableHttpsButton">Enable HTTPS</key>
<key alias="httpsCheckEnableHttpsDescription">Sets umbracoSSL setting to true in the appSettings of the web.config file.</key>
<key alias="httpsCheckEnableHttpsSuccess">The appSetting 'umbracoUseSSL' is now set to 'true' in your web.config file, your cookies will be marked as secure.</key>
<key alias="rectifyButton">Fix</key>
<key alias="cannotRectifyShouldNotEqual">Cannot fix a check with a value comparison type of 'ShouldNotEqual'.</key>
<key alias="cannotRectifyShouldEqualWithValue">Cannot fix a check with a value comparison type of 'ShouldEqual' with a provided value.</key>
<key alias="valueToRectifyNotProvided">Value to fix check not provided.</key>
<key alias="compilationDebugCheckSuccessMessage">Debug compilation mode is disabled.</key>
<key alias="compilationDebugCheckErrorMessage">Debug compilation mode is currently enabled. It is recommended to disable this setting before go live.</key>
<key alias="compilationDebugCheckRectifySuccessMessage">Debug compilation mode successfully disabled.</key>
<key alias="traceModeCheckSuccessMessage">Trace mode is disabled.</key>
<key alias="traceModeCheckErrorMessage">Trace mode is currently enabled. It is recommended to disable this setting before go live.</key>
<key alias="traceModeCheckRectifySuccessMessage">Trace mode successfully disabled.</key>
<key alias="folderPermissionsCheckMessage">All folders have the correct permissions set.</key>
<!-- The following keys get these tokens passed in:
0: Comma delimitted list of failed folder paths
-->
<key alias="requiredFolderPermissionFailed"><![CDATA[The following folders must be set up with modify permissions but could not be acccessed: <strong>%0%</strong>.]]></key>
<key alias="optionalFolderPermissionFailed"><![CDATA[The following folders must be set up with modify permissions for certain Umbraco operations to function but could not be acccessed: <strong>%0%</strong>. If they aren't being written to no action need be taken.]]></key>
<key alias="filePermissionsCheckMessage">All files have the correct permissions set.</key>
<!-- The following keys get these tokens passed in:
0: Comma delimitted list of failed folder paths
-->
<key alias="requiredFilePermissionFailed"><![CDATA[The following files must be set up with write permissions but could not be acccessed: <strong>%0%</strong>.]]></key>
<key alias="optionalFilePermissionFailed"><![CDATA[The following files must be set up with write permissions for certain Umbraco operations to function but could not be acccessed: <strong>%0%</strong>. If they aren't being written to no action need be taken.]]></key>
<key alias="clickJackingCheckHeaderFound"><![CDATA[The header or meta-tag <strong>X-Frame-Options</strong> used to control whether a site can be IFRAMed by another was found.]]></key>
<key alias="clickJackingCheckHeaderNotFound"><![CDATA[The header or meta-tag <strong>X-Frame-Options</strong> used to control whether a site can be IFRAMed by another was not found.]]></key>
<key alias="clickJackingSetHeaderInConfig">Set Header in Config</key>
<key alias="clickJackingSetHeaderInConfigDescription">Adds a value to the httpProtocol/customHeaders section of web.config to prevent the site being IFRAMed by other websites.</key>
<key alias="clickJackingSetHeaderInConfigSuccess">A setting to create a header preventing IFRAMing of the site by other websites has been added to your web.config file.</key>
<key alias="clickJackingSetHeaderInConfigError">Could not update web.config file. Error: %0%</key>
<!-- The following key get these tokens passed in:
0: Comma delimitted list of headers found
-->
<key alias="excessiveHeadersFound"><![CDATA[The following headers revealing information about the website technology were found: <strong>%0%</strong>.]]></key>
<key alias="excessiveHeadersNotFound">No headers revealing information about the website technology were found.</key>
<key alias="smtpMailSettingsNotFound">In the Web.config file, system.net/mailsettings could not be found.</key>
<key alias="smtpMailSettingsHostNotConfigured">In the Web.config file system.net/mailsettings section, the host is not configured.</key>
<key alias="smtpMailSettingsConnectionSuccess">SMTP settings are configured correctly and the service is operating as expected.</key>
<key alias="smtpMailSettingsConnectionFail">The SMTP server configured with host '%0%' and port '%1%' could not be reached. Please check to ensure the SMTP settings in the Web.config file system.net/mailsettings are correct.</key>
<key alias="notificationEmailsCheckSuccessMessage"><![CDATA[Notification email has been set to <strong>%0%</strong>.]]></key>
<key alias="notificationEmailsCheckErrorMessage"><![CDATA[Notification email is still set to the default value of <strong>%0%</strong>.]]></key>
</area>
</language>
@@ -28,6 +28,7 @@ using Umbraco.Core.Manifest;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Security;
using Umbraco.Web.HealthCheck;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
@@ -350,6 +351,10 @@ namespace Umbraco.Web.Editors
{
"xmlDataIntegrityBaseUrl", Url.GetUmbracoApiServiceBaseUrl<XmlDataIntegrityController>(
controller => controller.CheckContentXmlTable())
},
{
"healthCheckBaseUrl", Url.GetUmbracoApiServiceBaseUrl<HealthCheckController>(
controller => controller.GetAllHealthChecks())
}
}
},
+1 -19
View File
@@ -594,25 +594,7 @@ namespace Umbraco.Web.Editors
return Request.CreateResponse(HttpStatusCode.OK, tempFiles);
}
/// <summary>
/// This is used for the response of PostAddFile so that we can analyze the response in a filter and remove the
/// temporary files that were created.
/// </summary>
[DataContract]
private class PostedFiles : IHaveUploadedFiles, INotificationModel
{
public PostedFiles()
{
UploadedFiles = new List<ContentItemFile>();
Notifications = new List<Notification>();
}
public List<ContentItemFile> UploadedFiles { get; private set; }
[DataMember(Name = "notifications")]
public List<Notification> Notifications { get; private set; }
}
/// <summary>
/// Ensures the item can be moved/copied to the new location
/// </summary>
@@ -4,21 +4,39 @@ using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.UI.WebControls;
using System.Xml;
using System.Xml.Linq;
using umbraco;
using Umbraco.Core.Auditing;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.packager;
using umbraco.cms.businesslogic.packager.repositories;
using umbraco.cms.businesslogic.web;
using umbraco.cms.presentation.Trees;
using umbraco.presentation.developer.packages;
using umbraco.webservices;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Packaging.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.UI;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using File = System.IO.File;
using Notification = Umbraco.Web.Models.ContentEditing.Notification;
using Settings = umbraco.cms.businesslogic.packager.Settings;
namespace Umbraco.Web.Editors
{
@@ -26,9 +44,303 @@ namespace Umbraco.Web.Editors
[UmbracoApplicationAuthorize(Core.Constants.Applications.Developer)]
public class PackageInstallController : UmbracoAuthorizedJsonController
{
[HttpPost]
public IHttpActionResult Uninstall(int packageId)
{
var pack = InstalledPackage.GetById(packageId);
if (pack == null) return NotFound();
PerformUninstall(pack);
return Ok();
}
/// <summary>
/// SORRY :( I didn't have time to put this in a service somewhere - the old packager did this all manually too
/// </summary>
/// <param name="pack"></param>
protected void PerformUninstall(InstalledPackage pack)
{
if (pack == null) throw new ArgumentNullException("pack");
var refreshCache = false;
//Uninstall templates
foreach (var item in pack.Data.Templates.ToArray())
{
int nId;
if (int.TryParse(item, out nId) == false) continue;
var found = Services.FileService.GetTemplate(nId);
if (found != null)
{
ApplicationContext.Services.FileService.DeleteTemplate(found.Alias, Security.GetUserId());
}
pack.Data.Templates.Remove(nId.ToString());
}
//Uninstall macros
foreach (var item in pack.Data.Macros.ToArray())
{
int nId;
if (int.TryParse(item, out nId) == false) continue;
var macro = Services.MacroService.GetById(nId);
if (macro != null)
{
Services.MacroService.Delete(macro);
}
pack.Data.Macros.Remove(nId.ToString());
}
//Remove Document Types
var contentTypes = new List<IContentType>();
var contentTypeService = Services.ContentTypeService;
foreach (var item in pack.Data.Documenttypes.ToArray())
{
int nId;
if (int.TryParse(item, out nId) == false) continue;
var contentType = contentTypeService.GetContentType(nId);
if (contentType == null) continue;
contentTypes.Add(contentType);
pack.Data.Documenttypes.Remove(nId.ToString(CultureInfo.InvariantCulture));
// refresh content cache when document types are removed
refreshCache = true;
}
//Order the DocumentTypes before removing them
if (contentTypes.Any())
{
var orderedTypes = from contentType in contentTypes
orderby contentType.ParentId descending, contentType.Id descending
select contentType;
foreach (var contentType in orderedTypes)
{
contentTypeService.Delete(contentType);
}
}
//Remove Dictionary items
foreach (var item in pack.Data.DictionaryItems.ToArray())
{
int nId;
if (int.TryParse(item, out nId) == false) continue;
var di = Services.LocalizationService.GetDictionaryItemById(nId);
if (di != null)
{
Services.LocalizationService.Delete(di);
}
pack.Data.DictionaryItems.Remove(nId.ToString());
}
//Remove Data types
foreach (var item in pack.Data.DataTypes.ToArray())
{
int nId;
if (int.TryParse(item, out nId) == false) continue;
var dtd = Services.DataTypeService.GetDataTypeDefinitionById(nId);
if (dtd != null)
{
Services.DataTypeService.Delete(dtd);
}
pack.Data.DataTypes.Remove(nId.ToString());
}
pack.Save();
// uninstall actions
//TODO: We should probably report errors to the UI!!
// This never happened before though, but we should do something now
if (pack.Data.Actions.IsNullOrWhiteSpace() == false)
{
try
{
var actionsXml = new XmlDocument();
actionsXml.LoadXml("<Actions>" + pack.Data.Actions + "</Actions>");
LogHelper.Debug<installedPackage>("executing undo actions: {0}", () => actionsXml.OuterXml);
foreach (XmlNode n in actionsXml.DocumentElement.SelectNodes("//Action"))
{
try
{
global::umbraco.cms.businesslogic.packager.PackageAction
.UndoPackageAction(pack.Data.Name, n.Attributes["alias"].Value, n);
}
catch (Exception ex)
{
LogHelper.Error<installedPackage>("An error occurred running undo actions", ex);
}
}
}
catch (Exception ex)
{
LogHelper.Error<installedPackage>("An error occurred running undo actions", ex);
}
}
//moved remove of files here so custom package actions can still undo
//Remove files
foreach (var item in pack.Data.Files.ToArray())
{
//here we need to try to find the file in question as most packages does not support the tilde char
var file = IOHelper.FindFile(item);
if (file != null)
{
var filePath = IOHelper.MapPath(file);
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
pack.Data.Files.Remove(file);
}
pack.Save();
pack.Delete(Security.GetUserId());
//TODO: Legacy - probably not needed
if (refreshCache)
{
library.RefreshContent();
}
TreeDefinitionCollection.Instance.ReRegisterTrees();
global::umbraco.BusinessLogic.Actions.Action.ReRegisterActionsAndHandlers();
}
public IEnumerable<InstalledPackageModel> GetInstalled()
{
return data.GetAllPackages(IOHelper.MapPath(Settings.InstalledPackagesSettings))
.Select(pack => new InstalledPackageModel
{
Name = pack.Name,
Id = pack.Id,
Author = pack.Author,
Version = pack.Version,
Url = pack.Url,
License = pack.License,
LicenseUrl = pack.LicenseUrl,
Files = pack.Files
}).ToList();
}
private void PopulateFromPackageData(LocalPackageInstallModel model)
{
var ins = new global::umbraco.cms.businesslogic.packager.Installer(Security.CurrentUser.Id);
//this will load in all the metadata too
var tempDir = ins.Import(model.ZipFilePath, false);
model.TemporaryDirectoryPath = Path.Combine(SystemDirectories.Data, tempDir);
model.Name = ins.Name;
model.Author = ins.Author;
model.AuthorUrl = ins.AuthorUrl;
model.License = ins.License;
model.LicenseUrl = ins.LicenseUrl;
model.ReadMe = ins.ReadMe;
model.ConflictingMacroAliases = ins.ConflictingMacroAliases;
model.ConflictingStyleSheetNames = ins.ConflictingStyleSheetNames;
model.ConflictingTemplateAliases = ins.ConflictingTemplateAliases;
model.ContainsBinaryFileErrors = ins.ContainsBinaryFileErrors;
model.ContainsLegacyPropertyEditors = ins.ContainsLegacyPropertyEditors;
model.ContainsMacroConflict = ins.ContainsMacroConflict;
model.ContainsStyleSheetConflicts = ins.ContainsStyleSheeConflicts;
model.ContainsTemplateConflicts = ins.ContainsTemplateConflicts;
model.ContainsUnsecureFiles = ins.ContainsUnsecureFiles;
model.Url = ins.Url;
model.Version = ins.Version;
model.UmbracoVersion = ins.RequirementsType == RequirementsType.Strict
? string.Format("{0}.{1}.{2}", ins.RequirementsMajor, ins.RequirementsMinor, ins.RequirementsPatch)
: string.Empty;
//now we need to check for version comparison
model.IsCompatible = true;
if (ins.RequirementsType == RequirementsType.Strict)
{
var packageMinVersion = new System.Version(ins.RequirementsMajor, ins.RequirementsMinor, ins.RequirementsPatch);
if (UmbracoVersion.Current < packageMinVersion)
{
model.IsCompatible = false;
}
}
}
[HttpPost]
[FileUploadCleanupFilter(false)]
public async Task<LocalPackageInstallModel> UploadLocalPackage()
{
if (Request.Content.IsMimeMultipartContent() == false)
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads");
//ensure it exists
Directory.CreateDirectory(root);
var provider = new MultipartFormDataStreamProvider(root);
var result = await Request.Content.ReadAsMultipartAsync(provider);
//must have a file
if (result.FileData.Count == 0)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
//TODO: App/Tree Permissions?
var model = new LocalPackageInstallModel
{
PackageGuid = Guid.NewGuid()
};
//get the files
foreach (var file in result.FileData)
{
var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' });
var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower();
//TODO: Only allow .zip
if (ext.InvariantEquals("zip") || ext.InvariantEquals("umb"))
{
//TODO: Currently it has to be here, it's not ideal but that's the way it is right now
var packageTempDir = IOHelper.MapPath(SystemDirectories.Data);
//ensure it's there
Directory.CreateDirectory(packageTempDir);
//copy it - must always be '.umb' for the installer thing to work
//the file name must be a GUID - this is what the packager expects (strange yes)
//because essentially this is creating a temporary package Id that will be used
//for unpacking/installing/etc...
model.ZipFilePath = model.PackageGuid + ".umb";
var packageTempFileLocation = Path.Combine(packageTempDir, model.ZipFilePath);
File.Copy(file.LocalFileName, packageTempFileLocation, true);
//Populate the model from the metadata in the package file (zip file)
PopulateFromPackageData(model);
//TODO: We need to add the 'strict' requirement to the installer
}
else
{
model.Notifications.Add(new Notification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("media/disallowedFileType"),
SpeechBubbleIcon.Warning));
}
}
return model;
}
/// <summary>
/// Gets the package from Our to install
/// </summary>
/// <param name="packageGuid"></param>
/// <returns></returns>
[HttpGet]
public PackageInstallModel Fetch(string packageGuid)
public LocalPackageInstallModel Fetch(string packageGuid)
{
//Default path
string path = Path.Combine("packages", packageGuid + ".umb");
@@ -40,26 +352,52 @@ namespace Umbraco.Web.Editors
path = our.fetch(packageGuid, Security.CurrentUser.Id);
}
}
var p = new PackageInstallModel();
p.PackageGuid = Guid.Parse(packageGuid);
p.RepositoryGuid = Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66");
p.ZipFilePath = path;
//p.ZipFilePath = Path.Combine("temp", "package.umb");
return p;
var model = new LocalPackageInstallModel
{
PackageGuid = Guid.Parse(packageGuid),
RepositoryGuid = Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66"),
ZipFilePath = path
};
//Populate the model from the metadata in the package file (zip file)
PopulateFromPackageData(model);
return model;
}
/// <summary>
/// Extracts the package zip and gets the packages information
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public PackageInstallModel Import(PackageInstallModel model)
{
var ins = new global::umbraco.cms.businesslogic.packager.Installer(Security.CurrentUser.Id);
model.TemporaryDirectoryPath = Path.Combine(SystemDirectories.Data, ins.Import(model.ZipFilePath));
var tempPath = ins.Import(model.ZipFilePath);
//now we need to check for version comparison
if (ins.RequirementsType == RequirementsType.Strict)
{
var packageMinVersion = new System.Version(ins.RequirementsMajor, ins.RequirementsMinor, ins.RequirementsPatch);
if (UmbracoVersion.Current < packageMinVersion)
{
throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("This package cannot be installed, it requires a minimum Umbraco version of " + packageMinVersion));
}
}
model.TemporaryDirectoryPath = Path.Combine(SystemDirectories.Data, tempPath);
model.Id = ins.CreateManifest( IOHelper.MapPath(model.TemporaryDirectoryPath), model.PackageGuid.ToString(), model.RepositoryGuid.ToString());
return model;
}
/// <summary>
/// Installs the package files
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public PackageInstallModel InstallFiles(PackageInstallModel model)
{
@@ -69,7 +407,11 @@ namespace Umbraco.Web.Editors
return model;
}
/// <summary>
/// Installs the packages data/business logic
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public PackageInstallModel InstallData(PackageInstallModel model)
{
@@ -79,11 +421,16 @@ namespace Umbraco.Web.Editors
return model;
}
/// <summary>
/// Cleans up the package installation
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public PackageInstallModel CleanUp(PackageInstallModel model)
public PackageInstallResult CleanUp(PackageInstallModel model)
{
var ins = new global::umbraco.cms.businesslogic.packager.Installer(Security.CurrentUser.Id);
var tempDir = IOHelper.MapPath(model.TemporaryDirectoryPath);
ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath));
ins.InstallCleanUp(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath));
@@ -96,7 +443,30 @@ namespace Umbraco.Web.Editors
global::umbraco.BusinessLogic.Actions.Action.ReRegisterActionsAndHandlers();
return model;
var redirectUrl = "";
if (ins.Control.IsNullOrWhiteSpace())
{
redirectUrl = string.Format("/developer/framed/{0}",
Uri.EscapeDataString(
string.Format("/umbraco/developer/Packages/installer.aspx?installing=custominstaller&dir={0}&pId={1}&customUrl={2}", tempDir, model.Id, ins.Url)));
}
else
{
redirectUrl = string.Format("/developer/framed/{0}",
Uri.EscapeDataString(
string.Format("/umbraco/developer/Packages/installer.aspx?installing=custominstaller&dir={0}&pId={1}&customControl={2}&customUrl={3}", tempDir, model.Id, ins.Control, ins.Url)));
}
return new PackageInstallResult
{
Id = model.Id,
ZipFilePath = model.ZipFilePath,
PackageGuid = model.PackageGuid,
RepositoryGuid = model.RepositoryGuid,
TemporaryDirectoryPath = model.TemporaryDirectoryPath,
PostInstallationPath = redirectUrl
};
}
@@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.Config
{
public abstract class AbstractConfigCheck : HealthCheck
{
private readonly ConfigurationService _configurationService;
private readonly ILocalizedTextService _textService;
/// <summary>
/// Gets the config file path.
/// </summary>
public abstract string FilePath { get; }
/// <summary>
/// Gets XPath statement to the config element to check.
/// </summary>
public abstract string XPath { get; }
/// <summary>
/// Gets the values to compare against.
/// </summary>
public abstract IEnumerable<AcceptableConfiguration> Values { get; }
/// <summary>
/// Gets the current value
/// </summary>
public string CurrentValue { get; set; }
/// <summary>
/// Gets the provided value
/// </summary>
public string ProvidedValue { get; set; }
/// <summary>
/// Gets the comparison type for checking the value.
/// </summary>
public abstract ValueComparisonType ValueComparisonType { get; }
protected AbstractConfigCheck(HealthCheckContext healthCheckContext) : base(healthCheckContext)
{
_textService = healthCheckContext.ApplicationContext.Services.TextService;
_configurationService = new ConfigurationService(AbsoluteFilePath, XPath);
}
/// <summary>
/// Gets the name of the file.
/// </summary>
private string FileName
{
get { return Path.GetFileName(FilePath); }
}
/// <summary>
/// Gets the absolute file path.
/// </summary>
private string AbsoluteFilePath
{
get { return IOHelper.MapPath(FilePath); }
}
/// <summary>
/// Gets the message for when the check has succeeded.
/// </summary>
public virtual string CheckSuccessMessage
{
get
{
return _textService.Localize("healthcheck/checkSuccessMessage",
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath });
}
}
/// <summary>
/// Gets the message for when the check has failed.
/// </summary>
public virtual string CheckErrorMessage
{
get
{
return ValueComparisonType == ValueComparisonType.ShouldEqual
? _textService.Localize("healthcheck/checkErrorMessageDifferentExpectedValue",
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath })
: _textService.Localize("healthcheck/checkErrorMessageUnexpectedValue",
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, XPath, AbsoluteFilePath });
}
}
/// <summary>
/// Gets the rectify success message.
/// </summary>
public virtual string RectifySuccessMessage
{
get
{
var recommendedValue = Values.FirstOrDefault(v => v.IsRecommended);
var rectifiedValue = recommendedValue != null
? recommendedValue.Value
: ProvidedValue;
return _textService.Localize("healthcheck/rectifySuccessMessage",
new[]
{
CurrentValue,
rectifiedValue,
XPath,
AbsoluteFilePath
});
}
}
/// <summary>
/// Gets a value indicating whether this check can be rectified automatically.
/// </summary>
public virtual bool CanRectify
{
get { return ValueComparisonType == ValueComparisonType.ShouldEqual; }
}
/// <summary>
/// Gets a value indicating whether this check can be rectified automatically if a value is provided.
/// </summary>
public virtual bool CanRectifyWithValue
{
get { return ValueComparisonType == ValueComparisonType.ShouldNotEqual; }
}
public override IEnumerable<HealthCheckStatus> GetStatus()
{
var configValue = _configurationService.GetConfigurationValue();
if (configValue.Success == false)
{
var message = configValue.Result;
return new[] { new HealthCheckStatus(message) { ResultType = StatusResultType.Error } };
}
CurrentValue = configValue.Result;
var valueFound = Values.Any(value => string.Equals(CurrentValue, value.Value, StringComparison.InvariantCultureIgnoreCase));
if (ValueComparisonType == ValueComparisonType.ShouldEqual && valueFound || ValueComparisonType == ValueComparisonType.ShouldNotEqual && valueFound == false)
{
var message = string.Format(CheckSuccessMessage, FileName, XPath, Values, CurrentValue);
return new[] { new HealthCheckStatus(message) { ResultType = StatusResultType.Success } };
}
// Declare the action for rectifying the config value
var rectifyAction = new HealthCheckAction("rectify", Id)
{
Name = _textService.Localize("healthcheck/rectifyButton"),
ValueRequired = CanRectifyWithValue,
};
var resultMessage = string.Format(CheckErrorMessage, FileName, XPath, Values, CurrentValue);
return new[]
{
new HealthCheckStatus(resultMessage)
{
ResultType = StatusResultType.Error,
Actions = CanRectify || CanRectifyWithValue ? new[] { rectifyAction } : new HealthCheckAction[0]
}
};
}
/// <summary>
/// Rectifies this check.
/// </summary>
/// <returns></returns>
public virtual HealthCheckStatus Rectify()
{
if (ValueComparisonType == ValueComparisonType.ShouldNotEqual)
throw new InvalidOperationException(_textService.Localize("healthcheck/cannotRectifyShouldNotEqual"));
var recommendedValue = Values.First(v => v.IsRecommended).Value;
return UpdateConfigurationValue(recommendedValue);
}
/// <summary>
/// Rectifies this check with a provided value.
/// </summary>
/// <param name="value">Value provided</param>
/// <returns></returns>
public virtual HealthCheckStatus Rectify(string value)
{
if (ValueComparisonType == ValueComparisonType.ShouldEqual)
throw new InvalidOperationException(_textService.Localize("healthcheck/cannotRectifyShouldEqualWithValue"));
if (string.IsNullOrWhiteSpace(value))
throw new InvalidOperationException(_textService.Localize("healthcheck/valueToRectifyNotProvided"));
// Need to track provided value in order to correctly put together the rectify message
ProvidedValue = value;
return UpdateConfigurationValue(value);
}
private HealthCheckStatus UpdateConfigurationValue(string value)
{
var updateConfigFile = _configurationService.UpdateConfigFile(value);
if (updateConfigFile.Success == false)
{
var message = updateConfigFile.Result;
return new HealthCheckStatus(message) { ResultType = StatusResultType.Error };
}
var resultMessage = string.Format(RectifySuccessMessage, FileName, XPath, Values);
return new HealthCheckStatus(resultMessage) { ResultType = StatusResultType.Success };
}
public override HealthCheckStatus ExecuteAction(HealthCheckAction action)
{
return string.IsNullOrEmpty(action.ProvidedValue)
? Rectify()
: Rectify(action.ProvidedValue);
}
}
}
@@ -0,0 +1,8 @@
namespace Umbraco.Web.HealthCheck.Checks.Config
{
public class AcceptableConfiguration
{
public string Value { get; set; }
public bool IsRecommended { get; set; }
}
}
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.Config
{
[HealthCheck("61214FF3-FC57-4B31-B5CF-1D095C977D6D", "Debug Compilation Mode",
Description = "Leaving debug compilation mode enabled can severely slow down a website and take up more memory on the server.",
Group = "Live Environment")]
public class CompilationDebugCheck : AbstractConfigCheck
{
private readonly ILocalizedTextService _textService;
public CompilationDebugCheck(HealthCheckContext healthCheckContext) : base(healthCheckContext)
{
_textService = healthCheckContext.ApplicationContext.Services.TextService;
}
public override string FilePath
{
get { return "~/Web.config"; }
}
public override string XPath
{
get { return "/configuration/system.web/compilation/@debug"; }
}
public override ValueComparisonType ValueComparisonType
{
get { return ValueComparisonType.ShouldEqual; }
}
public override IEnumerable<AcceptableConfiguration> Values
{
get
{
return new List<AcceptableConfiguration>
{
new AcceptableConfiguration { IsRecommended = true, Value = bool.FalseString.ToLower() }
};
}
}
public override string CheckSuccessMessage
{
get { return _textService.Localize("healthcheck/compilationDebugCheckSuccessMessage"); }
}
public override string CheckErrorMessage
{
get { return _textService.Localize("healthcheck/compilationDebugCheckErrorMessage"); }
}
public override string RectifySuccessMessage
{
get { return _textService.Localize("healthcheck/compilationDebugCheckRectifySuccessMessage"); }
}
}
}
@@ -0,0 +1,115 @@
using System;
using System.IO;
using System.Xml;
using Umbraco.Core.Logging;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.Config
{
//TODO: Add config transform for when config with specified XPath is not found
public class ConfigurationService
{
private readonly string _configFilePath;
private readonly string _xPath;
private readonly ILocalizedTextService _textService;
/// <param name="configFilePath">The absolute file location of the configuration file</param>
/// <param name="xPath">The XPath to select the value</param>
/// <returns></returns>
public ConfigurationService(string configFilePath, string xPath)
{
_configFilePath = configFilePath;
_xPath = xPath;
_textService = UmbracoContext.Current.Application.Services.TextService;
}
/// <summary>
/// Gets a value from a given configuration file with the given XPath
/// </summary>
public ConfigurationServiceResult GetConfigurationValue()
{
try
{
if (File.Exists(_configFilePath) == false)
return new ConfigurationServiceResult
{
Success = false,
Result = _textService.Localize("healthcheck/configurationServiceFileNotFound", new[] { _configFilePath })
};
var xmlDocument = new XmlDocument();
xmlDocument.Load(_configFilePath);
var xmlNode = xmlDocument.SelectSingleNode(_xPath);
if (xmlNode == null)
return new ConfigurationServiceResult
{
Success = false,
Result = _textService.Localize("healthcheck/configurationServiceNodeNotFound", new[] { _xPath, _configFilePath })
};
return new ConfigurationServiceResult
{
Success = true,
Result = string.Format(xmlNode.Value ?? xmlNode.InnerText)
};
}
catch (Exception exception)
{
LogHelper.Error<ConfigurationService>("Error trying to get configuration value", exception);
return new ConfigurationServiceResult
{
Success = false,
Result = _textService.Localize("healthcheck/configurationServiceError", new[] { exception.Message })
};
}
}
/// <summary>
/// Updates a value in a given configuration file with the given XPath
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public ConfigurationServiceResult UpdateConfigFile(string value)
{
try
{
if (File.Exists(_configFilePath) == false)
return new ConfigurationServiceResult
{
Success = false,
Result = _textService.Localize("healthcheck/configurationServiceFileNotFound", new[] { _configFilePath })
};
var xmlDocument = new XmlDocument { PreserveWhitespace = true };
xmlDocument.Load(_configFilePath);
var node = xmlDocument.SelectSingleNode(_xPath);
if (node == null)
return new ConfigurationServiceResult
{
Success = false,
Result = _textService.Localize("healthcheck/configurationServiceNodeNotFound", new[] { _xPath, _configFilePath })
};
if (node.NodeType == XmlNodeType.Element)
node.InnerText = value;
else
node.Value = value;
xmlDocument.Save(_configFilePath);
return new ConfigurationServiceResult { Success = true };
}
catch (Exception exception)
{
LogHelper.Error<ConfigurationService>("Error trying to update configuration", exception);
return new ConfigurationServiceResult
{
Success = false,
Result = _textService.Localize("healthcheck/configurationServiceError", new[] { exception.Message })
};
}
}
}
}
@@ -0,0 +1,8 @@
namespace Umbraco.Web.HealthCheck.Checks.Config
{
public class ConfigurationServiceResult
{
public bool Success { get; set; }
public string Result { get; set; }
}
}
@@ -0,0 +1,74 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Configuration;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.Config
{
[HealthCheck("4090C0A1-2C52-4124-92DD-F028FD066A64", "Custom Errors",
Description = "Leaving custom errors off will display a complete stack trace to your visitors if an exception occurs.",
Group = "Live Environment")]
public class CustomErrorsCheck : AbstractConfigCheck
{
private readonly ILocalizedTextService _textService;
public CustomErrorsCheck(HealthCheckContext healthCheckContext) : base(healthCheckContext)
{
_textService = healthCheckContext.ApplicationContext.Services.TextService;
}
public override string FilePath
{
get { return "~/Web.config"; }
}
public override string XPath
{
get { return "/configuration/system.web/customErrors/@mode"; }
}
public override ValueComparisonType ValueComparisonType
{
get { return ValueComparisonType.ShouldEqual; }
}
public override IEnumerable<AcceptableConfiguration> Values
{
get
{
return new List<AcceptableConfiguration>
{
new AcceptableConfiguration { IsRecommended = true, Value = CustomErrorsMode.RemoteOnly.ToString() },
new AcceptableConfiguration { IsRecommended = false, Value = "On" }
};
}
}
public override string CheckSuccessMessage
{
get
{
return _textService.Localize("healthcheck/customErrorsCheckSuccessMessage",
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value });
}
}
public override string CheckErrorMessage
{
get
{
return _textService.Localize("healthcheck/customErrorsCheckErrorMessage",
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value });
}
}
public override string RectifySuccessMessage
{
get
{
return _textService.Localize("healthcheck/customErrorsCheckRectifySuccessMessage",
new[] { Values.First(v => v.IsRecommended).Value });
}
}
}
}
@@ -0,0 +1,83 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.Config
{
[HealthCheck("D0F7599E-9B2A-4D9E-9883-81C7EDC5616F", "Macro errors",
Description = "Checks to make sure macro errors are not set to throw a YSOD (yellow screen of death), which would prevent certain or all pages from loading completely.",
Group = "Configuration")]
public class MacroErrorsCheck : AbstractConfigCheck
{
private readonly ILocalizedTextService _textService;
public MacroErrorsCheck(HealthCheckContext healthCheckContext) : base(healthCheckContext)
{
_textService = healthCheckContext.ApplicationContext.Services.TextService;
}
public override string FilePath
{
get { return "~/Config/umbracoSettings.config"; }
}
public override string XPath
{
get { return "/settings/content/MacroErrors"; }
}
public override ValueComparisonType ValueComparisonType
{
get { return ValueComparisonType.ShouldEqual; }
}
public override IEnumerable<AcceptableConfiguration> Values
{
get
{
var values = new List<AcceptableConfiguration>
{
new AcceptableConfiguration
{
IsRecommended = true,
Value = "inline"
},
new AcceptableConfiguration
{
IsRecommended = false,
Value = "silent"
}
};
return values;
}
}
public override string CheckSuccessMessage
{
get
{
return _textService.Localize("healthcheck/macroErrorModeCheckSuccessMessage",
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value });
}
}
public override string CheckErrorMessage
{
get
{
return _textService.Localize("healthcheck/macroErrorModeCheckErrorMessage",
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value });
}
}
public override string RectifySuccessMessage
{
get
{
return _textService.Localize("healthcheck/macroErrorModeCheckRectifySuccessMessage",
new[] { Values.First(v => v.IsRecommended).Value });
}
}
}
}
@@ -0,0 +1,55 @@
using System.Collections.Generic;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.Config
{
[HealthCheck("3E2F7B14-4B41-452B-9A30-E67FBC8E1206", "Notification Email Settings",
Description = "If notifications are used, the 'from' email address should be specified and changed from the default value.",
Group = "Configuration")]
public class NotificationEmailCheck : AbstractConfigCheck
{
private readonly ILocalizedTextService _textService;
private const string DefaultFromEmail = "your@email.here";
public NotificationEmailCheck(HealthCheckContext healthCheckContext) : base(healthCheckContext)
{
_textService = healthCheckContext.ApplicationContext.Services.TextService;
}
public override string FilePath
{
get { return "~/Config/umbracoSettings.config"; }
}
public override string XPath
{
get { return "/settings/content/notifications/email"; }
}
public override ValueComparisonType ValueComparisonType
{
get { return ValueComparisonType.ShouldNotEqual; }
}
public override IEnumerable<AcceptableConfiguration> Values
{
get
{
return new List<AcceptableConfiguration>
{
new AcceptableConfiguration { IsRecommended = false, Value = DefaultFromEmail }
};
}
}
public override string CheckSuccessMessage
{
get { return _textService.Localize("healthcheck/notificationEmailsCheckSuccessMessage", new [] { CurrentValue } ); }
}
public override string CheckErrorMessage
{
get { return _textService.Localize("healthcheck/notificationEmailsCheckErrorMessage", new[] { DefaultFromEmail }); }
}
}
}
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.Config
{
[HealthCheck("9BED6EF4-A7F3-457A-8935-B64E9AA8BAB3", "Trace Mode",
Description = "Leaving trace mode enabled can make valuable information about your system available to hackers.",
Group = "Live Environment")]
public class TraceCheck : AbstractConfigCheck
{
private readonly ILocalizedTextService _textService;
public TraceCheck(HealthCheckContext healthCheckContext) : base(healthCheckContext)
{
_textService = healthCheckContext.ApplicationContext.Services.TextService;
}
public override string FilePath
{
get { return "~/Web.config"; }
}
public override string XPath
{
get { return "/configuration/system.web/trace/@enabled"; }
}
public override ValueComparisonType ValueComparisonType
{
get { return ValueComparisonType.ShouldEqual; }
}
public override IEnumerable<AcceptableConfiguration> Values
{
get
{
return new List<AcceptableConfiguration>
{
new AcceptableConfiguration { IsRecommended = true, Value = bool.FalseString.ToLower() }
};
}
}
public override string CheckSuccessMessage
{
get { return _textService.Localize("healthcheck/traceModeCheckSuccessMessage"); }
}
public override string CheckErrorMessage
{
get { return _textService.Localize("healthcheck/traceModeCheckErrorMessage"); }
}
public override string RectifySuccessMessage
{
get { return _textService.Localize("healthcheck/traceModeCheckRectifySuccessMessage"); }
}
}
}
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.Config
{
[HealthCheck("046A066C-4FB2-4937-B931-069964E16C66", "Try Skip IIS Custom Errors",
Description = "Starting with IIS 7.5, this must be set to true for Umbraco 404 pages to show. Otherwise, IIS will takeover and render its built-in error page.",
Group = "Configuration")]
public class TrySkipIisCustomErrorsCheck : AbstractConfigCheck
{
private readonly Version _serverVersion = HttpRuntime.IISVersion;
private readonly ILocalizedTextService _textService;
public TrySkipIisCustomErrorsCheck(HealthCheckContext healthCheckContext) : base(healthCheckContext)
{
_textService = healthCheckContext.ApplicationContext.Services.TextService;
}
public override string FilePath
{
get { return "~/Config/umbracoSettings.config"; }
}
public override string XPath
{
get { return "/settings/web.routing/@trySkipIisCustomErrors"; }
}
public override ValueComparisonType ValueComparisonType
{
get { return ValueComparisonType.ShouldEqual; }
}
public override IEnumerable<AcceptableConfiguration> Values
{
get
{
var recommendedValue = _serverVersion >= new Version("7.5.0")
? bool.TrueString.ToLower()
: bool.FalseString.ToLower();
return new List<AcceptableConfiguration> { new AcceptableConfiguration { IsRecommended = true, Value = recommendedValue } };
}
}
public override string CheckSuccessMessage
{
get
{
return _textService.Localize("healthcheck/trySkipIisCustomErrorsCheckSuccessMessage",
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, _serverVersion.ToString() });
}
}
public override string CheckErrorMessage
{
get
{
return _textService.Localize("healthcheck/trySkipIisCustomErrorsCheckErrorMessage",
new[] { CurrentValue, Values.First(v => v.IsRecommended).Value, _serverVersion.ToString() });
}
}
public override string RectifySuccessMessage
{
get
{
return _textService.Localize("healthcheck/trySkipIisCustomErrorsCheckRectifySuccessMessage",
new[] { Values.First(v => v.IsRecommended).Value, _serverVersion.ToString() });
}
}
}
}
@@ -0,0 +1,8 @@
namespace Umbraco.Web.HealthCheck.Checks.Config
{
public enum ValueComparisonType
{
ShouldEqual,
ShouldNotEqual,
}
}

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