From 3e7031290f49dbe2bef16df6453ade3df5ffc1dc Mon Sep 17 00:00:00 2001 From: simoncroak <97484717+simoncroak@users.noreply.github.com> Date: Mon, 30 May 2022 11:42:27 -0700 Subject: [PATCH 01/99] Update Wait-RemoteSitecoreJob.ps1 Update Wait-RemoteSitecoreJob.ps1 to support multiple jobs, job status will be output until all jobs have completed. --- Modules/SPE/Wait-RemoteSitecoreJob.ps1 | 30 +++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Modules/SPE/Wait-RemoteSitecoreJob.ps1 b/Modules/SPE/Wait-RemoteSitecoreJob.ps1 index 4933b345f..fe1cff604 100644 --- a/Modules/SPE/Wait-RemoteSitecoreJob.ps1 +++ b/Modules/SPE/Wait-RemoteSitecoreJob.ps1 @@ -45,7 +45,7 @@ [pscustomobject]$Session, [ValidateNotNull()] - [string]$Id, + [string[]]$Ids, [int]$Delay = 1 ) @@ -77,17 +77,23 @@ } } - $keepRunning = $true - while($keepRunning) { - $response = Invoke-RemoteScript -Session $session -ScriptBlock $doneScript - if($response -and $response.IsDone) { - $keepRunning = $false - Write-Verbose "Polling job $($response.Name). Status : $($response.Status)." - Write-Verbose "Finished polling job $($id)." - Invoke-RemoteScript -Session $session -ScriptBlock $finishScript - } else { - Write-Verbose "Polling job $($response.Name). Status : $($response.Status)." + $keepRunning = 0 + while($keepRunning -ne $Ids.Count) { + foreach ($id in $Ids) + { + $response = Invoke-RemoteScript -Session $session -ScriptBlock $doneScript + if($response -and $response.IsDone) { + $keepRunning++ + Write-Verbose "Polling job $($response.Name). Status : $($response.Status)." + Write-Verbose "Finished polling job $($id)." + } else { + Write-Verbose "Polling job $($response.Name). Status : $($response.Status)." + Start-Sleep -Seconds $Delay + } + } + if ($keepRunning -ne $Ids.Count) # Only sleep if we're not 'done' waiting for jobs + { Start-Sleep -Seconds $Delay } } -} \ No newline at end of file +} From ff052af01dfe2dee2151fa510357371a14571184 Mon Sep 17 00:00:00 2001 From: simoncroak <97484717+simoncroak@users.noreply.github.com> Date: Mon, 30 May 2022 11:45:06 -0700 Subject: [PATCH 02/99] Update Wait-RemoteSitecoreJob.ps1 Removed additional Start-Sleep --- Modules/SPE/Wait-RemoteSitecoreJob.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/SPE/Wait-RemoteSitecoreJob.ps1 b/Modules/SPE/Wait-RemoteSitecoreJob.ps1 index fe1cff604..d6b3bf6c8 100644 --- a/Modules/SPE/Wait-RemoteSitecoreJob.ps1 +++ b/Modules/SPE/Wait-RemoteSitecoreJob.ps1 @@ -88,7 +88,6 @@ Write-Verbose "Finished polling job $($id)." } else { Write-Verbose "Polling job $($response.Name). Status : $($response.Status)." - Start-Sleep -Seconds $Delay } } if ($keepRunning -ne $Ids.Count) # Only sleep if we're not 'done' waiting for jobs From 474b844c21903634af25aa7c57bf58bb45c46366 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Fri, 3 May 2024 02:08:37 +0200 Subject: [PATCH 03/99] #1326 It is not possible to search for scripts in the "Open" dropdown in ISE. --- .gitignore | 3 +++ src/Spe/Client/Controls/MruGallery.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bdfa2aaf9..24833d3ad 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,6 @@ node_modules/ # Ephemeral index caches used by serialization, do not commit *.scindex + +# Jetbrains Rider +.idea \ No newline at end of file diff --git a/src/Spe/Client/Controls/MruGallery.cs b/src/Spe/Client/Controls/MruGallery.cs index a556d1ece..74182e52d 100644 --- a/src/Spe/Client/Controls/MruGallery.cs +++ b/src/Spe/Client/Controls/MruGallery.cs @@ -260,7 +260,7 @@ private bool SearchDatabase(string indexName, string phrase) { var scriptsFound = false; // get all items in medialibrary - var rootID = ApplicationSettings.ScriptLibraryRoot.ID.ToShortID().ToString(); + var rootID = ApplicationSettings.ScriptLibraryRoot.ID.ToShortID().ToString().ToLower(); var query = context.GetQueryable() .Where( From 24deb25071c93f38a8ca983b681b10b39c1baac4 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Fri, 3 May 2024 04:19:45 +0200 Subject: [PATCH 04/99] #1327 : ISE Plugin "Scripts Containing" does not work most of the time --- .../Include/Spe/Spe.Serialization.config | 2 +- .../Internal/ISE Plugins/Scripts containing.yml | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Spe/App_Config/Include/Spe/Spe.Serialization.config b/src/Spe/App_Config/Include/Spe/Spe.Serialization.config index ae7e285a1..70dc92307 100644 --- a/src/Spe/App_Config/Include/Spe/Spe.Serialization.config +++ b/src/Spe/App_Config/Include/Spe/Spe.Serialization.config @@ -1,6 +1,6 @@ - + diff --git a/unicorn/SPE/Scripts/SPE/SPE/Core/Platform/Internal/ISE Plugins/Scripts containing.yml b/unicorn/SPE/Scripts/SPE/SPE/Core/Platform/Internal/ISE Plugins/Scripts containing.yml index 1847a6acc..eb6611b61 100644 --- a/unicorn/SPE/Scripts/SPE/SPE/Core/Platform/Internal/ISE Plugins/Scripts containing.yml +++ b/unicorn/SPE/Scripts/SPE/SPE/Core/Platform/Internal/ISE Plugins/Scripts containing.yml @@ -16,13 +16,17 @@ SharedFields: exit } + class ScriptSearchResultItem : SearchResultItem + { + [Sitecore.ContentSearch.IndexField("script")] + [string]$Script; + } + $searchProps = @{ Index = "sitecore_master_index" - Criteria = @( - @{ Filter = "StartsWith"; Field = "_fullpath"; Value = "/sitecore/system/modules/PowerShell/" }, - @{ Filter = "Equals"; Field = "_templatename"; Value = "PowerShell Script" }, - @{ Filter = "Contains"; Field = "script"; Value = "$selectionText"; CaseSensitive = $true } - ) + Where = "Paths.Contains(@0) and Script.Contains(@1) and Language== `"en`"" + WhereValues = [Spe.Core.Settings.ApplicationSettings]::ScriptLibraryRoot.ID, $selectionText + QueryType = [ScriptSearchResultItem] } $reportProps = @{ @@ -91,4 +95,4 @@ Languages: Value: 20150625T211456Z - ID: "8cdc337e-a112-42fb-bbb4-4143751e123f" Hint: __Revision - Value: "f7b824f0-4bfe-426a-bae8-9d69e361696e" + Value: "cab138dc-42f8-4987-98ff-7df638b178e9" From 3ced607a6400b19bee5b254a0972daa7a290e91d Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Fri, 3 May 2024 18:16:36 +0200 Subject: [PATCH 05/99] #1329 : Script execution results should be less in-the-way --- .../PowerShell/Scripts/ise.js | 25 +++++++++++++------ .../PowerShell/Styles/ise.css | 16 +++++------- .../Shell/PowerShell/PowerShellIse.xml | 5 +++- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/Spe/sitecore modules/PowerShell/Scripts/ise.js b/src/Spe/sitecore modules/PowerShell/Scripts/ise.js index 98e22b727..e029293ce 100644 --- a/src/Spe/sitecore modules/PowerShell/Scripts/ise.js +++ b/src/Spe/sitecore modules/PowerShell/Scripts/ise.js @@ -29,6 +29,7 @@ var debugMarkers = []; var resultsBottomOffset = 10; var typingTimer; + var resultsVisibilityIntent = true; var editor = $($("#Editor")[0]); editor.hide(); @@ -465,6 +466,9 @@ spe.preventCloseWhenRunning(false); } clearVariablesCache(); + if(!resultsVisibilityIntent){ + setTimeout(spe.closeResults, 2000); + } }; spe.clearBreakpoints = function() { @@ -510,7 +514,6 @@ $("#Result").height(resultsHeight); $("#Result").width($(window).width() - $("#Result").offset().left * 2); $("#ProgressOverlay").css("top",($("#Result").offset().top+4)+"px"); - $("#ResultsClose").css("top", ($("#Result").offset().top + 4) + "px"); }; function isEmpty(val) { @@ -536,7 +539,8 @@ spe.restoreResults = function() { $("#ResultsSplitter").show(); $("#ResultsRow").show(); - codeeditor.resize(); + spe.resizeEditor(); + $("#ResultsStatusBarAction").removeClass("status-bar-results-hidden") }; spe.closeResults = function() { @@ -544,6 +548,7 @@ $("#ResultsRow").hide("slow", function() { codeeditor.resize(); }); + $("#ResultsStatusBarAction").addClass("status-bar-results-hidden") }; function getCachedVariableValue(storageKey, variableName) { @@ -677,13 +682,17 @@ }); - $("#ResultsClose").click(function() { - $("#ResultsSplitter").hide(); - $("#ResultsRow").hide("slow", function() { - codeeditor.resize(); /* do something cool here? */ - }); + + $("#ShowHideResults").click(function() { + if ($("#ResultsRow").is(":visible")) { + resultsVisibilityIntent = false; + spe.closeResults(); + } else { + resultsVisibilityIntent = true; + spe.restoreResults(); + } }); - + function _getCommandHelp(str) { getPowerShellResponse({ "guid": guid, "command": str }, "GetHelpForCommand", function(json) { diff --git a/src/Spe/sitecore modules/PowerShell/Styles/ise.css b/src/Spe/sitecore modules/PowerShell/Styles/ise.css index 04e87d70e..0d092dc54 100644 --- a/src/Spe/sitecore modules/PowerShell/Styles/ise.css +++ b/src/Spe/sitecore modules/PowerShell/Styles/ise.css @@ -146,7 +146,7 @@ a.ps-link { text-transform: uppercase; } -#HelpClose, #ResultsClose { +#HelpClose { -moz-border-radius-bottomleft: 9px; -moz-border-radius-bottomright: 9px; -moz-border-radius-topleft: 9px; @@ -183,25 +183,17 @@ a.ps-link { top: 0; } -#HelpClose:hover, #ResultsClose:hover { +#HelpClose:hover { background: -moz-linear-gradient(center top, #dfdfdf 5%, #ededed 100%); background-color: #e3e3e3; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#dfdfdf', endColorstr='#ededed'); } -#ResultsClose:active { - top: 1px; -} - #ResultsSplitter { background: url(/sitecore%20modules/PowerShell/Assets/Splitter.png) no-repeat center; padding: 0 4px 0 4px; } -#ResultsClose { - position: fixed; -} - #Result { position: fixed; bottom: 27px; @@ -421,4 +413,8 @@ a.ps-link { #CodeEditor * { font-family: inherit !important; +} + +#ResultsStatusBarAction.status-bar-results-hidden { + background-color: rgb(1, 36, 86); } \ No newline at end of file diff --git a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml index 6c6b64a18..6ea8206cf 100644 --- a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml +++ b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml @@ -104,7 +104,6 @@

- x

                 
@@ -123,6 +122,10 @@ Copy results to clipboard + + Show/Hide Execution results pane + Show next usability hint... From b2d0728ea7c051761a9916aa2e24fda6783a21af Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Wed, 8 May 2024 19:41:54 +0200 Subject: [PATCH 06/99] #1332 : The ability to open multiple scripts in tabs in a single ISE --- src/Spe/Client/Applications/PowerShellIse.cs | 339 ++++++-- src/Spe/Core/Settings/ApplicationSettings.cs | 9 +- src/Spe/Texts.cs | 3 +- .../PowerShell/Assets/TabClose.png | Bin 0 -> 332 bytes .../PowerShell/Scripts/ise.js | 818 +++++++++++------- .../PowerShell/Styles/ise.css | 70 +- .../Shell/PowerShell/PowerShellIse.xml | 32 +- .../Behaviour/PerTabOutput.yml | 35 + .../PowerShellIse/Meta/EditIseSettings.yml | 6 +- 9 files changed, 906 insertions(+), 406 deletions(-) create mode 100644 src/Spe/sitecore modules/PowerShell/Assets/TabClose.png create mode 100644 unicorn/SPE/Core/Templates/Modules/PowerShell Console/PowerShell Console Settings/Behaviour/PerTabOutput.yml diff --git a/src/Spe/Client/Applications/PowerShellIse.cs b/src/Spe/Client/Applications/PowerShellIse.cs index 35cfa2943..e2e2df869 100644 --- a/src/Spe/Client/Applications/PowerShellIse.cs +++ b/src/Spe/Client/Applications/PowerShellIse.cs @@ -1,9 +1,11 @@ using System; +using System.Collections.Specialized; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Web; +using System.Web.UI.WebControls; using Sitecore; using Sitecore.Configuration; using Sitecore.Data; @@ -11,6 +13,7 @@ using Sitecore.Data.Managers; using Sitecore.Diagnostics; using Sitecore.IO; +using Sitecore.Mvc.Extensions; using Sitecore.Resources; using Sitecore.Security; using Sitecore.Security.Accounts; @@ -32,6 +35,7 @@ using Spe.Core.Settings; using Spe.Core.Settings.Authorization; using Spe.Core.VersionDecoupling; +using Literal = Sitecore.Web.UI.HtmlControls.Literal; namespace Spe.Client.Applications { @@ -42,11 +46,12 @@ public class PowerShellIse : BaseForm, IHasCommandContext, IPowerShellRunner public const string DefaultLanguage = "CurrentLanguage"; protected Memo Editor; + protected Memo OpenedScripts; + protected Memo ScriptItemIdMemo; + protected Memo ScriptItemDbMemo; protected Literal Progress; protected Border ProgressOverlay; - protected Border Result; protected Border RibbonPanel; - protected Literal ScriptName; protected Border ScriptResult; protected Memo SelectionText; protected Memo Breakpoints; @@ -54,7 +59,8 @@ public class PowerShellIse : BaseForm, IHasCommandContext, IPowerShellRunner protected GridPanel ElevatedPanel; protected GridPanel ElevationBlockedPanel; protected Border InfoPanel; - + protected Tabstrip Tabs; + protected Border TabsPanel; public bool Debugging { get; set; } public bool InBreakpoint { get; set; } @@ -76,16 +82,32 @@ public string ParentFrameName set { ServerProperties["ParentFrameName"] = value; } } - public static string ScriptItemId + public string ScriptItemId { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["ItemID"]); } - set { Context.ClientPage.ServerProperties["ItemID"] = value; } + get { return ScriptItemIdMemo.Value; } + set + { + if (value.IsWhiteSpaceOrNull()) + { + Log.Error("ScriptItemId is null or empty", this); + } + + ScriptItemIdMemo.Value = value; + } } - public static string ScriptItemDb + public string ScriptItemDb { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["ItemDb"]); } - set { Context.ClientPage.ServerProperties["ItemDb"] = value; } + get { return ScriptItemDbMemo.Value; } + set + { + if (value.IsWhiteSpaceOrNull()) + { + Log.Error("ScriptItemDb is null or empty", this); + } + + ScriptItemDbMemo.Value = value; + } } public static string ContextItemId @@ -106,6 +128,12 @@ public static bool ScriptModified set { Context.ClientPage.ServerProperties["ScriptModified"] = value ? "1" : string.Empty; } } + public static int TabSequencer + { + get { return (int)(Context.ClientPage.ServerProperties["TabSequencer"] ?? 0); } + set { Context.ClientPage.ServerProperties["TabSequencer"] = value; } + } + public static bool UseContext { get @@ -115,7 +143,7 @@ public static bool UseContext set { Context.ClientPage.ServerProperties["UseContext"] = value ? string.Empty : "0"; } } - public static Item ScriptItem + public Item ScriptItem { get { @@ -161,7 +189,7 @@ public CommandContext GetCommandContext() { var itemNotNull = Sitecore.Client.CoreDatabase.GetItem("{FDD5B2D5-31BE-41C3-AA76-64E5CC63B187}"); // /sitecore/content/Applications/PowerShell/PowerShellIse/Ribbon - var context = new CommandContext {RibbonSourceUri = itemNotNull.Uri}; + var context = new CommandContext { RibbonSourceUri = itemNotNull.Uri }; return context; } @@ -173,7 +201,8 @@ public bool MonitorActive private static bool IsHackedParameter(string parameter) { - var xssCleanup = new Regex(@"]*>[\s\S]*?|]*>[\s\S]*?|"); + var xssCleanup = + new Regex(@"]*>[\s\S]*?|]*>[\s\S]*?|"); if (xssCleanup.IsMatch(parameter)) { return true; @@ -193,20 +222,23 @@ protected override void OnLoad(EventArgs e) } base.OnLoad(e); - + if (Monitor == null) { if (!Context.ClientPage.IsEvent) { - Monitor = new SpeJobMonitor {ID = "Monitor"}; + Monitor = new SpeJobMonitor { ID = "Monitor" }; Context.ClientPage.Controls.Add(Monitor); } else { - Monitor = (SpeJobMonitor) Context.ClientPage.FindControl("Monitor"); + Monitor = (SpeJobMonitor)Context.ClientPage.FindControl("Monitor"); } } + Monitor.JobFinished += MonitorOnJobFinished; + + Tabs.OnChange += TabsOnChange; if (Context.ClientPage.IsEvent) return; @@ -219,11 +251,11 @@ protected override void OnLoad(EventArgs e) var itemId = WebUtil.GetQueryString("id"); var itemDb = WebUtil.GetQueryString("db"); - if (itemId.Length > 0) + + if (!itemId.IsWhiteSpaceOrNull()) { ScriptItemId = itemId; ScriptItemDb = itemDb; - LoadItem(itemDb, itemId); } ContextItemDb = Context.ContentDatabase.Name; @@ -238,6 +270,12 @@ protected override void OnLoad(EventArgs e) UpdateRibbon(); } + private void TabsOnChange(object sender, EventArgs e) + { + var tab = Tabs.Controls.OfType().Skip(Tabs.Active).FirstOrDefault(); + SelectTabByIndex(Tabs.Active + 1); + } + public override void HandleMessage(Message message) { Error.AssertObject(message, "message"); @@ -261,6 +299,7 @@ public override void HandleMessage(Message message) ? Context.Language.Name : CurrentLanguage; } + Dispatcher.Dispatch(message, context); } @@ -289,6 +328,7 @@ protected void Open(ClientPipelineArgs args) if (obj != null) str = obj.ID.ToString(); } + var urlString = new UrlString(UIUtil.GetUri("control:PowerShellScriptBrowser")); urlString.Append("id", selected); urlString.Append("fo", str); @@ -310,14 +350,13 @@ protected void MruOpen(ClientPipelineArgs args) LoadItem(args.Parameters["db"], args.Parameters["id"]); } - + [HandleMessage("ise:changecontextaccount", true)] protected void SecurityChangeContextAccount(ClientPipelineArgs args) { Assert.ArgumentNotNull(args, "args"); args.CarryResultToNextProcessor = false; args.AbortPipeline(); - //LoadItem(args.Parameters["db"], args.Parameters["id"]); } [HandleMessage("item:load", true)] @@ -339,10 +378,6 @@ protected void MruUpdate(Item scriptItem) var id = scriptItem.ID.ToString(); var name = scriptItem.Name; var icon = scriptItem[FieldIDs.Icon]; - var scriptName = scriptItem.Paths.Path.Substring(ApplicationSettings.ScriptLibraryPath.Length); - ScriptName.Text = scriptName; - SheerResponse.Eval($"spe.changeWindowTitle('{scriptName}', false);"); - UpdateStartbarTitle(icon, name); var mruMenu = ApplicationSettings.GetIseMruContainerItem(); var mruItems = mruMenu.Children; @@ -370,6 +405,7 @@ protected void MruUpdate(Item scriptItem) mruItem.Delete(); continue; } + if (!(mruItem["Message"].Contains(id))) { var item = mruItem; @@ -392,11 +428,8 @@ protected void NewScript(ClientPipelineArgs args) ScriptItemDb = string.Empty; Editor.Value = string.Empty; ScriptResult.Value = "
";
-            SheerResponse.Eval("spe.changeWindowTitle('Untitled', true);");
+            CreateNewTab(null);
             UpdateRibbon();
-
-            const string icon = "powershell/16x16/ise8.png";
-            UpdateStartbarTitle(icon, "Untitled");
         }
 
         [HandleMessage("ise:saveas", true)]
@@ -404,10 +437,11 @@ protected void SaveAs(ClientPipelineArgs args)
         {
             Assert.ArgumentNotNull(args, "args");
             args.Parameters["message"] = "ise:saveas";
-            if (!RequestSessionElevationEx(args,ApplicationNames.ItemSave,SessionElevationManager.SaveAction))
+            if (!RequestSessionElevationEx(args, ApplicationNames.ItemSave, SessionElevationManager.SaveAction))
             {
                 return;
             }
+
             if (args.IsPostBack)
             {
                 if (!args.HasResult)
@@ -437,12 +471,14 @@ protected void SaveAs(ClientPipelineArgs args)
                     if (obj != null)
                         str = obj.ID.ToString();
                 }
+
                 var urlString = new UrlString(UIUtil.GetUri("control:PowerShellScriptBrowser"));
                 urlString.Append("id", selected);
                 urlString.Append("fo", str);
                 urlString.Append("ro", root);
                 urlString.Append("he", Texts.PowerShellIse_SaveAs_Select_Script_Library);
-                urlString.Append("txt", Texts.PowerShellIse_SaveAs_Select_the_Library_that_you_want_to_save_your_script_to_);
+                urlString.Append("txt",
+                    Texts.PowerShellIse_SaveAs_Select_the_Library_that_you_want_to_save_your_script_to_);
                 urlString.Append("ic", icon);
                 urlString.Append("btn", Sitecore.Texts.SELECT);
                 SheerResponse.ShowModalDialog(urlString.ToString(), true);
@@ -465,12 +501,28 @@ protected void SaveItem(ClientPipelineArgs args)
                 {
                     return;
                 }
+
                 var scriptItem = ScriptItem;
                 if (scriptItem == null)
                     return;
                 scriptItem.Edit(
                     editArgs => { scriptItem.Fields[Templates.Script.Fields.ScriptBody].Value = Editor.Value; });
                 SheerResponse.Eval("spe.updateModificationFlag(true);");
+                var tabIndex = Tabs.Active + 1;
+                UpdateTabInfo(scriptItem, tabIndex);
+            }
+        }
+
+        [HandleMessage("ise:loadinitialscript", true)]
+        protected void LoadInitialScript(ClientPipelineArgs args)
+        {
+            if (ScriptItemId.Length > 0)
+            {
+                LoadItem(ScriptItemDb, ScriptItemId);
+            }
+            else
+            {
+                CreateNewTab(null);
             }
         }
 
@@ -495,9 +547,11 @@ private void LoadItem(string db, string id)
             if (scriptItem[Templates.Script.Fields.ScriptBody] != null)
             {
                 Editor.Value = scriptItem[Templates.Script.Fields.ScriptBody];
-                SheerResponse.Eval("spe.updateEditor();");
+                var createdNew = CreateNewTab(scriptItem);
+
                 ScriptItemId = scriptItem.ID.ToString();
                 ScriptItemDb = scriptItem.Database.Name;
+                SelectTabByIndex(Tabs.Controls.Count);
                 MruUpdate(scriptItem);
                 UpdateRibbon();
             }
@@ -507,6 +561,79 @@ private void LoadItem(string db, string id)
             }
         }
 
