diff --git a/README.md b/README.md
index 3e9be6cb5f..1d42543eb3 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
Umbraco CMS
===========
-The friendliest, most flexible and fastest growing ASP.NET CMS used by more than 350,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
+The friendliest, most flexible and fastest growing ASP.NET CMS used by more than 390,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
[](https://vimeo.com/172382998/)
diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs
index 1c8b3dabee..2464e1ba7b 100644
--- a/src/Umbraco.Core/DatabaseContext.cs
+++ b/src/Umbraco.Core/DatabaseContext.cs
@@ -33,6 +33,12 @@ namespace Umbraco.Core
private string _connectionString;
private string _providerName;
private DatabaseSchemaResult _result;
+ private DateTime? _connectionLastChecked = null;
+
+ ///
+ /// The number of minutes to throttle the checks to CanConnect
+ ///
+ private const int ConnectionCheckMinutes = 1;
[Obsolete("Use the constructor specifying all dependencies instead")]
public DatabaseContext(IDatabaseFactory factory)
@@ -148,13 +154,28 @@ namespace Umbraco.Core
{
get
{
- if (IsDatabaseConfigured == false) return false;
- var canConnect = DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DatabaseProvider);
- LogHelper.Info("CanConnect = " + canConnect);
- return canConnect;
+ if (IsDatabaseConfigured == false)
+ return false;
+
+ //Don't check again if the timeout period hasn't elapsed
+ //this ensures we don't keep checking the connection too many times in a row like during startup.
+ //Do check if the _connectionLastChecked is null which means we're just initializing or it could
+ //not connect last time it was checked.
+ if ((_connectionLastChecked.HasValue && (DateTime.Now - _connectionLastChecked.Value).TotalMinutes > ConnectionCheckMinutes)
+ || _connectionLastChecked.HasValue == false)
+ {
+ var canConnect = DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DatabaseProvider);
+ LogHelper.Info("CanConnect = " + canConnect);
+
+ _connectionLastChecked = canConnect == false ? null : (DateTime?) DateTime.Now;
+ return canConnect;
+ }
+
+ return _connectionLastChecked.HasValue;
}
}
+
///
/// Gets the configured umbraco db connection string.
///
diff --git a/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
index c0c7b2a777..86072c45d5 100644
--- a/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
+++ b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
@@ -4,9 +4,11 @@ using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
+using System.Data.SqlServerCe;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using MySql.Data.MySqlClient;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Persistence
@@ -69,6 +71,38 @@ namespace Umbraco.Core.Persistence
}
}
+ public static string GetConnStringExSecurityInfo(this IDbConnection connection)
+ {
+ try
+ {
+ if (connection is SqlConnection)
+ {
+ var builder = new SqlConnectionStringBuilder(connection.ConnectionString);
+ return string.Format("DataSource: {0}, InitialCatalog: {1}", builder.DataSource, builder.InitialCatalog);
+ }
+
+ if (connection is SqlCeConnection)
+ {
+ var builder = new SqlCeConnectionStringBuilder(connection.ConnectionString);
+ return string.Format("DataSource: {0}", builder.DataSource);
+ }
+
+ if (connection is MySqlConnection)
+ {
+ var builder = new MySqlConnectionStringBuilder(connection.ConnectionString);
+ return string.Format("Server: {0}, Database: {1}", builder.Server, builder.Database);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WarnWithException(typeof(DbConnectionExtensions),
+ "Could not resolve connection string parameters", ex);
+ return "(Could not resolve)";
+ }
+
+ throw new ArgumentException(string.Format("The connection type {0} is not supported", connection.GetType()));
+ }
+
public static bool IsAvailable(this IDbConnection connection)
{
try
@@ -79,7 +113,8 @@ namespace Umbraco.Core.Persistence
catch (DbException exc)
{
// Don't swallow this error, the exception is super handy for knowing "why" its not available
- LogHelper.WarnWithException("Configured database is reporting as not being available!", exc);
+ LogHelper.WarnWithException(typeof(DbConnectionExtensions),
+ "Configured database is reporting as not being available! {0}", exc, connection.GetConnStringExSecurityInfo);
return false;
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 0a47b428c7..f4bd24aa64 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -852,7 +852,8 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
if (withCache)
{
var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId));
- if (cached != null && cached.Published)
+ //only use this cached version if the dto returned is also the publish version, they must match
+ if (cached != null && cached.Published && dto.Published)
{
content[i] = cached;
continue;
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js
index 5dd7d266df..60bb4957ae 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js
@@ -172,6 +172,55 @@ angular.module("umbraco.directives")
});
+ // pin toolbar to top of screen if we have focus and it scrolls off the screen
+ var pinToolbar = function () {
+
+ var _toolbar = $(editor.editorContainer).find(".mce-toolbar");
+ var toolbarHeight = _toolbar.height();
+
+ var _tinyMce = $(editor.editorContainer);
+ var tinyMceRect = _tinyMce[0].getBoundingClientRect();
+ var tinyMceTop = tinyMceRect.top;
+ var tinyMceBottom = tinyMceRect.bottom;
+ var tinyMceWidth = tinyMceRect.width;
+
+ var _tinyMceEditArea = _tinyMce.find(".mce-edit-area");
+
+ // set padding in top of mce so the content does not "jump" up
+ _tinyMceEditArea.css("padding-top", toolbarHeight);
+
+ if (tinyMceTop < 160 && ((160 + toolbarHeight) < tinyMceBottom)) {
+ _toolbar
+ .css("visibility", "visible")
+ .css("position", "fixed")
+ .css("top", "160px")
+ .css("margin-top", "0")
+ .css("width", tinyMceWidth);
+ } else {
+ _toolbar
+ .css("visibility", "visible")
+ .css("position", "absolute")
+ .css("top", "auto")
+ .css("margin-top", "0")
+ .css("width", tinyMceWidth);
+ }
+
+ };
+
+ // unpin toolbar to top of screen
+ var unpinToolbar = function() {
+
+ var _toolbar = $(editor.editorContainer).find(".mce-toolbar");
+ var _tinyMce = $(editor.editorContainer);
+ var _tinyMceEditArea = _tinyMce.find(".mce-edit-area");
+
+ // reset padding in top of mce so the content does not "jump" up
+ _tinyMceEditArea.css("padding-top", "0");
+
+ _toolbar.css("position", "static");
+
+ };
+
//when we leave the editor (maybe)
editor.on('blur', function (e) {
editor.save();
@@ -185,6 +234,9 @@ angular.module("umbraco.directives")
scope.onBlur();
}
+ unpinToolbar();
+ $('.umb-panel-body').off('scroll', pinToolbar);
+
});
});
@@ -196,6 +248,9 @@ angular.module("umbraco.directives")
scope.onFocus();
}
+ pinToolbar();
+ $('.umb-panel-body').on('scroll', pinToolbar);
+
});
});
@@ -207,6 +262,9 @@ angular.module("umbraco.directives")
scope.onClick();
}
+ pinToolbar();
+ $('.umb-panel-body').on('scroll', pinToolbar);
+
});
});
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
index a3d1e5b0c6..67df26d50a 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
@@ -103,15 +103,15 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
* @description
* This returns a promise with an underlying http call, it is a helper method to reduce
* the amount of duplicate code needed to query http resources and automatically handle any
- * Http errors. See /docs/source/using-promises-resources.md
+ * 500 Http server errors.
*
- * @param {object} opts A mixed object which can either be a string representing the error message to be
- * returned OR an object containing either:
+ * @param {object} opts A mixed object which can either be a `string` representing the error message to be
+ * returned OR an `object` containing either:
* { success: successCallback, errorMsg: errorMessage }
* OR
* { success: successCallback, error: errorCallback }
- * In both of the above, the successCallback must accept these parameters: data, status, headers, config
- * If using the errorCallback it must accept these parameters: data, status, headers, config
+ * In both of the above, the successCallback must accept these parameters: `data`, `status`, `headers`, `config`
+ * If using the errorCallback it must accept these parameters: `data`, `status`, `headers`, `config`
* The success callback must return the data which will be resolved by the deferred object.
* The error callback must return an object containing: {errorMsg: errorMessage, data: originalData, status: status }
*/
diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2-Fluid.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2-Fluid.cshtml
index f6b93139ce..d8e9f40e15 100644
--- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2-Fluid.cshtml
+++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2-Fluid.cshtml
@@ -66,7 +66,7 @@
if(cfg != null)
foreach (JProperty property in cfg.Properties())
{
- var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
+ var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
}
@@ -76,7 +76,7 @@
var cssVals = new List();
foreach (JProperty property in style.Properties())
{
- var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
+ var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
@@ -84,7 +84,7 @@
}
if (cssVals.Any())
- attrs.Add("style='" + string.Join(" ", cssVals) + "'");
+ attrs.Add("style='" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "'");
}
return new MvcHtmlString(string.Join(" ", attrs));
diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml
index c5fabe2abf..c8f9ab7cd1 100644
--- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml
+++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap2.cshtml
@@ -66,7 +66,7 @@
if(cfg != null)
foreach (JProperty property in cfg.Properties())
{
- var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
+ var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
}
@@ -76,7 +76,7 @@
var cssVals = new List();
foreach (JProperty property in style.Properties())
{
- var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
+ var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
@@ -84,7 +84,7 @@
}
if (cssVals.Any())
- attrs.Add("style=\"" + string.Join(" ", cssVals) + "\"");
+ attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\"");
}
return new MvcHtmlString(string.Join(" ", attrs));
diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml
index b7e8ef34fb..defe59d808 100644
--- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml
+++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml
@@ -62,7 +62,7 @@
if(cfg != null)
foreach (JProperty property in cfg.Properties())
{
- var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
+ var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
}
@@ -72,7 +72,7 @@
var cssVals = new List();
foreach (JProperty property in style.Properties())
{
- var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
+ var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
@@ -80,7 +80,7 @@
}
if (cssVals.Any())
- attrs.Add("style='" + string.Join(" ", cssVals) + "'");
+ attrs.Add("style='" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "'");
}
return new MvcHtmlString(string.Join(" ", attrs));
diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml
index 3a4fa3b8e2..6ab5c1355a 100644
--- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml
+++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml
@@ -66,17 +66,17 @@
if(cfg != null)
foreach (JProperty property in cfg.Properties())
{
- var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
+ var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
}
-
+
JObject style = contentItem.styles;
if (style != null) {
var cssVals = new List();
foreach (JProperty property in style.Properties())
{
- var propertyValue = TemplateUtilities.CleanForXss(property.Value.ToString());
+ var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
@@ -84,7 +84,7 @@
}
if (cssVals.Any())
- attrs.Add("style=\"" + string.Join(" ", cssVals) + "\"");
+ attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\"");
}
return new MvcHtmlString(string.Join(" ", attrs));
diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml
index 5a570efdb5..8c92ca0d83 100644
--- a/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml
+++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Editors/TextString.cshtml
@@ -4,9 +4,9 @@
@if (Model.editor.config.markup != null)
{
string markup = Model.editor.config.markup.ToString();
- var UmbracoHelper = new UmbracoHelper(UmbracoContext.Current);
+ var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
- markup = markup.Replace("#value#", UmbracoHelper.ReplaceLineBreaksForHtml(TemplateUtilities.CleanForXss(Model.value.ToString())));
+ markup = markup.Replace("#value#", umbracoHelper.ReplaceLineBreaksForHtml(HttpUtility.HtmlEncode(Model.value.ToString())));
markup = markup.Replace("#style#", Model.editor.config.style.ToString());
diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config
index ec7948a09a..09df754e74 100644
--- a/src/Umbraco.Web.UI/web.Template.config
+++ b/src/Umbraco.Web.UI/web.Template.config
@@ -119,7 +119,7 @@
-
+
diff --git a/src/Umbraco.Web/Controllers/UmbLoginController.cs b/src/Umbraco.Web/Controllers/UmbLoginController.cs
index e86e48a18b..d446df5683 100644
--- a/src/Umbraco.Web/Controllers/UmbLoginController.cs
+++ b/src/Umbraco.Web/Controllers/UmbLoginController.cs
@@ -11,11 +11,11 @@ namespace Umbraco.Web.Controllers
public class UmbLoginController : SurfaceController
{
[HttpPost]
- public ActionResult HandleLogin([Bind(Prefix="loginModel")]LoginModel model)
+ public ActionResult HandleLogin([Bind(Prefix = "loginModel")]LoginModel model)
{
if (ModelState.IsValid == false)
{
- return CurrentUmbracoPage();
+ return CurrentUmbracoPage();
}
if (Members.Login(model.Username, model.Password) == false)
@@ -30,11 +30,20 @@ namespace Umbraco.Web.Controllers
//if there is a specified path to redirect to then use it
if (model.RedirectUrl.IsNullOrWhiteSpace() == false)
{
- return Redirect(model.RedirectUrl);
+ // validate the redirect url
+ if (Url.IsLocalUrl(model.RedirectUrl))
+ {
+ return Redirect(model.RedirectUrl);
+ }
+ else
+ {
+ // if it's not a local url we'll redirect to the root of the current site
+ return Redirect(base.CurrentPage.Site().Url);
+ }
}
//redirect to current page by default
-
+
return RedirectToCurrentUmbracoPage();
//return RedirectToCurrentUmbracoUrl();
}
diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs
index 89f6fbe7cf..e75f9200c6 100644
--- a/src/Umbraco.Web/ExamineExtensions.cs
+++ b/src/Umbraco.Web/ExamineExtensions.cs
@@ -9,12 +9,12 @@ using Umbraco.Web.PublishedCache;
namespace Umbraco.Web
{
- ///
- /// Extension methods for Examine
- ///
- internal static class ExamineExtensions
+ ///
+ /// Extension methods for Examine
+ ///
+ public static class ExamineExtensions
{
- internal static PublishedContentSet ConvertSearchResultToPublishedContent(this IEnumerable results,
+ public static PublishedContentSet ConvertSearchResultToPublishedContent(this IEnumerable results,
ContextualPublishedCache cache)
{
//TODO: The search result has already returned a result which SHOULD include all of the data to create an IPublishedContent,
diff --git a/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs b/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs
index 51622ed504..2f8ec85075 100644
--- a/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs
+++ b/src/Umbraco.Web/Media/EmbedProviders/AbstractOEmbedProvider.cs
@@ -1,8 +1,10 @@
-using System.Text;
+using System;
+using System.Text;
using System.Xml;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
+using System.Web;
using Newtonsoft.Json;
using Umbraco.Core.Media;
@@ -27,10 +29,13 @@ namespace Umbraco.Web.Media.EmbedProviders
public virtual string BuildFullUrl(string url, int maxWidth, int maxHeight)
{
+ if (Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute) == false)
+ throw new ArgumentException("Not a valid Url");
+
var fullUrl = new StringBuilder();
fullUrl.Append(APIEndpoint);
- fullUrl.Append("?url=" + url);
+ fullUrl.Append("?url=" + HttpUtility.UrlEncode(url));
foreach (var p in RequestParams)
fullUrl.Append(string.Format("&{0}={1}", p.Key, p.Value));