In my previous post, I was exploring taking URI parameters and passing them to an application.
The main issue we are facing is that Storefront launches the ica file via an iframe src. When launching the ica via this method the iframe does a simple ‘GET’ without passing any HEADER parameters — which is the only (documented) way to pass data to Storefront.
What can I do? I think what I need to do is create my own *custom* launchica command. Because this will be against an unauthenticated store we should be able remove the authentication portions AND any unique identifiers (eg, csrf data). Really, we just need the two options — the application to launch and the parameter to pass into it. I am NOT a web developer, I do not know what would be the best solution to this problem, but here is something I came up with.
My first thought is this needs to be a URL that must be queried and that URL must return a specific content-type. I know Powershell has lots of control over specifying things like this and I have some familiarity with Powershell so I’ve chosen that as my tool of choice to solve this problem.
In order to start I need to create or find something that will get data from a URL to powershell. Fortunately, a brilliant person by the name of Steve Lee solved this first problem for me.
What he created is a Powershell module that creates a HTTP listener than waits for a request. We can take this listener and modify it so it listens for our two variables (CTX_Application and NFuseAppCommandLine) and then returns a ICA file. Since this is an unauthenticated URL I had to remove the authentication feature of the script and I added a function to query the real Storefront services to generate the ICA file.
So what I’m envisioning is replacing the “LaunchIca” command with my custom one.
This is my modification of Steve’s script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# Copyright (c) 2014 Microsoft Corp. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # Modified by Trentent Tye for ICA file returning. Function ConvertTo-HashTable { <# .Synopsis Convert an object to a HashTable .Description Convert an object to a HashTable excluding certain types. For example, ListDictionaryInternal doesn't support serialization therefore can't be converted to JSON. .Parameter InputObject Object to convert .Parameter ExcludeTypeName Array of types to skip adding to resulting HashTable. Default is to skip ListDictionaryInternal and Object arrays. .Parameter MaxDepth Maximum depth of embedded objects to convert. Default is 4. .Example $bios = get-ciminstance win32_bios $bios | ConvertTo-HashTable #> Param ( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [Object]$InputObject, [string[]]$ExcludeTypeName = @("ListDictionaryInternal","Object[]"), [ValidateRange(1,10)][Int]$MaxDepth = 4 ) Process { Write-Verbose "Converting to hashtable $($InputObject.GetType())" #$propNames = Get-Member -MemberType Properties -InputObject $InputObject | Select-Object -ExpandProperty Name $propNames = $InputObject.psobject.Properties | Select-Object -ExpandProperty Name $hash = @{} $propNames | % { if ($InputObject.$_ -ne $null) { if ($InputObject.$_ -is [string] -or (Get-Member -MemberType Properties -InputObject ($InputObject.$_) ).Count -eq 0) { $hash.Add($_,$InputObject.$_) } else { if ($InputObject.$_.GetType().Name -in $ExcludeTypeName) { Write-Verbose "Skipped $_" } elseif ($MaxDepth -gt 1) { $hash.Add($_,(ConvertTo-HashTable -InputObject $InputObject.$_ -MaxDepth ($MaxDepth - 1))) } } } } $hash } } Function Get-ICA { <# .Synopsis Queries An Anonymous Store for an ICA file and sets the LongCommand in the ICA with a URI parameter .Description This function will take 3 parameters, the store, the program and the additional parameters to pass to the program. It will then query and pull the required ICA file then make the substitution and return the result as TEXT. You may need to convert the result to application/ica. .Parameter Store URL to the store. eg, "https://storefront.mydomain.local/Citrix/unauthWeb/" .Parameter Program The name of the program to launch. eg, "Notepad 2016 - PLB" .Parameter LongCommand The extra command string to pass to the program. Eg, "C:\Windows\WindowsUpdate.Log" .Example Get-ICA -Store "http://$env:COMPUTERNAME/Citrix/PLBWeb/" -Program "Notepad 2016 - PLB" -LongCommand "C:\Windows\WindowsUpdate.Log" #> Param ( [Parameter()] [String] $Store = "", [Parameter()] [String] $Program = "", [Parameter()] [String] $LongCommand = "" ) Process { Write-Verbose "Get-ICA - Gets required tokens" #Gets required tokens $headers = @{ "Accept"='application/xml, text/xml, */*; q=0.01'; "Content-Length"="0"; "X-Citrix-IsUsingHTTPS"="Yes"; "Referer"=$Store; } $result = Invoke-WebRequest -Uri ($Store + "Home/Configuration") -MaximumRedirection 0 -Method POST -Headers $headers -SessionVariable SFSession|Out-Null Write-Verbose "Get-ICA - Gets list of resources for the application" $headers = @{ "Content-Type"='application/x-www-form-urlencoded; charset=UTF-8'; "Accept"='application/json, text/javascript, */*; q=0.01'; "X-Citrix-IsUsingHTTPS"= "Yes"; "Referer"=$Store; "format"='json&resourceDetails=Default'; } $content = Invoke-WebRequest -Uri ($Store + "Resources/List") -MaximumRedirection 0 -Method POST -Headers $headers -SessionVariable SFSession #Creates ICA file $resources = $content.content | convertfrom-json $resourceurl = $resources.resources | where{$_.name -like $Program} if ($resourceurl.count) { write-host "MULTIPLE APPS FOUND for $Program. Check APP NAME!" -ForegroundColor Red $resourceurl|select id,name } else { Write-Verbose "Get-ICA - Getting ICA file" $icafile = Invoke-WebRequest -Uri ($Store + $resourceurl.launchurl) -MaximumRedirection 0 -Method GET -Headers $headers -SessionVariable SFSession $icafile = $icafile.ToString() Write-Verbose "Get-ICA - adding LongCommand" $icafile = $icafile -replace "LongCommandLine=", "LongCommandLine=$LongCommand" } $icafile } } Function Start-HTTPListener { <# .Synopsis Creates a new HTTP Listener accepting PowerShell command line to execute .Description Creates a new HTTP Listener enabling a remote client to execute PowerShell command lines using a simple REST API. This function requires running from an elevated administrator prompt to open a port. Use Ctrl-C to stop the listener. You'll need to send another web request to allow the listener to stop since it will be blocked waiting for a request. .Parameter Port Port to listen, default is 8888 .Parameter URL URL to listen, default is / .Parameter Auth Authentication Schemes to use, default is IntegratedWindowsAuthentication .Example Start-HTTPListener -Port 80 -Url "Citrix/PLBWeb/ica_launcher" -Auth Anonymous Invoke-WebRequest -Uri "http://localhost/Citrix/PLBWeb/ica_launcher?CTX_Application=Notepad%202016%20-%20PLB&NFuse_AppCommandLine=C:\Windows\WindowsUpdate.log" -UseDefaultCredentials | Format-List * #> Param ( [Parameter()] [Int] $Port = 8888, [Parameter()] [String] $Url = "", [Parameter()] [System.Net.AuthenticationSchemes] $Auth = [System.Net.AuthenticationSchemes]::IntegratedWindowsAuthentication ) Process { $ErrorActionPreference = "Stop" $CurrentPrincipal = New-Object Security.Principal.WindowsPrincipal( [Security.Principal.WindowsIdentity]::GetCurrent()) if ( -not ($currentPrincipal.IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator ))) { Write-Error "This script must be executed from an elevated PowerShell session" -ErrorAction Stop } if ($Url.Length -gt 0 -and -not $Url.EndsWith('/')) { $Url += "/" } $listener = New-Object System.Net.HttpListener $prefix = "http://*:$Port/$Url" $listener.Prefixes.Add($prefix) $listener.AuthenticationSchemes = $Auth try { $listener.Start() while ($true) { $statusCode = 200 Write-Warning "Note that thread is blocked waiting for a request. After using Ctrl-C to stop listening, you need to send a valid HTTP request to stop the listener cleanly." Write-Warning "Sending 'exit' command will cause listener to stop immediately" Write-Verbose "Listening on $port..." $context = $listener.GetContext() $request = $context.Request Write-Verbose "Request = $($request.QueryString)" if (-not $request.QueryString.HasKeys()) { $commandOutput = "SYNTAX: command=<string> format=[JSON|TEXT|XML|NONE|CLIXML]" $Format = "TEXT" } else { #change command to parameters... $CTX_Application = $request.QueryString.Item("CTX_Application") $NFuse_AppCommandLine = $request.QueryString.Item("NFuse_AppCommandLine") #uncomment next portion to allow remote exit of the listener <# if ($CTX_Application -eq "exit") { Write-Verbose "Received command to exit listener" return } #> $Format = $request.QueryString.Item("format") if ($Format -eq $Null) { $Format = "JSON" } Write-Verbose "Application = $CTX_Application" Write-Verbose "NFuse_AppCommandLine = $NFuse_AppCommandLine" Write-Verbose "Format = $Format" Write-Verbose "Executing Command" ## execute command here... --> ensure you change "PLBWeb" to your proper store $script = get-ica -store "http://$env:COMPUTERNAME/Citrix/PLBWeb/" -Program "$CTX_Application" -LongCommand $NFuse_AppCommandLine write-verbose "are we back yet?" $commandOutput = $script.ToString() } Write-Verbose "Response:" if (!$script) { $script = [string]::Empty } Write-Verbose $script $response = $context.Response $response.StatusCode = $statusCode $response.ContentType = "application/x-ica; charset=utf-8" $buffer = [System.Text.Encoding]::UTF8.GetBytes($script) $response.ContentLength64 = $buffer.Length $output = $response.OutputStream $output.Write($buffer,0,$buffer.Length) $output.Close() } } finally { $listener.Stop() } } } |
And the command to start the HTTP listener:
1 2 3 4 |
ipmo "C:\swinst\HttpListener_1.0.1\HttpListener\HTTPListener.psm1" #Example URL #http://bottheory.local/Citrix/PLBWeb/ica_launcher?CTX_Application=Notepad%202016%20-%20PLB&NFuse_AppCommandLine=C:\Windows\WindowsUpdate.log Start-HTTPListener -Port 80 -Url "Citrix/PLBWeb/ica_launcher" -Auth Anonymous |
Eventually, this will need to be converted to a scheduled task or a service. When running the listener manually, it looks like this:
I originally planned to use the ‘WebAPI’ and create a custom StoreFront, but I really, really want to use the new Storefront UI. In addition, I do NOT want to have to copy a file around to each Storefront server to enable this feature. So I started to wonder if it would be possible to modify Storefront via the extensible customization API’s it provides. This involves adding javascript to the “C:\inetpub\wwwroot\Citrix\StoreWeb\custom\script.js” and modifying the “C:\inetpub\wwwroot\Citrix\StoreWeb\custom\style.css” files. To start, my goal is to mimic our existing functionality and UI to an extent that makes sense.
The Web Interface 5.4 version of this web launcher looked like this:
When you browse to the URL in Web Interface 5.4 the application is automatically launched. If it doesn’t launch, “click to connect” will launch it for you manually. This is the function and features I want.
Storefront, without any modifications, looks like this with an authenticated store:
So, I need to make a few modifications.
- I need to hide all applications that are NOT my target application
- I need to add the additional messaging “If your application does not appear within a few seconds, click to connect.” with the underlined as a URL to our launcher.
- I want to minimize the interface by hiding the toolbar. Since only one application will be displayed we do not need to see “All | Categories Search All Apps”
- I want to hide the ‘All Apps’ text
- I want to hide “Details”, we’re going to keep this UI minimal.
The beauty of Storefront, in its current incarnation, is most of this is CSS modifications. I made the following modifications to the CSS to get my UI minimalized:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/* removes "Apps | Categories Search apps:" tool bar */ .large .myapps-view .store-toolbar, .large .desktops-view .store-toolbar, .large .tasks-view .store-toolbar, .large .store-view .store-toolbar { display : none; } /* this is to display a single app, we don't need to be told we're looking at 'All Apps' */ .largeTiles .store-view .store-apps-title { display : none; } /* hide the 'Details' link */ .largeTiles .storeapp-action-link, .taskapp-action-link { display : none; } /* stretch the app content to 100% of the width of the window */ .storeapp-list .storeapp, .storeapp-list .folder { width : 100%; } /* enlarge the app name text field to 100% of the screen width -- this allows our messaging to be read in it's entirety. */ .largeTiles .storeapp-name, .large .ruler-container { width : 100%; } |
This resulted in a UI that looked like this:
So now I want to remove all apps except my targeted application that should come in a query string.
I was curious if the ‘script.js’ would recognize the URI parameter passed to Storefront. I modified my ‘script.js’ with the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// Edit this file to add your customized JavaScript or load additional JavaScript files. //grab the URL and parse out the parameters we want (application name, launch parameters) var getUrlParameter = function getUrlParameter(sParam) { var sPageURL = decodeURIComponent(window.location.search.substring(1)), sURLVariables = sPageURL.split('&'), sParameterName, i; for (i = 0; i < sURLVariables.length; i++) { sParameterName = sURLVariables[i].split('='); if (sParameterName[0] === sParam) { return sParameterName[1] === undefined ? true : sParameterName[1]; } } }; var NFuse_AppCommandLine = getUrlParameter('NFuse_AppCommandLine'); var CTX_Application = getUrlParameter('CTX_Application'); console.log("NFuse_AppCommandLine " + NFuse_AppCommandLine); console.log("CTX_Application " + CTX_Application); |
Going to my URL and checking the ‘Console’ in Chrome revealed:
Yes, indeed, we are getting the URI parameters!
Great! So can we filter our application list to only display the app in the URI?
Citrix offers a bunch of ‘extensions’. Can one of them work for our purpose? This one sounds interesting:
1 2 |
excludeApp(app) Exclude an application completely from all UI, even if it would normally be included |
Can we do a simple check that if the application does not equal “CTX_Application” to exclude it?
The function looks like this:
1 2 3 4 5 6 7 8 |
CTXS.Extensions.excludeApp = function(app) { // return true or false if we don't match the target app name //we only want to show the application name found in the URI //if the app name does not equal the URI name then we hide it. if (app.name!= CTX_Application) { return true; } }; |
Did it work?
Yes! Perfectly! Can we append a message to the application? Looking at Citrix’s extensions this one looks promising:
1 2 |
onAppHTMLGeneration(element) Called when HTML is generated for one or more app tile, passing the parent container. Intended for deep customization. (Warning this sort of change is likely to be version specific) |
Ok. That warning sucks. My whole post is based on StoreFront 3.9 so I cannot guarantee these modifications will work in future versions or previous versions. Your Mileage May Vary.
So what elements could we manipulate to add our text?
Could we add another “p class=storeapp-name” (this is just text) for our messaging? The onAppHTMLGeneration function says it is returned when the HTML is generated for an app, so what does this look like?
I added the following to script.js:
1 2 3 4 5 |
CTXS.Extensions.onAppHTMLGeneration = function(element) { //this function is not guaranteed to work across Storefront versions. Ensure proper testing is conducted when upgrading. //tested on StoreFront 3.9 console.log(element); }; |
And this was the result in the Chrome Console:
So this is returning an DomHTMLElement. DOMHTMLElements have numerous methods to add/create/append/modify data to them. Perfect. Doing some research (I’m not a web developer) I found that you can modify the content of an element by this style command:
1 2 3 4 5 |
CTXS.Extensions.onAppHTMLGeneration = function(element) { //this function is not guaranteed to work across Storefront versions. Ensure proper testing is conducted when upgrading. //tested on StoreFront 3.9 $( "div.storeapp-details-container" ).append( "whatever text you want here" ); }; |
This results in the following:
We have text!
Great.
My preference is to have the text match the application name’s format. I also wanted to test if I could add a link to the text. So I modified my css line:
1 2 3 4 5 6 |
CTXS.Extensions.onAppHTMLGeneration = function(element) { //this function is not guaranteed to work across Storefront versions. Ensure proper testing is conducted when upgrading. //tested on StoreFront 3.9 $( "div.storeapp-details-container" ).append( "<p class=\"storeapp-name\"><br></p>" ); $( "div.storeapp-details-container" ).append( "<p class=\"storeapp-name\">If your application does not appear within a few seconds, <a href=\"http://www.google.ca\">click to connect</a></p>" ); }; |
The result?
Oh man. This is looking good. My ‘click to connect’ link isn’t working at this point, it just goes to google, but at least I know I can add a URL and have it work! Now I just need to generate a URL and set that to replace ‘click to connect’.
When I made my HTTPListener I purposefully made it with the following:
1 |
Start-HTTPListener -Port 80 -Url "Citrix/PLBWeb/ica_launcher" -Auth Anonymous |
The reason why I had it set to share the url of the Citrix Store is the launchurl generated by Storefront is:
1 |
Resources/LaunchIca/WEQ3Lk5vdGVwYWQgMjAxNiAtIFBMQg--.ica |
The full path for the URL is actually:
1 |
http://bottheory.local/Citrix/PLBWeb/Resources/LaunchIca/WEQ3Lk5vdGVwYWQgMjAxNiAtIFBMQg--.ica |
So the request actually starts at the storename. And if I want this to work with a URL re-write service like Netscaler I suspect I need to keep to relative paths. So to reach my custom ica_launcher I can just put this in my script.js file:
1 2 |
//generated launch URL var launchURL = "ica_launcher?CTX_Application=" + CTX_Application + "&NFuse_AppCommandLine=" + NFuse_AppCommandLine |
and then I can replace my ‘click to connect’ link with:
1 2 3 4 5 6 |
CTXS.Extensions.onAppHTMLGeneration = function(element) { //this function is not guaranteed to work across Storefront versions. Ensure proper testing is conducted when upgrading. //tested on StoreFront 3.9 $( "div.storeapp-details-container" ).append( "<p class=\"storeapp-name\"><br></p>" ); $( "div.storeapp-details-container" ).append( "<p class=\"storeapp-name\">If your application does not appear within a few seconds, <a href=\"" + launchURL + "\">click to connect</a></p>" ); }; |
The result?
The url on the “click to connect” goes to my launcher! And it works! Excellent!
Now I have one last thing I need to get working. If I click the ‘Notepad 2016 – PLB’ icon I get the regular Storefront ica file, so I don’t get my LongCommandLine added into it. Can I change where it’s trying to launch from?
Citrix appears to offer one extension that may allow this:
Huh. Well. That’s not much documentation at all.
Fortunately, a Citrix blog post came to rescue with some more information:
Hook APIs That Allow Delays / Cancellations
doLaunch
doSubscribe
doRemove
doInstall
On each of these the customization might show a dialog, perform some checks (etc) but ultimately should call ‘action’ if (and only if) they want the operation to proceed.
1 2 3 4 |
CTXS.Extensions.doLaunch = function(app, action) { // call 'action' function if/when action should proceed action(); }; |
This extension is taking an object (app). What properties does this object have? I did a simple console.log and examined the object:
1 2 3 4 5 |
CTXS.Extensions.doLaunch = function(app, action) { // call 'action' function if/when action should proceed console.log(app); action(); }; |
Well, look that that. There is a property called ‘launchurl’. Can we modify this property and have it point to our custom launcher?
I modified my function as such:
1 2 3 4 5 6 7 |
CTXS.Extensions.doLaunch = function(app, action) { // call 'action' function if/when action should proceed //modify launchurl to our PowerShell ICA creator app.launchurl = launchURL; console.log(app); action(); }; |
The result?
A modified launchurl!!!!
Excellent!
And launching it does return the ica file from my custom ica_launcher!
Lastly, I want to autolaunch my program. It turns out, this is pretty simple. Just add the following the script.js file:
1 2 3 4 5 6 |
//autolaunch application CTXS.Extensions.noteApp = function(app) { if (app.encodedName.indexOf(CTX_Application) != -1) { CTXS.ExtensionAPI.launch(app); } }; |
Beautiful. My full script.js file looks like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
// Edit this file to add your customized JavaScript or load additional JavaScript files. //grab the URL and parse out the parameters we want (application name, launch parameters) var getUrlParameter = function getUrlParameter(sParam) { var sPageURL = decodeURIComponent(window.location.search.substring(1)), sURLVariables = sPageURL.split('&'), sParameterName, i; for (i = 0; i < sURLVariables.length; i++) { sParameterName = sURLVariables[i].split('='); if (sParameterName[0] === sParam) { return sParameterName[1] === undefined ? true : sParameterName[1]; } } }; var NFuse_AppCommandLine = getUrlParameter('NFuse_AppCommandLine'); var CTX_Application = getUrlParameter('CTX_Application'); console.log("NFuse_AppCommandLine " + NFuse_AppCommandLine); console.log("CTX_Application " + CTX_Application); CTXS.Extensions.excludeApp = function(app) { // return true or false if we don't match the target app name //we only want to show the application name found in the URI //if the app name does not equal the URI name then we hide it. if (app.name!= CTX_Application) { return true; } }; //generated launch URL var launchURL = "ica_launcher?CTX_Application=" + CTX_Application + "&NFuse_AppCommandLine=" + NFuse_AppCommandLine CTXS.Extensions.doLaunch = function(app, action) { // call 'action' function if/when action should proceed //modify launchurl to our PowerShell ICA creator app.launchurl = launchURL; action(); }; CTXS.Extensions.onAppHTMLGeneration = function(element) { //this function is not guaranteed to work across Storefront versions. Ensure proper testing is conducted when upgrading. //tested on StoreFront 3.9 $( "div.storeapp-details-container" ).append( "<p class=\"storeapp-name\"><br></p>" ); $( "div.storeapp-details-container" ).append( "<p class=\"storeapp-name\">If your application does not appear within a few seconds, <a href=\"" + launchURL + "\">click to connect</a></p>" ); }; //autolaunch application CTXS.Extensions.noteApp = function(app) { if (app.encodedName.indexOf(CTX_Application) != -1) { CTXS.ExtensionAPI.launch(app); } }; |
And that’s it. We are able to accomplish this with a Powershell script, and two customization files. I think this has a better chance of ‘working’ across Storefront versions then the SDK attempt I did earlier, or creating my own custom Storefront front end.
Hi. Having issues passing a url to applications using this method. If I pass a short url like http://Citrix.com it works ask expected. However if I pass a longer url with % and such it’s getting encoded and the fails to open the correct site. It looks like this is caused by the http listener but I’m not sure at what stage this is happening. Have you run into this before?
Hi Trentent,
I am interested to do the same thing with an authenticated store (users will be authenticated from SAML), can you give more information about how could I adapt the httplistener module ?
I try to use Storefront Web Shortcut but I cannot pass any arguments.
Thanks for the time you will take to answer me.
Regards
Julien