From 998a54ec5bd36aed1cae7a0b322b1c0da9f6a1f9 Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Wed, 28 Aug 2024 16:14:35 -0400 Subject: [PATCH 01/12] feat: allow editing of files via web application First pass - probably needs a bit more work around the use case where a CSP file changes. (Theory: call OnBeforeTimestamp / OnBeforeLoad at end of AfterUserAction if the name is mapped?) --- cls/SourceControl/Git/Settings.cls | 74 ++++++++++++++++++++++++++++++ cls/SourceControl/Git/Utils.cls | 7 +++ 2 files changed, 81 insertions(+) diff --git a/cls/SourceControl/Git/Settings.cls b/cls/SourceControl/Git/Settings.cls index 6a4ce435..da9fdddf 100644 --- a/cls/SourceControl/Git/Settings.cls +++ b/cls/SourceControl/Git/Settings.cls @@ -47,6 +47,9 @@ Property defaultMergeBranch As %String [ InitialExpression = {##class(SourceCont /// Compile using the configured pull event handler when "Import All" is run Property compileOnImport As %Boolean [ InitialExpression = {##class(SourceControl.Git.Utils).CompileOnImport()} ]; +/// Define a namespace-level web application allowing access to multiple git repos across separate namespaces +Property namespaceLevelGitWebApp As %Boolean [ InitialExpression = {##class(SourceControl.Git.Settings).HasNamespaceWebApp()} ]; + Property Mappings [ MultiDimensional ]; Method %OnNew() As %Status @@ -160,6 +163,75 @@ ClassMethod Configure() As %Boolean [ CodeMode = objectgenerator ] do %code.WriteLine(" quit 1") } +Method ConfigureNamespaceWebApplication() +{ + Set root = ##class(%Library.File).NormalizeDirectory(##class(SourceControl.Git.Utils).TempFolder()) + Set deleteWebApp = ..HasNamespaceWebApp(.appDirectory) && '..namespaceLevelGitWebApp + Set createWebApp = ..namespaceLevelGitWebApp && '..HasNamespaceWebApp() + Do ..WebAppOperation("/git/"_$Namespace_"/", createWebApp, deleteWebApp, root) +} + +Method WebAppOperation(name, create As %Boolean, delete As %Boolean, root As %String) [ Private ] +{ + Set namespace = $Namespace + New $Namespace + Set $Namespace = "%SYS" + If $Extract(name) = "/" { + Set name = $Extract(name,1,*-1) + } + If delete { + If ##class(Security.Applications).Exists(name) { + $$$ThrowOnError(##class(Security.Applications).Delete(name)) + Write !,"Removed web application "_name + } + Quit + } + + // These are the only things we want to coerce. + Set props("AutheEnabled")=0 // No auth methods enabled = impossible to use + Set props("InbndWebServicesEnabled")=0 + Set props("ServeFiles")=0 + Set props("Enabled")=1 + Set props("Name")=name + Set props("NameSpace")=namespace + Set props("Path")=root + Set props("Type")=2 + Set props("Recurse")=1 + If create { + Write !,"Creating web application: "_name_"... " + $$$ThrowOnError(##class(Security.Applications).Create(name,.props)) + Write "done." + } ElseIf ##class(Security.Applications).Exists(name) { + Write !,"Web application '"_name_"' already exists." + $$$ThrowOnError(##class(Security.Applications).Get(name,.existingProps)) + Set changes = 0 + Set key = "" + For { + Set key = $Order(props(key),1,value) + Quit:key="" + If (value '= $Get(existingProps(key))) { + Write !,"Changing "_key_": "_$Get(existingProps(key))_" -> "_value + Set changes = 1 + } + } + If changes { + $$$ThrowOnError(##class(Security.Applications).Modify(name,.props)) + Write !,"Web application '"_name_"' updated." + } Else { + Write !,"No changes made to web application." + } + } +} + +ClassMethod HasNamespaceWebApp(Output webAppDirectory) As %Boolean +{ + Set webAppDirectory = $System.CSP.GetFileName("/git/"_$Namespace_"/") + If (webAppDirectory '= "") { + Set webAppDirectory = ##class(%Library.File).NormalizeDirectory(webAppDirectory) + } + Quit (webAppDirectory '= "") +} + Method OnAfterConfigure() As %Boolean { set defaultPromptFlag = $$$DisableBackupCharMask + $$$TrapCtrlCMask + $$$EnableQuitCharMask + $$$DisableHelpCharMask + $$$DisableHelpContextCharMask + $$$TrapErrorMask @@ -186,6 +258,8 @@ Method OnAfterConfigure() As %Boolean } } + do ..ConfigureNamespaceWebApplication() + set gitDir = ##class(%File).NormalizeDirectory(..namespaceTemp)_".git" if '##class(%File).DirectoryExists(gitDir) { set list(1) = "Initialize empty repo" diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls index 9b6405c7..9f9e159a 100644 --- a/cls/SourceControl/Git/Utils.cls +++ b/cls/SourceControl/Git/Utils.cls @@ -1132,6 +1132,13 @@ ClassMethod NormalizeInternalName(ByRef name As %String) As %String if ($extract(name) '= "/") && (type'="csp") { quit $piece(name,".",1,*-1)_"."_$zconvert($piece(name,".",*),"U") } + + if (name [ "/") && (type = "csp") { + set cspFilename = $System.CSP.GetFileName(name) + if (cspFilename '= "") && (cspFilename [ ..TempFolder()) { + set name = ..NameToInternalName(cspFilename) + } + } if (type = "inc") || (type = "mac") || (type = "int") { set name = $extract($translate(name, "/", "."), 2, *) From 13905752812ca19c9a7976d7709d49d35d8c710f Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Thu, 29 Aug 2024 09:24:57 -0400 Subject: [PATCH 02/12] feat: add VSCode workspace download link Also tweaks some styling and fixes behavior for interop-enabled namespaces with a URLPrefix. --- cls/SourceControl/Git/StreamServer.cls | 3 +- cls/SourceControl/Git/Utils.cls | 5 +- cls/SourceControl/Git/WebUIDriver.cls | 61 +++++++++++++++++++ .../share/git-webui/webui/css/git-webui.css | 18 +++++- .../share/git-webui/webui/img/context.svg | 2 +- .../share/git-webui/webui/js/git-webui.js | 3 + .../share/git-webui/webui/css/git-webui.less | 23 ++++++- .../src/share/git-webui/webui/img/context.svg | 2 +- .../src/share/git-webui/webui/js/git-webui.js | 3 + 9 files changed, 108 insertions(+), 12 deletions(-) diff --git a/cls/SourceControl/Git/StreamServer.cls b/cls/SourceControl/Git/StreamServer.cls index daf75a8d..42b2330e 100644 --- a/cls/SourceControl/Git/StreamServer.cls +++ b/cls/SourceControl/Git/StreamServer.cls @@ -7,7 +7,7 @@ Class SourceControl.Git.StreamServer Extends %CSP.StreamServer ClassMethod OnPage() As %Status { if (%stream '= $$$NULLOREF) && $data(%base)#2 { - set sourceControlInclude = ##class(SourceControl.Git.Utils).GetSourceControlInclude() + set sourceControlInclude = ##class(SourceControl.Git.Utils).GetSourceControlInclude(%request.URLPrefix) while '%stream.AtEnd { set text = %stream.Read() set text = $replace(text,"{{baseHref}}",..EscapeHTML(%base)) @@ -19,4 +19,3 @@ ClassMethod OnPage() As %Status } } - diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls index 9f9e159a..1efca933 100644 --- a/cls/SourceControl/Git/Utils.cls +++ b/cls/SourceControl/Git/Utils.cls @@ -2403,10 +2403,10 @@ ClassMethod GetPackageVersion() As %String [ CodeMode = objectgenerator ] quit $$$OK } -ClassMethod GetSourceControlInclude() As %String +ClassMethod GetSourceControlInclude(prefix As %String = "") As %String { quit $select(##class(%Library.EnsembleMgr).IsEnsembleInstalled(): - "", + "", 1: "") } @@ -2622,4 +2622,3 @@ ClassMethod BaselineExport(pCommitMessage = "", pPushToRemote = "") As %Status } } - diff --git a/cls/SourceControl/Git/WebUIDriver.cls b/cls/SourceControl/Git/WebUIDriver.cls index 5d8a2b17..1699453c 100644 --- a/cls/SourceControl/Git/WebUIDriver.cls +++ b/cls/SourceControl/Git/WebUIDriver.cls @@ -27,6 +27,67 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out if $isobject($get(responseJSON)) { do responseJSON.%ToJSON(%data) } + } elseif pathStart = "vscode-workspace" { + set handled = 1 + set namespace = $Namespace + set instanceName = $ZConvert(##class(%SYS.System).InstanceGUID(),"L") + set hasCSP = 0 + if (InternalName = "") || context.IsInDefaultPackage { + set hasCSP = ##class(SourceControl.Git.Settings).HasNamespaceWebApp() + set workspaceFilename = namespace_"-"_instanceName_"-.code-workspace" + set filter = "?mapped=0" + } elseif context.IsInGitEnabledPackage { + set workspaceFilename = context.Package.Name_"-"_instanceName_".code-workspace" + // Loading an IPM package in dev mode automatically creates a project for it, so filter to that. + set filter = "?project="_context.Package.Name + } else { + // TODO: Error? But really shouldn't happen. + quit + } + set server = { + "webServer": { + "host": (%request.CgiEnvs("SERVER_NAME")), + "port": (%request.CgiEnvs("SERVER_PORT")), + "scheme": ($Select(%request.Secure:"https",1:"http")) + }, + "username": ($Username) + } + if (%request.URLPrefix '= "") { + set server.webServer.pathPrefix = %request.URLPrefix + } + set workspaceDef = { + "folders": [ + { + "name": ($Namespace), + "uri": ("isfs://"_instanceName_":"_$Namespace_"/"_filter) + } + ], + "settings": { + "intersystems.servers": { + "/default": (instanceName) + }, + "objectscript.conn": { + "active": false + } + } + } + if hasCSP { + do workspaceDef.folders.%Push({ + "name": ($Namespace_" (Filesystem)"), + "uri": ("isfs://"_instanceName_":"_$Namespace_"/git/"_$Namespace_"?csp") + }) + } + set $Property(workspaceDef.settings."intersystems.servers",instanceName) = server + set stream = ##class(%CSP.CharacterStream).%New() + do stream.SetAttribute("ContentDisposition","attachment; filename="_workspaceFilename) + set formatter = ##class(%JSON.Formatter).%New() + do formatter.FormatToStream(workspaceDef,stream) + $$$ThrowOnError(stream.%Save()) + do %request.Set("STREAMOID",##class(%CSP.Page).Encrypt(stream.%Oid())) + set %request.Data("EXPIRES",1,"encrypt") = 1 + do %request.Set("EXPIRES",0) + do ##class(%CSP.StreamServer).OnPreHTTP() // Need to call this to set headers properly + set %stream = 1 // Leak this to webuidriver.csp } elseif $match(pathStart,"git-command|git|dirname|hostname|viewonly|contexts") { if (%request.Method = "GET") { set %data = ##class(%Stream.TmpCharacter).%New() diff --git a/git-webui/release/share/git-webui/webui/css/git-webui.css b/git-webui/release/share/git-webui/webui/css/git-webui.css index 0a0043b7..2eebf585 100644 --- a/git-webui/release/share/git-webui/webui/css/git-webui.css +++ b/git-webui/release/share/git-webui/webui/css/git-webui.css @@ -183,7 +183,7 @@ body { padding-bottom: 100px; } #sidebar #sidebar-content > :first-child, -#sidebar #sidebar-content > :nth-last-child(2) { +#sidebar #sidebar-content > :nth-last-child(3) { border-top: 1px solid #5e5e5e; } #sidebar #sidebar-content h4:before { @@ -216,12 +216,26 @@ body { #sidebar #sidebar-content #sidebar-tags h4:before { content: url(../img/tag.svg); } +#sidebar #sidebar-content #sidebar-vscode a { + color: white; +} +#sidebar #sidebar-content #sidebar-vscode h4:before { + content: url(../img/file.svg); +} +#sidebar #sidebar-content #sidebar-vscode { + position: absolute; + bottom: 80px; + width: 16.7em; + padding-bottom: 0.3rem; + margin-bottom: 0; + background-color: #333333; +} #sidebar #sidebar-content #sidebar-context h4:before { content: url(../img/context.svg); } #sidebar #sidebar-content #sidebar-context { position: absolute; - bottom: 50px; + bottom: 40px; width: 16.7em; padding-bottom: 0.3rem; margin-bottom: 0; diff --git a/git-webui/release/share/git-webui/webui/img/context.svg b/git-webui/release/share/git-webui/webui/img/context.svg index e3732172..f5ef6c98 100644 --- a/git-webui/release/share/git-webui/webui/img/context.svg +++ b/git-webui/release/share/git-webui/webui/img/context.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/git-webui/release/share/git-webui/webui/js/git-webui.js b/git-webui/release/share/git-webui/webui/js/git-webui.js index 9625c506..7161e0d8 100644 --- a/git-webui/release/share/git-webui/webui/js/git-webui.js +++ b/git-webui/release/share/git-webui/webui/js/git-webui.js @@ -973,6 +973,9 @@ webui.SideBarView = function(mainView, noEventHandlers) { '' + + '' + '' + diff --git a/git-webui/src/share/git-webui/webui/css/git-webui.less b/git-webui/src/share/git-webui/webui/css/git-webui.less index 652d5bdb..41b16101 100644 --- a/git-webui/src/share/git-webui/webui/css/git-webui.less +++ b/git-webui/src/share/git-webui/webui/css/git-webui.less @@ -257,7 +257,7 @@ body { color: @gray-lighter; padding-bottom: 100px; - > :first-child, >:nth-last-child(2) { + > :first-child, >:nth-last-child(3) { border-top: 1px solid darken(@separator-line, 50%); } @@ -301,13 +301,30 @@ body { content: url(../img/tag.svg); } + #sidebar-vscode a { + color: white; + } + + #sidebar-vscode h4:before { + content: url(../img/file.svg); + } + + #sidebar-vscode { + position: absolute; + bottom: 80px; + width: 16.7em; + padding-bottom: 0.3rem; + margin-bottom: 0; + background-color: rgba(51, 51, 51, 1); + } + #sidebar-context h4:before { - content: url(../img/context.svg) + content: url(../img/context.svg); } #sidebar-context { position: absolute; - bottom: 50px; + bottom: 40px; width: 16.7em; padding-bottom: 0.3rem; margin-bottom: 0; diff --git a/git-webui/src/share/git-webui/webui/img/context.svg b/git-webui/src/share/git-webui/webui/img/context.svg index e3732172..f5ef6c98 100644 --- a/git-webui/src/share/git-webui/webui/img/context.svg +++ b/git-webui/src/share/git-webui/webui/img/context.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/git-webui/src/share/git-webui/webui/js/git-webui.js b/git-webui/src/share/git-webui/webui/js/git-webui.js index 9625c506..7161e0d8 100644 --- a/git-webui/src/share/git-webui/webui/js/git-webui.js +++ b/git-webui/src/share/git-webui/webui/js/git-webui.js @@ -973,6 +973,9 @@ webui.SideBarView = function(mainView, noEventHandlers) { '' + + '' + '' + From 4ba8b80063f57a2f0cc616ee6e875b59a31537d5 Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Thu, 29 Aug 2024 09:55:03 -0400 Subject: [PATCH 03/12] feat: compile files modified via web application --- CHANGELOG.md | 3 +++ cls/SourceControl/Git/Extension.cls | 29 ++++++++++++++++++++--------- cls/SourceControl/Git/Utils.cls | 9 ++++++--- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e6a615a..5ee1b6f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - New UI for the basic mode Sync (#415) - Allow changing namespaces and IPM package context from web UI (#280) +- Support for editing repo from filesystem perspective via web application (#464) +- Support for downloading a VSCode workspace file from web UI ### Fixed - Instance wide settings are placed in proper global (#444) - Added support to switch branch in basic mode from menu (#451) +- Avoid delay/errors in loading interop JS when there is a URL prefix (e.g., instance name in multi-instance webserver configuration) ## [2.4.1] - 2024-08-02 diff --git a/cls/SourceControl/Git/Extension.cls b/cls/SourceControl/Git/Extension.cls index dacf2816..f4eab0b3 100644 --- a/cls/SourceControl/Git/Extension.cls +++ b/cls/SourceControl/Git/Extension.cls @@ -74,9 +74,13 @@ Method AfterUserAction(Type As %Integer, Name As %String, InternalName As %Strin if menu '= "%SourceMenu", menu'="%SourceContext" { quit $$$OK } - set InternalName = ##class(SourceControl.Git.Utils).NormalizeInternalName(InternalName) + set InternalName = ##class(SourceControl.Git.Utils).NormalizeInternalName(InternalName, .fromWebApp) set context = ##class(SourceControl.Git.PackageManagerContext).ForInternalName(InternalName) set ec = ##class(SourceControl.Git.Utils).AfterUserAction(Type, Name, InternalName, .Answer, .Msg, .Reload) + if fromWebApp { + // Force reload and compile of actual item if underlying file has changed + do ..OnBeforeLoad(InternalName,1,1) + } quit ec } @@ -235,12 +239,12 @@ Method OnMenuItem(MenuName As %String, InternalName As %String, SelectedText As /// This is called before the actual load of data to give the chance /// to load the item from an external format. -Method OnBeforeLoad(InternalName As %String, verbose As %Boolean) As %Status +Method OnBeforeLoad(InternalName As %String, verbose As %Boolean, compile As %Boolean = 0) As %Status { set context = ##class(SourceControl.Git.PackageManagerContext).ForInternalName(InternalName) set InternalName = ##class(SourceControl.Git.Utils).NormalizeInternalName(InternalName) if ##class(SourceControl.Git.Utils).IsInSourceControl(InternalName) { - quit ##class(SourceControl.Git.Utils).ImportItem(InternalName,,0) + quit ##class(SourceControl.Git.Utils).ImportItem(InternalName,,0,compile) } quit $$$OK } @@ -274,14 +278,21 @@ Method OnAfterSave(InternalName As %String, Object As %RegisteredObject = {$$$NU { set sc = $$$OK try { - set InternalName = ##class(SourceControl.Git.Utils).NormalizeInternalName(.InternalName) + set InternalName = ##class(SourceControl.Git.Utils).NormalizeInternalName(.InternalName,.fromWebApp,.fullExternalName) set context = ##class(SourceControl.Git.PackageManagerContext).ForInternalName(InternalName) if ##class(SourceControl.Git.Utils).IsNamespaceInGit() && ..IsInSourceControl(InternalName) { - set filename = ##class(SourceControl.Git.Utils).FullExternalName(InternalName) - $$$ThrowOnError(##class(SourceControl.Git.Utils).RemoveRoutineTSH(InternalName)) - $$$ThrowOnError(##class(SourceControl.Git.Utils).ExportItem(InternalName)) - if '##class(SourceControl.Git.Change).IsUncommitted(filename) { - $$$ThrowOnError(##class(SourceControl.Git.Change).SetUncommitted(filename, "edit", InternalName, $username, "", 1, "", "", 0)) + if fromWebApp { + if fullExternalName = ##class(SourceControl.Git.Utils).FullExternalName(InternalName) { + // Reimport item into database + $$$ThrowOnError(##class(SourceControl.Git.Utils).ImportItem(InternalName,,1,1)) + } + } else { + set filename = ##class(SourceControl.Git.Utils).FullExternalName(InternalName) + $$$ThrowOnError(##class(SourceControl.Git.Utils).RemoveRoutineTSH(InternalName)) + $$$ThrowOnError(##class(SourceControl.Git.Utils).ExportItem(InternalName)) + if '##class(SourceControl.Git.Change).IsUncommitted(filename) { + $$$ThrowOnError(##class(SourceControl.Git.Change).SetUncommitted(filename, "edit", InternalName, $username, "", 1, "", "", 0)) + } } } } catch e { diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls index 1efca933..94290b60 100644 --- a/cls/SourceControl/Git/Utils.cls +++ b/cls/SourceControl/Git/Utils.cls @@ -1116,7 +1116,7 @@ ClassMethod FullExternalName(ByRef InternalName As %String, ByRef MappingExists ..TempFolder()_..ExternalName(.InternalName, .MappingExists) } -ClassMethod NormalizeInternalName(ByRef name As %String) As %String +ClassMethod NormalizeInternalName(ByRef name As %String, Output fromWebApp As %Boolean = 0, Output cspFilename) As %String { //Studio passes name of routine with dots as it is in folders //e.g. Package.SubPackage.Routine.mac has InternalName = /Package/SubPackage/Routine.mac @@ -1137,6 +1137,9 @@ ClassMethod NormalizeInternalName(ByRef name As %String) As %String set cspFilename = $System.CSP.GetFileName(name) if (cspFilename '= "") && (cspFilename [ ..TempFolder()) { set name = ..NameToInternalName(cspFilename) + set fromWebApp = 1 + } else { + kill cspFilename } } @@ -1253,7 +1256,7 @@ ClassMethod FixProjectCspReferences(projectName As %String) As %Status /// imports file if version in system is newer then version on disk. /// if force = 1 then imports in any case. -ClassMethod ImportItem(InternalName As %String, force As %Boolean = 0, verbose As %Boolean = 1) As %Status +ClassMethod ImportItem(InternalName As %String, force As %Boolean = 0, verbose As %Boolean = 1, compile As %Boolean = 0) As %Status { #dim filename as %String = ..FullExternalName(.InternalName) #dim fileTSH = ##class(%File).GetFileDateModified(filename) @@ -1272,7 +1275,7 @@ ClassMethod ImportItem(InternalName As %String, force As %Boolean = 0, verbose A if ($extract(InternalName, 1) = "/"){ set sc = ..ImportCSPFile(InternalName) } else{ - set sc = $system.OBJ.Load(filename,"-l-d") + set sc = $system.OBJ.Load(filename,$Select(compile:"ck-l",1:"-l-d")) } } if sc { From b4905e867754fa5857ae25efbd69605dbb923702 Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:08:38 -0400 Subject: [PATCH 04/12] fix: tweak InternalName logic --- cls/SourceControl/Git/WebUIDriver.cls | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/cls/SourceControl/Git/WebUIDriver.cls b/cls/SourceControl/Git/WebUIDriver.cls index 1699453c..a8331485 100644 --- a/cls/SourceControl/Git/WebUIDriver.cls +++ b/cls/SourceControl/Git/WebUIDriver.cls @@ -32,17 +32,14 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out set namespace = $Namespace set instanceName = $ZConvert(##class(%SYS.System).InstanceGUID(),"L") set hasCSP = 0 - if (InternalName = "") || context.IsInDefaultPackage { - set hasCSP = ##class(SourceControl.Git.Settings).HasNamespaceWebApp() - set workspaceFilename = namespace_"-"_instanceName_"-.code-workspace" - set filter = "?mapped=0" - } elseif context.IsInGitEnabledPackage { + if context.IsInGitEnabledPackage && 'context.IsInDefaultPackage { set workspaceFilename = context.Package.Name_"-"_instanceName_".code-workspace" // Loading an IPM package in dev mode automatically creates a project for it, so filter to that. set filter = "?project="_context.Package.Name } else { - // TODO: Error? But really shouldn't happen. - quit + set hasCSP = ##class(SourceControl.Git.Settings).HasNamespaceWebApp() + set workspaceFilename = namespace_"-"_instanceName_"-.code-workspace" + set filter = "?mapped=0" } set server = { "webServer": { From 58b5e7f00bea3d7e0ba3a79624e9a3ef5e3c7d45 Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:24:44 -0400 Subject: [PATCH 05/12] fix: remove stray dash --- cls/SourceControl/Git/WebUIDriver.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cls/SourceControl/Git/WebUIDriver.cls b/cls/SourceControl/Git/WebUIDriver.cls index a8331485..5787162b 100644 --- a/cls/SourceControl/Git/WebUIDriver.cls +++ b/cls/SourceControl/Git/WebUIDriver.cls @@ -38,7 +38,7 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out set filter = "?project="_context.Package.Name } else { set hasCSP = ##class(SourceControl.Git.Settings).HasNamespaceWebApp() - set workspaceFilename = namespace_"-"_instanceName_"-.code-workspace" + set workspaceFilename = namespace_"-"_instanceName_".code-workspace" set filter = "?mapped=0" } set server = { From 23c2dc981d22beb863e7e5fa02dec95f73ebf67a Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Fri, 30 Aug 2024 09:33:45 -0400 Subject: [PATCH 06/12] fix: always just include one folder --- cls/SourceControl/Git/WebUIDriver.cls | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cls/SourceControl/Git/WebUIDriver.cls b/cls/SourceControl/Git/WebUIDriver.cls index 5787162b..a2888688 100644 --- a/cls/SourceControl/Git/WebUIDriver.cls +++ b/cls/SourceControl/Git/WebUIDriver.cls @@ -54,10 +54,6 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out } set workspaceDef = { "folders": [ - { - "name": ($Namespace), - "uri": ("isfs://"_instanceName_":"_$Namespace_"/"_filter) - } ], "settings": { "intersystems.servers": { @@ -73,6 +69,11 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out "name": ($Namespace_" (Filesystem)"), "uri": ("isfs://"_instanceName_":"_$Namespace_"/git/"_$Namespace_"?csp") }) + } else { + do workspaceDef.folders.%Push({ + "name": ($Namespace), + "uri": ("isfs://"_instanceName_":"_$Namespace_"/"_filter) + }) } set $Property(workspaceDef.settings."intersystems.servers",instanceName) = server set stream = ##class(%CSP.CharacterStream).%New() From 0bd96de98fa010f8ae6382f5a5bbb39752f06dd4 Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:12:34 -0400 Subject: [PATCH 07/12] fix: add JS escaping in sync output --- CHANGELOG.md | 1 + csp/sync.csp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ee1b6f6..0c6c52c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Instance wide settings are placed in proper global (#444) - Added support to switch branch in basic mode from menu (#451) - Avoid delay/errors in loading interop JS when there is a URL prefix (e.g., instance name in multi-instance webserver configuration) +- Added proper JS escaping in sync output ## [2.4.1] - 2024-08-02 diff --git a/csp/sync.csp b/csp/sync.csp index 78be9c2c..03404f08 100644 --- a/csp/sync.csp +++ b/csp/sync.csp @@ -146,7 +146,7 @@ &js< var outputContainer = document.getElementById('outputBox'); - var lineText = "#(escapedLine)#"; + var lineText = "#(..QuoteJS(escapedLine))#"; var lineTextNode = document.createTextNode(lineText); outputContainer.innerHTML += lineText + "
"; > From e649df77bc6f3302dc94b19062c1f5f24407c7dc Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:13:47 -0400 Subject: [PATCH 08/12] fix: don't double-quote --- csp/sync.csp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csp/sync.csp b/csp/sync.csp index 03404f08..bab0d58d 100644 --- a/csp/sync.csp +++ b/csp/sync.csp @@ -146,7 +146,7 @@ &js< var outputContainer = document.getElementById('outputBox'); - var lineText = "#(..QuoteJS(escapedLine))#"; + var lineText = #(..QuoteJS(escapedLine))#; var lineTextNode = document.createTextNode(lineText); outputContainer.innerHTML += lineText + "
"; > From a52a079dca6ba26817572796b5e8b8565881af87 Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:16:49 -0400 Subject: [PATCH 09/12] fix: tweaks to workspace setup --- cls/SourceControl/Git/WebUIDriver.cls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cls/SourceControl/Git/WebUIDriver.cls b/cls/SourceControl/Git/WebUIDriver.cls index a2888688..d5580448 100644 --- a/cls/SourceControl/Git/WebUIDriver.cls +++ b/cls/SourceControl/Git/WebUIDriver.cls @@ -44,7 +44,7 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out set server = { "webServer": { "host": (%request.CgiEnvs("SERVER_NAME")), - "port": (%request.CgiEnvs("SERVER_PORT")), + "port": (+%request.CgiEnvs("SERVER_PORT")), "scheme": ($Select(%request.Secure:"https",1:"http")) }, "username": ($Username) @@ -66,7 +66,7 @@ ClassMethod HandleRequest(pagePath As %String, InternalName As %String = "", Out } if hasCSP { do workspaceDef.folders.%Push({ - "name": ($Namespace_" (Filesystem)"), + "name": ($Namespace), "uri": ("isfs://"_instanceName_":"_$Namespace_"/git/"_$Namespace_"?csp") }) } else { From de9eb688459d3d9af7b2ee54f897fa26d8f1fa1b Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:09:17 -0400 Subject: [PATCH 10/12] fix: tweak IE box model Also fixed an issue with regex matching where I couldn't view my local git-source-control repo --- .../share/git-webui/webui/css/git-webui.css | 15 +++++++++------ .../share/git-webui/webui/js/git-webui.js | 3 +++ .../share/git-webui/webui/css/git-webui.less | 18 ++++++++++++------ .../src/share/git-webui/webui/js/git-webui.js | 3 +++ 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/git-webui/release/share/git-webui/webui/css/git-webui.css b/git-webui/release/share/git-webui/webui/css/git-webui.css index 2eebf585..0c75c1f4 100644 --- a/git-webui/release/share/git-webui/webui/css/git-webui.css +++ b/git-webui/release/share/git-webui/webui/css/git-webui.css @@ -219,6 +219,9 @@ body { #sidebar #sidebar-content #sidebar-vscode a { color: white; } +#sidebar #sidebar-content #sidebar-vscode h4 { + padding: 0px; +} #sidebar #sidebar-content #sidebar-vscode h4:before { content: url(../img/file.svg); } @@ -226,10 +229,11 @@ body { position: absolute; bottom: 80px; width: 16.7em; - padding-bottom: 0.3rem; - margin-bottom: 0; background-color: #333333; } +#sidebar #sidebar-content #sidebar-context h4 { + padding: 0px; +} #sidebar #sidebar-content #sidebar-context h4:before { content: url(../img/context.svg); } @@ -237,10 +241,11 @@ body { position: absolute; bottom: 40px; width: 16.7em; - padding-bottom: 0.3rem; - margin-bottom: 0; background-color: #333333; } +#sidebar #sidebar-content #sidebar-settings h4 { + padding: 0px; +} #sidebar #sidebar-content #sidebar-settings h4:before { content: url(../img/gear-fill.svg); } @@ -248,8 +253,6 @@ body { position: absolute; bottom: 0px; width: 16.7em; - padding-bottom: 0.3rem; - margin-bottom: 0px; background-color: #333333; } #sidebar #sidebar-content ul { diff --git a/git-webui/release/share/git-webui/webui/js/git-webui.js b/git-webui/release/share/git-webui/webui/js/git-webui.js index 7161e0d8..632c10f1 100644 --- a/git-webui/release/share/git-webui/webui/js/git-webui.js +++ b/git-webui/release/share/git-webui/webui/js/git-webui.js @@ -421,6 +421,9 @@ webui.SideBarView = function(mainView, noEventHandlers) { if (id.indexOf("local-branches") > -1) { // parses the output of git branch --verbose --verbose var matches = /^\*?\s*([\w-\/]+)\s+([^\s]+)\s+(\[.*\])?.*/.exec(ref); + if (!matches) { + continue; + } var branchInfo = { "branch_name": matches[1], "hash": matches[2], diff --git a/git-webui/src/share/git-webui/webui/css/git-webui.less b/git-webui/src/share/git-webui/webui/css/git-webui.less index 41b16101..8fb86733 100644 --- a/git-webui/src/share/git-webui/webui/css/git-webui.less +++ b/git-webui/src/share/git-webui/webui/css/git-webui.less @@ -305,6 +305,10 @@ body { color: white; } + #sidebar-vscode h4 { + padding: 0px; + } + #sidebar-vscode h4:before { content: url(../img/file.svg); } @@ -313,11 +317,13 @@ body { position: absolute; bottom: 80px; width: 16.7em; - padding-bottom: 0.3rem; - margin-bottom: 0; background-color: rgba(51, 51, 51, 1); } + #sidebar-context h4 { + padding: 0px; + } + #sidebar-context h4:before { content: url(../img/context.svg); } @@ -326,11 +332,13 @@ body { position: absolute; bottom: 40px; width: 16.7em; - padding-bottom: 0.3rem; - margin-bottom: 0; background-color: rgba(51, 51, 51, 1); } + #sidebar-settings h4 { + padding: 0px; + } + #sidebar-settings h4:before { content: url(../img/gear-fill.svg); } @@ -339,8 +347,6 @@ body { position: absolute; bottom: 0px; width: 16.7em; - padding-bottom: 0.3rem; - margin-bottom:0px; background-color: rgba(51, 51, 51, 1); } diff --git a/git-webui/src/share/git-webui/webui/js/git-webui.js b/git-webui/src/share/git-webui/webui/js/git-webui.js index 7161e0d8..632c10f1 100644 --- a/git-webui/src/share/git-webui/webui/js/git-webui.js +++ b/git-webui/src/share/git-webui/webui/js/git-webui.js @@ -421,6 +421,9 @@ webui.SideBarView = function(mainView, noEventHandlers) { if (id.indexOf("local-branches") > -1) { // parses the output of git branch --verbose --verbose var matches = /^\*?\s*([\w-\/]+)\s+([^\s]+)\s+(\[.*\])?.*/.exec(ref); + if (!matches) { + continue; + } var branchInfo = { "branch_name": matches[1], "hash": matches[2], From c8fffb9e0f7cb130b339185b779cb7bb71b965c5 Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:53:46 -0400 Subject: [PATCH 11/12] fix: various pull event handler issues --- CHANGELOG.md | 5 ++ .../Git/PullEventHandler/IncrementalLoad.cls | 18 +++-- cls/SourceControl/Git/Utils.cls | 62 +++++++++++++--- cls/_zpkg/isc/sc/git/Socket.cls | 74 ++++++++++--------- 4 files changed, 109 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c6c52c3..29824901 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow changing namespaces and IPM package context from web UI (#280) - Support for editing repo from filesystem perspective via web application (#464) - Support for downloading a VSCode workspace file from web UI +- IncrementalLoad pull event handler will update the running production, if any (#473) ### Fixed - Instance wide settings are placed in proper global (#444) - Added support to switch branch in basic mode from menu (#451) - Avoid delay/errors in loading interop JS when there is a URL prefix (e.g., instance name in multi-instance webserver configuration) - Added proper JS escaping in sync output +- Pull event handler will not fail when change set includes unmapped files (#453) +- Pull event handler will attempt compile even if there are failures to load (#457) +- Improved logging in preview and when errors occur via WebSocket commands (#467) +- Fixed pull event handler handling of extremely long class names from diff (#467) ## [2.4.1] - 2024-08-02 diff --git a/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls b/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls index 8dd64d3b..ae13af6f 100644 --- a/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls +++ b/cls/SourceControl/Git/PullEventHandler/IncrementalLoad.cls @@ -9,7 +9,7 @@ Parameter DESCRIPTION = "Performs an incremental load and compile of all changes Method OnPull() As %Status { - set loadSC = $$$OK + set sc = $$$OK set nFiles = 0 for i=1:1:$get(..ModifiedFiles){ @@ -17,8 +17,8 @@ Method OnPull() As %Status if ((internalName = "") && (..ModifiedFiles(i).changeType '= "D")) { write !, ..ModifiedFiles(i).externalName, " was not imported into the database and will not be compiled. " } elseif (..ModifiedFiles(i).changeType = "D") { - set sc = ..DeleteFile(internalName) - if sc { + set delSC = ..DeleteFile(internalName) + if delSC { write !, ..ModifiedFiles(i).externalName, " was deleted." } else { write !, "WARNING: Deletion of ", ..ModifiedFiles(i).externalName, " failed." @@ -26,8 +26,7 @@ Method OnPull() As %Status } else { set compilelist(internalName) = "" set nFiles = nFiles + 1 - set loadSC = $$$ADDSC(loadSC,##class(SourceControl.Git.Utils).ImportItem(internalName, 1)) - $$$ThrowOnError(loadSC) + set sc = $$$ADDSC(sc,##class(SourceControl.Git.Utils).ImportItem(internalName, 1)) } } @@ -35,7 +34,13 @@ Method OnPull() As %Status write !, "Nothing to compile." quit $$$OK } - quit $system.OBJ.CompileList(.compilelist, "ck") + set sc = $$$ADDSC(sc,$system.OBJ.CompileList(.compilelist, "ck")) + if $$$comClassDefined("Ens.Director") && ##class(Ens.Director).IsProductionRunning() { + write !,"Updating production... " + set sc = $$$ADDSC(sc,##class(Ens.Director).UpdateProduction()) + write "done." + } + quit sc } Method DeleteFile(item As %String) As %Status @@ -71,4 +76,3 @@ Method DeleteFile(item As %String) As %Status } } - diff --git a/cls/SourceControl/Git/Utils.cls b/cls/SourceControl/Git/Utils.cls index 94290b60..bd96f37e 100644 --- a/cls/SourceControl/Git/Utils.cls +++ b/cls/SourceControl/Git/Utils.cls @@ -546,14 +546,19 @@ ClassMethod GetCurrentBranch() As %String ClassMethod Pull(remote As %String = "origin") As %Status { + New %gitSCOutputFlag + Set %gitSCOutputFlag = 1 #define Force 1 do ##class(SourceControl.Git.Utils).RunGitCommandWithInput("branch",,.errStream,.outStream,"--show-current") set branchName = outStream.ReadLine(outStream.Size) write !, "Pulling from branch: ", branchName kill errStream, outStream set returnCode = ..RunGitWithArgs(.errStream, .outStream, "pull", remote, branchName) - - w !, "Pull ran with return code: " _ returnCode + write ! + do outStream.OutputToDevice() + write ! + do errStream.OutputToDevice() + write !, "Pull ran with return code: " _ returnCode quit $$$OK } @@ -626,10 +631,13 @@ ClassMethod ExternalName(InternalName As %String, ByRef MappingExists As %Boolea ClassMethod AddToServerSideSourceControl(InternalName As %String) As %Status { - #dim i as %Integer + #dim i as %Integer #dim ec as %Status = $$$OK for i = 1:1:$length(InternalName, ",") { #dim item as %String = ..NormalizeExtension($piece(InternalName, ",", i)) + if (item = "") { + continue + } set @..#Storage@("items", item) = "" #dim sc as %Status = ..ImportItem(item, 1) if 'sc { @@ -1791,12 +1799,16 @@ ClassMethod RunGitCommandWithInput(command As %String, inFile As %String = "", O } } } elseif syncIrisWithCommand { - do ..PrintStreams(errStream, outStream) + if '$data(%gitSCOutputFlag)#2 { + do ..PrintStreams(errStream, outStream) + } set buffer = ##class(SourceControl.Git.Util.Buffer).%New() do buffer.BeginCaptureOutput() set st = ..SyncIrisWithRepoThroughCommand(.outStream) set out = ##class(%Stream.GlobalCharacter).%New() do buffer.EndCaptureOutput(.out) + do outStream.MoveToEnd() + do errStream.MoveToEnd() if $$$ISOK(st) { while 'out.AtEnd { do outStream.WriteLine(out.ReadLine()) @@ -1836,11 +1848,31 @@ ClassMethod SyncIrisWithRepoThroughCommand(ByRef outStream) As %Status } } elseif (line [ "|") { set externalName = $zstrip($piece(line,"|",1),"<>W") - set modification = ##class(SourceControl.Git.Modification).%New() - set modification.changeType = "C" - set modification.internalName = ##class(SourceControl.Git.Utils).NameToInternalName(externalName,,0) - set modification.externalName = externalName - set files($i(files)) = modification + if $Extract(externalName,1,3) = "..." { + // For extremely long file names, git may truncate the path. + // Simplifying assumption: this is a class, because nothing else would have that long a name. + // In other cases, we'll just end up logging the invalid externalName. + if $Piece(externalName,".",*) = "cls" { + set possibleClasses = ..ExpandClasses(externalName) + set pointer = 0 + while $ListNext(possibleClasses,pointer,class) { + set modification = ##class(SourceControl.Git.Modification).%New() + set modification.changeType = "C" + set modification.internalName = class_".CLS" + set modification.externalName = ..ExternalName(modification.internalName) + set files($i(files)) = modification + } + } else { + write !,"WARNING: unable to translate external name ",externalName + continue + } + } else { + set modification = ##class(SourceControl.Git.Modification).%New() + set modification.changeType = "C" + set modification.internalName = ##class(SourceControl.Git.Utils).NameToInternalName(externalName,,0) + set modification.externalName = externalName + set files($i(files)) = modification + } } } @@ -1858,6 +1890,18 @@ ClassMethod SyncIrisWithRepoThroughCommand(ByRef outStream) As %Status quit ##class(SourceControl.Git.PullEventHandler).ForModifications(.files) } +ClassMethod ExpandClasses(externalName As %String) As %List +{ + set internalName = $Piece(externalName,".",1,*-1) + set internalName = $Extract(internalName,4,*) + set internalName = $Translate(internalName,"/\%",".."_..PercentClassReplace()) + &sql(select %DLIST(Name) into :classes from %Dictionary.ClassDefinition where Name like '%'||:internalName) + if (SQLCODE < 0) { + Throw ##class(%Exception.SQL).CreateFromSQLCODE(SQLCODE,%msg) + } + quit classes +} + ClassMethod ParseDiffStream(stream As %Stream.Object, verbose As %Boolean = 1, Output files) { kill files diff --git a/cls/_zpkg/isc/sc/git/Socket.cls b/cls/_zpkg/isc/sc/git/Socket.cls index 0bc691f8..48c5e4ed 100644 --- a/cls/_zpkg/isc/sc/git/Socket.cls +++ b/cls/_zpkg/isc/sc/git/Socket.cls @@ -11,36 +11,40 @@ Property OriginalDevice; ClassMethod Run() { - If %request.Get("method") = "preview" { - set branchName = ##class(SourceControl.Git.Utils).GetCurrentBranch() - do ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "fetch") - kill errStream, outStream - do ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "log", "HEAD..origin", "--name-status") - do ##class(SourceControl.Git.Utils).PrintStreams(errStream, outStream) - } ElseIf %request.Get("method") = "pull" { - Do ##class(SourceControl.Git.API).Pull() - } ElseIf %request.Get("method") = "init" { - Do ##class(SourceControl.Git.Utils).Init() + If %request.Get("method") = "preview" { + Set branchName = ##class(SourceControl.Git.Utils).GetCurrentBranch() + Write !,"Current branch: ",branchName + Do ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "fetch") + Kill errStream, outStream + Do ##class(SourceControl.Git.Utils).RunGitWithArgs(.errStream, .outStream, "log", "HEAD..origin", "--name-status") + Do ##class(SourceControl.Git.Utils).PrintStreams(errStream, outStream) + If (outStream.Size = 0) && (errStream.Size = 0) { + Write !,"Already up to date." + } + } ElseIf %request.Get("method") = "pull" { + Do ##class(SourceControl.Git.API).Pull() + } ElseIf %request.Get("method") = "init" { + Do ##class(SourceControl.Git.Utils).Init() Write !,"Done." - } ElseIf %request.Get("method") = "clone" { - Set remote = %request.Get("remote") - Do ##class(SourceControl.Git.Utils).Clone(remote) + } ElseIf %request.Get("method") = "clone" { + Set remote = %request.Get("remote") + Do ##class(SourceControl.Git.Utils).Clone(remote) Write !,"Done." - } ElseIf %request.Get("method") = "sshkeygen" { - Do ##class(SourceControl.Git.Utils).GenerateSSHKeyPair() + } ElseIf %request.Get("method") = "sshkeygen" { + Do ##class(SourceControl.Git.Utils).GenerateSSHKeyPair() Write !,"Done." - } Else { - Write !!,"Invalid method selected.",!! - } + } Else { + Write !!,"Invalid method selected.",!! + } } Method OnPreServer() As %Status { - If '$System.Security.Check("%Development","USE") { - Quit $$$ERROR($$$AccessDenied) - } + If '$SYSTEM.Security.Check("%Development","USE") { + Quit $$$ERROR($$$AccessDenied) + } If (%request.Get("$NAMESPACE") '= "") { - Set $Namespace = %request.Get("$NAMESPACE") + Set $NAMESPACE = %request.Get("$NAMESPACE") } Quit $$$OK } @@ -56,9 +60,11 @@ Method Server() As %Status // In subclasses: Do Something that produces output to the current device. // It will be sent back to the client, Base64-encoded, over the web socket connection. - Do ..Run() + Do ..Run() } Catch e { Do e.Log() + Write !,"An error occurred. More details can be found in the Application error log." + Write !,$SYSTEM.Status.GetErrorText(e.AsStatus()) Set tSC = e.AsStatus() } @@ -77,7 +83,7 @@ Method StartOutputCapture() [ ProcedureBlock = 0 ] #dim tSC As %Status = $$$OK #dim tRedirected As %Boolean = 0 Try { - Set %server = $this + Set %server = $THIS Set ..OriginallyRedirected = 0 Set ..OriginalMnemonic = "" Set ..OriginalDevice = $IO @@ -107,27 +113,27 @@ Method StartOutputCapture() [ ProcedureBlock = 0 ] #; Public entry points for I/O redirection wstr(s) Do write(s) Quit -wchr(a) Do write($char(a)) +wchr(a) Do write($CHAR(a)) Quit wnl Do write($$$EOL) Set $X = 0 Quit wff Do wnl Quit wtab(n) New tTab - Set tTab = $J("",$S(n>$X:n-$X,1:0)) + Set tTab = $JUSTIFY("",$SELECT(n>$X:n-$X,1:0)) Do write(tTab) Quit write(str) // If there was an argumentless NEW, cache the output and leave it at that. // This will be output next time there's a write with %server in scope. - If '$IsObject($Get(%server)) { - Set ^||OutputCapture.Cache($i(^||OutputCapture.Cache)) = str + If '$ISOBJECT($GET(%server)) { + Set ^||OutputCapture.Cache($INCREMENT(^||OutputCapture.Cache)) = str Quit } // Restore previous I/O redirection settings. New tOriginalDevice,i - Set tOriginalDevice = $io + Set tOriginalDevice = $IO If ##class(%Library.Device).ReDirectIO(0) { Use tOriginalDevice } @@ -138,8 +144,8 @@ write(str) Do ##class(%Library.Device).ReDirectIO(1) } - If $Data(^||OutputCapture.Cache) { - For i=1:1:$Get(^||OutputCapture.Cache) { + If $DATA(^||OutputCapture.Cache) { + For i=1:1:$GET(^||OutputCapture.Cache) { Do reallywrite(^||OutputCapture.Cache(i)) } Kill ^||OutputCapture.Cache @@ -155,7 +161,7 @@ write(str) reallywrite(pString) New tMsg Set tMsg = {"content":(pString)} // This is handy because it handles escaping of newlines, etc. - Do %server.Write($System.Encryption.Base64Encode(tMsg.%ToJSON())) + Do %server.Write($SYSTEM.Encryption.Base64Encode(tMsg.%ToJSON())) Quit rstr(len, time) Quit "" rchr(time) Quit "" @@ -179,7 +185,7 @@ Method EndOutputCapture() Method SendJSON(pObject As %DynamicAbstractObject) { - Set tOriginalDevice = $io + Set tOriginalDevice = $IO If ##class(%Library.Device).ReDirectIO(0) { Use tOriginalDevice } @@ -189,7 +195,7 @@ Method SendJSON(pObject As %DynamicAbstractObject) If ..OriginallyRedirected { Do ##class(%Library.Device).ReDirectIO(1) } - Do ..Write($System.Encryption.Base64Encode(pObject.%ToJSON())) + Do ..Write($SYSTEM.Encryption.Base64Encode(pObject.%ToJSON())) Do ##class(%Library.Device).ReDirectIO(1) Use tOriginalDevice::("^"_$ZNAME) } From e1ca8533f3649c96d6a2addf72f9dee42b9cc7e7 Mon Sep 17 00:00:00 2001 From: isc-tleavitt <73311181+isc-tleavitt@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:55:50 -0400 Subject: [PATCH 12/12] Update CHANGELOG.md Remote duplicated content --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5cbc2a0..2e14cc25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Avoid delay/errors in loading interop JS when there is a URL prefix (e.g., instance name in multi-instance webserver configuration) - Added proper JS escaping in sync output - Added support to switch branch in basic mode from menu (#451) -- Avoid delay/errors in loading interop JS when there is a URL prefix (e.g., instance name in multi-instance webserver configuration) -- Added proper JS escaping in sync output - Pull event handler will not fail when change set includes unmapped files (#453) - Pull event handler will attempt compile even if there are failures to load (#457) - Improved logging in preview and when errors occur via WebSocket commands (#467)