You can embed content from a post on your Higher Logic Vanilla (Vanilla) community in an external site, such as a corporate website.
Use cases
Two examples of why you might want to do this are:
- You could feature — on a dedicated page of your corporate website — product ideas that are being suggested in your Vanilla community. This would increase the exposure of the ideas and increase the likelihood of feedback.
- You could create a discussion tag called "Props!" that you, as a community manager, assign to every discussion in which one of your products or team members is praised, and then embed the most recent "Props!" posts on a dedicated page on your corporate site.
Requirements
To embed community content in an external site, you must have a some knowledge of:
- JavaScript,
- Vanilla's API v2,
- HTML, and
- CSS
You must also have a website where you can inject JavaScript, CSS, and HTML.
📝 NOTE: The demo in this article uses a WordPress site with an addon called Snippets (similar to Vanilla's Pockets addon). One Snippet each is used to:
- inject HTML and CSS where the discussions will display, Community Content HTML.
- add the JavaScript that makes the API call and transforms the output, Community Content Javascript.
Embed community content demo
This section provides a "demo" of how to embed content from your Vanilla community in an external site.
📝 NOTE: This demo calls the three most recent discussions on the demonstrator's (herein "SME" [subject-matter expert]) forum into a personal blog site (MIND = BLOWN).
Decide what content you want to embed in an external site.
⚠️ IMPORTANT: The SME's demo notes and steps are provided below in italicized text.
There is an interactive Swagger document in the Vanilla Dashboard that you can play with. I prefer to use a tool called Postman instead for two reasons: 1) it will not return content that guest users would not have access to, and 2) it automatically generates the code I need to make these calls with JavaScript. Check out our API v2 Reference & Endpoints for all the filters and parameters you can use to refine the request you will use to retrieve the content from the community. For this POC, I simply got the three most recent discussions (GET /api/v2/discussions?limit=3), but I could have been more restrictive. I could have gotten the most recent discussions from, say, the ideas category (GET /api/v2/discussions?limit=3&categoryID=9), or the questions that have been answered (/api/v2/discussions?limit=3&status=answered). You get the idea.
Now I can copy this code snippet to my WordPress site and adapt it, process the data, and use it to generate HTML.
I make sure that I have some way to surface any errors. This is just a POC, so I have kept it simple. I'm sure it could be a lot more robust. In my example, I only pull out the title, body, and URL of the post but I could also add the date, who posted it, etc.
Now let's see what happens when I deploy and visit the page of my blog site:
Fail. But there's a reason. Notice the Status (column, in the lower portion of the image) says CORS. That is because when you call content from one domain to another in a browser, the source domain has to give permissions to publish or use that content on another domain. (The concept is known as Cross-Origin Resource Sharing, and you can look it up or read about it here.). Vanilla has a solution. Get the domain of the site that you are embedding the content into and put it in the Vanilla Dashboard > Settings > Security > Trusted Domains.
Now let's see what happens:
Voila!
Small note: all the content you have called in has to be content that would be visible on your forum to any guest user. We are not passing any kind of tokens in this request. So if your community is a closed/private community, this will not work. Or if you were to call discussions only from a category that has special privileges (for example, an employees-only category) you would get a permissions error:
JavaScript used for this demo
/**
Create a single Discussion HTML Element.@param discussion JSON obj. One Discussion from GET Discussions request.@return HTML Element of a Discussion to be appended to the document.
**/
const displayADiscussion = (discussion) => {
console.log('A discussion', {
"title": discussion.name,
"body": discussion.body,
"url": discussion.url
});
const discussionWrapper = document.createElement('div');
discussionWrapper.classList.add('discussionWrapper');
discussionWrapper.innerHTML = `<h4><a href = "${discussion.url}" target="_blank">${discussion.name}</a></h4>${discussion.body}`;
return discussionWrapper;
};
/**
Inject all the Discussions returned into an existing HTML Element with the ID #forumContent.@param result JSON obj. Return of GET Discussions request.@return
**/
const displayCommunityContent = (result) => {
const contentContainer = document.getElementById('forumContent');
if (!contentContainer) {
console.warn("Failed to inject Discussions, could not find #forumContent");
}
if (result?.status) { // If the status is returned, there was an error (permissions, cors, etc.).
const errorMsg = document.createElement('div');
errorMsg.innerHTML = <div><h4>${result.message}(${result.status})</h4>${result.description}</div>;
contentContainer.appendChild(errorMsg);
return;
}
// else, loop through the discussions and add HTML Elements of each discussion to #forumContent.
[...result].forEach((discussion) => {
var aDiscussion = displayADiscussion(discussion);
contentContainer.appendChild(aDiscussion);
});
};
const myHeaders = new Headers();
const requestOptions = {
method: "GET",
headers: myHeaders,
redirect: "follow"
};
const requestURL = "https://dev.vanilla.localhost/api/v2/discussions?limit=3";
fetch(requestURL, requestOptions)
.then((response) => response.json())
.then((result) => {
displayCommunityContent(result);
})
.catch((error) => {
console.warn(Failed to retrieve ${requestURL}. Error message:, error);
console.trace();
});
HTML used for this demo
<style>
.forumContentWrapper h3 {
position: relative;
top: -47px;
text-align: right;
background-color: #eee;
display: inline;
padding: 0 18px;
margin: auto;
border-radius: 6px;
color: orangered;
}
.forumContentWrapper {
background-color: #eee;
border-radius: 6px;
padding: 20px;
margin-top: 50px;
}
div#forumContent {
position: relative;
top: -27px;
}
.discussionWrapper h4 {
white-space: nowrap;
overflow-x: hidden;
font-size: 24px;
}
.discussionWrapper {
margin-bottom: 30px;
padding-bottom: 30px;
border-bottom: dotted 1px #999;
}
.discussionWrapper:last-child {
margin-bottom: 0px;
padding-bottom: 0px;
border-bottom: none;
}
</style>
<div class="forumContentWrapper">
<h3>The Buzz on the Community</h3>
<div id="forumContent"></div>
</div>
You could get creative by adding a loading message, more data in each post (author, date, etc.).
Embed community content in a gated community
The SME was asked whether it was possible to embed community content in a gated community. The following solution was offered.
Here's how you might do it:
- Create a special role, let's call it the Widget Role, with very narrow privileges (i.e., sign in, add tokens, and read discussions — no more. No profile edit.).
- Create a user, let's call the user "UserWidget", and assign that user the Widget Role.
- Create an access token for UserWidget. In order to do this you will have to:
- Temporarily give UserWidget an additional role so that UserWidget can have Edit Profile access.
- Log in as UserWidget and create a token (Profile > Edit Profile > Access Token).
- Keep a copy of that token.
- Sign out as UserWidget.
- Sign in as an Administrator and un-assign the additional role you gave to UserWidget.
You want UserWidget to have the bare minimum of permissions.
Now you can use UserWidget's token in the above JavaScript fetch call by adding the Authorization header:
const myHeaders = new Headers();
myHeaders.append('Authorization', 'Bearer [UserWidgetsAccessToken]');
A few words of caution.
- Understand that this access token will be visible to anyone. That's why you want that token to have the minimum of permissions.
- With this token someone can see any discussion. You might want to restrict it to one specific category in the Role permissions.
- You might periodically follow the steps above to change UserWidget's access token.