Script Link in Header - EnableOptimizations Strips Attributes - dotnetnuke

Not sure if this is a bug or if 2sxc is doing this on purpose, "for reasons."
First, here is the goal. I am in a 2sxc App, in a View using Razor/C#. I need to get the following JS module linked up in the <head> like this:
<script src="//unpkg.com/#dnncommunity/dnn-elements/dist/dnn/dnn.esm.js"
type="module" async="async" defer="defer" crossorigin="anonymous">
</script>
Obviously if I just put it in as-is, it gets on the page, but not in the <head>.
If I add the attribute, data-enableoptimizations="true" then its in the <head> but all 4 of my attributes get stripped:
<script src="https://unpkg.com/#dnncommunity/dnn-elements/dist/dnn/dnn.esm.js" type="text/javascript"></script>
Additionally, notice that the src had only the leading "//unpkg...", and for some reason, the optimizations have added back "https:" which I do not want.
And where did type="text/javascript" come from? That hasn't been required for years.
I can solve this in the theme/skin by using the DnnJsInclude control like this. It uses ClientResourceManagement and allows me to specify all the attributes in HtmlAttributesAsString like this:
<dnn:DnnJsInclude
FilePath="//unpkg.com/#dnncommunity/dnn-elements/dist/esm/dnn.js"
ForceProvider="DnnPageHeaderProvider"
HtmlAttributesAsString="type:module,async:async,defer:defer,crossorigin:anonymous"
Priority="1001"
runat="server"
/>
This gets me the correct result, but it means my standard content editors now need the ability to edit page settings to use my 2sxc App. Which I do not want (the permissions or the training complication).
I realize I can probably use that DnnJsInclude control from my Razor code, but it seems like this is a valid use case that data-enableoptimizations should handle. Yes? No? Maybe? Maybe it already does it but I don't know the right syntax?
Anyone know how to get 2sxc to do this? Or is this a bug worth reporting regarding data-enableoptimizations? I was especially surprised that it just stripped my valid attributes.

So for some background: once the attribute is set, it will be picked up and taken apart, because DNN will need the parts given one by one, not as a <script> tag.
I would assume that as of now, there is no mechanism which tries to preserve the remaining bits. We haven't looked at this in a long time, and maybe the DNN APIs are missing in v7.4.2 (our minimum compatibility).
But in general: this is currently by design, and could be improved. I suggest you open an issue on github and/or contribute a change ;)

Okay, so I worked it out in code using the DnnJsInclude control. Here is the solution in a nutshell:
#using DotNetNuke.Web.Client.ClientResourceManagement
#{
// Add a <script> tag in the head as a JS module
var include = new DnnJsInclude
{
FilePath = "https://unpkg.com/#dnncommunity/dnn-elements/dist/dnn/dnn.esm.js",
ForceProvider = "DnnPageHeaderProvider",
Priority = 1001, // stay out of the way?
HtmlAttributesAsString = "type:module,async:async,defer:defer,crossorigin:anonymous",
};
var loader = (Context.CurrentHandler as Page).FindControl("ClientResourceIncludes");
if (loader != null)
{
loader.Controls.Add(include);
}
}
Which gets the exact output needed thanks to the HtmlAttributesAsString parameter.
I even wrote it up as a blog post with a lot of details.
DNN Details 004: Using the New Dnn-Elements in a 2sxc View?
Since this is dependent on Dnn, it doesn't benefit the Oqtane/hybrid coders.

Related

Correct way to get access to Dnn Stuff while using Modern 2sxc Hybrid Views

