'\n");
- if (!typeInited)
+ if (typeInited == false)
{
descr.Text = "
" +
docDescription + "
'";
@@ -94,34 +94,14 @@ namespace umbraco.cms.presentation.create.controls
}
}
- #region Web Form Designer generated code
-
- protected override void OnInit(EventArgs e)
- {
- //
- // CODEGEN: This call is required by the ASP.NET Web Form Designer.
- //
- InitializeComponent();
- base.OnInit(e);
- }
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent()
- {
- }
-
- #endregion
-
+
protected void sbmt_Click(object sender, EventArgs e)
{
- doCreation();
+ DoCreation();
}
- private void doCreation()
+ private void DoCreation()
{
if (Page.IsValid)
{
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/contentTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/contentTasks.cs
index a0c6c616fb..45cc4a737c 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/contentTasks.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/contentTasks.cs
@@ -13,14 +13,14 @@ namespace umbraco
{
private string _alias;
- private int _parentID;
- private int _typeID;
- private int _userID;
+ private int _parentId;
+ private int _typeId;
+ private int _userId;
private string _returnUrl = "";
public int UserId
{
- set { _userID = value; }
+ set { _userId = value; }
}
public string ReturnUrl
@@ -30,8 +30,8 @@ namespace umbraco
public int TypeID
{
- set { _typeID = value; }
- get { return _typeID; }
+ set { _typeId = value; }
+ get { return _typeId; }
}
public string Alias
@@ -44,18 +44,18 @@ namespace umbraco
{
set
{
- _parentID = value;
+ _parentId = value;
}
get
{
- return _parentID;
+ return _parentId;
}
}
public bool Save()
{
- cms.businesslogic.web.DocumentType dt = new cms.businesslogic.web.DocumentType(TypeID);
- cms.businesslogic.web.Document d = cms.businesslogic.web.Document.MakeNew(Alias, dt, BusinessLogic.User.GetUser(_userID), ParentID);
+ var dt = new cms.businesslogic.web.DocumentType(TypeID);
+ var d = cms.businesslogic.web.Document.MakeNew(Alias, dt, User.GetUser(_userId), ParentID);
if (d == null)
{
//TODO: Slace - Fix this to use the language files
@@ -71,13 +71,13 @@ namespace umbraco
public bool Delete()
{
- cms.businesslogic.web.Document d = new cms.businesslogic.web.Document(ParentID);
-
- // Log
- BusinessLogic.Log.Add(BusinessLogic.LogTypes.Delete, User.GetCurrent(), d.Id, "");
+ var d = new cms.businesslogic.web.Document(ParentID);
d.delete();
+ // Log
+ Log.Add(LogTypes.Delete, User.GetCurrent(), d.Id, "");
+
return true;
}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs
index ca384bd3af..7bc9354816 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/create.aspx.cs
@@ -9,8 +9,8 @@ using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Xml;
+using Umbraco.Core.IO;
using umbraco.cms.businesslogic;
-using umbraco.IO;
using umbraco.presentation;
using umbraco.BusinessLogic.Actions;
using umbraco.BasePages;
@@ -44,19 +44,19 @@ namespace umbraco.dialogs
if (helper.Request("app") == Constants.Applications.Media || CheckCreatePermissions(nodeId))
{
//pane_chooseName.Text = ui.Text("create", "updateData", this.getUser());
- cms.businesslogic.CMSNode c = new cms.businesslogic.CMSNode(nodeId);
+ var c = new CMSNode(nodeId);
path.Value = c.Path;
pane_chooseNode.Visible = false;
panel_buttons.Visible = false;
pane_chooseName.Visible = true;
- XmlDocument createDef = new XmlDocument();
- XmlTextReader defReader = new XmlTextReader(Server.MapPath(umbraco.IO.IOHelper.ResolveUrl(umbraco.IO.SystemDirectories.Umbraco) + "/config/create/UI.xml"));
+ var createDef = new XmlDocument();
+ var defReader = new XmlTextReader(Server.MapPath(IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/config/create/UI.xml"));
createDef.Load(defReader);
defReader.Close();
// Find definition for current nodeType
XmlNode def = createDef.SelectSingleNode("//nodeType [@alias = '" + Request.QueryString["app"] + "']");
- phCreate.Controls.Add(new UserControl().LoadControl(umbraco.IO.IOHelper.ResolveUrl(umbraco.IO.SystemDirectories.Umbraco) + def.SelectSingleNode("./usercontrol").FirstChild.Value));
+ phCreate.Controls.Add(new UserControl().LoadControl(IOHelper.ResolveUrl(SystemDirectories.Umbraco) + def.SelectSingleNode("./usercontrol").FirstChild.Value));
}
else
{
@@ -71,8 +71,8 @@ namespace umbraco.dialogs
protected override void OnPreRender(EventArgs e) {
base.OnPreRender(e);
- ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference( IOHelper.ResolveUrl( SystemDirectories.Webservices) +"/cmsnode.asmx"));
- ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference( IOHelper.ResolveUrl( SystemDirectories.Webservices) +"/legacyAjaxCalls.asmx"));
+ ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference( IOHelper.ResolveUrl( SystemDirectories.WebServices) +"/cmsnode.asmx"));
+ ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference( IOHelper.ResolveUrl( SystemDirectories.WebServices) +"/legacyAjaxCalls.asmx"));
}
private bool CheckCreatePermissions(int nodeId)
diff --git a/src/umbraco.cms/Actions/ActionNew.cs b/src/umbraco.cms/Actions/ActionNew.cs
index ec07d8096a..ff17c91a0b 100644
--- a/src/umbraco.cms/Actions/ActionNew.cs
+++ b/src/umbraco.cms/Actions/ActionNew.cs
@@ -10,9 +10,7 @@ namespace umbraco.BusinessLogic.Actions
public class ActionNew : IAction
{
//create singleton
-#pragma warning disable 612,618
- private static readonly ActionNew m_instance = new ActionNew();
-#pragma warning restore 612,618
+ private static readonly ActionNew InnerInstance = new ActionNew();
///
/// A public constructor exists ONLY for backwards compatibility in regards to 3rd party add-ons.
@@ -24,7 +22,7 @@ namespace umbraco.BusinessLogic.Actions
public static ActionNew Instance
{
- get { return m_instance; }
+ get { return InnerInstance; }
}
#region IAction Members
From 93def731ef3ce8151b6aab37d922338c7d208054 Mon Sep 17 00:00:00 2001
From: Morten Christensen
Date: Fri, 5 Jul 2013 10:51:19 +0200
Subject: [PATCH 18/25] Fixes #U4-2466
---
.../umbraco_client/tags/css/jquery.tagsinput.css | 5 +++++
src/umbraco.controls/DatePicker/DateTimePicker.cs | 4 ----
src/umbraco.editorControls/tags/DataEditor.cs | 1 -
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/Umbraco.Web.UI/umbraco_client/tags/css/jquery.tagsinput.css b/src/Umbraco.Web.UI/umbraco_client/tags/css/jquery.tagsinput.css
index c595e249f9..3af1d36e55 100644
--- a/src/Umbraco.Web.UI/umbraco_client/tags/css/jquery.tagsinput.css
+++ b/src/Umbraco.Web.UI/umbraco_client/tags/css/jquery.tagsinput.css
@@ -1,3 +1,8 @@
+/* From jquery-ui.custom.css */
+.ui-autocomplete { position: absolute; cursor: default; }
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
div.tagsinput { border:1px solid #CCC; background: #FFF; padding:5px; width:300px; height:100px; overflow-y: auto;}
div.tagsinput span.tag { border: 1px solid #a5d24a; -moz-border-radius:2px; -webkit-border-radius:2px; display: block; float: left; padding: 5px; text-decoration:none; background: #cde69c; color: #638421; margin-right: 5px; margin-bottom:5px;font-family: helvetica; font-size:13px;}
div.tagsinput span.tag a { font-weight: bold; color: #82ad2b; text-decoration:none; font-size: 11px; }
diff --git a/src/umbraco.controls/DatePicker/DateTimePicker.cs b/src/umbraco.controls/DatePicker/DateTimePicker.cs
index 5c453649e1..4df20fbb9d 100644
--- a/src/umbraco.controls/DatePicker/DateTimePicker.cs
+++ b/src/umbraco.controls/DatePicker/DateTimePicker.cs
@@ -1,11 +1,7 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using ClientDependency.Core;
using System.Web.UI;
using System.Web.UI.WebControls;
-using umbraco.IO;
using System.Web.UI.HtmlControls;
namespace umbraco.uicontrols.DatePicker
diff --git a/src/umbraco.editorControls/tags/DataEditor.cs b/src/umbraco.editorControls/tags/DataEditor.cs
index 4281e1f12a..22c9fd423b 100644
--- a/src/umbraco.editorControls/tags/DataEditor.cs
+++ b/src/umbraco.editorControls/tags/DataEditor.cs
@@ -62,7 +62,6 @@ namespace umbraco.editorControls.tags
base.OnInit(e);
// register all dependencies - only registered once
- ClientDependencyLoader.Instance.RegisterDependency("ui/ui-lightness/jquery-ui.custom.css", "UmbracoClient", ClientDependencyType.Css);
ClientDependencyLoader.Instance.RegisterDependency("css/umbracoGui.css", "UmbracoRoot", ClientDependencyType.Css);
ClientDependencyLoader.Instance.RegisterDependency("tags/css/jquery.tagsinput.css", "UmbracoClient", ClientDependencyType.Css);
ClientDependencyLoader.Instance.RegisterDependency("tags/js/jquery.tagsinput.min.js", "UmbracoClient", ClientDependencyType.Javascript);
From b7f6195791bacf4784a73cc76c6d76aa43b256e9 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Mon, 8 Jul 2013 17:29:26 +1000
Subject: [PATCH 19/25] Started adding base PermissionsRepository for sub
classes to use for assigning permissions, created a BulkImport extension
methods for PetaPoco with tests since we'll be needing that for assigning
permissions in a nice way. Wrote unit tests for all sql gen for permissions
and assigning permissions. This all starts fixing #U4-2161 but there's still
a bit more work to do.
---
src/Umbraco.Core/Models/Membership/IUser.cs | 2 +-
src/Umbraco.Core/Models/Membership/User.cs | 2 +-
.../Persistence/Factories/UserFactory.cs | 4 +-
.../Persistence/Mappers/UserMapper.cs | 2 +-
src/Umbraco.Core/Persistence/PetaPoco.cs | 2 +-
.../Persistence/PetaPocoExtensions.cs | 78 +++++++++-
.../Repositories/ContentRepository.cs | 17 ++-
.../Repositories/PermissionRepository.cs | 135 ++++++++++++++++++
.../Repositories/UserRepository.cs | 7 +-
.../Repositories/VersionableRepositoryBase.cs | 2 +-
.../Persistence/UmbracoDatabase.cs | 5 +-
src/Umbraco.Core/Services/UserService.cs | 2 +-
src/Umbraco.Core/Umbraco.Core.csproj | 1 +
.../Persistence/PetaPocoExtensionsTest.cs | 85 +++++++++++
.../Persistence/Querying/PetaPocoSqlTests.cs | 23 +++
.../Repositories/ContentRepositoryTest.cs | 38 +++++
.../Repositories/UserRepositoryTest.cs | 34 ++++-
.../Services/UserServiceTests.cs | 6 +-
.../TestHelpers/Entities/MockedUser.cs | 2 +-
src/Umbraco.Tests/Umbraco.Tests.csproj | 1 +
src/umbraco.businesslogic/User.cs | 15 +-
21 files changed, 432 insertions(+), 31 deletions(-)
create mode 100644 src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs
create mode 100644 src/Umbraco.Tests/Persistence/PetaPocoExtensionsTest.cs
diff --git a/src/Umbraco.Core/Models/Membership/IUser.cs b/src/Umbraco.Core/Models/Membership/IUser.cs
index 25a4133eb2..97e7e4132f 100644
--- a/src/Umbraco.Core/Models/Membership/IUser.cs
+++ b/src/Umbraco.Core/Models/Membership/IUser.cs
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Models.Membership
bool NoConsole { get; set; }
IUserType UserType { get; }
- string Permissions { get; set; }
+ string DefaultPermissions { get; set; }
}
internal interface IUserProfile : IProfile
diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs
index d6b71135ef..ddcc28cccb 100644
--- a/src/Umbraco.Core/Models/Membership/User.cs
+++ b/src/Umbraco.Core/Models/Membership/User.cs
@@ -91,7 +91,7 @@ namespace Umbraco.Core.Models.Membership
[DataMember]
public string Language { get; set; }
[DataMember]
- public string Permissions { get; set; }
+ public string DefaultPermissions { get; set; }
[DataMember]
public bool DefaultToLiveEditing { get; set; }
diff --git a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs
index 6d38db9739..0a046ca7a0 100644
--- a/src/Umbraco.Core/Persistence/Factories/UserFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/UserFactory.cs
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Persistence.Factories
Language = dto.UserLanguage,
DefaultToLiveEditing = dto.DefaultToLiveEditing,
NoConsole = dto.NoConsole,
- Permissions = dto.DefaultPermissions
+ DefaultPermissions = dto.DefaultPermissions
};
foreach (var app in dto.User2AppDtos)
@@ -62,7 +62,7 @@ namespace Umbraco.Core.Persistence.Factories
UserLanguage = entity.Language,
UserName = entity.Name,
Type = short.Parse(entity.UserType.Id.ToString()),
- DefaultPermissions = entity.Permissions,
+ DefaultPermissions = entity.DefaultPermissions,
User2AppDtos = new List()
};
diff --git a/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs
index 1a7add097a..5e2947c3a7 100644
--- a/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/UserMapper.cs
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Mappers
CacheMap(src => src.Username, dto => dto.Login);
CacheMap(src => src.Password, dto => dto.Password);
CacheMap(src => src.Name, dto => dto.UserName);
- CacheMap(src => src.Permissions, dto => dto.DefaultPermissions);
+ CacheMap(src => src.DefaultPermissions, dto => dto.DefaultPermissions);
CacheMap(src => src.StartMediaId, dto => dto.MediaStartId);
CacheMap(src => src.StartContentId, dto => dto.ContentStartId);
CacheMap(src => src.DefaultToLiveEditing, dto => dto.DefaultToLiveEditing);
diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs
index 269fea5f9a..d8507ac681 100644
--- a/src/Umbraco.Core/Persistence/PetaPoco.cs
+++ b/src/Umbraco.Core/Persistence/PetaPoco.cs
@@ -384,7 +384,7 @@ namespace Umbraco.Core.Persistence
}
// Add a parameter to a DB command
- void AddParam(IDbCommand cmd, object item, string ParameterPrefix)
+ internal void AddParam(IDbCommand cmd, object item, string ParameterPrefix)
{
// Convert value to from poco type to db type
if (Database.Mapper != null && item!=null)
diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
index 85ab884857..6faf4d637a 100644
--- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
+++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Data;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
@@ -28,6 +30,76 @@ namespace Umbraco.Core.Persistence
CreateTable(db, overwrite, tableType);
}
+ public static void BulkInsertRecords(this Database db, IEnumerable collection)
+ {
+ using (var tr = db.GetTransaction())
+ {
+ try
+ {
+ if (SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
+ {
+ //SqlCe doesn't support bulk insert statements!
+
+ foreach (var poco in collection)
+ {
+ db.Insert(poco);
+ }
+
+ }
+ else
+ {
+ string sql;
+ using (var cmd = db.GenerateBulkInsertCommand(collection, db.Connection, out sql))
+ {
+ cmd.CommandText = sql;
+ cmd.ExecuteNonQuery();
+ }
+ }
+
+ tr.Complete();
+ }
+ catch
+ {
+ tr.Dispose();
+ throw;
+ }
+ }
+ }
+
+ internal static IDbCommand GenerateBulkInsertCommand(this Database db, IEnumerable collection, IDbConnection connection, out string sql)
+ {
+ var pd = Database.PocoData.ForType(typeof(T));
+ var tableName = db.EscapeTableName(pd.TableInfo.TableName);
+
+ //get all columns but not the primary key if it is auto-incremental
+ var cols = string.Join(", ", (
+ from c in pd.Columns
+ where c.Value.ResultColumn == false
+ select tableName + "." + db.EscapeSqlIdentifier(c.Key))
+ .ToArray());
+
+ var cmd = db.CreateCommand(connection, "");
+
+ var pocoValues = new List();
+ var index = 0;
+ foreach (var poco in collection)
+ {
+ var values = new List();
+ foreach (var i in pd.Columns)
+ {
+ //if (pd.TableInfo.AutoIncrement && i.Key == pd.TableInfo.PrimaryKey)
+ //{
+ // continue;
+ //}
+ values.Add(string.Format("{0}{1}", "@", index++));
+ db.AddParam(cmd, i.Value.GetValue(poco), "@");
+ }
+ pocoValues.Add("(" + string.Join(",", values.ToArray()) + ")");
+ }
+ sql = string.Format("INSERT INTO {0} ({1}) VALUES {2}", tableName, cols, string.Join(", ", pocoValues));
+ return cmd;
+ }
+
public static void CreateTable(this Database db, bool overwrite, Type modelType)
{
var tableDefinition = DefinitionFactory.GetTableDefinition(modelType);
@@ -67,7 +139,7 @@ namespace Umbraco.Core.Persistence
//Turn on identity insert if db provider is not mysql
if (SqlSyntaxContext.SqlSyntaxProvider.SupportsIdentityInsert() && tableDefinition.Columns.Any(x => x.IsIdentity))
db.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(tableName))));
-
+
//Call the NewTable-event to trigger the insert of base/default data
NewTable(tableName, db, e);
@@ -106,7 +178,7 @@ namespace Umbraco.Core.Persistence
public static void DropTable(this Database db)
where T : new()
{
- Type type = typeof (T);
+ Type type = typeof(T);
var tableNameAttribute = type.FirstAttribute();
if (tableNameAttribute == null)
throw new Exception(
@@ -184,5 +256,5 @@ namespace Umbraco.Core.Persistence
}
}
- internal class TableCreationEventArgs : System.ComponentModel.CancelEventArgs{}
+ internal class TableCreationEventArgs : System.ComponentModel.CancelEventArgs { }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 58b07e1583..a2b720d06d 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -206,7 +206,7 @@ namespace Umbraco.Core.Persistence.Repositories
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
-
+
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
Database.Update(nodeDto);
@@ -217,6 +217,21 @@ namespace Umbraco.Core.Persistence.Repositories
entity.SortOrder = sortOrder;
entity.Level = level;
+
+ //Assign the same permissions to it as the parent node
+ // http://issues.umbraco.org/issue/U4-2161
+ var parentPermissions = GetPermissionsForEntity(entity.ParentId).ToArray();
+ //if there are parent permissions then assign them, otherwise leave null and permissions will become the
+ // user's default permissions.
+ if (parentPermissions.Any())
+ {
+ //group by the unique permission and assign then for the users of that permission set.
+ foreach (var assignedPermission in parentPermissions.GroupBy(x => x.Permission))
+ {
+ AssignEntityPermissions(entity, assignedPermission.Key, assignedPermission.Select(x => (object)x.UserId));
+ }
+ }
+
//Create the Content specific data - cmsContent
var contentDto = dto.ContentVersionDto.ContentDto;
contentDto.NodeId = nodeDto.NodeId;
diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs
new file mode 100644
index 0000000000..88087c7999
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Text;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.Caching;
+using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Core.Persistence.UnitOfWork;
+
+namespace Umbraco.Core.Persistence.Repositories
+{
+ ///
+ /// A repository that exposes functionality to modify assigned permissions to a node
+ ///
+ ///
+ ///
+ internal abstract class PermissionRepository : PetaPocoRepositoryBase
+ where TEntity : class, IAggregateRoot
+ {
+ protected PermissionRepository(IDatabaseUnitOfWork work)
+ : base(work)
+ {
+ }
+
+ protected PermissionRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache)
+ : base(work, cache)
+ {
+ }
+
+ protected internal IEnumerable GetPermissionsForEntity(int entityId)
+ {
+ var sql = new Sql();
+ sql.Select("*")
+ .From()
+ .Where(dto => dto.NodeId == entityId);
+ return Database.Fetch(sql);
+ }
+
+ ///
+ /// Assigns permissions to an entity for multiple users
+ ///
+ ///
+ ///
+ ///
+ protected internal void AssignEntityPermissions(TEntity entity, string permissions, IEnumerable
public void initCruds()
{
if (!_isInitialized)
@@ -904,7 +905,7 @@ namespace umbraco.BusinessLogic
public void FlushFromCache()
{
OnFlushingFromCache(EventArgs.Empty);
- ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("UmbracoUser{0}", Id.ToString()));
+ ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserCacheKey, Id.ToString()));
}
///
@@ -915,7 +916,7 @@ namespace umbraco.BusinessLogic
public static User GetUser(int id)
{
return ApplicationContext.Current.ApplicationCache.GetCacheItem(
- string.Format("UmbracoUser{0}", id.ToString()), () =>
+ string.Format("{0}{1}", CacheKeys.UserCacheKey, id.ToString()), () =>
{
try
{
From 3c4cf56309ac02423ca7ef71ad0d2d78abe9c32a Mon Sep 17 00:00:00 2001
From: Shannon
Date: Mon, 8 Jul 2013 18:47:53 +1000
Subject: [PATCH 20/25] Fixes: #U4-2475
---
.../Cache/RuntimeCacheProviderBase.cs | 2 +-
.../umbraco/dialogs/moveOrCopy.aspx.cs | 34 +++++++++++++------
2 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs b/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs
index 9dddb4c576..d88a3922bb 100644
--- a/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs
+++ b/src/Umbraco.Core/Cache/RuntimeCacheProviderBase.cs
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Cache
/// An abstract class for implementing a runtime cache provider
///
///
- /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, ETC...
+ /// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, REQUEST CACHE, ETC...
///
internal abstract class RuntimeCacheProviderBase : CacheProviderBase
{
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs
index ba48e944fa..2e804cee13 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs
@@ -217,23 +217,35 @@ namespace umbraco.dialogs
var nodeAllowed = false;
IContentBase currContent;
- IContentBase parentContent;
- IContentTypeBase parentContentType;
+ IContentBase parentContent = null;
+ IContentTypeBase parentContentType = null;
if (CurrentApp == "content")
{
currContent = Services.ContentService.GetById(Request.GetItemAs("id"));
- parentContent = Services.ContentService.GetById(Request.GetItemAs("copyTo"));
- parentContentType = Services.ContentTypeService.GetContentType(parentContent.ContentTypeId);
+ if (Request.GetItemAs("copyTo") != -1)
+ {
+ parentContent = Services.ContentService.GetById(Request.GetItemAs("copyTo"));
+ if (parentContent != null)
+ {
+ parentContentType = Services.ContentTypeService.GetContentType(parentContent.ContentTypeId);
+ }
+ }
}
else
{
currContent = Services.MediaService.GetById(Request.GetItemAs("id"));
- parentContent = Services.MediaService.GetById(Request.GetItemAs("copyTo"));
- parentContentType = Services.ContentTypeService.GetMediaType(parentContent.ContentTypeId);
+ if (Request.GetItemAs("copyTo") != -1)
+ {
+ parentContent = Services.MediaService.GetById(Request.GetItemAs("copyTo"));
+ if (parentContent != null)
+ {
+ parentContentType = Services.ContentTypeService.GetMediaType(parentContent.ContentTypeId);
+ }
+ }
}
// Check on contenttypes
- if (Request.GetItemAs("copyTo") == -1)
+ if (parentContentType == null)
{
nodeAllowed = true;
}
@@ -268,7 +280,7 @@ namespace umbraco.dialogs
pane_form_notice.Visible = false;
panel_buttons.Visible = false;
- var newNodeCaption = Request.GetItemAs("copyTo") == -1
+ var newNodeCaption = parentContent == null
? ui.Text(CurrentApp)
: parentContent.Name;
@@ -290,19 +302,19 @@ namespace umbraco.dialogs
feedback.type = uicontrols.Feedback.feedbacktype.success;
// refresh tree
- ClientTools.MoveNode(currContent.Id.ToString(), parentContent.Path);
+ ClientTools.MoveNode(currContent.Id.ToString(), currContent.Path);
}
else
{
//NOTE: We ONLY support Copy on content not media for some reason.
- Services.ContentService.Copy((IContent)currContent, Request.GetItemAs("copyTo"), RelateDocuments.Checked, getUser().Id);
+ var newContent = Services.ContentService.Copy((IContent)currContent, Request.GetItemAs("copyTo"), RelateDocuments.Checked, getUser().Id);
feedback.Text = ui.Text("moveOrCopy", "copyDone", nodes, getUser()) + "
" + ui.Text("closeThisWindow") + "";
feedback.type = uicontrols.Feedback.feedbacktype.success;
// refresh tree
- ClientTools.CopyNode(currContent.Id.ToString(), parentContent.Path);
+ ClientTools.CopyNode(currContent.Id.ToString(), newContent.Path);
}
}
}
From 90b94f6ffbb299b22a332a0a5e254aaf0a60ca4f Mon Sep 17 00:00:00 2001
From: Shannon
Date: Mon, 8 Jul 2013 18:58:38 +1000
Subject: [PATCH 21/25] Fixes: #U4-2476
---
src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 1 +
.../umbraco/config/lang/en_us.xml | 1 +
.../umbraco/dialogs/moveOrCopy.aspx.cs | 17 ++++++++++++++++-
3 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
index e895731f4b..f331e88d9b 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml
@@ -548,6 +548,7 @@ To manage your website, simply open the umbraco back office and start adding con
No node selected yet, please select a node in the list above before clicking 'ok'The current node is not allowed under the chosen node because of its typeThe current node cannot be moved to one of its subpages
+ The current node cannot exist at the rootThe action isn't allowed since you have insufficient permissions on 1 or more child documents.Relate copied items to original
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
index a3e66517da..2723e209f7 100644
--- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
@@ -535,6 +535,7 @@ To manage your website, simply open the umbraco back office and start adding con
No node selected yet, please select a node in the list above before clicking 'ok'The current node is not allowed under the chosen node because of its typeThe current node cannot be moved to one of its subpages
+ The current node cannot exist at the rootThe action isn't allowed since you have insufficient permissions on 1 or more child documents.Relate copied items to original
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs
index 2e804cee13..3cb48ce274 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs
@@ -247,7 +247,22 @@ namespace umbraco.dialogs
// Check on contenttypes
if (parentContentType == null)
{
- nodeAllowed = true;
+ //check if this is allowed at root
+ IContentTypeBase currContentType;
+ if (CurrentApp == "content")
+ {
+ currContentType = Services.ContentTypeService.GetContentType(currContent.ContentTypeId);
+ }
+ else
+ {
+ currContentType = Services.ContentTypeService.GetMediaType(currContent.ContentTypeId);
+ }
+ nodeAllowed = currContentType.AllowedAsRoot;
+ if (!nodeAllowed)
+ {
+ feedback.Text = ui.Text("moveOrCopy", "notAllowedAtRoot", UmbracoUser);
+ feedback.type = uicontrols.Feedback.feedbacktype.error;
+ }
}
else
{
From b91d0bb117b9ee343b7114e5c43e464d26d8f91e Mon Sep 17 00:00:00 2001
From: Shannon
Date: Tue, 9 Jul 2013 10:08:31 +1000
Subject: [PATCH 22/25] Updates ContentRepository to bulk insert all parent
permissions at once (overload to the AssignEntityPermissions method)
---
.../Repositories/ContentRepository.cs | 8 +++-----
.../Repositories/PermissionRepository.cs | 19 +++++++++++++++++++
.../Persistence/Querying/PetaPocoSqlTests.cs | 8 ++++----
.../Repositories/ContentRepositoryTest.cs | 4 ++--
4 files changed, 28 insertions(+), 11 deletions(-)
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index a2b720d06d..93179f7e91 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -225,11 +225,9 @@ namespace Umbraco.Core.Persistence.Repositories
// user's default permissions.
if (parentPermissions.Any())
{
- //group by the unique permission and assign then for the users of that permission set.
- foreach (var assignedPermission in parentPermissions.GroupBy(x => x.Permission))
- {
- AssignEntityPermissions(entity, assignedPermission.Key, assignedPermission.Select(x => (object)x.UserId));
- }
+ var userPermissions = parentPermissions.ToDictionary(
+ permissionDto => (object) permissionDto.UserId, permissionDto => permissionDto.Permission);
+ AssignEntityPermissions(entity, userPermissions);
}
//Create the Content specific data - cmsContent
diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs
index 88087c7999..0bf6a6eeb8 100644
--- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs
@@ -58,6 +58,25 @@ namespace Umbraco.Core.Persistence.Repositories
Database.BulkInsertRecords(actions);
}
+ ///
+ /// Assigns permissions to an entity for multiple users/permission entries
+ ///
+ ///
+ ///
+ /// A key/value pair list containing a userId and a permission to assign
+ ///
+ protected internal void AssignEntityPermissions(TEntity entity, IEnumerable> userPermissions)
+ {
+ var actions = userPermissions.Select(p => new User2NodePermissionDto
+ {
+ NodeId = entity.Id,
+ Permission = p.Value,
+ UserId = (int)p.Key
+ });
+
+ Database.BulkInsertRecords(actions);
+ }
+
///
/// Replace permissions for an entity for multiple users
///
diff --git a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs
index 3ee82a820e..8494ec7c8c 100644
--- a/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs
+++ b/src/Umbraco.Tests/Persistence/Querying/PetaPocoSqlTests.cs
@@ -16,20 +16,20 @@ namespace Umbraco.Tests.Persistence.Querying
public void Generate_Replace_Entity_Permissions_Test()
{
// Act
- var sql = PermissionRepository.GenerateReplaceEntityPermissionsSql(123, "abcd", new object[] {10, 11, 12});
+ var sql = PermissionRepository.GenerateReplaceEntityPermissionsSql(123, "A", new object[] {10, 11, 12});
// Assert
- Assert.AreEqual(@"SET [permission]='abcd' WHERE (([nodeId]=123) AND ([userId]=10 OR [userId]=11 OR [userId]=12))", sql);
+ Assert.AreEqual(@"SET [permission]='A' WHERE (([nodeId]=123) AND ([userId]=10 OR [userId]=11 OR [userId]=12))", sql);
}
[Test]
public void Generate_Replace_Entity_Permissions_With_Descendants_Test()
{
// Act
- var sql = PermissionRepository.GenerateReplaceEntityPermissionsSql(new[] {123, 456}, "abcd", new object[] {10, 11, 12});
+ var sql = PermissionRepository.GenerateReplaceEntityPermissionsSql(new[] {123, 456}, "A", new object[] {10, 11, 12});
// Assert
- Assert.AreEqual(@"SET [permission]='abcd' WHERE (([nodeId]=123 OR [nodeId]=456) AND ([userId]=10 OR [userId]=11 OR [userId]=12))", sql);
+ Assert.AreEqual(@"SET [permission]='A' WHERE (([nodeId]=123 OR [nodeId]=456) AND ([userId]=10 OR [userId]=11 OR [userId]=12))", sql);
}
[Test]
diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
index 0fb31e831c..973000b9ca 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs
@@ -59,7 +59,7 @@ namespace Umbraco.Tests.Persistence.Repositories
unitOfWork.Commit();
// Act
- repository.AssignEntityPermissions(parentPage, "ABCD", new object[] {0});
+ repository.AssignEntityPermissions(parentPage, "A", new object[] {0});
var childPage = MockedContent.CreateSimpleContent(contentType, "child", parentPage);
repository.AddOrUpdate(childPage);
unitOfWork.Commit();
@@ -67,7 +67,7 @@ namespace Umbraco.Tests.Persistence.Repositories
// Assert
var permissions = repository.GetPermissionsForEntity(childPage.Id);
Assert.AreEqual(1, permissions.Count());
- Assert.AreEqual("ABCD", permissions.Single().Permission);
+ Assert.AreEqual("A", permissions.Single().Permission);
}
[Test]
From 868edee5e647f6210e0a2507e615a93a2671f251 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Tue, 9 Jul 2013 11:47:46 +1000
Subject: [PATCH 23/25] Fixes: #U4-2161 - permissions inheritance. Fixes up how
user permissions cache is invalidated and looked up (it is now lazy and not
forced). User permissions are also cached as a low priority for now and only
for 20 mins based on #U4-2474.
---
src/Umbraco.Core/Cache/CacheKeys.cs | 1 +
src/Umbraco.Core/Models/Content.cs | 19 +++++-
.../Repositories/ContentRepository.cs | 13 +++-
.../Cache/CacheRefresherEventHandler.cs | 49 ++++++++++++--
src/Umbraco.Web/Cache/DistributedCache.cs | 1 +
.../Cache/DistributedCacheExtensions.cs | 17 +++++
src/Umbraco.Web/Cache/UserCacheRefresher.cs | 2 +
.../Cache/UserPermissionsCacheRefresher.cs | 47 ++++++++++++++
.../Publishing/UpdateCacheAfterPublish.cs | 2 +
.../Publishing/UpdateCacheAfterUnPublish.cs | 2 +
src/Umbraco.Web/Umbraco.Web.csproj | 1 +
.../umbraco/dialogs/cruds.aspx.cs | 14 ----
src/umbraco.businesslogic/User.cs | 64 ++++++++++---------
13 files changed, 179 insertions(+), 53 deletions(-)
create mode 100644 src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs
diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs
index 1c89d7662f..54f9ab4832 100644
--- a/src/Umbraco.Core/Cache/CacheKeys.cs
+++ b/src/Umbraco.Core/Cache/CacheKeys.cs
@@ -31,6 +31,7 @@ namespace Umbraco.Core.Cache
public const string UserContextCacheKey = "UmbracoUserContext";
public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout";
public const string UserCacheKey = "UmbracoUser";
+ public const string UserPermissionsCacheKey = "UmbracoUserPermissions";
public const string ContentTypeCacheKey = "UmbracoContentType";
diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index 41ceaf9b90..0e5c3340fa 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -22,7 +22,7 @@ namespace Umbraco.Core.Models
private DateTime? _expireDate;
private int _writer;
private string _nodeName;//NOTE Once localization is introduced this will be the non-localized Node Name.
-
+ private bool _permissionsChanged;
///
/// Constructor for creating a Content object
///
@@ -82,6 +82,7 @@ namespace Umbraco.Core.Models
private static readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo(x => x.ExpireDate);
private static readonly PropertyInfo WriterSelector = ExpressionHelper.GetPropertyInfo(x => x.WriterId);
private static readonly PropertyInfo NodeNameSelector = ExpressionHelper.GetPropertyInfo(x => x.NodeName);
+ private static readonly PropertyInfo PermissionsChangedSelector = ExpressionHelper.GetPropertyInfo(x => x.PermissionsChanged);
///
/// Gets or sets the template used by the Content.
@@ -243,6 +244,22 @@ namespace Umbraco.Core.Models
}
}
+ ///
+ /// Used internally to track if permissions have been changed during the saving process for this entity
+ ///
+ internal bool PermissionsChanged
+ {
+ get { return _permissionsChanged; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _permissionsChanged = value;
+ return _permissionsChanged;
+ }, _permissionsChanged, PermissionsChangedSelector);
+ }
+ }
+
///
/// Gets the ContentType used by this content object
///
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 93179f7e91..755fc5f04a 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -225,9 +225,14 @@ namespace Umbraco.Core.Persistence.Repositories
// user's default permissions.
if (parentPermissions.Any())
{
- var userPermissions = parentPermissions.ToDictionary(
- permissionDto => (object) permissionDto.UserId, permissionDto => permissionDto.Permission);
+ var userPermissions = parentPermissions.Select(
+ permissionDto => new KeyValuePair(
+ permissionDto.UserId,
+ permissionDto.Permission));
AssignEntityPermissions(entity, userPermissions);
+ //flag the entity's permissions changed flag so we can track those changes.
+ //Currently only used for the cache refreshers to detect if we should refresh all user permissions cache.
+ ((Content) entity).PermissionsChanged = true;
}
//Create the Content specific data - cmsContent
@@ -296,6 +301,10 @@ namespace Umbraco.Core.Persistence.Repositories
"SELECT coalesce(max(sortOrder),0) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType",
new {ParentId = entity.ParentId, NodeObjectType = NodeObjectTypeId});
entity.SortOrder = maxSortOrder + 1;
+
+ //Question: If we move a node, should we update permissions to inherit from the new parent if the parent has permissions assigned?
+ // if we do that, then we'd need to propogate permissions all the way downward which might not be ideal for many people.
+ // Gonna just leave it as is for now, and not re-propogate permissions.
}
var factory = new ContentFactory(NodeObjectTypeId, entity.Id);
diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
index 435fb9c7ef..adec7b6f85 100644
--- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
+++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
@@ -10,6 +10,7 @@ using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.member;
using System.Linq;
using umbraco.cms.businesslogic.web;
+using Content = Umbraco.Core.Models.Content;
using Macro = umbraco.cms.businesslogic.macro.Macro;
using Template = umbraco.cms.businesslogic.template.Template;
@@ -123,8 +124,46 @@ namespace Umbraco.Web.Cache
MediaService.Deleting += MediaServiceDeleting;
MediaService.Moving += MediaServiceMoving;
MediaService.Trashing += MediaServiceTrashing;
+
+ ContentService.Created += ContentServiceCreated;
+ ContentService.Copied += ContentServiceCopied;
}
+ #region Content service event handlers
+
+ ///
+ /// When an entity is copied new permissions may be assigned to it based on it's parent, if that is the
+ /// case then we need to clear all user permissions cache.
+ ///
+ ///
+ ///
+ static void ContentServiceCopied(IContentService sender, Core.Events.CopyEventArgs e)
+ {
+ //check if permissions have changed
+ var permissionsChanged = ((Content)e.Copy).WasPropertyDirty("PermissionsChanged");
+ if (permissionsChanged)
+ {
+ DistributedCache.Instance.RefreshAllUserPermissionsCache();
+ }
+ }
+
+ ///
+ /// When an entity is created new permissions may be assigned to it based on it's parent, if that is the
+ /// case then we need to clear all user permissions cache.
+ ///
+ ///
+ ///
+ static void ContentServiceCreated(IContentService sender, Core.Events.NewEventArgs e)
+ {
+ //check if permissions have changed
+ var permissionsChanged = ((Content)e.Entity).WasPropertyDirty("PermissionsChanged");
+ if (permissionsChanged)
+ {
+ DistributedCache.Instance.RefreshAllUserPermissionsCache();
+ }
+ }
+ #endregion
+
#region ApplicationTree event handlers
static void ApplicationTreeNew(ApplicationTree sender, System.EventArgs e)
{
@@ -396,15 +435,15 @@ namespace Umbraco.Web.Cache
{
if (sender.User != null)
{
- DistributedCache.Instance.RefreshUserCache(sender.User.Id);
+ DistributedCache.Instance.RefreshUserPermissionsCache(sender.User.Id);
}
- if (sender.UserId > -1)
+ else if (sender.UserId > -1)
{
- DistributedCache.Instance.RefreshUserCache(sender.UserId);
+ DistributedCache.Instance.RefreshUserPermissionsCache(sender.UserId);
}
- if (sender.NodeIds.Any())
+ else if (sender.NodeIds.Any())
{
- DistributedCache.Instance.RefreshAllUserCache();
+ DistributedCache.Instance.RefreshAllUserPermissionsCache();
}
}
diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Web/Cache/DistributedCache.cs
index a0ce50d4c7..1f7307b289 100644
--- a/src/Umbraco.Web/Cache/DistributedCache.cs
+++ b/src/Umbraco.Web/Cache/DistributedCache.cs
@@ -43,6 +43,7 @@ namespace Umbraco.Web.Cache
public const string MediaCacheRefresherId = "B29286DD-2D40-4DDB-B325-681226589FEC";
public const string MacroCacheRefresherId = "7B1E683C-5F34-43dd-803D-9699EA1E98CA";
public const string UserCacheRefresherId = "E057AF6D-2EE6-41F4-8045-3694010F0AA6";
+ public const string UserPermissionsCacheRefresherId = "840AB9C5-5C0B-48DB-A77E-29FE4B80CD3A";
public const string UserTypeCacheRefresherId = "7E707E21-0195-4522-9A3C-658CC1761BD4";
public const string ContentTypeCacheRefresherId = "6902E22C-9C10-483C-91F3-66B7CAE9E2F5";
public const string LanguageCacheRefresherId = "3E0F95D8-0BE5-44B8-8394-2B8750B62654";
diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
index 5e46927d57..274705439e 100644
--- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
+++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
@@ -61,6 +61,23 @@ namespace Umbraco.Web.Cache
}
#endregion
+ #region User permissions cache
+ public static void RemoveUserPermissionsCache(this DistributedCache dc, int userId)
+ {
+ dc.Remove(new Guid(DistributedCache.UserPermissionsCacheRefresherId), userId);
+ }
+
+ public static void RefreshUserPermissionsCache(this DistributedCache dc, int userId)
+ {
+ dc.Refresh(new Guid(DistributedCache.UserPermissionsCacheRefresherId), userId);
+ }
+
+ public static void RefreshAllUserPermissionsCache(this DistributedCache dc)
+ {
+ dc.RefreshAll(new Guid(DistributedCache.UserPermissionsCacheRefresherId));
+ }
+ #endregion
+
#region Template cache
///
/// Refreshes the cache amongst servers for a template
diff --git a/src/Umbraco.Web/Cache/UserCacheRefresher.cs b/src/Umbraco.Web/Cache/UserCacheRefresher.cs
index 0e4e32bc48..c80e8ed9b7 100644
--- a/src/Umbraco.Web/Cache/UserCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/UserCacheRefresher.cs
@@ -28,6 +28,7 @@ namespace Umbraco.Web.Cache
public override void RefreshAll()
{
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserCacheKey);
+ ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey);
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserContextCacheKey);
}
@@ -39,6 +40,7 @@ namespace Umbraco.Web.Cache
public override void Remove(int id)
{
ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserCacheKey, id));
+ ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id));
//we need to clear all UserContextCacheKey since we cannot invalidate based on ID since the cache is done so based
//on the current contextId stored in the database
diff --git a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs
new file mode 100644
index 0000000000..fdafdedb09
--- /dev/null
+++ b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs
@@ -0,0 +1,47 @@
+using System;
+using Umbraco.Core;
+using Umbraco.Core.Cache;
+
+namespace Umbraco.Web.Cache
+{
+ ///
+ /// Used only to invalidate the user permissions cache
+ ///
+ ///
+ /// The UserCacheRefresher will also clear a user's permissions cache, this refresher is for invalidating only permissions
+ /// for users/content, not the users themselves.
+ ///
+ public sealed class UserPermissionsCacheRefresher : CacheRefresherBase
+ {
+ protected override UserPermissionsCacheRefresher Instance
+ {
+ get { return this; }
+ }
+
+ public override Guid UniqueIdentifier
+ {
+ get { return Guid.Parse(DistributedCache.UserPermissionsCacheRefresherId); }
+ }
+
+
+ public override string Name
+ {
+ get { return "User permissions cache refresher"; }
+ }
+
+ public override void RefreshAll()
+ {
+ ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey);
+ }
+
+ public override void Refresh(int id)
+ {
+ Remove(id);
+ }
+
+ public override void Remove(int id)
+ {
+ ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs
index f76198d2c3..1371c6fdb9 100644
--- a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs
+++ b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterPublish.cs
@@ -8,6 +8,8 @@ using Umbraco.Web.Cache;
namespace Umbraco.Web.Strategies.Publishing
{
+ //TODO: I think we should move this logic into the CacheRefresherEventHandler since all other handlers are registered there for invalidating cache.
+
///
/// Represents the UpdateCacheAfterPublish class, which subscribes to the Published event
/// of the class and is responsible for doing the actual
diff --git a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs
index 7af7724c7e..39ca0beda3 100644
--- a/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs
+++ b/src/Umbraco.Web/Strategies/Publishing/UpdateCacheAfterUnPublish.cs
@@ -10,6 +10,8 @@ using Umbraco.Web.Cache;
namespace Umbraco.Web.Strategies.Publishing
{
+ //TODO: I think we should move this logic into the CacheRefresherEventHandler since all other handlers are registered there for invalidating cache.
+
///
/// Represents the UpdateCacheAfterUnPublish class, which subscribes to the UnPublished event
/// of the class and is responsible for doing the actual
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index d99c76a624..7d0761cf1e 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -286,6 +286,7 @@
+
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs
index cf570d93c1..7a96b7ae3e 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/cruds.aspx.cs
@@ -35,13 +35,8 @@ namespace umbraco.dialogs
// Put user code to initialize the page here
}
- #region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
- //
- // CODEGEN: This call is required by the ASP.NET Web Form Designer.
- //
- InitializeComponent();
base.OnInit(e);
node = new cms.businesslogic.CMSNode(int.Parse(helper.Request("id")));
@@ -99,15 +94,6 @@ namespace umbraco.dialogs
PlaceHolder1.Controls.Add(ht);
}
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent()
- {
-
- }
- #endregion
protected void Button1_Click(object sender, System.EventArgs e)
{
diff --git a/src/umbraco.businesslogic/User.cs b/src/umbraco.businesslogic/User.cs
index 3ee8608da0..5da83d2a6d 100644
--- a/src/umbraco.businesslogic/User.cs
+++ b/src/umbraco.businesslogic/User.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections;
+using System.Web.Caching;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
@@ -28,9 +29,6 @@ namespace umbraco.BusinessLogic
private bool _userDisabled;
private bool _defaultToLiveEditing;
- private Hashtable _cruds = new Hashtable();
- private bool _crudsInitialized = false;
-
private Hashtable _notifications = new Hashtable();
private bool _notificationsInitialized = false;
@@ -670,19 +668,42 @@ namespace umbraco.BusinessLogic
setupUser(_id);
string defaultPermissions = UserType.DefaultPermissions;
- if (!_crudsInitialized)
- initCruds();
+ //get the cached permissions for the user
+ var cachedPermissions = ApplicationContext.Current.ApplicationCache.GetCacheItem(
+ string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, _id),
+ //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will make this priority below average
+ CacheItemPriority.BelowNormal,
+ null,
+ //Since this cache can be quite large (http://issues.umbraco.org/issue/U4-2161) we will only have this exist in cache for 20 minutes,
+ // then it will refresh from the database.
+ new TimeSpan(0, 20, 0),
+ () =>
+ {
+ var cruds = new Hashtable();
+ using (var dr = SqlHelper.ExecuteReader("select * from umbracoUser2NodePermission where userId = @userId order by nodeId", SqlHelper.CreateParameter("@userId", this.Id)))
+ {
+ while (dr.Read())
+ {
+ if (!cruds.ContainsKey(dr.GetInt("nodeId")))
+ {
+ cruds.Add(dr.GetInt("nodeId"), string.Empty);
+ }
+ cruds[dr.GetInt("nodeId")] += dr.GetString("permission");
+ }
+ }
+ return cruds;
+ });
// NH 4.7.1 changing default permission behavior to default to User Type permissions IF no specific permissions has been
// set for the current node
- int nodeId = Path.Contains(",") ? int.Parse(Path.Substring(Path.LastIndexOf(",")+1)) : int.Parse(Path);
- if (_cruds.ContainsKey(nodeId))
+ var nodeId = Path.Contains(",") ? int.Parse(Path.Substring(Path.LastIndexOf(",", StringComparison.Ordinal)+1)) : int.Parse(Path);
+ if (cachedPermissions.ContainsKey(nodeId))
{
- return _cruds[int.Parse(Path.Substring(Path.LastIndexOf(",")+1))].ToString();
+ return cachedPermissions[int.Parse(Path.Substring(Path.LastIndexOf(",", StringComparison.Ordinal) + 1))].ToString();
}
// exception to everything. If default cruds is empty and we're on root node; allow browse of root node
- if (String.IsNullOrEmpty(defaultPermissions) && Path == "-1")
+ if (string.IsNullOrEmpty(defaultPermissions) && Path == "-1")
defaultPermissions = "F";
// else return default user type cruds
@@ -691,29 +712,10 @@ namespace umbraco.BusinessLogic
///
/// Initializes the user node permissions
- ///
+ ///
+ [Obsolete("This method doesn't do anything whatsoever and will be removed in future versions")]
public void initCruds()
- {
- if (!_isInitialized)
- setupUser(_id);
-
- // clear cruds
- System.Web.HttpContext.Current.Application.Lock();
- _cruds.Clear();
- System.Web.HttpContext.Current.Application.UnLock();
-
- using (IRecordsReader dr = SqlHelper.ExecuteReader("select * from umbracoUser2NodePermission where userId = @userId order by nodeId", SqlHelper.CreateParameter("@userId", this.Id)))
- {
- // int currentId = -1;
- while (dr.Read())
- {
- if (!_cruds.ContainsKey(dr.GetInt("nodeId")))
- _cruds.Add(dr.GetInt("nodeId"), String.Empty);
-
- _cruds[dr.GetInt("nodeId")] += dr.GetString("permission");
- }
- }
- _crudsInitialized = true;
+ {
}
///
From ba68da5736f194cb36a5222f09e21d0bf8779cd3 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Tue, 9 Jul 2013 11:55:29 +1000
Subject: [PATCH 24/25] Makes Domain.GetDomains public (#U4-2483)
---
src/umbraco.cms/businesslogic/web/Domain.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/umbraco.cms/businesslogic/web/Domain.cs b/src/umbraco.cms/businesslogic/web/Domain.cs
index 83bbb850ac..f5bcb46090 100644
--- a/src/umbraco.cms/businesslogic/web/Domain.cs
+++ b/src/umbraco.cms/businesslogic/web/Domain.cs
@@ -138,12 +138,12 @@ namespace umbraco.cms.businesslogic.web
#region Statics
- internal static List GetDomains()
+ public static IEnumerable GetDomains()
{
return GetDomains(false);
}
- internal static List GetDomains(bool includeWildcards)
+ internal static IEnumerable GetDomains(bool includeWildcards)
{
var domains = ApplicationContext.Current.ApplicationCache.GetCacheItem(
CacheKeys.DomainCacheKey,
@@ -177,7 +177,7 @@ namespace umbraco.cms.businesslogic.web
public static Domain GetDomain(string DomainName)
{
- return GetDomains().Find(delegate(Domain d) { return d.Name == DomainName; });
+ return GetDomains().FirstOrDefault(d => d.Name == DomainName);
}
public static int GetRootFromDomain(string DomainName)
@@ -189,7 +189,7 @@ namespace umbraco.cms.businesslogic.web
public static Domain[] GetDomainsById(int nodeId)
{
- return GetDomains().FindAll(delegate(Domain d) { return d._root == nodeId; }).ToArray();
+ return GetDomains().Where(d => d._root == nodeId).ToArray();
}
public static bool Exists(string DomainName)
From aa327696feb0dbe1d132491a49bf4f7f75d2ccef Mon Sep 17 00:00:00 2001
From: Sebastiaan Janssen
Date: Wed, 10 Jul 2013 12:35:21 +0200
Subject: [PATCH 25/25] Fixes U4-2354 MiniProfiler - Show SQL parameter values
and more stacktrace info
Refactored duplicate code
---
src/Umbraco.Core/Profiling/WebProfiler.cs | 46 +++++++++++------------
1 file changed, 22 insertions(+), 24 deletions(-)
diff --git a/src/Umbraco.Core/Profiling/WebProfiler.cs b/src/Umbraco.Core/Profiling/WebProfiler.cs
index 56918ff349..3a1974279e 100644
--- a/src/Umbraco.Core/Profiling/WebProfiler.cs
+++ b/src/Umbraco.Core/Profiling/WebProfiler.cs
@@ -1,6 +1,7 @@
using System;
using System.Web;
using StackExchange.Profiling;
+using StackExchange.Profiling.SqlFormatters;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
@@ -52,13 +53,8 @@ namespace Umbraco.Core.Profiling
///
void UmbracoApplicationEndRequest(object sender, EventArgs e)
{
- if (GlobalSettings.DebugMode == false) return;
- var request = TryGetRequest(sender);
- if (request.Success == false) return;
- if (request.Result.Url.IsClientSideRequest()) return;
- if (string.IsNullOrEmpty(request.Result["umbDebug"]) == false)
+ if (CanPerformProfilingAction(sender))
{
- //stop the profiler
Stop();
}
}
@@ -70,17 +66,28 @@ namespace Umbraco.Core.Profiling
///
void UmbracoApplicationBeginRequest(object sender, EventArgs e)
{
- if (GlobalSettings.DebugMode == false) return;
- var request = TryGetRequest(sender);
- if (request.Success == false) return;
- if (request.Result.Url.IsClientSideRequest()) return;
- if (string.IsNullOrEmpty(request.Result["umbDebug"]) == false)
+ if (CanPerformProfilingAction(sender))
{
- //start the profiler
Start();
}
}
+ private bool CanPerformProfilingAction(object sender)
+ {
+ if (GlobalSettings.DebugMode == false)
+ return false;
+
+ //will not run in medium trust
+ if (SystemUtilities.GetCurrentTrustLevel() < AspNetHostingPermissionLevel.High)
+ return false;
+
+ var request = TryGetRequest(sender);
+ if (request.Success == false || request.Result.Url.IsClientSideRequest() || string.IsNullOrEmpty(request.Result["umbDebug"]))
+ return false;
+
+ return true;
+ }
+
///
/// Render the UI to display the profiler
///
@@ -103,9 +110,7 @@ namespace Umbraco.Core.Profiling
///
public IDisposable Step(string name)
{
- if (GlobalSettings.DebugMode == false) return null;
-
- return MiniProfiler.Current.Step(name);
+ return GlobalSettings.DebugMode == false ? null : MiniProfiler.Current.Step(name);
}
///
@@ -113,10 +118,8 @@ namespace Umbraco.Core.Profiling
///
public void Start()
{
- if (GlobalSettings.DebugMode == false) return;
- //will not run in medium trust
- if (SystemUtilities.GetCurrentTrustLevel() < AspNetHostingPermissionLevel.High) return;
-
+ MiniProfiler.Settings.SqlFormatter = new SqlServerFormatter();
+ MiniProfiler.Settings.StackMaxLength = 5000;
MiniProfiler.Start();
}
@@ -129,10 +132,6 @@ namespace Umbraco.Core.Profiling
///
public void Stop(bool discardResults = false)
{
- if (GlobalSettings.DebugMode == false) return;
- //will not run in medium trust
- if (SystemUtilities.GetCurrentTrustLevel() < AspNetHostingPermissionLevel.High) return;
-
MiniProfiler.Stop(discardResults);
}
@@ -156,6 +155,5 @@ namespace Umbraco.Core.Profiling
return new Attempt(ex);
}
}
-
}
}
\ No newline at end of file