+        private bool CreateNewTab(Item scriptItem)
+        {
+            var itemEditing = scriptItem != null;
+            var path = itemEditing
+                ? scriptItem.Paths.Path.Substring(ApplicationSettings.ScriptLibraryPath.Length)
+                : String.Empty;
+            var tabIndex = 0;
+            var searchPath = $"{path}:";
+            var openedScript = OpenedScripts.Value.Split('\n').Select(line => line.Trim())
+                .FirstOrDefault(line => line.StartsWith(searchPath, StringComparison.OrdinalIgnoreCase));
+            var scriptAlreadyOpened = itemEditing && openedScript != null;
+            if (scriptAlreadyOpened)
+            {
+                tabIndex = int.Parse(openedScript.Split(':')[1]);
+            }
+            else
+            {
+                TabSequencer++;
+                SheerResponse.Eval($"spe.createEditor({TabSequencer});");
+                var newTab = new Tab
+                    { Header = $"{Tabs.Controls.Count + 1}", Active = true };
+                Tabs.Controls.Add(newTab);
+                tabIndex = Tabs.Controls.Count;
+                UpdateTabInfo(scriptItem, tabIndex);
+            }
+
+            SelectTabByIndex(tabIndex);
+            SheerResponse.Eval("spe.updateEditor();");
+            return !scriptAlreadyOpened;
+        }
+
+        private void SelectTabByIndex(int tabIndex)
+        {
+            if (tabIndex < 0)
+                return;
+
+            var tabIndexString = tabIndex.ToString();
+            Tabs.Controls.OfType().ForEach(tab => tab.Active = tab.Header == tabIndexString);
+            Tabs.Active = tabIndex - 1;
+            var tabsHtml = HtmlUtil.RenderControl(Tabs);
+            TabsPanel.InnerHtml = tabsHtml;
+
+            SheerResponse.Eval($"spe.changeTab({tabIndex});");
+        }
+
+        private void UpdateTabInfo(Item scriptItem, int tabIndex)
+        {
+            var itemEditing = scriptItem != null;
+            var path = itemEditing
+                ? scriptItem.Paths.Path.Substring(ApplicationSettings.ScriptLibraryPath.Length)
+                : String.Empty;
+
+            var title = itemEditing ? scriptItem.Name : $"Untitled{TabSequencer}";
+            var icon = itemEditing ? scriptItem[FieldIDs.Icon] : "powershell/16x16/ise8.png";
+            path = itemEditing ? path : title;
+            var id = itemEditing ? scriptItem.ID.ToString() : string.Empty;
+            var db = itemEditing ? scriptItem.Database.Name : string.Empty;
+
+            var builder = new ImageBuilder
+            {
+                Src = Images.GetThemedImageSource(icon, ImageDimension.id16x16),
+                Width = 16,
+                Height = 16,
+                Margin = "0px 8px 0px 0px",
+                Align = "middle"
+            };
+            var startbarHtml = $"{builder}{title} - ISE";
+
+            SheerResponse.Eval($"spe.changeTabDetails({tabIndex},'{path}', '{startbarHtml}', '{id}', '{db}');");
+            SelectTabByIndex(tabIndex);
+        }
+
+
         [HandleMessage("ise:run", true)]
         protected virtual void ClientExecute(ClientPipelineArgs args)
         {
@@ -527,6 +654,7 @@ protected virtual void ClientExecute(ClientPipelineArgs args)
                     {
                         scriptSession.SetItemLocationContext(ContextItem);
                     }
+
                     scriptSession.SetExecutedScript(ScriptItem);
                     scriptSession.ExecuteScriptPart(Editor.Value);
                     ClearOutput();
@@ -540,6 +668,7 @@ protected virtual void ClientExecute(ClientPipelineArgs args)
                     var error = ScriptSession.GetExceptionString(exc, ScriptSession.ExceptionStringFormat.Html);
                     PrintSessionUpdate($"
{error}
"); } + if (settings.SaveLastScript) { settings.Load(); @@ -573,7 +702,7 @@ protected virtual void JobExecuteSelection(ClientPipelineArgs args) protected virtual void JobExecuteScript(ClientPipelineArgs args, string scriptToExecute, bool debug) { if (!RequestSessionElevationEx(args, ApplicationNames.ISE, SessionElevationManager.ExecuteAction)) - { + { return; } @@ -602,8 +731,10 @@ protected virtual void JobExecuteScript(ClientPipelineArgs args, string scriptTo var bPoints = strBrPoints.Select(int.Parse); scriptSession.SetBreakpoints(bPoints); } + scriptToExecute = scriptSession.DebugFile; } + if (UseContext) { scriptSession.SetItemLocationContext(ContextItem); @@ -633,16 +764,17 @@ protected virtual void JobExecuteScript(ClientPipelineArgs args, string scriptTo "ScriptResult", string.Format( "
" + - ""+
-                    Texts.PowerShellIse_JobExecuteScript_Working+
+                    "<img src=" + - "
"+ - Texts.PowerShellIse_JobExecuteScript_Please_wait___0_+ + "
" + + Texts.PowerShellIse_JobExecuteScript_Please_wait___0_ + "
" + "
" + "
", executionMessage));
 
-            Context.ClientPage.ClientResponse.Eval("if(spe.preventCloseWhenRunning){spe.preventCloseWhenRunning(true);}");
+            Context.ClientPage.ClientResponse.Eval(
+                "if(spe.preventCloseWhenRunning){spe.preventCloseWhenRunning(true);}");
 
             scriptSession.Debugging = debug;
             Monitor.Start($"{DefaultSessionName}", "ISE", progressBoxRunner.Run,
@@ -721,7 +853,8 @@ private static void PrintSessionUpdate(string result)
         {
             if (!string.IsNullOrEmpty(result))
             {
-                var xssCleanup = new Regex(@"]*>[\s\S]*?|]*>[\s\S]*?|");
+                var xssCleanup =
+                    new Regex(@"]*>[\s\S]*?|]*>[\s\S]*?|");
                 if (xssCleanup.IsMatch(result))
                 {
                     result = xssCleanup.Replace(result, "
"); @@ -740,6 +873,7 @@ protected void ExecuteInternal(ScriptSession scriptSession, string script) { scriptSession.InitBreakpoints(); } + scriptSession.ExecuteScriptPart(script); } finally @@ -750,6 +884,7 @@ protected void ExecuteInternal(ScriptSession scriptSession, string script) scriptSession.DebugFile = string.Empty; scriptSession.ExecuteScriptPart("Get-PSBreakpoint | Remove-PSBreakpoint"); } + scriptSession.Debugging = false; scriptSession.Interactive = false; } @@ -772,6 +907,7 @@ protected virtual void JobAbort(ClientPipelineArgs args) ScriptRunning = false; UpdateRibbon(); } + Monitor.SessionID = string.Empty; ScriptRunning = false; } @@ -787,9 +923,11 @@ private void MonitorOnJobFinished(object sender, EventArgs eventArgs) if (result?.Exception != null) { - var error = ScriptSession.GetExceptionString(result.Exception, ScriptSession.ExceptionStringFormat.Html); + var error = ScriptSession.GetExceptionString(result.Exception, + ScriptSession.ExceptionStringFormat.Html); PrintSessionUpdate($"
{error}
"); } + SheerResponse.SetInnerHtml("PleaseWait", ""); ProgressOverlay.Visible = false; ScriptRunning = false; @@ -802,7 +940,8 @@ private void MonitorOnJobFinished(object sender, EventArgs eventArgs) protected virtual void UpdateProgress(ClientPipelineArgs args) { var showProgress = ScriptRunning && - !string.Equals(args.Parameters["RecordType"], "Completed", StringComparison.OrdinalIgnoreCase); + !string.Equals(args.Parameters["RecordType"], "Completed", + StringComparison.OrdinalIgnoreCase); ProgressOverlay.Visible = showProgress; var sb = new StringBuilder(); if (showProgress) @@ -824,9 +963,9 @@ protected virtual void UpdateProgress(ClientPipelineArgs args) { var secondsRemaining = int.Parse(args.Parameters["SecondsRemaining"]); if (secondsRemaining > -1) - sb.AppendFormat("

{0:c} "+ - Texts.PowerShellIse_UpdateProgress_remaining+ - ".

", + sb.AppendFormat("

{0:c} " + + Texts.PowerShellIse_UpdateProgress_remaining + + ".

", new TimeSpan(0, 0, secondsRemaining)); } @@ -847,6 +986,82 @@ protected void NotifyScriptModified(Message message) UpdateRibbon(); } + [HandleMessage("ise:closetab")] + protected void CloseTabInitiated(Message message) + { + bool.TryParse(message.Arguments["modified"], out var modified); + if (!int.TryParse(message.Arguments["index"], out var tabIndex)) + tabIndex = -1; + + if (!int.TryParse(message.Arguments["selectIndex"], out var previouslySelectedIndex)) + previouslySelectedIndex = -1; + + if (modified) + { + var parameters = new NameValueCollection + { + ["index"] = $"{tabIndex}", + ["message"] = + Texts.PowerShellIse_The_script_is_modified_Do_you_want_to_close_it_without_saving_the_changes, + ["selectIndex"] = $"{previouslySelectedIndex}" + }; + Context.ClientPage.Start(this, "CloseModifiedScript", parameters); + } + else + { + CloseTab(tabIndex, previouslySelectedIndex); + } + + UpdateRibbon(); + } + + protected void CloseModifiedScript(ClientPipelineArgs args) + { + if (args.IsPostBack) + { + if (args.Result == "yes") + { + if (!int.TryParse(args.Parameters["index"], out int index)) + { + index = -1; + } + + if (!int.TryParse(args.Parameters["selectIndex"], out int selectIndex)) + selectIndex = -1; + + CloseTab(index, selectIndex); + } + } + else + { + var index = int.Parse(args.Parameters["index"]); + SelectTabByIndex(index); + SheerResponse.Confirm(args.Parameters["message"]); + args.WaitForPostBack(); + } + } + + private void CloseTab(int index, int newSelectedIndex) + { + if (index < 0) + return; + var tab = Tabs.Controls.OfType().Skip(Tabs.Active).FirstOrDefault(); + if (tab != null) + { + Tabs.Controls.RemoveAt(Tabs.Controls.Count - 1); + } + + SheerResponse.Eval($"spe.closeScript({index});"); + if (Tabs.Controls.Count == 0) + { + CreateNewTab(null); + } + else + { + SelectTabByIndex(newSelectedIndex); + } + } + [HandleMessage("ise:updateribbon")] protected void UpdateRibbon(Message message) { @@ -858,7 +1073,7 @@ protected void UpdateRibbon(Message message) /// private void UpdateRibbon() { - var ribbon = new Ribbon {ID = "PowerShellRibbon"}; + var ribbon = new Ribbon { ID = "PowerShellRibbon" }; var item = ScriptItem; ribbon.CommandContext = new CommandContext(item); ribbon.ShowContextualTabs = false; @@ -890,10 +1105,10 @@ private void UpdateRibbon() ? Texts.PowerShellIse_UpdateRibbon_Single_execution : (sessionName == DefaultSessionName) ? Factory.GetDatabase("core") - .GetItem( - "/sitecore/content/Applications/PowerShell/PowerShellIse/Menus/Sessions/ISE editing session") - ? - .DisplayName ?? DefaultSessionName + .GetItem( + "/sitecore/content/Applications/PowerShell/PowerShellIse/Menus/Sessions/ISE editing session") + ? + .DisplayName ?? DefaultSessionName : sessionName; var obj2 = Context.Database.GetItem("/sitecore/content/Applications/PowerShell/PowerShellIse/Ribbon"); Error.AssertItemFound(obj2, "/sitecore/content/Applications/PowerShell/PowerShellIse/Ribbon"); @@ -915,20 +1130,6 @@ private void UpdateRibbon() UpdateWarning(); } - private void UpdateStartbarTitle(string icon, string title) - { - var builder = new ImageBuilder - { - Src = Images.GetThemedImageSource(icon, ImageDimension.id16x16), - Width = 16, - Height = 16, - Margin = "0px 8px 0px 0px", - Align = "middle" - }; - var startbarTitle = $"{builder}{title}"; - SheerResponse.Eval($"spe.changeStartbarTitle('{startbarTitle}');"); - } - private void UpdateWarning(string updateFromMessage = "") { var isSessionElevated = SessionElevationManager.IsSessionTokenElevated(ApplicationNames.ISE); @@ -961,6 +1162,7 @@ private void UpdateWarning(string updateFromMessage = "") controlContent = HtmlUtil.RenderControl(ElevationRequiredPanel); } } + break; case SessionElevationManager.TokenDefinition.ElevationAction.Block: controlContent = HtmlUtil.RenderControl(ElevationBlockedPanel); @@ -970,7 +1172,6 @@ private void UpdateWarning(string updateFromMessage = "") InfoPanel.InnerHtml = controlContent; InfoPanel.Visible = !hidePanel; SheerResponse.Eval($"spe.showInfoPanel({(!hidePanel).ToString().ToLower()}, '{updateFromMessage}');"); - } [HandleMessage("item:updated", true)] @@ -984,10 +1185,11 @@ protected void SetCurrentSessionId(ClientPipelineArgs args) { Assert.ArgumentNotNull(args, "args"); var sessionId = args.Parameters["id"]; - if(IsHackedParameter(sessionId)) + if (IsHackedParameter(sessionId)) { return; } + CurrentSessionId = sessionId; SheerResponse.Eval($"spe.changeSessionId('{sessionId}');"); UpdateRibbon(); @@ -1002,6 +1204,7 @@ protected void SetCurrentLanguage(ClientPipelineArgs args) { return; } + CurrentLanguage = language; new LanguageHistory().Add(language); UpdateRibbon(); @@ -1016,6 +1219,7 @@ protected void SetCurrentUser(ClientPipelineArgs args) { return; } + CurrentUser = user; new UserHistory().Add(user); UpdateRibbon(); @@ -1040,6 +1244,7 @@ protected void SetContextItem(ClientPipelineArgs args) ContextItemDb = contextDb; ContextItemId = contextId; } + UpdateRibbon(); } @@ -1050,7 +1255,10 @@ protected virtual void UpdateSettings(ClientPipelineArgs args) var backgroundColor = OutputLine.ProcessHtmlColor(settings.BackgroundColor); var bottomPadding = CurrentVersion.IsAtLeast(SitecoreVersion.V80) ? 0 : 10; SheerResponse.Eval( - $"spe.changeSettings('{settings.FontFamilyStyle}', {settings.FontSize}, '{backgroundColor}', {bottomPadding}, {settings.LiveAutocompletion.ToString().ToLower()});"); + $"spe.changeSettings('{settings.FontFamilyStyle}', {settings.FontSize}, " + + $"'{backgroundColor}', {bottomPadding}," + + $" {settings.LiveAutocompletion.ToString().ToLower()}," + + $" {settings.PerTabOutput.ToString().ToLower()});"); } [HandleMessage("ise:setbreakpoint", true)] @@ -1158,6 +1366,7 @@ public void SessionElevationPipeline(ClientPipelineArgs args) { message = args.Parameters["message"] + "(elevationResult=1)"; } + UpdateWarning(message); } } @@ -1180,7 +1389,7 @@ private bool RequestSessionElevationEx(ClientPipelineArgs args, string appName, var pipelineArgs = new ClientPipelineArgs { - Parameters = {["message"] = args.Parameters["message"], ["app"] = appName, ["action"] = action} + Parameters = { ["message"] = args.Parameters["message"], ["app"] = appName, ["action"] = action } }; Context.ClientPage.Start(this, nameof(SessionElevationPipeline), pipelineArgs); return false; diff --git a/src/Spe/Core/Settings/ApplicationSettings.cs b/src/Spe/Core/Settings/ApplicationSettings.cs index 51c0db081..d73f8320b 100644 --- a/src/Spe/Core/Settings/ApplicationSettings.cs +++ b/src/Spe/Core/Settings/ApplicationSettings.cs @@ -30,6 +30,7 @@ public class ApplicationSettings private const string LastScriptSettingFieldName = "LastScript"; private const string SaveLastScriptSettingFieldName = "SaveLastScript"; private const string LiveAutocompletionSettingFieldName = "LiveAutocompletion"; + private const string PerTabOutputSettingFieldName = "PerTabOutput"; private const string HostWidthSettingFieldName = "HostWidth"; private const string ForegroundColorSettingFieldName = "ForegroundColor"; private const string BackgroundColorSettingFieldName = "BackgroundColor"; @@ -105,6 +106,7 @@ public string FontFamilyStyle } public bool LiveAutocompletion { get; set; } + public bool PerTabOutput { get; set; } private string AppSettingsPath => SettingsItemPath + ApplicationName + "/"; private string CurrentUserSettingsPath => AppSettingsPath + CurrentDomain + "/" + CurrentUserName; @@ -229,7 +231,8 @@ public void Save() { configuration[LastScriptSettingFieldName] = HttpUtility.HtmlEncode(LastScript); ((CheckboxField) configuration.Fields[SaveLastScriptSettingFieldName]).Checked = SaveLastScript; - ((CheckboxField)configuration.Fields[LiveAutocompletionSettingFieldName]).Checked = LiveAutocompletion; + ((CheckboxField)configuration.Fields[LiveAutocompletionSettingFieldName]).Checked = LiveAutocompletion; + ((CheckboxField)configuration.Fields[PerTabOutputSettingFieldName]).Checked = PerTabOutput; configuration[HostWidthSettingFieldName] = HostWidth.ToString(CultureInfo.InvariantCulture); configuration[ForegroundColorSettingFieldName] = ForegroundColor.ToString(); configuration[BackgroundColorSettingFieldName] = BackgroundColor.ToString(); @@ -257,6 +260,9 @@ internal void Load() LiveAutocompletion = TryGetSettingValue(LiveAutocompletionSettingFieldName, false, () => ((CheckboxField) configuration.Fields[LiveAutocompletionSettingFieldName]).Checked); + PerTabOutput = + TryGetSettingValue(PerTabOutputSettingFieldName, false, + () => ((CheckboxField) configuration.Fields[PerTabOutputSettingFieldName]).Checked); HostWidth = TryGetSettingValue(HostWidthSettingFieldName,150, () => int.TryParse(configuration[HostWidthSettingFieldName], out var hostWidth) ? hostWidth : 150); @@ -297,6 +303,7 @@ private void SetToDefault() LastScript = string.Empty; SaveLastScript = true; LiveAutocompletion = false; + PerTabOutput = false; HostWidth = 150; ForegroundColor = ConsoleColor.White; BackgroundColor = ConsoleColor.DarkBlue; diff --git a/src/Spe/Texts.cs b/src/Spe/Texts.cs index 25ab48354..4c13109de 100644 --- a/src/Spe/Texts.cs +++ b/src/Spe/Texts.cs @@ -26,7 +26,8 @@ public class Texts public const string PowerShellIse_JobExecuteScript_Working = "Working"; public const string PowerShellIse_LoadItem_The_item_is_not_a_script_ = "The item is not a script."; public const string PowerShellIse_Open_Open_Script = "Open Script"; - + public const string PowerShellIse_The_script_is_modified_Do_you_want_to_close_it_without_saving_the_changes = "The script is modified. Do you want to close it without saving the changes?"; + public const string PowerShellIse_Open_Select_the_script_item_that_you_want_to_open_ = "Select the script item that you want to open."; diff --git a/src/Spe/sitecore modules/PowerShell/Assets/TabClose.png b/src/Spe/sitecore modules/PowerShell/Assets/TabClose.png new file mode 100644 index 0000000000000000000000000000000000000000..1c1dac4e37c5a30331b0871e89f2d4cf89aa0b6b GIT binary patch literal 332 zcmV-S0ki&zP)g$l2)pwN7Is$h1a_VVrTi^i{QH}~(kQ2H-Fwdc{_eT=*X>kQ zWt!+tMgN7sOns4=+gQhSL>xD218m?bB2L;mSivHOnK`P#LyWM1<<<^xhb7EmkeL-; zv0HLaWzT1Hnl5DK5w>c8+Wayi_J0(ZAfgSX-Gxr0)@|GSEt#3)a(WNXW%RIFYV87< zxraVxOWPMX!(++yGjpfTG+kKp^8m e3%`l~|4eU%#84KxhQWOR0000Ctrl+Space to show the Auto Suggest drop down that will show you all the matching comands/parameters/files depending on your caret position", "You can show help by pressing Ctrl+Enter for the closest command to the left of the cursor.", @@ -18,9 +18,27 @@ "You can search for keywords using the Ctrl+F hotkey.", "You can toggle a comment block using the Ctrl+Shift+/ hotkey.", "You can toggle a comment using the Ctrl+/ hotkey.", - "You can find more documentation in the Sitecore PowerShell Extensions book." + "You can find more documentation in the Sitecore PowerShell Extensions book." ]; + class EditorSession { + constructor(selectedText, editor, name, editorContainerId, path, index) { + this.index = index; // int: sort order + this.editor = editor; // string: the ace editor + this.name = name; // string: Script name + this.editorContainerId = editorContainerId; + this.path = path; // string: path + this.selectedText = selectedText; // string: selected text + this.startBarHtml = ""; // string: start bar html + this.tabTitleInnerHTML = ""; // string: tab title inner html + this.windowTitleInnerHTML = ""; // string: window title inner html + this.isModified = false; // bool: is modified + this.initialAssignment = true; // bool: initial assignment + this.breakpoints = ""; // string: breakpoints + this.results = ""; // string: Script execution results + } + } + var TokenTooltip = ace.require("tooltip").TokenTooltip; var guid = "ISE_Editing_Session"; const speVariablesCacheKey = "spe::variables"; @@ -30,35 +48,34 @@ var resultsBottomOffset = 10; var typingTimer; var resultsVisibilityIntent = true; - - var editor = $($("#Editor")[0]); - editor.hide(); + var editorSessions = []; + var editorFontFamily = "Monaco"; + var editorFontSize = 12; + var editorLiveAutocompletion = false; + var currentEditorIndex = 1; + var currentEditorSession; + var currentAceEditor; + var previousIndex = 1; + var perTabResults = true; + var editor = $($("#Editor")[0]); + var openedScriptsMemo = $($("#OpenedScripts")[0]); + var selectionTextMemo = $($("#SelectionText")[0]); + var breakpointsMemo = $($("#Breakpoints")[0]); + var scriptItemIdMemo = $($("#ScriptItemIdMemo")[0]); + var scriptItemDbMemo = $($("#ScriptItemDbMemo")[0]); // Setup the ace code editor. - var codeeditor = ace.edit("CodeEditor"); - codeeditor.setTheme("ace/theme/powershellise"); - codeeditor.session.setMode("ace/mode/powershell"); - codeeditor.setShowPrintMargin(false); - codeeditor.session.setValue(editor.val()); - codeeditor.session.on("change", function () { - editor.val(codeeditor.session.getValue()); - }); - codeeditor.tokenTooltip = new TokenTooltip(codeeditor); - - addProxy(codeeditor, "onPaste", function () { - spe.updateRibbon(); - }); addProxy(scForm, "invoke", function (args) { if (args[0] === "ise:immediatewindow") { clearVariablesCache(); } }); - + function registerEventListenersForRibbonButtons() { [].forEach.call(document.querySelectorAll('.scRibbonToolbarSmallGalleryButton, .scRibbonToolbarLargeComboButtonBottom'), function (div) { div.addEventListener("click", - function() { + function () { clearTimeout(typingTimer); }); }); @@ -71,7 +88,7 @@ } registerEventListenersForRibbonButtons(); - + window.parent.focus(); window.focus(); @@ -84,32 +101,40 @@ }; } - function setFocusOnConsole() { - $("body").focus(); - $(codeeditor).focus(); - ("WebForm_AutoFocus" in this) && WebForm_AutoFocus && WebForm_AutoFocus("CodeEditor"); - } - - window.addEventListener("focus", function(event) { + window.addEventListener("focus", function (event) { setFocusOnConsole(); }, false); function getQueryStringValue(key) { key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, "\\$&"); - var match = location.search.match(new RegExp("[?&]"+key+"=([^&]+)(&|$)")); + var match = location.search.match(new RegExp("[?&]" + key + "=([^&]+)(&|$)")); return match && decodeURIComponent(match[1].replace(/\+/g, " ")); } - if(getQueryStringValue("sc_bw") === "1"){ - //$("#RibbonPanel").css("padding-top","50px"); - $("#Wrapper").css("padding-top","0px"); + if (getQueryStringValue("sc_bw") === "1") { + $("#Wrapper").css("padding-top", "0px"); + } + + function hasSessionWithIndex(targetIndex) { + return editorSessions.some(session => session.index === targetIndex); + } + + function getSessionByIndex(index) { + // Find the session with the specified index + return editorSessions.find(session => session.index === index); } - setTimeout(setFocusOnConsole, 1000); + function setFocusOnConsole() { + $("body").focus(); + $(currentAceEditor).focus(); + ("WebForm_AutoFocus" in this) && WebForm_AutoFocus && WebForm_AutoFocus("CodeEditor"); + } + + setTimeout(setFocusOnConsole, 1000); spe.updateRibbon = function () { - if (!codeeditor.getReadOnly()) { - scForm.postRequest("", "", "", "ise:scriptchanged(modified=" + !codeeditor.session.getUndoManager().isClean() + ")"); + if (!currentAceEditor.getReadOnly()) { + scForm.postRequest("", "", "", "ise:scriptchanged(modified=" + !currentEditorSession.isModified + ")"); registerEventListenersForRibbonButtons(); } }; @@ -125,249 +150,343 @@ var posx = $("#PosX"); var posy = $("#PosY"); - $("#CodeEditor").on("keyup mousedown", function() { - var position = codeeditor.getCursorPosition(); - posx.text(position.column); - posy.text((position.row + 1)); - spe.updateRibbonNeeded(); - }); - $("#CodeEditor").on("keyup mouseup", function() { - var range = codeeditor.getSelectionRange(); - $("#SelectionText")[0].value = codeeditor.session.getTextRange(range); - }); + spe.updateModificationFlag = function (clear) { + + if (currentEditorSession.initialAssignment) { + currentEditorSession.initialAssignment = false; + } else + if (clear === currentEditorSession.isModified) { + currentEditorSession.isModified = !clear; + spe.applyWindowTitle(currentEditorIndex); + } + }; + + spe.changeTab = function (index) { + + if (!hasSessionWithIndex(index)) { + return; + } + + previousIndex = currentEditorIndex; + currentEditorIndex = index; + currentEditorSession = getSessionByIndex(currentEditorIndex); + currentAceEditor = currentEditorSession.editor; + clearVariablesCache(); + editor.val(currentAceEditor.session.getValue()); + selectionTextMemo.val(currentEditorSession.selectedText); + breakpointsMemo.val(currentEditorSession.breakpoints); + scriptItemIdMemo.val(currentEditorSession.scriptId); + scriptItemDbMemo.val(currentEditorSession.scriptDb); + spe.applyWindowTitle(currentEditorIndex); + + editorSessions.forEach(function (editorSession) { + if (editorSession.index === index) { + $("#" + editorSession.editorContainerId).show(); + } else { + $("#" + editorSession.editorContainerId).hide(); + } + }); + + if(perTabResults) { + $("#ScriptResultCode").text(""); + $("#ScriptResultCode").append(currentEditorSession.results); + $("#Result").scrollTop($("#Result")[0].scrollHeight); + } + } + + spe.createEditor = function (index) { + previousIndex = currentEditorIndex; + + currentEditorIndex = editorSessions.length + 1; + + var editorContainerId = "CodeEditor" + index; + $("#CodeEditors").append("
"); + currentAceEditor = ace.edit(editorContainerId); - $("#CopyResultsToClipboard").on("click", function() { - clipboard.copy(spe.getOutput()); - }); + const newSession = new EditorSession("", currentAceEditor, "", editorContainerId, "", currentEditorIndex); + editorSessions.push(newSession); + currentEditorSession = newSession; - ace.config.loadModule("ace/ext/emmet", function() { - ace.require("ace/lib/net").loadScript("/sitecore modules/PowerShell/Scripts/ace/emmet-core/emmet.js", function() { - codeeditor.setOption("enableEmmet", true); + currentAceEditor.setTheme("ace/theme/powershellise"); + currentAceEditor.session.setMode("ace/mode/powershell"); + currentAceEditor.setShowPrintMargin(false); + currentAceEditor.session.setValue(editor.val()); + currentAceEditor.session.on("change", function () { + editor.val(currentAceEditor.session.getValue()); + }); + currentAceEditor.tokenTooltip = new TokenTooltip(currentAceEditor); + currentAceEditor.setOption("fontFamily", editorFontFamily); + currentAceEditor.setOption("fontSize", editorFontSize); + currentAceEditor.setOptions({ + enableLiveAutocompletion: editorLiveAutocompletion }); - codeeditor.setOptions({ - enableSnippets: true, - enableBasicAutocompletion: true + addProxy(currentAceEditor, "onPaste", function () { + spe.updateRibbon(); }); - }); - ace.config.loadModule("ace/ext/language_tools", function(module) { - codeeditor.setOptions({ - enableSnippets: true, - enableBasicAutocompletion: true + $("#CodeEditor" + currentEditorIndex).on("keyup mousedown", function () { + var position = currentAceEditor.getCursorPosition(); + posx.text(position.column); + posy.text((position.row + 1)); + spe.updateRibbonNeeded(); }); - var keyWordCompleter = { - insertMatch: function(editor) { + $("#CodeEditor" + currentEditorIndex).on("keyup mouseup", function () { + var range = currentAceEditor.getSelectionRange(); + currentEditorSession.selectedText = currentAceEditor.session.getTextRange(range); + selectionTextMemo.val(currentEditorSession.selectedText); + }); - var data = editor.completer.popup.getData(editor.completer.popup.getRow()); + ace.config.loadModule("ace/ext/emmet", function () { + ace.require("ace/lib/net").loadScript("/sitecore modules/PowerShell/Scripts/ace/emmet-core/emmet.js", function () { + currentAceEditor.setOption("enableEmmet", true); + }); - var ranges = editor.selection.getAllRanges(); - for (var i = 0, range; range = ranges[i]; i++) { - if (data.meta === "Signature") { - data.value = data.fullValue; - } else if (data.meta === "Type") { - while (range.start.column > 0 && codeeditor.session.getTextRange(range).lastIndexOf("[") !== 0) { - range.start.column--; - } - range.start.column++; - data.value = data.fullValue; - } else if (data.meta === "Item" || data.meta === "ProviderItem" || data.meta === "ProviderContainer") { - range.start.column = data.position; - data.value = data.fullValue; - - //try trim prefix quotes - range.start.column--; - range.end.column++; - var replacedText = codeeditor.session.getTextRange(range); - var charStart = replacedText.charAt(0); - var charEnd = replacedText.charAt(replacedText.length-1); - if (charStart !== '"' && charStart !== "'") { + currentAceEditor.setOptions({ + enableSnippets: true, + enableBasicAutocompletion: true + }); + }); + + ace.config.loadModule("ace/ext/language_tools", function (module) { + currentAceEditor.setOptions({ + enableSnippets: true, + enableBasicAutocompletion: true + }); + + var keyWordCompleter = { + insertMatch: function (editor) { + + var data = editor.completer.popup.getData(editor.completer.popup.getRow()); + + var ranges = editor.selection.getAllRanges(); + for (var i = 0, range; range = ranges[i]; i++) { + if (data.meta === "Signature") { + data.value = data.fullValue; + } else if (data.meta === "Type") { + while (range.start.column > 0 && currentAceEditor.session.getTextRange(range).lastIndexOf("[") !== 0) { + range.start.column--; + } range.start.column++; - } + data.value = data.fullValue; + } else if (data.meta === "Item" || data.meta === "ProviderItem" || data.meta === "ProviderContainer") { + range.start.column = data.position; + data.value = data.fullValue; - //try trim trailing quotes - if (charEnd !== '"' && charEnd !== "'") { - range.end.column--; + //try trim prefix quotes + range.start.column--; + range.end.column++; + var replacedText = currentAceEditor.session.getTextRange(range); + var charStart = replacedText.charAt(0); + var charEnd = replacedText.charAt(replacedText.length - 1); + if (charStart !== '"' && charStart !== "'") { + range.start.column++; + } + + //try trim trailing quotes + if (charEnd !== '"' && charEnd !== "'") { + range.end.column--; + } + } else { + range.start.column -= editor.completer.completions.filterText.length; } - } else { - range.start.column -= editor.completer.completions.filterText.length; + editor.session.remove(range); } - editor.session.remove(range); - } - editor.execCommand("insertstring", data.value || data); - $.lastPrefix = ""; + editor.execCommand("insertstring", data.value || data); + $.lastPrefix = ""; - }, - getCompletions: function(editor, session, pos, prefix, callback) { - session.$mode.$keywordList = []; + }, + getCompletions: function (editor, session, pos, prefix, callback) { + session.$mode.$keywordList = []; - var range = codeeditor.getSelectionRange(); - range.start.column = 0; - var line = codeeditor.session.getTextRange(range); + var range = currentAceEditor.getSelectionRange(); + range.start.column = 0; + var line = currentAceEditor.session.getTextRange(range); - if (line) { + if (line) { - if (!$.tabCompletions || !$.lastPrefix || $.lastPrefix.length === 0 || prefix.indexOf($.lastPrefix) !== 0) { - $.lastPrefix = prefix; - _getTabCompletions(line); + if (!$.tabCompletions || !$.lastPrefix || $.lastPrefix.length === 0 || prefix.indexOf($.lastPrefix) !== 0) { + $.lastPrefix = prefix; + _getTabCompletions(line); + } + } else { + $.tabCompletions = [""]; } - } else { - $.tabCompletions = [""]; - } - var keywords = $.tabCompletions; + var keywords = $.tabCompletions; - if (keywords && keywords.length > 0 && keywords[0].indexOf("Signature", 0) === 0) { + if (keywords && keywords.length > 0 && keywords[0].indexOf("Signature", 0) === 0) { - callback(null, []); - $.tabCompletions = null; + callback(null, []); + $.tabCompletions = null; + + var msgType = "information"; + if (keywords.length === 1 && keywords[0].indexOf("not found in session", 0) > 0) { + msgType = "error"; + } - var msgType = "information"; - if (keywords.length === 1 && keywords[0].indexOf("not found in session", 0) > 0) { - msgType = "error"; + session.setAnnotations(keywords.map(function (word) { + var hint = word.split("|"); + return { + row: pos.row, + column: pos.column, + text: hint[3], + type: msgType // error, warning or information + }; + })); + ace.config.loadModule("ace/ext/error_marker", function (module) { + module.showErrorMarker(currentAceEditor, -1); + }); + return; } - session.setAnnotations(keywords.map(function (word) { + var psCompleter = this; + callback(null, keywords.map(function (word) { var hint = word.split("|"); return { - row: pos.row, - column: pos.column, - text: hint[3], - type: msgType // error, warning or information + name: hint[1], + value: hint[1], + score: 1000, + meta: hint[0], + position: hint[2], + fullValue: hint[3], + completer: psCompleter }; })); - ace.config.loadModule("ace/ext/error_marker", function (module) { - module.showErrorMarker(codeeditor, -1); - }); - return; } + }; - var psCompleter = this; - callback(null, keywords.map(function(word) { - var hint = word.split("|"); - return { - name: hint[1], - value: hint[1], - score: 1000, - meta: hint[0], - position: hint[2], - fullValue: hint[3], - completer: psCompleter - }; - })); - } - }; + module.addCompleter(keyWordCompleter); + }); - module.addCompleter(keyWordCompleter); - }); + currentAceEditor.setAutoScrollEditorIntoView(true); - codeeditor.setAutoScrollEditorIntoView(true); + currentAceEditor.on("guttermousedown", function (editor) { + var target = editor.domEvent.target; + if (target.className.indexOf("ace_gutter-cell") === -1) + return; - codeeditor.on("guttermousedown", function(editor) { - var target = editor.domEvent.target; - if (target.className.indexOf("ace_gutter-cell") === -1) - return; + if (editor.clientX > 25 + target.getBoundingClientRect().left) + return; - if (editor.clientX > 25 + target.getBoundingClientRect().left) - return; + var currRow = editor.getDocumentPosition().row; + spe.breakpointSet(currRow, "toggle"); + editor.stop(); + var sparseKeys = Object.keys(editor.editor.session.getBreakpoints()); + currentEditorSession.breakpoints = sparseKeys.toString(); + breakpointsMemo.val(currentEditorSession.breakpoints); + }); - var currRow = editor.getDocumentPosition().row; - spe.breakpointSet(currRow, "toggle"); - editor.stop(); - var sparseKeys = Object.keys(editor.editor.session.getBreakpoints()); - $("#Breakpoints")[0].value = sparseKeys.toString(); - }); + currentAceEditor.on("input", function () { + spe.updateModificationFlag(false); + }); - codeeditor.on("input", function () { - spe.updateModificationFlag(false); - }); + var codeeeditorcommands = [ + { + name: "help", + bindKey: {win: "ctrl-enter|shift-enter", mac: "ctrl-enter|command-enter", sender: "codeeditor|cli"}, + exec: function (env, args, request) { + var range = currentAceEditor.getSelectionRange(); + if (range.start.row === range.end.row && range.start.column === range.end.column) { + range.start.column = 0; + } + var command = currentAceEditor.session.getTextRange(range); + if (command) { + spe.showCommandHelp(command); + } + }, + readOnly: true + }, { + name: "fontSizeIncrease", + bindKey: {win: "Ctrl-Alt-Shift-=|Ctrl-Alt-Shift-+", mac: "Ctrl-Alt-Shift-=|Ctrl-Alt-Shift-+"}, + exec: function (editor) { + spe.changeFontSize(editor.getFontSize() + 1); + }, + readOnly: true + }, { + name: "fontSizeDecrease", + bindKey: {win: "Ctrl-Alt-Shift--", mac: "Ctrl-Alt-Shift--"}, + exec: function (editor) { + spe.changeFontSize(Math.max(editor.getFontSize() - 1, 8)); + }, + readOnly: true + }, { + name: "setDebugPoint", + bindKey: {win: "F8", mac: "F8"}, + exec: function (editor) { + var currRow = editor.selection.getCursor().row; + spe.breakpointSet(currRow, "toggle"); + }, + readOnly: true + } + ]; - var codeeeditorcommands = [ - { - name: "help", - bindKey: { win: "ctrl-enter|shift-enter", mac: "ctrl-enter|command-enter", sender: "codeeditor|cli" }, - exec: function(env, args, request) { - var range = codeeditor.getSelectionRange(); - if (range.start.row === range.end.row && range.start.column === range.end.column) { - range.start.column = 0; - } - var command = codeeditor.session.getTextRange(range); - if (command) { - spe.showCommandHelp(command); - } - }, - readOnly: true - }, { - name: "fontSizeIncrease", - bindKey: { win: "Ctrl-Alt-Shift-=|Ctrl-Alt-Shift-+", mac: "Ctrl-Alt-Shift-=|Ctrl-Alt-Shift-+" }, - exec: function(editor) { - spe.changeFontSize(editor.getFontSize() + 1); - }, - readOnly: true - }, { - name: "fontSizeDecrease", - bindKey: { win: "Ctrl-Alt-Shift--", mac: "Ctrl-Alt-Shift--" }, - exec: function(editor) { - spe.changeFontSize(Math.max(editor.getFontSize() - 1, 8)); - }, - readOnly: true - }, { - name: "setDebugPoint", - bindKey: { win: "F8", mac: "F8" }, - exec: function (editor) { - var currRow = editor.selection.getCursor().row; - spe.breakpointSet(currRow, "toggle"); - }, - readOnly: true - } - ]; + currentAceEditor.commands.addCommands(codeeeditorcommands); - codeeditor.commands.addCommands(codeeeditorcommands); + } - spe.getOutput = function() { - return $("#ScriptResultCode")[0].innerText; + spe.changeLiveAutocompletion = function (liveAutocompletion) { + editorLiveAutocompletion = liveAutocompletion; + editorSessions.forEach(function (editor) { + editor.editor.setOptions({ + enableLiveAutocompletion: liveAutocompletion + }); + }); }; - spe.changeFontSize = function(setting) { - setting = parseInt(setting) || 12; - codeeditor.setOption("fontSize", setting); - $("#ScriptResult").css({ "font-size": setting + "px" }); - }; - spe.changeLiveAutocompletion = function(liveAutocompletion) { - codeeditor.setOptions({ - enableLiveAutocompletion: liveAutocompletion - }); + $("#CopyResultsToClipboard").on("click", function () { + clipboard.copy(spe.getOutput()); + }); + + spe.getOutput = function () { + return $("#ScriptResultCode")[0].innerText; }; - spe.clearOutput = function() { + spe.clearOutput = function () { $("#ScriptResultCode").text(""); $("#Result").scrollTop($("#Result")[0].scrollHeight); + currentEditorSession.results = ""; clearVariablesCache(); }; - spe.appendOutput = function(outputToAppend) { + spe.appendOutput = function (outputToAppend) { var decoded = $("
").html(outputToAppend).text(); $("#ScriptResultCode").append(decoded); $("#Result").scrollTop($("#Result")[0].scrollHeight); + currentEditorSession.results = currentEditorSession.results + decoded; clearVariablesCache(); }; - spe.changeFontFamily = function(setting) { + spe.changeFontFamily = function (setting) { setting = setting || "Monaco"; - codeeditor.setOption("fontFamily", setting); + editorFontFamily = setting; + editorSessions.forEach(function (editor) { + editor.editor.setOption("fontFamily", setting); + }); + document.getElementById("ScriptResult").style.fontFamily = setting; }; + spe.changeFontSize = function (setting) { + setting = parseInt(setting) || 12; + editorFontSize = setting; + editorSessions.forEach(function (editor) { + editor.editor.setOption("fontSize", setting); + }); + $("#ScriptResult").css({"font-size": setting + "px"}); + }; + + spe.changeBackgroundColor = function (setting) { - $("#ScriptResult").css({ "background-color": setting }); - $("#Result").css({ "background-color": setting }); + $("#ScriptResult").css({"background-color": setting}); + $("#Result").css({"background-color": setting}); }; - spe.changeSettings = function(fontFamily, fontSize, backgroundColor, bottomOffset, liveAutocompletion) { + spe.changeSettings = function (fontFamily, fontSize, backgroundColor, bottomOffset, liveAutocompletion, perTabOutput) { spe.changeBackgroundColor(backgroundColor); spe.changeFontFamily(fontFamily); spe.changeFontSize(fontSize); @@ -375,54 +494,44 @@ spe.changeLiveAutocompletion(liveAutocompletion); } resultsBottomOffset = bottomOffset; + perTabResults = perTabOutput; }; spe.changeSessionId = function (sessionId) { guid = sessionId; }; - spe.debugStart = function(sessionId) { - codeeditor.setReadOnly(true); + spe.debugStart = function (sessionId) { + currentAceEditor.setReadOnly(true); }; spe.debugStop = function (sessionId) { setTimeout(spe.breakpointHandled, 100); - codeeditor.setReadOnly(false); - }; - - spe.updateModificationFlag = function (clear) { - if (clear) { - codeeditor.getSession().getUndoManager().markClean(); - } - var scriptModified = $("#scriptModified", window.parent.document); - if (codeeditor.getSession().getUndoManager().isClean()) - scriptModified.hide(); - else - scriptModified.show(); + currentAceEditor.setReadOnly(false); }; - spe.toggleBreakpoint = function(row, set) { + spe.toggleBreakpoint = function (row, set) { if (set) { - codeeditor.session.setBreakpoint(row); + currentAceEditor.session.setBreakpoint(row); } else { - codeeditor.session.clearBreakpoint(row); + currentAceEditor.session.clearBreakpoint(row); } scForm.postRequest("", "", "", "ise:togglebreakpoint(line=" + row + ",state=" + set + ")"); }; - spe.breakpointSet = function(row, action) { + spe.breakpointSet = function (row, action) { if (action === "toggle") { - if (codeeditor.session.getBreakpoints()[row] === "ace_breakpoint") { + if (currentAceEditor.session.getBreakpoints()[row] === "ace_breakpoint") { spe.toggleBreakpoint(row, false); } else { spe.toggleBreakpoint(row, true); } } else if (action === "Set" || action === "Enabled") { - if (codeeditor.session.getBreakpoints()[row] !== "ace_breakpoint") { + if (currentAceEditor.session.getBreakpoints()[row] !== "ace_breakpoint") { spe.toggleBreakpoint(row, true); } } else if (action === "Removed" || action === "Disabled") { - if (codeeditor.session.getBreakpoints()[row] === "ace_breakpoint") { + if (currentAceEditor.session.getBreakpoints()[row] === "ace_breakpoint") { spe.toggleBreakpoint(row, false); } } @@ -434,30 +543,27 @@ scContent.ribbonNavigatorButtonClick(this, event, "PowerShellRibbon_Strip_DebugStrip"); var Range = ace.require("ace/range").Range; setTimeout(function () { - debugMarkers.push(codeeditor.session.addMarker(new Range(line, column, endLine, endColumn + 1), "breakpoint", "text")); + debugMarkers.push(currentAceEditor.session.addMarker(new Range(line, column, endLine, endColumn + 1), "breakpoint", "text")); }, 100); - if (line < codeeditor.getFirstVisibleRow() || line > codeeditor.getLastVisibleRow()) { - codeeditor.gotoLine(line); + if (line < currentAceEditor.getFirstVisibleRow() || line > currentAceEditor.getLastVisibleRow()) { + currentAceEditor.gotoLine(line); } }; - spe.breakpointHandled = function() { + spe.breakpointHandled = function () { while (debugMarkers.length > 0) { - codeeditor.session.removeMarker(debugMarkers.shift()); + currentAceEditor.session.removeMarker(debugMarkers.shift()); } scContent.ribbonNavigatorButtonClick(this, event, "PowerShellRibbon_Strip_ImageStrip"); }; - scForm.postRequest("", "", "", "ise:updatesettings"); - spe.updateEditor = function () { - codeeditor.getSession().setValue(editor.val()); - spe.clearBreakpoints(); + currentAceEditor.getSession().setValue(editor.val()); }; spe.insertEditorContent = function (text) { - var position = codeeditor.getCursorPosition(); - codeeditor.getSession().insert(position, text); + var position = currentAceEditor.getCursorPosition(); + currentAceEditor.getSession().insert(position, text); spe.clearBreakpoints(); }; @@ -466,16 +572,16 @@ spe.preventCloseWhenRunning(false); } clearVariablesCache(); - if(!resultsVisibilityIntent){ + if (!resultsVisibilityIntent) { setTimeout(spe.closeResults, 2000); } }; - spe.clearBreakpoints = function() { - var breakPoints = Object.keys(codeeditor.session.getBreakpoints()); + spe.clearBreakpoints = function () { + var breakPoints = Object.keys(currentAceEditor.session.getBreakpoints()); var bpCount = breakPoints.length; for (var i = 0; i < bpCount; i++) { - codeeditor.session.clearBreakpoint(breakPoints[i]); + currentAceEditor.session.clearBreakpoint(breakPoints[i]); } }; @@ -487,40 +593,115 @@ return string.replace(new RegExp(escapeRegExp(find), "g"), replace); } - spe.changeWindowTitle = function(newTitle, clearWindow) { - if (clearWindow) { - codeeditor.getSession().setValue(""); - } - newTitle = replaceAll(newTitle, "/", " / "); + spe.changeTabDetails = function (codeEditorIndex, newTitle, startBarHtml, scriptId, scriptDb) { + + var editorSession = getSessionByIndex(codeEditorIndex); + var itemName = newTitle.split("/").last(); + editorSession.name = itemName + editorSession.path = newTitle; + const newTabTitle = replaceAll(editorSession.path, "/", " / "); + editorSession.tabTitleInnerHTML = " " + editorSession.name + + "" + + " "; + const newWindowTitle = replaceAll(editorSession.path, "/", " / "); + editorSession.windowTitleInnerHTML = " " + newWindowTitle + " - "; + editorSession.tabTitleHtml = replaceAll(editorSession.path, "/", " / "); + editorSession.scriptId = scriptId; + editorSession.scriptDb = scriptDb; + editorSession.startBarHtml = startBarHtml; + var openedScripts = editorSessions.map(session => session.path + ":" + session.index).join('\n'); + openedScriptsMemo.val(openedScripts); + } + + spe.applyWindowTitle = function (codeEditorIndex) { + + var editorSession = getSessionByIndex(codeEditorIndex); var windowCaption = $("#WindowCaption", window.parent.document); if (windowCaption.length > 0) { - windowCaption[0].innerHTML = "" + newTitle + " (*) - "; + windowCaption[0].innerHTML = + editorSession.windowTitleInnerHTML. + replace("#tabindex#", editorSession.index). + replace("#styles#", editorSession.isModified ? "display:inline;" : "display:none;"); + } - codeeditor.getSession().getUndoManager().markClean(); - spe.clearBreakpoints(); - }; - spe.changeStartbarTitle = function (newTitle) { + var styles = $("#ModifiedStatusStyles"); + if (styles.length === 0) { + var head = $("head"); + head.append(""); + styles = $("#ModifiedStatusStyles"); + } + var stylesinnerHTML = ""; + editorSessions.forEach(function (currEditor) { + var tabHeader = $("#Tabs_tab_" + (currEditor.index - 1)) + if (tabHeader.length > 0) { + tabHeader[0].innerHTML = currEditor.tabTitleInnerHTML.replaceAll("#tabindex#", currEditor.index); + if (currEditor.isModified) { + stylesinnerHTML = stylesinnerHTML + ".ModifiedMark" + currEditor.index + "{display:inline;}"; + } + } + }); + styles[0].innerHTML = stylesinnerHTML; + + if (window.parent && window.parent.frameElement) { var frameId = window.parent.frameElement.id; var startbar = window.parent.parent.document.getElementById('startbar_application_' + frameId); - $(startbar).find('span').html(newTitle); + $(startbar).find('span').html(editorSession.startBarHtml); } }; - spe.resizeEditor = function() { - codeeditor.resize(); - var resultsHeight =$(window).height() -$("#ResultsSplitter").offset().top - $("#ResultsSplitter").height() - $("#StatusBar").height() - resultsBottomOffset - 10; - $("#Result").height(resultsHeight); + + spe.closeTab = function (event, index) { + + event.stopPropagation(); + var closingEditorSession = getSessionByIndex(index); + var selectIndex = currentEditorIndex; + if (index === currentEditorIndex) { + selectIndex = previousIndex; + } + if (selectIndex > index) { + selectIndex--; + } + selectIndex = Math.min(selectIndex, editorSessions.length - 1); + scForm.postRequest("", "", "", "ise:closetab(index=" + index + ",modified=" + closingEditorSession.isModified + ",selectIndex=" + selectIndex + ")"); + }; + + spe.closeScript = function (index) { + + // Remove the session with the specified index + var closingEditorSession = getSessionByIndex(index); + + editorSessions = editorSessions.filter(session => session.index !== index); + + // cleanup the DOM and script content + var editorContainer = $(closingEditorSession.editor.container) + closingEditorSession.editor.destroy(); + editorContainer.remove(); + editor.val(""); + + // Update the indices of the remaining sessions + editorSessions.forEach((session, i) => { + session.index = i + 1; + }); + } + + spe.resizeEditor = function () { + if (currentAceEditor !== undefined) { + currentAceEditor.resize(); + } + var resultsHeight = $(window).height() - $("#ResultsSplitter").offset().top - $("#ResultsSplitter").height() - $("#StatusBar").height() - resultsBottomOffset - 10; + $("#Result").height(resultsHeight); $("#Result").width($(window).width() - $("#Result").offset().left * 2); - $("#ProgressOverlay").css("top",($("#Result").offset().top+4)+"px"); + $("#ProgressOverlay").css("top", ($("#Result").offset().top + 4) + "px"); + }; function isEmpty(val) { return (val === undefined || val == null || val.length <= 0) ? true : false; } - spe.showInfoPanel = function(showPanel, updateFromMessage) { + spe.showInfoPanel = function (showPanel, updateFromMessage) { if (showPanel) { $("#InfoPanel").css("display", "block"); } else { @@ -532,21 +713,21 @@ } }; - spe.requestElevation = function() { + spe.requestElevation = function () { scForm.postRequest("", "", "", "ise:requestelevation"); }; - spe.restoreResults = function() { + spe.restoreResults = function () { $("#ResultsSplitter").show(); $("#ResultsRow").show(); spe.resizeEditor(); $("#ResultsStatusBarAction").removeClass("status-bar-results-hidden") }; - spe.closeResults = function() { + spe.closeResults = function () { $("#ResultsSplitter").hide(); - $("#ResultsRow").hide("slow", function() { - codeeditor.resize(); + $("#ResultsRow").hide("slow", function () { + currentAceEditor.resize(); }); $("#ResultsStatusBarAction").addClass("status-bar-results-hidden") }; @@ -599,7 +780,7 @@ return cachedValue; } - getPowerShellResponse({ "guid": sessionId, "variableName": variableName }, "GetVariableValue", + getPowerShellResponse({"guid": sessionId, "variableName": variableName}, "GetVariableValue", function (json) { data = json.d; }); @@ -608,16 +789,16 @@ return data; }; - spe.getAutocompletionPrefix = function(text) { + spe.getAutocompletionPrefix = function (text) { var data; - getPowerShellResponse({ "guid": guid, "command": text }, "GetAutoCompletionPrefix", - function(json) { + getPowerShellResponse({"guid": guid, "command": text}, "GetAutoCompletionPrefix", + function (json) { data = JSON.parse(json.d); }); return data; }; - spe.showCommandHelp = function(command) { + spe.showCommandHelp = function (command) { _getCommandHelp(command); if (spe.ajaxDialog) spe.ajaxDialog.remove(); @@ -631,7 +812,7 @@ spe.ajaxDialog.dialog("close"); }); }, - close: function(event, ui) { + close: function (event, ui) { $(this).remove(); }, height: $(window).height() - 20, @@ -645,7 +826,7 @@ $.commandHelp = ""; - spe.changeWindowTitle($("#ScriptName")[0].innerHTML, false); + var tipIndex = Math.floor(Math.random() * tips.length); var tip = tips[tipIndex]; @@ -653,37 +834,37 @@ $("#StatusTip").html(tip); $("#TipOfTheSession").position({ - my: "left bottom", - at: "left bottom-30px", - within: $("#Result"), - of: $("#Result") - }).css({ - right: 0, - left: 0 - }).hide() - .show("drop", { direction: "down" }, "400") + my: "left bottom", + at: "left bottom-30px", + within: $("#Result"), + of: $("#Result") + }).css({ + right: 0, + left: 0 + }).hide() + .show("drop", {direction: "down"}, "400") .delay(5000) - .hide("drop", { direction: "down" }, "400", - function() { - $(".status-bar-text").animate({ backgroundColor: "#fcefa1" }).animate({ backgroundColor: "#fff" }); + .hide("drop", {direction: "down"}, "400", + function () { + $(".status-bar-text").animate({backgroundColor: "#fcefa1"}).animate({backgroundColor: "#fff"}); }); - $(".status-bar-text").click(function() { + $(".status-bar-text").click(function () { tipIndex++; if (tipIndex >= tips.length) { tipIndex = 0; } var nextTip = tips[tipIndex]; - $(".status-bar-text").animate({ backgroundColor: "#fcefa1" }, - function() { + $(".status-bar-text").animate({backgroundColor: "#fcefa1"}, + function () { $("#TipText").html(nextTip); $("#StatusTip").html(nextTip); - }).animate({ backgroundColor: "#fff" }); + }).animate({backgroundColor: "#fff"}); }); - - $("#ShowHideResults").click(function() { + + $("#ShowHideResults").click(function () { if ($("#ResultsRow").is(":visible")) { resultsVisibilityIntent = false; spe.closeResults(); @@ -692,44 +873,51 @@ spe.restoreResults(); } }); - + function _getCommandHelp(str) { - getPowerShellResponse({ "guid": guid, "command": str }, "GetHelpForCommand", - function(json) { + getPowerShellResponse({"guid": guid, "command": str}, "GetHelpForCommand", + function (json) { var data = JSON.parse(json.d); $.commandHelp = data[0]; }); } function _getTabCompletions(str) { - getPowerShellResponse({ "guid": guid, "command": str }, "CompleteAceCommand", - function(json) { + getPowerShellResponse({"guid": guid, "command": str}, "CompleteAceCommand", + function (json) { var data = JSON.parse(json.d); $.tabCompletions = data; }); } function getPowerShellResponse(callData, remotefunction, doneFunction, errorFunction) { - if(remotefunction != "GetVariableValue"){ + if (remotefunction != "GetVariableValue") { spe.requestElevation(); - }; + } + ; var datastring = JSON.stringify(callData); $.ajax({ - type: "POST", - contentType: "application/json; charset=utf-8", - dataType: "json", - url: "/sitecore modules/PowerShell/Services/PowerShellWebService.asmx/" + remotefunction, - data: datastring, - processData: false, - cache: false, - async: false - }).done(doneFunction) + type: "POST", + contentType: "application/json; charset=utf-8", + dataType: "json", + url: "/sitecore modules/PowerShell/Services/PowerShellWebService.asmx/" + remotefunction, + data: datastring, + processData: false, + cache: false, + async: false + }).done(doneFunction) .fail(errorFunction); } - $(window).on('resize', function(){ - spe.resizeEditor(); + $(window).on('resize', function () { + spe.resizeEditor(); }).trigger('resize'); + + setTimeout(function () { + scForm.postRequest("", "", "", "ise:updatesettings"); + scForm.postRequest("", "", "", "ise:loadinitialscript"); + }, 100); + }); }(jQuery, window, window.spe = window.spe || {}, window.ace = window.ace || {})); diff --git a/src/Spe/sitecore modules/PowerShell/Styles/ise.css b/src/Spe/sitecore modules/PowerShell/Styles/ise.css index 0d092dc54..406b1a7f1 100644 --- a/src/Spe/sitecore modules/PowerShell/Styles/ise.css +++ b/src/Spe/sitecore modules/PowerShell/Styles/ise.css @@ -9,7 +9,10 @@ background-color: white; } -#ResultsSplitter + tr > td { height: 100%; } +#ResultsSplitter + tr > td { + height: 100%; + overflow: auto; +} #Databases { border: solid 1px ButtonShadow; @@ -194,10 +197,10 @@ a.ps-link { padding: 0 4px 0 4px; } -#Result { - position: fixed; - bottom: 27px; -} +/*#Result {*/ +/* position: fixed;*/ +/* bottom: 27px;*/ +/*}*/ #HelpClose { font-size: 18px; @@ -417,4 +420,59 @@ a.ps-link { #ResultsStatusBarAction.status-bar-results-hidden { background-color: rgb(1, 36, 86); -} \ No newline at end of file +} + +.scTabPage { + padding: 4px 0 0 0; +} + +.scTab, .scTab_Hover, .scTabActive, .scTabActive_Hover { + text-transform: none; + line-height: 30px; +} + +.scTabActive:after, .scTabActive_Hover:after { + height: 30px; +} + +.ff .scTabstrip { + margin-top: 4px; + height: 33px; +} + +.scTabs { + top: 30px; + display:none; +} + +.aceCodeEditor, #EditingArea, #Content, #Wrapper, #Result { + width: 100%; + height: 100%; +} + +#TabsPanel { + width: 100%; + height: 35px; +} + +#EditingArea { + overflow: auto; +} + +#CodeEditors { + width: 100%; + height: calc(100% - 40px); +} + +.scriptModified{ + display:none; + color:#fc2929 +} + +.closeTab { + display: inline-block; + cursor: pointer; + background: url(/sitecore%20modules/PowerShell/Assets/TabClose.png) no-repeat center; + padding: 0 4px 0 4px; + margin: 0 -10px 0 10px; +} diff --git a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml index 6ea8206cf..7339182a3 100644 --- a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml +++ b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml @@ -3,15 +3,15 @@ - - + + + - @@ -32,11 +32,14 @@ + + + + \ No newline at end of file diff --git a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellResultViewerList.xml b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellResultViewerList.xml index 8451584a4..7afa03e29 100644 --- a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellResultViewerList.xml +++ b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellResultViewerList.xml @@ -68,7 +68,6 @@ - Show next usability hint...
From fd9ba32d948f4825b17a744ecc0a9f177e39cce4 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Mon, 20 May 2024 17:19:25 +0200 Subject: [PATCH 19/99] #1287 : Rename-Item do not take info account language version --- src/Spe/Core/Extensions/ItemExtensions.cs | 23 ++++++++++++++++++- .../Core/Provider/PsSitecoreItemProvider.cs | 10 ++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Spe/Core/Extensions/ItemExtensions.cs b/src/Spe/Core/Extensions/ItemExtensions.cs index 138a6b853..114d1a0bf 100644 --- a/src/Spe/Core/Extensions/ItemExtensions.cs +++ b/src/Spe/Core/Extensions/ItemExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Sitecore.Data; using Sitecore.Data.Items; using Sitecore.Data.Managers; @@ -42,7 +43,7 @@ public static bool IsPowerShellLibrary(this Item item) public static void Edit(this Item item, Action action) { - var args = new ItemEditArgs(); + var args = new ItemEditArgs() { Item = item }; try { var wasEditing = item.Editing.IsEditing; @@ -102,6 +103,12 @@ public class ItemEditArgs /// default: false /// public bool SaveOnError { get; set; } + + /// + /// Edited Item + /// default: false + /// + public Item Item { get; set; } } public static bool IsVersioned(this Item item) @@ -115,5 +122,19 @@ public static bool IsVersioned(this Item item) return false; } + + public static Item FirstExistingVersion(this Item item) + { + if (item != null && item.Versions.Count == 0) + { + var allVersions = item.Versions.GetVersions(true); + if (allVersions.Any()) + { + return allVersions.FirstOrDefault(); + } + } + + return item; + } } } \ No newline at end of file diff --git a/src/Spe/Core/Provider/PsSitecoreItemProvider.cs b/src/Spe/Core/Provider/PsSitecoreItemProvider.cs index d187d521c..4f4cfa501 100644 --- a/src/Spe/Core/Provider/PsSitecoreItemProvider.cs +++ b/src/Spe/Core/Provider/PsSitecoreItemProvider.cs @@ -542,7 +542,7 @@ private Item TransferItem(Item sourceItem, Item destinationItem, string leafName AuditFormatter.FormatItem(destinationItem)); if (transferredItem.Name != leafName) { - transferredItem.Edit(args => transferredItem.Name = leafName); + transferredItem.FirstExistingVersion().Edit(args => args.Item.Name = leafName); } return transferredItem; @@ -664,8 +664,8 @@ protected override void MoveItem(string path, string destination) if (sourceItem.Name != leafName) { - sourceItem.Edit( - args => { sourceItem.Name = leafName; }); + sourceItem.FirstExistingVersion().Edit( + args => { args.Item.Name = leafName; }); } } catch (Exception ex) @@ -689,8 +689,8 @@ protected override void RenameItem(string path, string newName) CheckOperationAllowed("rename", item.Access.CanRename(), item.Uri.ToString()); if (ShouldProcess(item.Paths.Path, "Rename to '" + newName + "'")) { - item.Edit( - args => { item.Name = newName; }); + item.FirstExistingVersion().Edit( + args => { args.Item.Name = newName; }); } } } From ac58a6c1d35fc78e198a69a969939acb90d80bed Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Mon, 20 May 2024 19:35:49 +0200 Subject: [PATCH 20/99] #1331 : NullReferenceException occurs when trying to accept VersionAddedNotification via PowerShell in Sitecore --- .../Data/Clones/ReceiveItemCloneNotificationCommand.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Spe/Commands/Data/Clones/ReceiveItemCloneNotificationCommand.cs b/src/Spe/Commands/Data/Clones/ReceiveItemCloneNotificationCommand.cs index 47ba4279c..b8e14bf5f 100644 --- a/src/Spe/Commands/Data/Clones/ReceiveItemCloneNotificationCommand.cs +++ b/src/Spe/Commands/Data/Clones/ReceiveItemCloneNotificationCommand.cs @@ -37,6 +37,10 @@ protected override void ProcessNotification(Notification notification) switch (Action) { case (NotificationAction.Accept): + if(notification is VersionAddedNotification versionAddedNotification) + { + versionAddedNotification.ForceAccept = true; + } notification.Accept(clone); break; case (NotificationAction.Reject): From 6402209b78e8740c8731b6e8d1cd4c5379708ca4 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Mon, 20 May 2024 20:22:02 +0200 Subject: [PATCH 21/99] #1016 : Get-ItemField command should not define the -Language parameter --- src/Spe/Commands/Data/GetItemFieldCommand.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Spe/Commands/Data/GetItemFieldCommand.cs b/src/Spe/Commands/Data/GetItemFieldCommand.cs index a1fec7514..08afca1b8 100644 --- a/src/Spe/Commands/Data/GetItemFieldCommand.cs +++ b/src/Spe/Commands/Data/GetItemFieldCommand.cs @@ -12,7 +12,7 @@ namespace Spe.Commands.Data [Cmdlet(VerbsCommon.Get, "ItemField")] [OutputType(typeof (TemplateField), typeof (string), typeof (Field), ParameterSetName = new[] {"Item from Pipeline", "Item from Path", "Item from ID"})] - public class GetItemFieldCommand : BaseItemCommand + public class GetItemFieldCommand : BaseLanguageAgnosticItemCommand { public enum ReturnValue { @@ -35,7 +35,6 @@ public enum ReturnValue protected override void BeginProcessing() { - Language = null; base.BeginProcessing(); if (Name != null && Name.Any()) From 71e785707daabd53f2f3ef2949808f7807861e16 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Thu, 30 May 2024 23:35:13 +0200 Subject: [PATCH 22/99] #1340 - Add ability to send deferred sheer messages and execute javascript to make it possible to e.g. refresh the content editor tree post script execution --- src/Spe/Client/Applications/PowerShellIse.cs | 61 ++++++------ .../PowerShellResultViewerList.cs | 8 ++ .../Client/Applications/PowerShellRunner.cs | 11 +-- src/Spe/Client/Applications/RunnerOutput.cs | 3 +- src/Spe/Client/Applications/ScriptRunner.cs | 4 +- .../MenuItems/ExecutePowerShellScript.cs | 20 ++-- .../Interactive/InvokeJavaScriptCommand.cs | 11 ++- .../Interactive/SendSheerMessageCommand.cs | 29 ++++-- src/Spe/Core/Extensions/MessageExtensions.cs | 26 +++++ src/Spe/Core/Host/CommandCompletion.cs | 7 +- src/Spe/Core/Host/ScriptExecutionResult.cs | 94 +++++++++++++++++++ src/Spe/Core/Host/ScriptSession.cs | 44 +++++---- src/Spe/Core/Host/ScriptingHost.cs | 6 +- src/Spe/Core/Host/ScriptingHostPrivateData.cs | 7 +- src/Spe/Spe.csproj | 5 + .../PowerShell/Styles/ise.css | 32 +++++++ 16 files changed, 280 insertions(+), 88 deletions(-) create mode 100644 src/Spe/Core/Extensions/MessageExtensions.cs create mode 100644 src/Spe/Core/Host/ScriptExecutionResult.cs diff --git a/src/Spe/Client/Applications/PowerShellIse.cs b/src/Spe/Client/Applications/PowerShellIse.cs index e2e2df869..348afe1e6 100644 --- a/src/Spe/Client/Applications/PowerShellIse.cs +++ b/src/Spe/Client/Applications/PowerShellIse.cs @@ -66,25 +66,25 @@ public class PowerShellIse : BaseForm, IHasCommandContext, IPowerShellRunner protected bool ScriptRunning { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["ScriptRunning"]) == "1"; } - set { Context.ClientPage.ServerProperties["ScriptRunning"] = value ? "1" : string.Empty; } + get => StringUtil.GetString(Context.ClientPage.ServerProperties["ScriptRunning"]) == "1"; + set => Context.ClientPage.ServerProperties["ScriptRunning"] = value ? "1" : string.Empty; } protected bool WasElevated { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["WasElevated"]) == "1"; } - set { Context.ClientPage.ServerProperties["WasElevated"] = value ? "1" : string.Empty; } + get => StringUtil.GetString(Context.ClientPage.ServerProperties["WasElevated"]) == "1"; + set => Context.ClientPage.ServerProperties["WasElevated"] = value ? "1" : string.Empty; } public string ParentFrameName { - get { return StringUtil.GetString(ServerProperties["ParentFrameName"]); } - set { ServerProperties["ParentFrameName"] = value; } + get => StringUtil.GetString(ServerProperties["ParentFrameName"]); + set => ServerProperties["ParentFrameName"] = value; } public string ScriptItemId { - get { return ScriptItemIdMemo.Value; } + get => ScriptItemIdMemo.Value; set { if (value.IsWhiteSpaceOrNull()) @@ -98,7 +98,7 @@ public string ScriptItemId public string ScriptItemDb { - get { return ScriptItemDbMemo.Value; } + get => ScriptItemDbMemo.Value; set { if (value.IsWhiteSpaceOrNull()) @@ -112,35 +112,32 @@ public string ScriptItemDb public static string ContextItemId { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["ContextItemID"]); } - set { Context.ClientPage.ServerProperties["ContextItemID"] = value; } + get => StringUtil.GetString(Context.ClientPage.ServerProperties["ContextItemID"]); + set => Context.ClientPage.ServerProperties["ContextItemID"] = value; } public static string ContextItemDb { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["ContextItemDb"]); } - set { Context.ClientPage.ServerProperties["ContextItemDb"] = value; } + get => StringUtil.GetString(Context.ClientPage.ServerProperties["ContextItemDb"]); + set => Context.ClientPage.ServerProperties["ContextItemDb"] = value; } public static bool ScriptModified { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["ScriptModified"]) == "1"; } - set { Context.ClientPage.ServerProperties["ScriptModified"] = value ? "1" : string.Empty; } + get => StringUtil.GetString(Context.ClientPage.ServerProperties["ScriptModified"]) == "1"; + set => Context.ClientPage.ServerProperties["ScriptModified"] = value ? "1" : string.Empty; } public static int TabSequencer { - get { return (int)(Context.ClientPage.ServerProperties["TabSequencer"] ?? 0); } - set { Context.ClientPage.ServerProperties["TabSequencer"] = value; } + get => (int)(Context.ClientPage.ServerProperties["TabSequencer"] ?? 0); + set => Context.ClientPage.ServerProperties["TabSequencer"] = value; } public static bool UseContext { - get - { - return string.IsNullOrEmpty(StringUtil.GetString(Context.ClientPage.ServerProperties["UseContext"])); - } - set { Context.ClientPage.ServerProperties["UseContext"] = value ? string.Empty : "0"; } + get => string.IsNullOrEmpty(StringUtil.GetString(Context.ClientPage.ServerProperties["UseContext"])); + set => Context.ClientPage.ServerProperties["UseContext"] = value ? string.Empty : "0"; } public Item ScriptItem @@ -167,20 +164,20 @@ public static Item ContextItem public static string CurrentSessionId { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["CurrentSessionId"]); } - set { Context.ClientPage.ServerProperties["CurrentSessionId"] = value; } + get => StringUtil.GetString(Context.ClientPage.ServerProperties["CurrentSessionId"]); + set => Context.ClientPage.ServerProperties["CurrentSessionId"] = value; } public static string CurrentUser { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["CurrentUser"]); } - set { Context.ClientPage.ServerProperties["CurrentUser"] = value; } + get => StringUtil.GetString(Context.ClientPage.ServerProperties["CurrentUser"]); + set => Context.ClientPage.ServerProperties["CurrentUser"] = value; } public static string CurrentLanguage { - get { return StringUtil.GetString(Context.ClientPage.ServerProperties["CurrentLanguage"]); } - set { Context.ClientPage.ServerProperties["CurrentLanguage"] = value; } + get => StringUtil.GetString(Context.ClientPage.ServerProperties["CurrentLanguage"]); + set => Context.ClientPage.ServerProperties["CurrentLanguage"] = value; } public SpeJobMonitor Monitor { get; private set; } @@ -195,8 +192,8 @@ public CommandContext GetCommandContext() public bool MonitorActive { - get { return Monitor.Active; } - set { Monitor.Active = value; } + get => Monitor.Active; + set => Monitor.Active = value; } private static bool IsHackedParameter(string parameter) @@ -302,7 +299,7 @@ public override void HandleMessage(Message message) Dispatcher.Dispatch(message, context); } - + [HandleMessage("ise:open", true)] protected void Open(ClientPipelineArgs args) { @@ -683,6 +680,7 @@ protected virtual void JobExecute(ClientPipelineArgs args) { args.Parameters.Add("message", "ise:execute"); JobExecuteScript(args, Editor.Value, false); + ClearOutput(); } [HandleMessage("ise:debug", true)] @@ -928,6 +926,9 @@ private void MonitorOnJobFinished(object sender, EventArgs eventArgs) PrintSessionUpdate($"
{error}
"); } + var executionResult = new ScriptExecutionResult(result); + executionResult.GetIseResult(result.CloseRunner).ForEach(PrintSessionUpdate); + SheerResponse.SetInnerHtml("PleaseWait", ""); ProgressOverlay.Visible = false; ScriptRunning = false; diff --git a/src/Spe/Client/Applications/PowerShellResultViewerList.cs b/src/Spe/Client/Applications/PowerShellResultViewerList.cs index 031a5f758..c5e790dc1 100644 --- a/src/Spe/Client/Applications/PowerShellResultViewerList.cs +++ b/src/Spe/Client/Applications/PowerShellResultViewerList.cs @@ -244,6 +244,13 @@ private void MonitorOnJobFinished(object sender, EventArgs eventArgs) { var args = eventArgs as SessionCompleteEventArgs; var result = args?.RunnerOutput; + + if(result != null) + { + var resultActions = new ScriptExecutionResult(result); + resultActions.ExecuteResults(); + } + if (result?.CloseRunner ?? false) { Sitecore.Shell.Framework.Windows.Close(); @@ -505,6 +512,7 @@ private void ExecuteScriptJob(Item scriptItem, ScriptSession scriptSession, Mess scriptSession.SetExecutedScript(scriptItem); scriptSession.Interactive = true; ScriptSessionId = scriptSession.ID; + scriptSession.PrivateData.DeferredMessages.Clear(); var jobUser = DelegatedAccessManager.GetDelegatedUser(Context.User, scriptItem); PowerShellLog.Info($"[Report] Running script {scriptItem.ID} for Context User {Context.User.Name} as {jobUser.Name}."); diff --git a/src/Spe/Client/Applications/PowerShellRunner.cs b/src/Spe/Client/Applications/PowerShellRunner.cs index ce8ff95f8..79432ba5e 100644 --- a/src/Spe/Client/Applications/PowerShellRunner.cs +++ b/src/Spe/Client/Applications/PowerShellRunner.cs @@ -3,6 +3,7 @@ using System.Management.Automation; using System.Text; using System.Web; +using Newtonsoft.Json; using Sitecore; using Sitecore.Configuration; using Sitecore.Data; @@ -320,10 +321,6 @@ private void MonitorOnJobFinished(object sender, EventArgs e) printResults += $"
{error}
"; } Result.Value = printResults; - if(!(result?.DialogResult).IsEmptyOrWhitespace()) - { - SheerResponse.SetDialogValue(result?.DialogResult); - } PsProgress.Text = string.Empty; SitecoreVersion.V80 @@ -337,10 +334,10 @@ private void MonitorOnJobFinished(object sender, EventArgs e) OkButton.Visible = true; AbortButton.Visible = false; Monitor.SessionID = string.Empty; - if (result.CloseMessages.Any()) + if (result.DeferredMessages.Any() || result.DialogResult != null) { - SheerResponse.SetDialogValue( - result.CloseMessages.Aggregate((serialized, message) => serialized + "\n" + message)); + ScriptExecutionResult dialogResult = new ScriptExecutionResult(result); + SheerResponse.SetDialogValue(dialogResult.ToString()); } if (result.CloseRunner) { diff --git a/src/Spe/Client/Applications/RunnerOutput.cs b/src/Spe/Client/Applications/RunnerOutput.cs index 53bf78369..a85fe9052 100644 --- a/src/Spe/Client/Applications/RunnerOutput.cs +++ b/src/Spe/Client/Applications/RunnerOutput.cs @@ -10,7 +10,6 @@ public class RunnerOutput public Exception Exception { get; set; } public bool HasErrors { get; set; } public bool CloseRunner { get; set; } - public List CloseMessages { get; set; } - + public List DeferredMessages { get; set; } } } \ No newline at end of file diff --git a/src/Spe/Client/Applications/ScriptRunner.cs b/src/Spe/Client/Applications/ScriptRunner.cs index 6c871916f..30785510a 100644 --- a/src/Spe/Client/Applications/ScriptRunner.cs +++ b/src/Spe/Client/Applications/ScriptRunner.cs @@ -48,7 +48,7 @@ public void Run() DialogResult = Session.Output.GetDialogRessult(), HasErrors = Session.Output.HasErrors, CloseRunner = Session.CloseRunner, - CloseMessages = Session.CloseMessages + DeferredMessages = Session.DeferredMessages }; job.StatusResult = output; @@ -76,7 +76,7 @@ public void Run() DialogResult = Session.Output.GetDialogRessult(), HasErrors = true, CloseRunner = Session.CloseRunner, - CloseMessages = Session.CloseMessages + DeferredMessages = Session.DeferredMessages }; job.StatusResult = output; diff --git a/src/Spe/Client/Commands/MenuItems/ExecutePowerShellScript.cs b/src/Spe/Client/Commands/MenuItems/ExecutePowerShellScript.cs index df98c0234..ce4fba9b5 100644 --- a/src/Spe/Client/Commands/MenuItems/ExecutePowerShellScript.cs +++ b/src/Spe/Client/Commands/MenuItems/ExecutePowerShellScript.cs @@ -3,11 +3,16 @@ using Sitecore; using Sitecore.Data; using Sitecore.Diagnostics; +using Sitecore.Globalization; +using Sitecore.Shell.Applications.ContentManager; using Sitecore.Shell.Framework.Commands; using Sitecore.StringExtensions; using Sitecore.Text; +using Sitecore.Web.UI.HtmlControls; using Sitecore.Web.UI.Sheer; using Spe.Core.Extensions; +using Spe.Core.Host; +using Version = Sitecore.Data.Version; namespace Spe.Client.Commands.MenuItems { @@ -59,19 +64,20 @@ public override void Execute(CommandContext context) Context.ClientPage.Start(this, "Process"); } + protected void Test() + { + + } protected void Process(ClientPipelineArgs args) { Assert.ArgumentNotNull(args, "args"); if (args.IsPostBack) { - Context.ClientPage.SendMessage(this, $"item:refresh(id={itemId})"); - Context.ClientPage.SendMessage(this, $"item:refreshchildren(id={itemId})"); + var item1 = ContentEditorForm.GetContextItem(); + var item = Database.GetDatabase(itemDb).GetItem(itemId, Language.Parse(itemLang), Version.Parse(itemVer)); if (!args.HasResult || args.Result.IsNullOrEmpty()) return; - - foreach (var closeMessage in args.Result.Split('\n')) - { - Context.ClientPage.ClientResponse.Timer(closeMessage, 2); - } + ScriptExecutionResult result = ScriptExecutionResult.Parse(args.Result); + result.ExecuteResults(); } else { diff --git a/src/Spe/Commands/Interactive/InvokeJavaScriptCommand.cs b/src/Spe/Commands/Interactive/InvokeJavaScriptCommand.cs index 81aca595f..a92a216b1 100644 --- a/src/Spe/Commands/Interactive/InvokeJavaScriptCommand.cs +++ b/src/Spe/Commands/Interactive/InvokeJavaScriptCommand.cs @@ -9,10 +9,19 @@ public class InvokeJavaScriptCommand : BaseShellCommand [Parameter(ValueFromPipeline = true, Position = 0, Mandatory = true)] public string Script { get; set; } + [Parameter] + public SwitchParameter OnScriptEnd { get; set; } + protected override void ProcessRecord() { - if (!CheckSessionCanDoInteractiveAction()) return; + if (OnScriptEnd) + { + HostData.DeferredMessages.Add($"script:{Script}"); + return; + } + if (!CheckSessionCanDoInteractiveAction()) + return; LogErrors(() => PutMessage(new InvokeJavaScriptMessage(Script))); } } diff --git a/src/Spe/Commands/Interactive/SendSheerMessageCommand.cs b/src/Spe/Commands/Interactive/SendSheerMessageCommand.cs index c01c7538f..2aecbbaae 100644 --- a/src/Spe/Commands/Interactive/SendSheerMessageCommand.cs +++ b/src/Spe/Commands/Interactive/SendSheerMessageCommand.cs @@ -3,6 +3,9 @@ using Sitecore.Jobs.AsyncUI; using Sitecore.Web.UI.Sheer; using Spe.Abstractions.VersionDecoupling.Interfaces; +using Spe.Commands.Interactive.Messages; +using Spe.Core.Extensions; +using Spe.Core.Host; using Spe.Core.VersionDecoupling; namespace Spe.Commands.Interactive @@ -13,15 +16,18 @@ public class SendSheerMessageCommand : BaseShellCommand [Parameter(ValueFromPipeline = true, Position = 0, Mandatory = true)] public string Name { get; set; } - [Parameter] + [Parameter(ParameterSetName = "InRunner")] public SwitchParameter GetResult { get; set; } [Parameter] public Hashtable Parameters { get; set; } + [Parameter(ParameterSetName = "OnScriptEnd")] + public SwitchParameter OnScriptEnd { get; set; } + protected override void ProcessRecord() { - if (!CheckSessionCanDoInteractiveAction()) return; + if (!OnScriptEnd && !CheckSessionCanDoInteractiveAction()) return; var message = Message.Parse(null, Name); message.Arguments.Add("ScriptSession.Id", HostData.SessionId); @@ -29,16 +35,23 @@ protected override void ProcessRecord() { foreach (var key in Parameters.Keys) { - message.Arguments.Add(key.ToString(), Parameters[key].ToString()); + message.Arguments.Add(key.ToString(), Parameters[key]?.ToString()); } } var msgHandler = new SendMessageMessage(message, GetResult.IsPresent); - LogErrors(() => PutMessage(msgHandler)); - var jobManager = TypeResolver.ResolveFromCache(); - var job = jobManager.GetContextJob(); - if (job != null && GetResult.IsPresent) + if (OnScriptEnd) + { + HostData.DeferredMessages.Add($"message:{message.Serialize()}"); + } + else { - WriteObject(job.MessageQueue.GetResult()); + LogErrors(() => PutMessage(msgHandler)); + var jobManager = TypeResolver.ResolveFromCache(); + var job = jobManager.GetContextJob(); + if (job != null && GetResult.IsPresent) + { + WriteObject(job.MessageQueue.GetResult()); + } } } } diff --git a/src/Spe/Core/Extensions/MessageExtensions.cs b/src/Spe/Core/Extensions/MessageExtensions.cs new file mode 100644 index 000000000..395d3578f --- /dev/null +++ b/src/Spe/Core/Extensions/MessageExtensions.cs @@ -0,0 +1,26 @@ +using System.Linq; +using Sitecore.Web.UI.Sheer; + +namespace Spe.Core.Extensions +{ + public static class MessageExtensions + { + public static string Serialize(this Message message) + { + if (message == null) + { + return string.Empty; + } + + if (message.Arguments != null && message.Arguments.Count > 0) + { + var paramsArray = message.Arguments.AllKeys.Select(key => $"{key}={message.Arguments[key]}").ToArray(); + var result = + $"{message.Name}({string.Join(",", paramsArray)})"; + return result; + } + + return message.Name; + } + } +} \ No newline at end of file diff --git a/src/Spe/Core/Host/CommandCompletion.cs b/src/Spe/Core/Host/CommandCompletion.cs index 2b2c4991c..3d7bdc5d9 100644 --- a/src/Spe/Core/Host/CommandCompletion.cs +++ b/src/Spe/Core/Host/CommandCompletion.cs @@ -394,8 +394,11 @@ private static string TruncatedCommand(ScriptSession session, string command, ou if (session.TryInvokeInRunningSession(teCmd, out psResult)) { var teResult = psResult.Cast().FirstOrDefault(); - lastToken = command.Substring(teResult); - return command.Substring(0, teResult); + if (teResult >= 0) + { + lastToken = command.Substring(teResult); + return command.Substring(0, teResult); + } } } lastToken = string.Empty; diff --git a/src/Spe/Core/Host/ScriptExecutionResult.cs b/src/Spe/Core/Host/ScriptExecutionResult.cs new file mode 100644 index 000000000..18d1c96af --- /dev/null +++ b/src/Spe/Core/Host/ScriptExecutionResult.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Sitecore; +using Sitecore.Web.UI.Sheer; +using Spe.Client.Applications; + +namespace Spe.Core.Host +{ + [Serializable] + public class ScriptExecutionResult + { + public ScriptExecutionResult() + { + } + + public ScriptExecutionResult(RunnerOutput result) + { + ScriptResult = result.DialogResult; + DeferredMessages = result.DeferredMessages.ToArray(); + } + + public string[] DeferredMessages { get; set; } + + public string ScriptResult { get; set; } + + public override string ToString() + { + return JsonConvert.SerializeObject(this); + } + + public static ScriptExecutionResult Parse(string json) + { + return JsonConvert.DeserializeObject(json); + } + + public void ExecuteResults() + { + foreach (var deferredMessage in DeferredMessages) + { + switch(deferredMessage) + { + case string message when message.StartsWith("message:"): + Context.ClientPage.ClientResponse.Timer(UnpackMessage(message), 2); + break; + case string message when message.StartsWith("script:"): + SheerResponse.Eval(UnpackMessage(message)); + break; + case string message when message.StartsWith("alert:"): + SheerResponse.Alert(UnpackMessage(message),false); + break; + default: + Context.ClientPage.ClientResponse.Timer(deferredMessage, 2); + break; + } + } + } + + private string UnpackMessage(string message) + { + return message.Substring(message.IndexOf(":", StringComparison.Ordinal) + 1); + } + + public IEnumerable GetIseResult(bool closeWindow) + { + foreach (var deferredMessage in DeferredMessages) + { + switch(deferredMessage) + { + case string message when message.StartsWith("message:"): + yield return + "Send shell message" + + UnpackMessage(message) + "
"; + break; + case string message when message.StartsWith("script:"): + yield return + "Execute JavaScript" + + UnpackMessage(message) + "
"; + break; + case string message when message.StartsWith("alert:"): + SheerResponse.Alert(UnpackMessage(message),false); + break; + default: + Context.ClientPage.ClientResponse.Timer(deferredMessage, 2); + break; + } + } + if (closeWindow) + { + yield return "Close Runner Window Request
"; + } + } + } +} \ No newline at end of file diff --git a/src/Spe/Core/Host/ScriptSession.cs b/src/Spe/Core/Host/ScriptSession.cs index a980cd8b0..bbe7b77f6 100644 --- a/src/Spe/Core/Host/ScriptSession.cs +++ b/src/Spe/Core/Host/ScriptSession.cs @@ -142,7 +142,7 @@ private void OnRunspaceStateEvent(object sender, RunspaceStateEventArgs e) internal bool IsRunning => powerShell != null && powerShell.InvocationStateInfo.State == PSInvocationState.Running; - internal System.Management.Automation.PowerShell NewPowerShell() + private System.Management.Automation.PowerShell NewPowerShell() { if (IsRunning) return powerShell.CreateNestedPowerShell(); @@ -177,57 +177,53 @@ public InitialSessionState SpeInitialSessionState } } - public static Version PsVersion { get; private set; } + public static Version PsVersion { get; } public bool AutoDispose { - get { return host.AutoDispose; } - internal set { host.AutoDispose = value; } + get => host.AutoDispose; + internal set => host.AutoDispose = value; } public bool CloseRunner { - get { return host.CloseRunner; } - internal set { host.CloseRunner = value; } + get => host.CloseRunner; + internal set => host.CloseRunner = value; } - public List CloseMessages - { - get { return host.CloseMessages; } - internal set { host.CloseMessages = value; } - } + public List DeferredMessages => host.DeferredMessages; public string ID { - get { return host.SessionId; } - internal set { host.SessionId = value; } + get => host.SessionId; + internal set => host.SessionId = value; } public bool Interactive { - get { return host.Interactive; } - internal set { host.Interactive = value; } + get => host.Interactive; + set => host.Interactive = value; } public string UserName { - get { return host.User; } - internal set { host.User = value; } + get => host.User; + private set => host.User = value; } public string JobName { - get { return host.JobName; } - internal set { host.JobName = value; } + get => host.JobName; + internal set => host.JobName = value; } public List LastErrors { get; set; } public string Key { - get { return host.SessionKey; } - internal set { host.SessionKey = value; } + get => host.SessionKey; + internal set => host.SessionKey = value; } public ApplicationSettings Settings { get; } @@ -375,7 +371,7 @@ private void DebuggerOnDebuggerStop(object sender, DebuggerStopEventArgs args) position = args.InvocationInfo.GetType() .GetProperty("ScriptPosition", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty) - .GetValue(args.InvocationInfo) as IScriptExtent; + ?.GetValue(args.InvocationInfo) as IScriptExtent; } catch (Exception) { @@ -499,7 +495,7 @@ public enum OutTarget OutNone } - public Collection InvokeInNewPowerShell(Command command, OutTarget target) + private Collection InvokeInNewPowerShell(Command command, OutTarget target) { return InvokeInNewPowerShell(ps => ps.Commands.AddCommand(command), target); } @@ -588,6 +584,7 @@ public ScriptBlock GetScriptBlock(string scriptBlock) public List ExecuteScriptPart(string script) { + PrivateData.DeferredMessages.Clear(); return ExecuteScriptPart(script, true, false); } @@ -718,6 +715,7 @@ public static Command OutNullCommand MergeUnclaimedPreviousCommandResults = PipelineResultTypes.Output | PipelineResultTypes.Error }; + private List ExecuteCommand(bool stringOutput, bool marshallResults = true) { var jobManager = TypeResolver.ResolveFromCache(); diff --git a/src/Spe/Core/Host/ScriptingHost.cs b/src/Spe/Core/Host/ScriptingHost.cs index 04aa4a5cf..f6b59fce2 100644 --- a/src/Spe/Core/Host/ScriptingHost.cs +++ b/src/Spe/Core/Host/ScriptingHost.cs @@ -36,7 +36,7 @@ public ScriptingHost(ApplicationSettings settings, InitialSessionState initialSt privateData = new ScriptingHostPrivateData(this); sessionState = initialState; CloseRunner = false; - CloseMessages = new List(); + DeferredMessages = new List(); } /// @@ -72,7 +72,9 @@ public ScriptingHost(ApplicationSettings settings, InitialSessionState initialSt public string SessionKey { get; internal set; } public bool CloseRunner { get; internal set; } - public List CloseMessages { get; internal set; } + [Obsolete("Use DeferredMessages instead." )] + public List CloseMessages => DeferredMessages; + public List DeferredMessages { get; } public string User { get; internal set; } public string JobName { get; internal set; } public bool Interactive { get; internal set; } diff --git a/src/Spe/Core/Host/ScriptingHostPrivateData.cs b/src/Spe/Core/Host/ScriptingHostPrivateData.cs index ccaf14644..e3d10e28c 100644 --- a/src/Spe/Core/Host/ScriptingHostPrivateData.cs +++ b/src/Spe/Core/Host/ScriptingHostPrivateData.cs @@ -30,13 +30,12 @@ public bool CloseRunner internal set { ScriptingHost.CloseRunner = value; } } - public List CloseMessages + public List DeferredMessages { - get { return ScriptingHost.CloseMessages; } - internal set { ScriptingHost.CloseMessages = value; } + get { return ScriptingHost.DeferredMessages; } } - + public ConsoleColor BackgroundColor { get diff --git a/src/Spe/Spe.csproj b/src/Spe/Spe.csproj index a47c19049..8154eb4b2 100644 --- a/src/Spe/Spe.csproj +++ b/src/Spe/Spe.csproj @@ -87,6 +87,9 @@ False ..\..\..\Windows\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.Utility\1.0.0.0__31bf3856ad364e35\Microsoft.PowerShell.Commands.Utility.dll + + ..\..\..\..\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\root\bb3a725f\1a866d6a\assembly\dl3\5275b215\00f4dad7_9cb8d901\Newtonsoft.Json.dll + ..\..\packages\Rainbow.Core.2.1.4\lib\net452\Rainbow.dll @@ -226,7 +229,9 @@ + + diff --git a/src/Spe/sitecore modules/PowerShell/Styles/ise.css b/src/Spe/sitecore modules/PowerShell/Styles/ise.css index 406b1a7f1..a93e6de4a 100644 --- a/src/Spe/sitecore modules/PowerShell/Styles/ise.css +++ b/src/Spe/sitecore modules/PowerShell/Styles/ise.css @@ -476,3 +476,35 @@ a.ps-link { padding: 0 4px 0 4px; margin: 0 -10px 0 10px; } + +#ScriptResultCode .deferred { + padding: 1px; + border-radius: 6px; + margin: 1px; + display: inline-block; +} + +#ScriptResultCode .script{ + background: #E34C24; +} + +#ScriptResultCode .message{ + background: darkcyan; +} + +#ScriptResultCode .close{ + background: darkred; + padding: 3px; +} + +#ScriptResultCode .deferred .label { + margin:4px; +} + +#ScriptResultCode .deferred .content { + margin:0px 1px; + padding: 3px; + border-radius: 6px; + background-color: rgb(1, 36, 86); + display: inline-block; +} From f47fdbce835192ce7c0ced0eef860df5df758099 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Fri, 31 May 2024 00:01:22 +0200 Subject: [PATCH 23/99] #1113 - fixed $SitecoreContextItem not being in the language selected in the ISE ribbon upon script execution. --- src/Spe/Client/Applications/PowerShellIse.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Spe/Client/Applications/PowerShellIse.cs b/src/Spe/Client/Applications/PowerShellIse.cs index 348afe1e6..55211f356 100644 --- a/src/Spe/Client/Applications/PowerShellIse.cs +++ b/src/Spe/Client/Applications/PowerShellIse.cs @@ -12,6 +12,7 @@ using Sitecore.Data.Items; using Sitecore.Data.Managers; using Sitecore.Diagnostics; +using Sitecore.Globalization; using Sitecore.IO; using Sitecore.Mvc.Extensions; using Sitecore.Resources; @@ -158,7 +159,7 @@ public static Item ContextItem var contextItemId = ContextItemId; return string.IsNullOrEmpty(contextItemId) ? null - : Factory.GetDatabase(ContextItemDb).GetItem(new ID(contextItemId)); + : Factory.GetDatabase(ContextItemDb).GetItem(new ID(contextItemId),Language.Parse(CurrentLanguage)); } } From c7b59247925c1009f4a3dce22797a8ce79a7e9ec Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Fri, 31 May 2024 16:21:23 +0200 Subject: [PATCH 24/99] Fix for issue introduced with f47fdbce835192ce7c0ced0eef860df5df758099 --- src/Spe/Client/Applications/PowerShellIse.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Spe/Client/Applications/PowerShellIse.cs b/src/Spe/Client/Applications/PowerShellIse.cs index 55211f356..02f7c3383 100644 --- a/src/Spe/Client/Applications/PowerShellIse.cs +++ b/src/Spe/Client/Applications/PowerShellIse.cs @@ -159,7 +159,8 @@ public static Item ContextItem var contextItemId = ContextItemId; return string.IsNullOrEmpty(contextItemId) ? null - : Factory.GetDatabase(ContextItemDb).GetItem(new ID(contextItemId),Language.Parse(CurrentLanguage)); + : Factory.GetDatabase(ContextItemDb).GetItem(new ID(contextItemId), + CurrentLanguage == DefaultLanguage ? Sitecore.Context.Language : Language.Parse(CurrentLanguage)); } } From a5c69fc3cbe85e188289b47026576349b71ee8e9 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Fri, 31 May 2024 16:29:04 +0200 Subject: [PATCH 25/99] #1341 : DoubleClick on item in ListView does not run default action as intended but instead throws an error. --- src/Spe/App_Config/Include/Spe/Spe.config | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Spe/App_Config/Include/Spe/Spe.config b/src/Spe/App_Config/Include/Spe/Spe.config index 0a1b57ba5..654b9fec9 100644 --- a/src/Spe/App_Config/Include/Spe/Spe.config +++ b/src/Spe/App_Config/Include/Spe/Spe.config @@ -389,6 +389,7 @@ + From f339c4db472ff38508c637514dee1c2a2c4ebad1 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Fri, 31 May 2024 17:45:47 +0200 Subject: [PATCH 26/99] #1161 : Show-ListView Report | Make Select/Un-select all option for listed items more apparent --- .../Client/Applications/PowerShellResultViewerList.cs | 11 +++++++++++ src/Spe/Client/Controls/PowerShellListView.cs | 11 ++++++++++- .../Shell/PowerShell/PowerShellResultViewerList.xml | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Spe/Client/Applications/PowerShellResultViewerList.cs b/src/Spe/Client/Applications/PowerShellResultViewerList.cs index c5e790dc1..82516e155 100644 --- a/src/Spe/Client/Applications/PowerShellResultViewerList.cs +++ b/src/Spe/Client/Applications/PowerShellResultViewerList.cs @@ -38,6 +38,7 @@ public class PowerShellResultViewerList : BaseForm, IHasCommandContext, IPowerSh protected Literal InfoTitle; protected Literal ItemCount; protected Literal ReportContextUser; + protected Literal SelectedResults; protected PowerShellListView ListViewer; protected Border LvProgressOverlay; protected Literal PageCount; @@ -162,6 +163,7 @@ protected override void OnLoad(EventArgs e) } } Monitor.JobFinished += MonitorOnJobFinished; + ListViewer.OnSelectionChanged += SelectionChanged; if (Context.ClientPage.IsEvent) return; @@ -526,5 +528,14 @@ protected void ExecuteInternal(ScriptSession scriptSession, string script) { scriptSession.ExecuteScriptPart(script); } + + private void SelectionChanged(object sender, EventArgs e) + { + + var selectedItemsCount = ListViewer.SelectedItems.Length; + SelectedResults.Text = selectedItemsCount.ToString(); + UpdateRibbon(); + } + } } \ No newline at end of file diff --git a/src/Spe/Client/Controls/PowerShellListView.cs b/src/Spe/Client/Controls/PowerShellListView.cs index 026797059..e6a727f99 100644 --- a/src/Spe/Client/Controls/PowerShellListView.cs +++ b/src/Spe/Client/Controls/PowerShellListView.cs @@ -7,7 +7,9 @@ using System.Web.Caching; using Sitecore; using Sitecore.Data.Items; +using Sitecore.Resources; using Sitecore.StringExtensions; +using Sitecore.Web.UI; using Sitecore.Web.UI.HtmlControls; using Sitecore.Web.UI.Sheer; using Spe.Commands.Interactive; @@ -158,6 +160,8 @@ private void SelectAll() { item.Selected = !item.Selected; } + + SelectionChanged(); } protected void Sort(int columnIndex) @@ -201,7 +205,12 @@ public override void Refresh() Controls.Clear(); ColumnNames.Clear(); - ColumnNames.Add("Icon", "Icon"); + var builder = new ImageBuilder + { + Src = Images.GetThemedImageSource("OfficeWhite/32x32/checkbox_group.png", ImageDimension.id16x16), + Alt = "Click to invert the selection" + }; + ColumnNames.Add("Icon", $"{builder}"); if (Data == null) { diff --git a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellResultViewerList.xml b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellResultViewerList.xml index 7afa03e29..9af051b56 100644 --- a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellResultViewerList.xml +++ b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellResultViewerList.xml @@ -59,6 +59,9 @@ Results: + + Selected: + Page: / From 7644734152f2cb8ebaee19271ac63f2abe0cfdff Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Fri, 31 May 2024 18:22:11 +0200 Subject: [PATCH 27/99] #1252 : Show-ListView trims leading spaces --- src/Spe/Client/Controls/PowerShellListView.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Spe/Client/Controls/PowerShellListView.cs b/src/Spe/Client/Controls/PowerShellListView.cs index e6a727f99..f3d08aab2 100644 --- a/src/Spe/Client/Controls/PowerShellListView.cs +++ b/src/Spe/Client/Controls/PowerShellListView.cs @@ -22,6 +22,7 @@ public class PowerShellListView : Listview private static readonly object locker = new object(); private List _filteredItems; private const string ExpirationSetting = "Spe.HttpCacheExpirationMinutes"; + private static readonly Regex xssCleanup = new Regex(@"]*>[\s\S]*?|]*>[\s\S]*?|",RegexOptions.Compiled); public int CurrentPage { @@ -249,8 +250,8 @@ public override void Refresh() val = $"
"; break; default: - var xssCleanup = new Regex(@"]*>[\s\S]*?|]*>[\s\S]*?|"); val = xssCleanup.Replace(val, "
"); + val = val.Replace(" ", "  "); break; } lvi.ColumnValues.Add(column, val); From f1379726e0aeb80b0dbcc5c5915773e6c13bfed1 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Fri, 31 May 2024 18:25:46 +0200 Subject: [PATCH 28/99] #1252 : Show-ListView trims leading spaces - pair reversed not to trim the first space. --- src/Spe/Client/Controls/PowerShellListView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Spe/Client/Controls/PowerShellListView.cs b/src/Spe/Client/Controls/PowerShellListView.cs index f3d08aab2..dd23c9ebd 100644 --- a/src/Spe/Client/Controls/PowerShellListView.cs +++ b/src/Spe/Client/Controls/PowerShellListView.cs @@ -251,7 +251,7 @@ public override void Refresh() break; default: val = xssCleanup.Replace(val, "
"); - val = val.Replace(" ", "  "); + val = val.Replace(" ", "  "); break; } lvi.ColumnValues.Add(column, val); From 8943f11e28472cda13f5d77841f22d8c489d03b6 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Sun, 2 Jun 2024 19:21:35 +0200 Subject: [PATCH 29/99] #1067 : Impossible to run scripts due to hanging debug session (ISE Editing session) --- src/Spe/Client/Applications/PowerShellIse.cs | 12 +++++++++++- .../Commands/MenuItems/ExecutePowerShellScript.cs | 2 -- src/Spe/Core/Host/ScriptSession.cs | 8 ++++---- src/Spe/sitecore modules/PowerShell/Scripts/ise.js | 5 +++++ 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Spe/Client/Applications/PowerShellIse.cs b/src/Spe/Client/Applications/PowerShellIse.cs index 02f7c3383..1d9bc83a8 100644 --- a/src/Spe/Client/Applications/PowerShellIse.cs +++ b/src/Spe/Client/Applications/PowerShellIse.cs @@ -2,6 +2,7 @@ using System.Collections.Specialized; using System.IO; using System.Linq; +using System.Management.Automation.Runspaces; using System.Text; using System.Text.RegularExpressions; using System.Web; @@ -682,7 +683,6 @@ protected virtual void JobExecute(ClientPipelineArgs args) { args.Parameters.Add("message", "ise:execute"); JobExecuteScript(args, Editor.Value, false); - ClearOutput(); } [HandleMessage("ise:debug", true)] @@ -719,6 +719,16 @@ protected virtual void JobExecuteScript(ClientPipelineArgs args, string scriptTo ? ScriptSessionManager.NewSession(ApplicationNames.ISE, true) : ScriptSessionManager.GetSession(sessionName, ApplicationNames.ISE, true); + ClearOutput(); + if (scriptSession.State == RunspaceAvailability.AvailableForNestedCommand || scriptSession.State == RunspaceAvailability.Busy) + { + var errorMessage = + "A Script is already executing in this script session. Use another session or wait for the other script to finish."; + PrintSessionUpdate($"{errorMessage}"); + SheerResponse.Eval( + "spe.showSessionIDGallery();"); + return; + } if (debug) { scriptSession.DebugFile = FileUtil.MapPath(Settings.TempFolderPath) + "\\" + diff --git a/src/Spe/Client/Commands/MenuItems/ExecutePowerShellScript.cs b/src/Spe/Client/Commands/MenuItems/ExecutePowerShellScript.cs index ce4fba9b5..d3595ebbd 100644 --- a/src/Spe/Client/Commands/MenuItems/ExecutePowerShellScript.cs +++ b/src/Spe/Client/Commands/MenuItems/ExecutePowerShellScript.cs @@ -73,8 +73,6 @@ protected void Process(ClientPipelineArgs args) Assert.ArgumentNotNull(args, "args"); if (args.IsPostBack) { - var item1 = ContentEditorForm.GetContextItem(); - var item = Database.GetDatabase(itemDb).GetItem(itemId, Language.Parse(itemLang), Version.Parse(itemVer)); if (!args.HasResult || args.Result.IsNullOrEmpty()) return; ScriptExecutionResult result = ScriptExecutionResult.Parse(args.Result); result.ExecuteResults(); diff --git a/src/Spe/Core/Host/ScriptSession.cs b/src/Spe/Core/Host/ScriptSession.cs index bbe7b77f6..7534af1c1 100644 --- a/src/Spe/Core/Host/ScriptSession.cs +++ b/src/Spe/Core/Host/ScriptSession.cs @@ -547,7 +547,7 @@ public void Initialize() Initialize(false); } - public void Initialize(bool reinitialize) + private void Initialize(bool reinitialize) { lock (this) { @@ -685,7 +685,7 @@ public void Abort() abortRequested = true; } - public static Command OutDefaultCommand + private static Command OutDefaultCommand => new Command("Out-Default") { MergeUnclaimedPreviousCommandResults = PipelineResultTypes.Output | PipelineResultTypes.Error @@ -709,7 +709,7 @@ public static Command OutHostCommand /// /// "Out-Default" is not suitable for external apps, output goes to console. /// - public static Command OutNullCommand + private static Command OutNullCommand => new Command("Out-Null") { MergeUnclaimedPreviousCommandResults = PipelineResultTypes.Output | PipelineResultTypes.Error @@ -882,7 +882,7 @@ public void Dispose() GC.SuppressFinalize(this); } - public void Close() + private void Close() { Runspace.DefaultRunspace = null; host.Runspace.Dispose(); diff --git a/src/Spe/sitecore modules/PowerShell/Scripts/ise.js b/src/Spe/sitecore modules/PowerShell/Scripts/ise.js index b4efb2c25..9edc7c21d 100644 --- a/src/Spe/sitecore modules/PowerShell/Scripts/ise.js +++ b/src/Spe/sitecore modules/PowerShell/Scripts/ise.js @@ -138,6 +138,11 @@ registerEventListenersForRibbonButtons(); } }; + + spe.showSessionIDGallery = function () { + contextGalery = jQuery("#B0C784F542B464EE2B0BA72384125E123") + contextGalery.click(); + }; spe.updateRibbonNeeded = function () { clearTimeout(typingTimer); From 6b2a689d25cc4c249ef0d3a406ca4ed8df406b74 Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Wed, 24 Jul 2024 17:55:02 +0200 Subject: [PATCH 30/99] #1346 Add ability to preview non cached variable value when Ctrl is pressed --- .../sitecore modules/PowerShell/Scripts/ise.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Spe/sitecore modules/PowerShell/Scripts/ise.js b/src/Spe/sitecore modules/PowerShell/Scripts/ise.js index 9edc7c21d..f03848df7 100644 --- a/src/Spe/sitecore modules/PowerShell/Scripts/ise.js +++ b/src/Spe/sitecore modules/PowerShell/Scripts/ise.js @@ -778,6 +778,16 @@ } }; + var ctrlPressed = false; + $(window).keydown(function(evt) { + if (evt.which == 17) { // ctrl + ctrlPressed = true; + } + }).keyup(function(evt) { + if (evt.which == 17) { // ctrl + ctrlPressed = false; + } + }); spe.variableValue = function (variableName) { var data; var sessionId = debugSessionId; @@ -786,9 +796,11 @@ } var storageKey = speVariablesCacheKey + sessionId; - var cachedValue = getCachedVariableValue(storageKey, variableName) - if (cachedValue) { - return cachedValue; + if(ctrlPressed === false) { + var cachedValue = getCachedVariableValue(storageKey, variableName) + if (cachedValue) { + return cachedValue; + } } getPowerShellResponse({"guid": sessionId, "variableName": variableName}, "GetVariableValue", From 33811df44738a8702d795f97c0d68b2e4af485ba Mon Sep 17 00:00:00 2001 From: Adam Najmanowicz Date: Wed, 24 Jul 2024 18:11:39 +0200 Subject: [PATCH 31/99] #1332 & 1347 Improvements to enable opening multiple scripts in a single ISE at once. --- src/Spe/App_Config/Include/Spe/Spe.config | 1 + src/Spe/Client/Applications/PowerShellIse.cs | 68 +++++++++++++++++-- src/Spe/Client/Controls/MruGallery.cs | 2 +- .../PowerShell/Scripts/ise.js | 33 +++++++-- .../PowerShell/Styles/Dialogs.css | 3 +- .../PowerShell/Styles/ise.css | 54 +++++++++++++-- .../Shell/PowerShell/PowerShellIse.xml | 21 +++++- 7 files changed, 161 insertions(+), 21 deletions(-) diff --git a/src/Spe/App_Config/Include/Spe/Spe.config b/src/Spe/App_Config/Include/Spe/Spe.config index 654b9fec9..61f745058 100644 --- a/src/Spe/App_Config/Include/Spe/Spe.config +++ b/src/Spe/App_Config/Include/Spe/Spe.config @@ -390,6 +390,7 @@ + diff --git a/src/Spe/Client/Applications/PowerShellIse.cs b/src/Spe/Client/Applications/PowerShellIse.cs index 1d9bc83a8..82f9ea55d 100644 --- a/src/Spe/Client/Applications/PowerShellIse.cs +++ b/src/Spe/Client/Applications/PowerShellIse.cs @@ -19,6 +19,7 @@ using Sitecore.Resources; using Sitecore.Security; using Sitecore.Security.Accounts; +using Sitecore.Shell.Controls.Splitters; using Sitecore.Shell.Framework; using Sitecore.Shell.Framework.Commands; using Sitecore.StringExtensions; @@ -63,6 +64,10 @@ public class PowerShellIse : BaseForm, IHasCommandContext, IPowerShellRunner protected Border InfoPanel; protected Tabstrip Tabs; protected Border TabsPanel; + protected TreeviewEx ContentTreeview; + protected DataContext ContentDataContext; + protected VSplitterXmlControl VSplitter; + public bool Debugging { get; set; } public bool InBreakpoint { get; set; } @@ -241,7 +246,7 @@ protected override void OnLoad(EventArgs e) Tabs.OnChange += TabsOnChange; if (Context.ClientPage.IsEvent) return; - + var settings = ApplicationSettings.GetInstance(ApplicationNames.ISE); if (settings.SaveLastScript) @@ -268,12 +273,51 @@ protected override void OnLoad(EventArgs e) CurrentLanguage = DefaultLanguage; ParentFrameName = WebUtil.GetQueryString("pfn"); UpdateRibbon(); + + if(itemDb.IsWhiteSpaceOrNull()) + { + itemDb = Context.ContentDatabase.Name; + } + ContentDataContext.GetFromQueryString(); + ContentDataContext.BeginUpdate(); + ContentDataContext.Parameters = $"databasename={itemDb}"; + ContentDataContext.Database = itemDb; + ContentTreeview.RefreshRoot(); + ContentDataContext.Root = ApplicationSettings.ScriptLibraryRoot.ID.ToString(); + + if (!ScriptItemId.IsNullOrEmpty() && !ScriptItemDb.IsNullOrEmpty()) + { + ContentDataContext.SetFolder(ScriptItem.Uri); + } + + ContentDataContext.EndUpdate(); + ContentTreeview.RefreshRoot(); } private void TabsOnChange(object sender, EventArgs e) { var tab = Tabs.Controls.OfType().Skip(Tabs.Active).FirstOrDefault(); SelectTabByIndex(Tabs.Active + 1); + var openedScript = OpenedScripts.Value.Split('\n')[Tabs.Active].Split(':')[0]; + + if (openedScript.IndexOf("/") > 0) + { + var scriptItem = Sitecore.Client.ContentDatabase.GetItem(ApplicationSettings.ScriptLibraryPath+openedScript); + + ContentDataContext.BeginUpdate(); + ContentDataContext.SetFolder(scriptItem.Uri); + ContentDataContext.EndUpdate(); + ContentTreeview.SetSelectedItem(scriptItem); + ContentTreeview.RefreshRoot(); + ContentTreeview.RefreshSelected(); + } + else + { + Item selectionItem = ContentTreeview.GetSelectionItem(); + ContentTreeview.SelectedIDs.Clear(); + if (selectionItem != null) + ContentTreeview.Refresh(selectionItem); + } } public override void HandleMessage(Message message) @@ -347,9 +391,23 @@ protected void Open(ClientPipelineArgs args) protected void MruOpen(ClientPipelineArgs args) { Assert.ArgumentNotNull(args, "args"); - LoadItem(args.Parameters["db"], args.Parameters["id"]); + var db = args.Parameters["db"]; + var id = args.Parameters["id"]; + var item = Database.GetDatabase(db).GetItem(id); + if(item.IsPowerShellScript()){ + LoadItem(db, id); + } + TabsOnChange(this, EventArgs.Empty); } + protected void ContentTreeview_Click() + { + var folder = ContentDataContext.GetFolder(); + if (folder.IsPowerShellScript() || folder.IsPowerShellModule()) + { + LoadItem(folder.Database.Name, folder.ID.ToString()); + } + } [HandleMessage("ise:changecontextaccount", true)] protected void SecurityChangeContextAccount(ClientPipelineArgs args) @@ -551,7 +609,6 @@ private void LoadItem(string db, string id) ScriptItemId = scriptItem.ID.ToString(); ScriptItemDb = scriptItem.Database.Name; - SelectTabByIndex(Tabs.Controls.Count); MruUpdate(scriptItem); UpdateRibbon(); } @@ -588,7 +645,10 @@ private bool CreateNewTab(Item scriptItem) } SelectTabByIndex(tabIndex); - SheerResponse.Eval("spe.updateEditor();"); + if (!scriptAlreadyOpened) + { + SheerResponse.Eval("spe.updateEditor();"); + } return !scriptAlreadyOpened; } diff --git a/src/Spe/Client/Controls/MruGallery.cs b/src/Spe/Client/Controls/MruGallery.cs index 74182e52d..df7a5ce7b 100644 --- a/src/Spe/Client/Controls/MruGallery.cs +++ b/src/Spe/Client/Controls/MruGallery.cs @@ -54,7 +54,7 @@ public override void HandleMessage(Message message) protected void ContentTreeview_Click() { var folder = ContentDataContext.GetFolder(); - if (folder.IsPowerShellScript()) + if (folder.IsPowerShellScript() || folder.IsPowerShellModule()) { Load(folder.Uri.ToString()); } diff --git a/src/Spe/sitecore modules/PowerShell/Scripts/ise.js b/src/Spe/sitecore modules/PowerShell/Scripts/ise.js index f03848df7..137ff465a 100644 --- a/src/Spe/sitecore modules/PowerShell/Scripts/ise.js +++ b/src/Spe/sitecore modules/PowerShell/Scripts/ise.js @@ -704,7 +704,13 @@ var resultsHeight = $(window).height() - $("#ResultsSplitter").offset().top - $("#ResultsSplitter").height() - $("#StatusBar").height() - resultsBottomOffset - 10; $("#Result").height(resultsHeight); $("#Result").width($(window).width() - $("#Result").offset().left * 2); + $("#Result").width($("#ResultsPane").width()); $("#ProgressOverlay").css("top", ($("#Result").offset().top + 4) + "px"); + $("#TreeViewToggle").css("top", ($("#TabsPanel").offset().top + 8) + "px"); + $("#TreeViewToggle").css("left", ($("#TabsPanel").offset().left + 6) + "px"); + if ($("#TreeView").is(":visible")) { + + } }; @@ -788,6 +794,7 @@ ctrlPressed = false; } }); + spe.variableValue = function (variableName) { var data; var sessionId = debugSessionId; @@ -858,16 +865,16 @@ $("#TipOfTheSession").position({ my: "left bottom", - at: "left bottom-30px", + at: "left bottom-20px", within: $("#Result"), of: $("#Result") }).css({ right: 0, left: 0 }).hide() - .show("drop", {direction: "down"}, "400") + .show("drop", {direction: "down"}, "200") .delay(5000) - .hide("drop", {direction: "down"}, "400", + .hide("drop", {direction: "down"}, "200", function () { $(".status-bar-text").animate({backgroundColor: "#fcefa1"}).animate({backgroundColor: "#fff"}); }); @@ -896,7 +903,18 @@ spe.restoreResults(); } }); - + $("#TreeViewToggle").click(function () { + if ($("#TreeViewPanel").is(":visible")) { + $("#TreeViewPanel").hide(); + $("#TreeSplitterPanel").hide(); + spe.resizeEditor(); + } else { + $("#TreeViewPanel").show(); + $("#TreeSplitterPanel").show(); + spe.resizeEditor(); + } + }); + function _getCommandHelp(str) { getPowerShellResponse({"guid": guid, "command": str}, "GetHelpForCommand", function (json) { @@ -935,13 +953,16 @@ $(window).on('resize', function () { spe.resizeEditor(); }).trigger('resize'); - - + setTimeout(function () { scForm.postRequest("", "", "", "ise:updatesettings"); scForm.postRequest("", "", "", "ise:loadinitialscript"); spe.resizeEditor(); }, 100); + const resizeObserver = new ResizeObserver((entries) => { + spe.resizeEditor(); + }) + resizeObserver.observe(document.getElementById("EditingPanel")); }); }(jQuery, window, window.spe = window.spe || {}, window.ace = window.ace || {})); diff --git a/src/Spe/sitecore modules/PowerShell/Styles/Dialogs.css b/src/Spe/sitecore modules/PowerShell/Styles/Dialogs.css index 3deeecdf2..9469cf1a1 100644 --- a/src/Spe/sitecore modules/PowerShell/Styles/Dialogs.css +++ b/src/Spe/sitecore modules/PowerShell/Styles/Dialogs.css @@ -148,4 +148,5 @@ textarea[disabled] { .scRibbonToolbar { height: 100%; background: #f7f7f7; - } \ No newline at end of file + } + diff --git a/src/Spe/sitecore modules/PowerShell/Styles/ise.css b/src/Spe/sitecore modules/PowerShell/Styles/ise.css index a93e6de4a..979a83b55 100644 --- a/src/Spe/sitecore modules/PowerShell/Styles/ise.css +++ b/src/Spe/sitecore modules/PowerShell/Styles/ise.css @@ -121,19 +121,22 @@ a.ps-link { } .ui-state-highlight .ui-icon.ui-icon-info { - background: url(/sitecore%20modules/PowerShell/Assets/spe-32.png) center center; - background-size: 16px; + /*background: url(/sitecore%20modules/PowerShell/Assets/spe-32.png) center center;*/ + /*background-size: 16px;*/ + display: none; } .ui-state-highlight { margin-bottom: 6px; margin-top: 20px; padding: 0.7em; + background: #FCE99C; + color: #897B2F; } #TipOfTheSession { float: left; - padding-left: 40px; + padding-left: 30px; padding-right: 30px; width: 400px; } @@ -219,8 +222,6 @@ a.ps-link { #ScriptName { display: none; } -.ui-state-highlight { background: white; } - .progressActivity { font-weight: bold; } #ProgressOverlay #progressbar { @@ -451,8 +452,9 @@ a.ps-link { } #TabsPanel { - width: 100%; + width: calc(100% - 40px); height: 35px; + pointer-events: none; } #EditingArea { @@ -508,3 +510,43 @@ a.ps-link { background-color: rgb(1, 36, 86); display: inline-block; } + +.scRibbon { + height: auto; +} + +.scRibbonToolbar { + height: auto; +} + +#RibbonPanel { + background: #f7f7f7; +} + +#TreeView { + padding: 6px 6px 6px 0; +} + +#TreeViewToggle { + position: absolute; +} +#TreePane { + display: none; +} + +nobr:first-child > .scTabActive, +nobr:first-child > .scTabActive_Hover, +nobr:first-child > .scTab, +nobr:first-child > .scTab_Hover { + margin-left: 30px; + pointer-events: all; + +} +nobr > .scTabActive, +nobr > .scTabActive_Hover, +nobr > .scTab, +nobr > .scTab_Hover { + pointer-events: all; + +} + diff --git a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml index 687eb0a82..cb7905011 100644 --- a/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml +++ b/src/Spe/sitecore modules/Shell/PowerShell/PowerShellIse.xml @@ -38,6 +38,7 @@