React - Parse HTML string and insert components at particular places within it - javascript

I'm working on an app for a client who wants to display Zendesk Support article data with richer content than the API/tool supports. The API, powered by a basic WYSIWYG editor on Zendesk, is able to serve up an HTML blob with <p> and <img> and <a> tags. But it's unable to, say, easily embed a video, show an image gallery, or link to another location in the app without necessitating a hard refresh.
So we decided to build out a small amount of DSL syntax whereby the client could type something that would be parsed on our app and turned into the richer content. For example:
[[ video src="youtube.com/whatever" ]]
might, on our app, be parsed out, and replaced with an iframe embedding that video directly in the article.
I've got the above example working just fine, using all sorts of fun/problematic RegExp magic.
I'm aware that parsing HTML with RegExp is a major anti-pattern, and I'm getting around it by not really parsing HTML per se -- I'm just looking for [[...]]).
Basically, I parse each code out, determine what to replace it with, pass back that component, and then call ReactDOMServer.renderToString(replacementEl) to insert the resulting HTML into the article HTML string, which is then rendered to the page using dangerouslySetInnerHtml.
This is working great, despite all the obvious messiness issues. (I know, but the client wants a very feature-rich solution, and wants to use the Zendesk API).
Here's the problem: This solution only works when rendering HTML. It doesn't work when I try to render, for example, a <Link> (from React Router) or a home-made <Gallery /> component.
So I'm trying to figure out if there's a good way to do this. One that ideally doesn't involve going full RegExp on the HTML blob (which is obviously a mess).
I'm guessing that the solution will involve removing the codes, converting the code-free HTML string into actual React components (as opposed to just spitting the string into another element), and then, based on some sort of ledger of where the short codes go in this new React component, inserting the contents into the page.
But I'd appreciate some suggestions if anyone has encountered a similar problem. I want to do this in a way that isn't totally crazy and brittle, though it's obviously an inherently messy problem.
EDIT: To clarify, when I say it doesn't work, what I mean is that the JavaScript associated with the React components doesn't run. If I replace a code with a <Link to="whatever" />, for example, ReactDOMServer.renderToString successfully turns that component into its final HTML (an <a> tag), but nothing happens on click of that tag, because the JavaScript that's actually changing browserHistory on a regular click just isn't run.
So maybe the more pointed question is:
Is there a way to insert React Components into an HTML string in such a way that they retain their JavaScript functionality and don't just become HTML?

Related

PHP and React.js - Best way to store a <svg> element. And read/return/fetch for both languages

Background information##
I'm working on a Wordpress Gutenberg project. And as you might know, Gutenberg uses React on the backend. But PHP on the frontend. (if can't store it pre-rendered.)
First I stored the <svg> HTML elements inside the post content. But after 3 SVG images, it gets too big to store for MySQL. So, now I can't store the Post pre-rendered in the backend I just have to get it dynamically each page load with PHP.
The question
Moving on the question itself. Is there a way to store an HTML <svg> element (with CSS animations) to an external file that both React and PHP can load/fetch?
Thoughts
at first (with react) I had stored it in a .jsxfiletype. Exported it as a const returning a multi-line string.
I thought of placing it within a .JSON filetype. But then I would have to JSON.stringify() it first to escape the characters. But that would result in a human unfriendly way to edit it.
Saving it an SVG won't help, since it's animated.
I could translate all JSX constants containing all the svgs to a PHP function. But that would make it less flexible if I decide to go back to React.
Any help or pointers are very welcome

Get data from another HTML page

I am making an on-line shop for selling magazines, and I need to show the image of the magazine. For that, I would like to show the same image that is shown in the website of the company that distributes the magazines.
For that, it would be easy with an absolute path, like this:
<img src="http://www.remotewebsite.com/image.jpg" />
But, it is not possible in my case, because the name of the image changes everytime there is a new magazine.
In Javascript, it is possible to get the path of an image with this code:
var strImage = document.getElementById('Image').src;
But, is it possible to use something similar to get the path of an image if it is in another HTML page?
Assuming that you know how to find the correct image in the magazine website's DOM (otherwise, forget it):
the magazine website must explicitly allow clients showing your website to fetch their content by enabling CORS
you fetch their HTML -> gets you a stream of text
parse it with DOMParser -> gets you a Document
using your knowledge or their layout (or good heuristics, if you're feeling lucky), use regular DOM navigation to find the image and get its src attribute
I'm not going to detail any of those steps (there are already lots of SO answers around), especially since you haven't described a specific issue you may have with the technical part.
You can, but it is inefficient. You would have to do a request to load all the HTML of that other page and then in that HTML find the image you are looking for.
It can be achieved (using XMLHttpRequest or fetch), but I would maybe try to find a more efficient way.
What you are asking for is technically possible, and other answers have already gone into the details about how you could accomplish this.
What I'd like to go over in this answer is how you probably should architect this given the requirements that you described. Keep in mind that what I am describing is one way to do this, there are certainly other correct methods as well.
Create a database on the server where your app will live. A simple MySQL DB will work, but you could use anything. Create a table called magazine, with a column url. Your code would pull the url from this DB. Whenever the magazine URL changes, just update the DB and the code itself won't need to be changed.
Your front-end code needs some sort of way to access the DB. One possible solution is a REST API. This code would query the DB for the latest values (in your case magazine URLs), and make them accessible to your web page. This could be done in a myriad of different languages/frameworks, here's a good tutorial on doing something like this in Node.js and express (which is what I'd personally use).
Finally, your front-end code needs to call your REST API to get the updated URLs. This needs to be done with some kind of JavaScript based language. jQuery would make this really easy, something like this:
$(document).ready(function() {
$.Get("http://uri_to_your_rest_api", function(data) {
$("#myImage").attr("scr", data.url);
}
});
Assuming you had HTML like this:
<img id="myImage" src="">
And there you go - You have a webpage that pulls the image sources dynamically from your database.
Now if you're just dipping your toes into web development, this may seem a bit overwhelming. But I promise you, in the long run it'll be easier then trying to parse code from an HTML page :)

