diff --git a/App_Config/Include/SitecoreSolutions.Commands.config b/App_Config/Include/SitecoreSolutions.Commands.config new file mode 100644 index 0000000..fd75c1d --- /dev/null +++ b/App_Config/Include/SitecoreSolutions.Commands.config @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Extensions/Gutters/ExtendedWorkflowState.cs b/Extensions/Gutters/ExtendedWorkflowState.cs new file mode 100644 index 0000000..1922188 --- /dev/null +++ b/Extensions/Gutters/ExtendedWorkflowState.cs @@ -0,0 +1,65 @@ +using Sitecore; +using Sitecore.Configuration; +using Sitecore.Data.Items; +using Sitecore.Diagnostics; +using Sitecore.Shell.Applications.ContentEditor.Gutters; +using Sitecore.Workflows; + +/// +/// This class is a straight extract-and-copy of the WorflowState class in Sitecore.Kernel under the namespace +/// Sitecore.Shell.Applications.ContentEditor.Gutters.WorkflowState. +/// There is one single change between this file and the base workflowState file, at line 61 , which calls a custom extendedShowWorkflowState +/// command that contains additional logic to check if user can execute workflow state without locking. +/// +/// Don't forget to change namespace to your environment +namespace SS.BaseConfig.Extensions.Gutters +{ + public class ExtendedWorkflowState : GutterRenderer + { + /// + /// Determines whether this instance is visible and should be rendered in context menu. + /// + /// + /// true if this instance is visible; otherwise, false. + /// + public override bool IsVisible() + { + if (!Settings.Workflows.Enabled) + return false; + return base.IsVisible(); + } + + /// Gets the icon. + /// The item. + /// The icon. + protected override GutterIconDescriptor GetIconDescriptor(Item item) + { + Assert.ArgumentNotNull((object)item, nameof(item)); + string str1 = item[FieldIDs.Workflow]; + string str2 = item[FieldIDs.WorkflowState]; + if (!Settings.Workflows.Enabled || !item.Access.CanWrite()) + return (GutterIconDescriptor)null; + if (string.IsNullOrEmpty(str1) || string.IsNullOrEmpty(str2)) + return (GutterIconDescriptor)null; + IWorkflowProvider workflowProvider = item.Database.WorkflowProvider; + if (workflowProvider == null) + return (GutterIconDescriptor)null; + IWorkflow workflow = workflowProvider.GetWorkflow(item); + if (workflow == null) + return (GutterIconDescriptor)null; + Sitecore.Workflows.WorkflowState state = workflow.GetState(item); + if (state == null) + return (GutterIconDescriptor)null; + if (state.FinalState) + return (GutterIconDescriptor)null; + GutterIconDescriptor gutterIconDescriptor = new GutterIconDescriptor(); + gutterIconDescriptor.Icon = state.Icon; + gutterIconDescriptor.Tooltip = state.DisplayName; + WorkflowCommand[] workflowCommandArray = WorkflowFilterer.FilterVisibleCommands(workflow.GetCommands(item), item); + if (workflowCommandArray != null && workflowCommandArray.Length != 0) + //Modify the event subscribed to the gutterIconDescriptor to call custom command, found at ExtendedShowWorkflowCommands.cs + gutterIconDescriptor.Click = "ss:extendedshowworkflowcommands(id=" + (object)item.ID + ",language=" + (object)item.Language + ",version=" + (object)item.Version + ",database=" + item.Database.Name + ")"; + return gutterIconDescriptor; + } + } +} \ No newline at end of file diff --git a/Extensions/ShowWorkflowCommands/ExtendedShowWorkflowCommands.cs b/Extensions/ShowWorkflowCommands/ExtendedShowWorkflowCommands.cs new file mode 100644 index 0000000..6934a8a --- /dev/null +++ b/Extensions/ShowWorkflowCommands/ExtendedShowWorkflowCommands.cs @@ -0,0 +1,74 @@ +using Sitecore; +using Sitecore.Configuration; +using Sitecore.Data; +using Sitecore.Data.Items; +using Sitecore.Diagnostics; +using Sitecore.Globalization; +using Sitecore.Security.Accounts; +using Sitecore.Shell.Framework.CommandBuilders; +using Sitecore.Shell.Framework.Commands; +using Sitecore.Web.UI.HtmlControls; +using Sitecore.Web.UI.Sheer; +using Sitecore.Workflows; +using System; +using System.Collections.Generic; + +/// +/// This class is a extact-and-copy of the ShowWorkflowCommand in Sitecore.Kernel. This class contains a single modification to the menu.Add code +/// at line 68 that makes a call to Utilities.canUserRunCommandsWithoutLocking() method to check if context user meets the criteria set out by this method +/// to allow execution of workflow commands without locking. If they pass this check, the gutter workflow command buttons become active even without +/// locking the item. +/// + +///Don't forget to change Namespace to suit your environment! +namespace SS.BaseConfig.Extensions.ShowWorkflowCommands +{ + [Serializable] + public class ExtendedShowWorkflowCommands : Command + { + /// Queries the state of the command. + /// The context. + /// The state of the command. + public override CommandState QueryState(CommandContext context) + { + if (!Settings.Workflows.Enabled) + return CommandState.Hidden; + return base.QueryState(context); + } + + /// Executes the command in the specified context. + /// The context. + public override void Execute(CommandContext context) + { + Assert.ArgumentNotNull((object)context, nameof(context)); + string parameter1 = context.Parameters["database"]; + string parameter2 = context.Parameters["id"]; + string parameter3 = context.Parameters["language"]; + string parameter4 = context.Parameters["version"]; + Database database = Factory.GetDatabase(parameter1); + if (database == null) + return; + Item obj = database.GetItem(parameter2, Language.Parse(parameter3), Sitecore.Data.Version.Parse(parameter4)); + if (obj == null) + return; + IWorkflow workflow = obj.Database.WorkflowProvider.GetWorkflow(obj); + if (workflow == null) + return; + WorkflowCommand[] workflowCommandArray = WorkflowFilterer.FilterVisibleCommands(workflow.GetCommands(obj), obj); + if (workflowCommandArray == null || workflowCommandArray.Length == 0) + return; + Menu menu = new Menu(); + SheerResponse.DisableOutput(); + foreach (WorkflowCommand command in workflowCommandArray) + { + string click = new WorkflowCommandBuilder(obj, workflow, command).ToString(); + //Add new logical condition to call canUserRunCommandsWithoutEdit() in Utilities class to check if user has permissions to execute + //workflow commands without locking. The rest of the conditions are same as in default class + menu.Add("C" + command.CommandID, command.DisplayName, command.Icon, string.Empty, click, false, string.Empty, MenuItemType.Normal).Disabled + = !Utilities.canUserRunCommandsWithoutLocking() && !Context.User.IsAdministrator && !obj.Locking.HasLock() && Settings.RequireLockBeforeEditing; + } + SheerResponse.EnableOutput(); + SheerResponse.ShowContextMenu(Context.ClientPage.ClientRequest.Control, "right", (Control)menu); + } + } +} \ No newline at end of file diff --git a/Extensions/Utilities.cs b/Extensions/Utilities.cs new file mode 100644 index 0000000..7fff602 --- /dev/null +++ b/Extensions/Utilities.cs @@ -0,0 +1,44 @@ +using Sitecore; +using Sitecore.Security.Accounts; +using System.Collections.Generic; + +/// Don't forget to change the Namespace to suit your environment! +namespace SS.BaseConfig.Extensions +{ + /// + /// Contains various generic and/or abstract utility methods to acomplish tasks in specific Extension code files + /// + public class Utilities + { + /// + /// Determines whether the current context user has the ability to execute workflow commands without locking the item + /// for editing. In its current implementation the class simply checks the current user against a list of roles that + /// shoudl have this permission. You can extend and expand this class to include all sorts of validation as desired. + /// + /// + /// true if user meets the cireteria set out by the class + /// + public static bool canUserRunCommandsWithoutLocking() + { + User user = Context.User; + //Define list of roles that are approved to have this access. Add the full name for your environment here + List roleList = new List + { + "sitecore\\Author", //This is the standard Author role provided with Sitecore + "sitecore\\anotherRoleHere" //This is a fake role just serving as an example! + }; + //Iterate over each role in the list and check if user is a member of the role. If they are return true + foreach (string s in roleList) + { + if (user.IsInRole(s)) { return true; } + } + /// + /// Add your own validation here. Maybe if a user is of a certain account. Or if they have edit rights to the item. Or if + /// their full name contains every vowel in the english langauge! Any validation goes!!! + /// + + //Return false if conditions aren't met, indicating user should not have the right to execute commands without locking item + return false; + } + } +} \ No newline at end of file diff --git a/Extensions/ExtendedWorkflowPanel.cs b/Extensions/WorkflowPanel/ExtendedWorkflowPanel.cs similarity index 77% rename from Extensions/ExtendedWorkflowPanel.cs rename to Extensions/WorkflowPanel/ExtendedWorkflowPanel.cs index e388b6c..a18149b 100644 --- a/Extensions/ExtendedWorkflowPanel.cs +++ b/Extensions/WorkflowPanel/ExtendedWorkflowPanel.cs @@ -1,20 +1,4 @@ -/* - * This class is a standalone, extended copy of the WorkflowPanel class as defined in Sitecore.Client - * and makes minimal modifications to allow a user who meets certain role criteria (defined by you!) to - * execute Workflow Commands from the workflow panel without locking the item for editing. - * - * This is the way Sitecore worked prior to 8.1 and this class is a reversion to this functionality with the - * added benefit of allowing you to dole out this 'inline workflow command execute' ability as you desire. - * - * The extensiosn to this class are minimal: a new canUserRunCommandsWithoutEdit class is created which returns a - * Boolean value. This boolean value is used in line 62 in the flag4 definition to allow the workflowpanel to enable - * user interaction with the Workflow Panel buttons. - * - * You can modify the canUserRunCommandsWithoutEdit() method to include additional roles, or expand it even further - * to suite your needs when it comes to limiting this inline workflow command execution ability. - */ - -using Sitecore; +using Sitecore; using Sitecore.Configuration; using Sitecore.Data.Items; using Sitecore.Diagnostics; @@ -29,10 +13,13 @@ using System.Collections.Generic; using System.Web.UI; -//CHANGE THE NAMESPACE TO SUIT YOUR ENVIRONMENT! -namespace BaseConfig.Extensions +/// +/// This class is a straight extract-and-copy from Sitecore.Client's WorkflowPanel method. This extends class makes a single modification to +/// the flag4 boolean code at line 53, adding a call to Utilities.canUserRunCommandsWithoutLocking() method, which checks if the context user meets +/// the conditions outlined in the utilities method to execute workflow commands without locking the item. +/// +namespace SS.BaseConfig.Extensions.WorkflowPanel { - /// Represents a WorkflowPanel. public class ExtendedWorkflowPanel : RibbonPanel { /// The _check in item. @@ -60,8 +47,10 @@ public override void Render(HtmlTextWriter output, Ribbon ribbon, Item button, C bool flag1 = this.IsCommandEnabled("item:checkout", obj); bool flag2 = ExtendedWorkflowPanel.CanShowCommands(obj, commands); bool flag3 = this.IsCommandEnabled("item:checkin", obj); + //Add call to Utilities.canUserRunCommandsWithoutLocking() to validate user against custom criteria. If method returns true, this flag4 will be set + //to true and the workflow commands will be clickable even if item is not locked by user bool flag4 = Context.User.IsAdministrator || obj.Locking.HasLock() || !Settings.RequireLockBeforeEditing || - canUserRunCommandsWithoutEdit(); + Utilities.canUserRunCommandsWithoutLocking(); this.RenderText(output, ExtendedWorkflowPanel.GetText(context.Items)); if (!(workflow != null | flag1 | flag2 | flag3)) return; @@ -207,31 +196,5 @@ private bool IsCommandEnabled(string command, Item item) return commandState == CommandState.Enabled; return true; } - - /// - /// Determines whether the current context user has the ability to execute workflow commands without locking the item - /// for editing. In its current implementation the class simply checks the current user against a list of roles that - /// shoudl have this permission. You can extend and expand this class to include all sorts of validation as desired. - /// - /// - /// true if user meets the cireteria set out by the class - /// - private bool canUserRunCommandsWithoutEdit() - { - User user = Context.User; - //Define list of roles that are approved to have this access. Add the full name for your environment here - List roleList = new List - { - "sitecore\\Author", //This is the standard Author role provided with Sitecore - "sitecore\\anotherRoleHere" //This is a fake role just serving as an example! - }; - //Iterate over each role in the list and check if user is a member of the role. If they are return true - foreach(string s in roleList) - { - if(user.IsInRole(s)) { return true; } - } - //Otherwise return false, indicating user should not have the right to execute commands without locking item - return false; - } } } \ No newline at end of file