Now that my new views start with
#inherits Custom.Hybrid.Razor12
What do I need to do to continue to get access to Dnn Stuff when needed? I've been bouncing around the docs, but haven't spotted anything yet.
Are there compile codes that will let us do things like <### IF DNN ###>?
My specific need right now, and I am hoping someone can offer an example, is we always wrapped our module/View output like this
<div id="viewtype-#(Dnn.Module.ModuleID)">
</div>
And this allowed us to include some very module-targeted (non-leaky) CSS.
<style>
#viewtype-#(Dnn.Module.ModuleID) .navbar{
...
}
</style>
So then this sort of becomes two questions:
How do you do DNN specific things in a View properly?
What is the new-correct way to do the thing in the example above so that it would ALSO work on Oqtane? Do they have a Helper that would provide a unique ID at the same level as the View?
When you are editing a template, there is a menu at the left of the edit window. If you expand the <> you will see all sorts of helpers, DNN and otherwise.
And, the top icon expands to a list of all of the files/views/css that you might want to edit.
Or am I misunderstanding?
Oh, you don't have to do any of the <## xxxx ##> stuff any more. You can just refer to the DNN variables and methode (perhaps preceded by #) and just use them. After all, the template is a mixture of HTML and Razor stuff. The Razor stuff is darned close to c#.
So, you could drop something in like:
[p]
This is an example of using the portal id: #Dnn.Module.PortalID
[/p]
Note: < and > replaced by [ and ] so that they aren't interpreted as tags.

Adding Segment telemtry to React component that is only one DOM node

I'm looking to add segment analytics to my JupyterLab extension. No worries if you've never heard of a JupyterLab extension - the best way to think about it: I get control over a single node in the DOM where I can place some HTML, so I'm doing the following:
function Welcome(props) {return <h1>Hello</h1>;}
ReactDOM.render(<Welcome/>, dom_element_i_control)
This all works fine - I'm now looking to add some analytics code to this. For example, I'd like to be able to:
See when my code is rendered
See when someone interacts with my rendered element (e.g. if there was a button in the Welcome function, when the user clicked on it).
However, segment is a JS library that is delivered as a script that you load into a webpage at the top in a string tag like:
<script>
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&...}}();
</script>
Where would I even put this code? I don't have control over the larger page + HTML, so I'm not sure where I can slap this so I can start using analytics.
Thanks for any information!
My workaround:
Instead of using the above linked segment script, I used the analytics-node package from segment.
I create an Analytics object right before ReactDOM.render - and then can use it wherever I want :)
Note that this will not work for anyone who uses an add blocker, obviously!

Convert regular AdSense units into responsive ones in an easy way

