Is it possible for header-portlet-javascript to pick up from system properties?
For example:
<header-portlet-javascript>${external.js.url}</header-portlet-javascript>
In general, this is not possible.
However, if it's ok that the Javascript you want to include occurs on every page of your portal, you could just add a reference to it inside your Liferay theme. Inside the theme, you can do dynamic stuff to retrieve the right JS url, e.g. using a portal property:
#set($jsUrl = $propsUtil.get("external.js.url"))
<script type="text/javascript" src="$jsUrl"></script>
To have the same effect with System properties, things get a bit more complex. To my knowledge there is no way to get System properties from an injected Velocity variable. Therefore, we need to create a small event handler hook that will inject this property into the Velocity context.
portal.properties
servlet.service.events.pre=my.custom.ServicePreAction
ServicePreAction.java
public class ServicePreAction extends Action {
public void run(HttpServletRequest request, HttpServletResponse response) {
Map<String,Object> veloVars = new HashMap<String,Object>();
veloVars.put("externalJSurl", System.getProperty("external.js.url"));
request.setAttribute(WebKeys.VM_VARIABLES, veloVars);
}
}
portal_normal.vm
<script type="text/javascript" src="$externalJSurl"></script>
Related
We have a running IS4 instance for corporate customers (built on top of QuickUI), now the mgmt wants to extend it with another client and they want to have a special layout and page adjustments when the authorization process comes from this client (in essence we need to collect additional info in login form and it has to be branded slightly differently).
Now, adjusting the form and visual appearance is not a problem, but deciding when to show it is proving a challenge. We have our templating and branding in _Layout which is used by all the pages, and I somehow need to know inside _layout, if the page load is part of the authentication context and if so, which one.
The way QuickUI did it in AccountController, is using IS interaction, which generates AuthenticationRequest which contains the client:
var context = await interaction.GetAuthorizationContextAsync(returnUrl);
var clientName = context.Client?.ClientName;
This fits nicely with AccountController logic and works just fine for authentication purposes, but does little for visual. I do not have the returnUrl inside _Layout (and layout is used by other pages, for registration, etc, so I cannot really cram specific viewmodels into it) and apparently no clean way to determine the context.
a) I could start digging through HttpRequest and parsing request URLs, fishing for returnUrls, but I would first like to see if there is a more streamlined, or even existing solution.
b) Another option is to have multiple layout files, and expose Client to the ViewModel and switch layouts based on it.
Ideally however, I would like something "clean" and maintainable, a service I could #inject into _Layout.cshtml and just do #if (contextService.Client == ...)
Has someone seen or done something like it?
Until (and if) someone posts a "cleaner" solution (especially the one that does not revolve around returnUrl magic string), this is the workaround I implemented. It's dirty, I admit, it pokes on HttpContext from a service, which is not really asp.net core way, but at this point, I see no other way to obtain the information I need to construct authorization context:
using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Http;
namespace MyServices
{
public interface IContextService
{
Task<AuthorizationRequest> GetContextAsync();
}
public class ContextService : IContextService
{
private readonly IIdentityServerInteractionService interaction;
private readonly IHttpContextAccessor contextAccessor;
private AuthorizationRequest context;
public ContextService(IIdentityServerInteractionService interaction, IHttpContextAccessor contextAccessor)
{
this.interaction = interaction;
this.contextAccessor = contextAccessor;
}
public async Task<AuthorizationRequest> GetContextAsync() => context ??= await InternalObtainContextAsync();
private async Task<AuthorizationRequest> InternalObtainContextAsync()
{
var query = contextAccessor.HttpContext?.Request.Query;
if (query == null || query.Count == 0 || !query.ContainsKey("returnUrl")) return new AuthorizationRequest(); // empty context
return await interaction.GetAuthorizationContextAsync(query["returnUrl"]);
}
}
}
Register in startup with services.AddTransient<IContextService, ContextService>(); and then you can inject it into any page and/or layout:
#inject IContextService contextService;
#{
// render only when invoked from authorization context
var context = await contextService.GetContextAsync();
if (context.Client != null)
{
<div>Hi, I am inside authorization context for client #context.Client.ClientId</div>
}
}
In Blazor server the "appsettings.json" file is great for storing globally accessible variables. But what if these need changing during runtime? For example, lets say we have a stored value for "IsMaintenanceMode".
Given that;
"IsMaintenanceMode" may need to be set to "True" during runtime (to then direct users to a maintenance page)
If we were using a middleware to check this value for True (i.e. redirect the user to maintenance page) - then we would not want to look this variable up each time - eg from a database - on every request.
Traditionally this might have been accomplished using Application variables but I'm just not sure of the best approach with Blazor.
So my question is - what's the best way of storing this value in a way that can be "cached" for ease of lookup, but also easily changed during runtime?
Thanks for any advice.
StateServer.cs
public class StateServer {
public bool IsMaintenanceMode {get; set;}
}
Add it in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<StateServer >();
}
Component.razor // Can be layout, main page, etc.
#inject StateServer _stateServer
#if (_stateServer.IsMaintenanceMode){
<Warning />
}
else {
<Body />
}
#code {
}
Or, you can check the value in one of the page lifecyle events, and navigate to whatever page you like.
I'm trying to write a VSTO-Add-In with a System.Windows.Forms.WebBrowser-Control enabling something similar to the Office-JS-Add-In model.
The WebBrowser-control would show some HTML/JS-Page and be able to call C#-functions in the VSTO-Add-In from JavaScript via window.external and the ObjectForScripting-property of the WebBrowser-object.
That is in JS the call would be
window.external.DoFancyStuffToMyDocument(withTheseParams)
while there had to be some
class MyFunctionProxy() {
public void DoFancyStuffToMyDocument(string theParam) {
//code here
}
}
in the C#-Code an this would be attached to the WebBrowser
myWebBrowser.ObjectForScripting = new MyFunctionProxy();
So far so good. Now comes the catch. I want my HTML/JS-Code be able to also utilize the office.js code and functions like
Word.run(function (context) {
var thisDocument = context.document;
var range = thisDocument.getSelection();
range.insertText('"Hitch your wagon to a star."\n', Word.InsertLocation.replace);
//...
}
Does anyone see a way of getting this to work?
My initial guess was that the OfficeJS-taskpane-add-ins in Word on-prem use some some similar methode as above with a class derived from WebBrowser and the appropriate ObjectForScripting. This would then suggest that there must be a (hopefully accessible) class which is assigned to the ObjectForScripting-property handling the function calls from office.js. Then I could proxy this ObjectForScripting-class and add my own functions like 'DoFancyStuffToMyDocument()'.
I maintain a large legacy ASP.NET MVC application, which was recently converted to .Net Core.
I need to introduce cache busting for our JavaScript and CSS files. I appreciate this can easily be done using the asp-append-version="true" attribute on the new .Net Core script tag helper.
However, my application has script tags in over a 100 places. Adding the attribute in all those places will touch large numbers of pages, which means a lot of regression testing.
Is there a way to create a new script tag helper that inherits from the .Net Core script tag helper, and that always has the asp-append-version="true" attribute? That will give me cache busting without having to update lots of files.
...create a new script tag helper that inherits from the .Net Core script tag helper, and that always has the asp-append-version="true" attribute?
Code (View on GitHub)
using System.Linq;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Caching.Memory;
namespace AspNetCoreScriptTagHelperOverride
{
[HtmlTargetElement("script")] // A
public class MyScriptTagHelper : ScriptTagHelper
{
public MyScriptTagHelper(
IHostingEnvironment env,
IMemoryCache cache,
HtmlEncoder html,
JavaScriptEncoder js,
IUrlHelperFactory url) : base(env, cache, html, js, url) { } // B
public override void Process(TagHelperContext context, TagHelperOutput output)
{
const string appendVersion = "asp-append-version";
if (!context.AllAttributes.Any(a => a.Name == appendVersion))
{
var attributes = new TagHelperAttributeList(context.AllAttributes);
attributes.Add(appendVersion, true);
context = new TagHelperContext(attributes, context.Items, context.UniqueId);
} // E
base.AppendVersion = true; // C
base.Process(context, output); // D
}
}
}
Explanation
A: Set the TagName to "script".
B: Implement the base constructor.
C: Hard code AppendVersion to true.
D: Call the base class's Process.
E: Overcome AttributeMatcher.TryDetermineMode
Usage
In _ViewImports.cshtml remove the existing tag helper and add your override.
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.ScriptTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
#addTagHelper *, AspNetCoreScriptTagHelperOverride
Be sure to use the name of your assembly.
Once that is done, your code will execute wherever there is a script tag helper. For instance, both of the following will have AppendVersion set to true.
<script src="~/js/site.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
<script src="~/js/site.js" asp-append-version="false"></script>
This will be the the resultant HTML:
<script src="/js/site.js?v=4q1jwFhaPaZgr8WAUSrux6hAuh0XDg9kPS3xIVq36I0"></script>
See Also
https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.TagHelpers/ScriptTagHelper.cs
How to minify JavaScript inside a view page's script block with minimal effort?
I have some page specific scripts that would like to put on specific view pages. But the ASP.NET MVC4 bundling and minification only works with script files, not script code inside a view page.
UPDATE
I took Sohnee's advice to extract the scripts into files. But I need to use them on specific pages so what I end up doing is:
on layout page, i created an optional section for page specific javascript block:
#RenderSection("js", required: false)
</body>
then in the view page, let's say Index.cshtml, i render the script section like such:
#section js{
#Scripts.Render("~/bundles/js/" + Path.GetFileNameWithoutExtension(this.VirtualPath))
}
as you can see, it assumes the javascript filename (index.js) is the same as the view page name (index.cshtml). then in the bundle config, i have:
var jsFiles = Directory.GetFiles(HttpContext.Current.Server.MapPath("Scripts/Pages"), "*.js");
foreach (var jsFile in jsFiles)
{
var bundleName = Path.GetFileNameWithoutExtension(jsFile);
bundles.Add(new ScriptBundle("~/bundles/js/" + bundleName).Include(
"~/Scripts/pages/" + Path.GetFileName(jsFile)));
}
then, if you are on index page, the HTML output will be:
<script src="/bundles/js/Index?v=ydlmxiUb9gTRm508o0SaIcc8LJwGpVk-V9iUQwxZGCg1"></script>
</body>
and if you are on products page, the HTML output will be:
<script src="/bundles/js/Products?v=ydlmxiUb9gTRm508o0SaIcc8LJwGpVk-V9iUQwxZGCg1"></script>
</body>
You can minify inline scripts using this HTML helper
using Microsoft.Ajax.Utilities;
using System;
namespace System.Web.Mvc
{
public class HtmlHelperExtensions
{
public static MvcHtmlString JsMinify(
this HtmlHelper helper, Func<object, object> markup)
{
string notMinifiedJs =
markup.Invoke(helper.ViewContext)?.ToString() ?? "";
var minifier = new Minifier();
var minifiedJs = minifier.MinifyJavaScript(notMinifiedJs, new CodeSettings
{
EvalTreatment = EvalTreatment.MakeImmediateSafe,
PreserveImportantComments = false
});
return new MvcHtmlString(minifiedJs);
}
}
}
And inside your Razor View use it like this
<script type="text/javascript">
#Html.JsMinify(#<text>
window.Yk = window.Yk || {};
Yk.__load = [];
window.$ = function (f) {
Yk.__load.push(f);
}
</text>)
</script>
If you use System.Web.Optimization than all necessary dlls are already referenced otherwise you can install WebGrease NuGet package.
Some additional details available here: http://www.cleansoft.lv/minify-inline-javascript-in-asp-net-mvc-with-webgrease/
EDIT:
Replaced DynamicInvoke() with Invoke(). No need for runtime checks here, Invoke is much faster than DynamicInvoke. Added .? to check for possible null.
The way to do this with minimal effort is to extract it into a script file. Then you can use bundling and minification just as you want.
If you want to minify it inline, it will be a much greater effort than simply moving the script off-page.
Based on #samfromlv's answer, I created an extension to handle CSS as well. It also takes BundleTable.EnableOptimizations into consideration.
OptimizationExtensions.cs
Adding in an answer for ASP.NET MVC Core. The solution I used to minify inline JS and razor generated html was WebMarkupMin.
It ultimately boiled down to adding these two minuscule changes to my project:
public void Configure(IApplicationBuilder app)
{
app.UseStaticFiles();
//added
app.UseWebMarkupMin();
app.UseMvc(.....
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//added
services.AddWebMarkupMin(
options =>
{
//i comment these two lines out after testing locally
options.AllowMinificationInDevelopmentEnvironment = true;
options.AllowCompressionInDevelopmentEnvironment = true;
})
.AddHttpCompression();
}
There's a great blog post by Andrew Lock (author of ASP.NET Core in Action) about using WebMarkupMin https://andrewlock.net/html-minification-using-webmarkupmin-in-asp-net-core/ WebMarkupMin is highly configurable and Andrew's post goes way more indepth, highly recommended reading it intently before just copying and pasting.
A little late for the party, but for .NET Core you could use a TagHelper to minify the content of a script tag like this:
[HtmlTargetElement("script", Attributes = MinifyAttributeName)]
public class ScriptTagHelper : TagHelper
{
private const string MinifyAttributeName = "minify";
[HtmlAttributeName(MinifyAttributeName)]
public bool ShouldMinify { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (!ShouldMinify)
{
await base.ProcessAsync(context, output);
return;
}
var textChildContent = await output.GetChildContentAsync();
var scriptContent = textChildContent.GetContent();
// or use any other minifier here
var minifiedContent = NUglify.Uglify.Js(scriptContent).Code;
output.Content.SetHtmlContent(minifiedContent);
}
}
and then use it in your views:
<script minify="true">
...
</script>
Fenton had a great answer about this: "rather than minify inline JavaScript code, externalize the inline JavaScript code and then you can minify with any standard JavaScript minifiers / bundlers."
Here is how you externalize the JavaScript: https://webdesign.tutsplus.com/tutorials/how-to-externalize-and-minify-javascript--cms-30718
Here is my direct answer to minify the inline JavaScript code (require a bit of manual work).
Copy the inline JavaScript code snippet and paste them into a separate JavaScript file and save it, e.g. inline.js
Use esbuild to minify the inline code snippet in inline.js, see more details about minification here
esbuild --minify < inline.js > inline-minified.js
Copy the minified JavaScript code snippet in inline-minified.js and paste it back into the original HTML to replace the original code inside of the tag.
Done.