loading a external content so that searchable by Google for SEO purposes

I'm working on a project where we'd like to load external content onto a customers site. The main requirements are that we'd like the customer to have as simple of an include as possible (like a one-line link similar to Doubleclick) and would preferably not have to be involved in any server-side language. The two proposed ways of doing this were an iframe or loading a javascript file that document.write's out the content.
We looked more at the latter since it seemed to produce more reliable legibility and simplicity for the end user - a single line of Javascript. We have been hit with the reality that this will be indexed unpredictably by Google. I have read most of the posts on this topic regarding javascript and indexing (for example http://www.seroundtable.com/google-ajax-execute-15169.html, https://twitter.com/mattcutts/status/131425949597179904). Currenlty we have (for example):
<html>
<body>
<div class='main-container'>
<script src='http://www.other.com/page.js'></script>
</div>
</body>
</html>
and
// at http://www.other.com/page.js
document.write('blue fish and green grass');
but it looks like google indexes this type of content only sometimes based upon 'Fetch As Google' used in Google's webmaster tools. Since it does sometimes work, I know it's possible for this indexing to be ok. More specifically, if we isolate our content to something like the above and remove extraneous content, it will index it each time (as opposed to the EXACT SAME Javascript in a regular customer html page). If we have our content in a customer's html file it doesn't seem to get indexed.
What would be a better option to ensure that Google has indexed the content (remote isn't any better)? Ideas I have tried / come across would be to load a remote file in for example PHP, something like:
echo file_get_contents('http://www.other.com/page');
This is obviously blocking but possibly not a deal-breaker.
Given the above requirements, would there be any other solution?
thx
This is a common problem and I've created a JS plugin that you can use to solve this.
Url: https://github.com/kubrickology/Logical-escaped_fragment
Make sure to use the: __init() function instead of standard DOM ready functions and you know for sure that Google is able to index.

Angular $sce vs HTML in external locale files

A question regarding ng-bind-html whilst upgrading an Angular app from 1.0.8 to 1.2.8:
I have locale strings stored in files named en_GB.json, fr_FR.json, etc. So far, I have allowed the use of HTML within the locale strings to allow the team writing the localized content to apply basic styling or adding inline anchor tags. This would result in the following example JSON:
{
"changesLater": "<strong>Don't forget</strong> that you can always make changes later."
"errorEmailExists": "That email address already exists, please sign in to continue."
}
When using these strings with ng-bind-html="myStr", I understand that I now need to use $sce.trustAsHtml(myStr). I could even write a filter as suggested in this StackOverflow answer which would result in using ng-bind-html="myStr | unsafe".
Questions:
By doing something like this, is my app now insecure? And if so, how might an attacker exploit this?
I can understand potential exploits if the source of the displayed HTML string was a user (ie. blog post-style comments that will be displayed to other users), but would my app really be at risk if I'm only displaying HTML from a JSON file hosted on the same domain?
Is there any other way I should be looking to achieve the marking-up of externally loaded content strings in an angular app?
You are not making your app any less secure. You were already inserting HTML in your page with the old method of ng-bind-html-unsafe. You are still doing the same thing, except now you have to explicitly trust the source of the HTML rather than just specifying that part of your template can output raw HTML. Requiring the use of $sce makes it harder to accidentally accept raw HTML from an untrusted source - in the old method where you only declared the trust in the template, bad input might make its way into your model in ways you didn't think of.
If the content comes from your domain, or a domain you control, then you're safe - at least as safe as you can be. If someone is somehow able to highjack the payload of a response from your own domain, then your security is already all manner of screwed. Note, however, you should definitely not ever call $sce.trustAsHtml on content that comes from a domain that isn't yours.
Apart from maintainability concerns, I don't see anything wrong with the way you're doing it. Having a ton of HTML live in a JSON file is maybe not ideal, but as long as the markup is reasonably semantic and not too dense, I think it's fine. If the markup becomes significantly more complex, I'd consider splitting it into separate angular template files or directives as needed, rather than trying to manage a bunch of markup wrapped in JSON strings.

releasing content by role within a D2L topic

I'm trying to release content by role within a topic in D2L's LMS. Is this possible using Javascript? Something like, "if {RoleName}=Student, then display this, else display that"...? I realize I can restrict/release content by role on a topic level, but I'm trying to do so within a topic and thus can't use release conditions. Any ideas?
You can control that functionality directly through the Content tool interface without needing to add in JavaScript. If you don't have access to that in Content, talk to your site administrator.
A roundabout way to do this would be to parse the QueryString to get the OU, then make a Valence request to find out the user role in the course. It would take a lot of work to get all the pieces connected for what seems like a really simple use case. This is the strategy I'm using for tools I've made that get embedded right in D2L pages.
If Replacement Strings worked properly then you could use a combination of them and JavaScript. But since the replacement happens at save time rather than render time in most places, they're really not usable for your scenario.
Desire2Learn Replace Strings in Content
Another option would be to create your own custom widget and put it on the course home page. Since Replacement Strings work properly in widgets, you could read the value of the {rolename} replacement string and store it in a cookie. Then, in your pages you would read the value of the cookie to create your conditionals.

Categories

Resources