I am changing the HTML design of my website, converting all the pages into responsive ones. I am also planning the conversion of the AdSense units of the pages into responsive ones, and wondered if the transformation is as easy as the following.
This is the piece of code of one of the current AdSense units:
<script type="text/javascript"><!--
google_ad_client = "ca-pub-XXXXXXXXXXXXXXX";
google_ad_slot = "YYYYYYY";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script>
<script type="text/javascript"
src="">
</script>
Would it be as easy as adding the 'data-ad-format', removing the 'google_ad_width' and 'google_ad_height', and leaving the information of 'data-ad-client' and 'data-ad-slot'?
<script async src="//"></script>
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-XXXXXXXXXXXXXXX"
data-ad-slot="YYYYYYY"
data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
I mean, I do not want to remove my current custom channels and create new ones, and wondered if they would work with a new responsive design.
Thank you very much.
Info about YYYYYYYunit (with google_ad_width = 300 and google_ad_height = 250) is stored on the AdSense side, on AdSense servers, as "size":"SIZE_300_250".
(In my understanding scenario is:
with google_ad_width = 300 and google_ad_height = 250, their
script show_ads.js (or adsbygoogle.js) can quickly reserve 300x250 space for ad unit
YYYYYYY
which will be returned by
http://googleads.g.doubleclick.net/pagead/ads as
"size":"SIZE_300_250".)
Responsive ad units must be created as "responsive". (And in that case they'll be "size":"RESPONSIVE".)
(The AdSense platform has been, and is being, continually updated and refined, and I think they are sanitizing some bad requests now. But, relying on that is potentially risky - even if you see that your ad code modification works, that doesn't mean that it is safe to use. Only a limited set of modifications is allowed in AdSense and what you described is not one of them.)
I mean, I do not want to remove my current custom channels and create
new ones, and wondered if they would work with a new responsive
design.
When you create new responsive units you'll add them to custom channels you already have. There is no need to remove existing custom channels.
(If you mean "I do not want to remove my current ad units and create new ones", then I'm afraid you don't have other choice: standard ad unit can't be "converted" to responsive.)
Disclaimer: I can't quote relevant source for what I said above about AdSense ad serving scenario. What I said is based on my ten years experience of being an AdSense publisher, and few years RS and TC on the AdSense Help Forum. On that forum you can see cases of disabled accounts and ad serving because of code modification.)
No, it's not possible. You have to create a new tag.
Hint: You can use vertical, horizontal and auto for the data-ad-format attribute (e.g. data-ad-format="auto").

Using ngSanitize to allow some html tags

I have an insecure string from the user that I want to display.
I want a few html-tags like < strong > (without spaces) to work.
All other html should be displayed like it was typed in (that is < should be replace with & lt; and so on)
I'm pretty sure I can use ngSanitize to do this but I can't figure out how.
$compileProvider allows you to set up sanitization "whitelists" for HREF and SRC URLs:
app.config(function($compileProvider) {
var imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
$compileProvider.imgSrcSanitizationWhitelist(imgSrcSanitizationWhitelist);
});
However, the whitelists for "safe" tags are hard-coded and can't be changed the same way. You can see the list here in the source:
https://github.com/angular/angular.js/blob/master/src/ngSanitize/sanitize.js#L186
There is an open request to enhance this functionality:
https://github.com/angular/angular.js/issues/5900
But it has not been completed (yet).
In the meantime, you have a few options:
"Fork" the project and adjust ngSanitize to suit your purposes. Most people don't like to "hack core" in this way, but it's the whole point of Open Source to be able to do things like this. This module doesn't change so much that it would be that hard to keep it relatively up to date as you develop your project.
Live with the list defined there. Most of the time you find that this list is actually pretty good, and it's just that IMG or A HREF tags are broken. That's not because the tag is filtered - that's because THOSE are white-listed separately, and you can use the technique above to accept specific URLs into each of those tags as "safe".
By the way, there is now a possibility.

Uncaught ReferenceError: SWFObject is not defined

It sounds like a dumb question and FAQ as well, but I truly don't see any reason not to be able to run it.
I try to run a imagerotator with SWFObject and that's the error I get. I do include properly the swfobject.js file (accessible via the direct url) and I pass the parameters through an XML file which loads independently fine as well. I do use version 2.2.
Here there is my SWF call:
<div id="slide1">Get the Flash Player to see this rotator.</div>
<div id="rotator"></div>
<script type="text/javascript">
var s1 = new SWFObject("/admin/cms/imagerotator.swf","rotator","606","199","5");
s1.addVariable("file","imagerotator.php");
s1.addParam("allowfullscreen","false");
s1.addVariable("linkfromdisplay", "false");
s1.addVariable("transition","lines");//bgfade,blocks,bubbles,circles,fade,flash,fluids,lines,random,slowfade
s1.addVariable("rotatetime","5");
s1.addVariable("overstretch","false");
s1.addVariable("backcolor","0xFFFFFF");// change if its helps to fit better in design (optional)
s1.addVariable("shuffle","false");
s1.addParam("wmode", "transparent");
s1.addVariable("showicons","false");
s1.addVariable("shownavigation","false");
s1.write("slide1");
</script>
I doubt that this usage of the SWFobject is completely inappropriate for this version, although I use a sample code of a CMS including this exact version of the library.
The implementation changed:
1) Your problem is described (and solved) here.
2) Check the original docs here.
Basically you need to use swfobject.embedSWF(...) now instead of var x = new swfobject(...)
In any case, Google is your friend on this one ;)

Resources