Merging/aggregating disqus - merge

Odd question - Would it be possible to merge two disqus widgets so they aggregate comments from between two sources?
So say if you have two identical articles, A and B, on two separate websites that both have disqus. Is there anyway so that when person X comments on article A and person Y comments on article B, that the disqus thread of both A and B will include the comments from X and Y?

Definitely, this is basic usage of a 'disqus_identifier' which is documented here: http://help.disqus.com/customer/portal/articles/472099
The one caveat here is that a unique thread on Disqus can only have one link-to URL (which is what we use for Discovery, notification alerts, etc.), so you have to pick the preferred website for that.
If we assume that you have two websites at http://mycoolwebsite.com/ and http://example.com/, and "mycoolwebsite.com" is the preferred link-to site, you would use the same exact embed code on both sites. Here's an example:
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: THIS CODE IS ONLY AN EXAMPLE * * */
var disqus_shortname = 'example'; // Make sure you use the same shortname on both sites
var disqus_identifier = 'some_unique_identifier_for_your_discussion';
var disqus_title = 'The Title of the article';
var disqus_url = 'http://mycoolwebsite.com/2013/a-cool-article/'; // Make sure this is the same, even on example.com
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
The other caveat is to make sure that if you've set trusted (whitelisted) domains in your Disqus site settings, to make sure both domains are included.

Related

Select a random node on every reload

I want to select a random node on every reload. My fusion file looks like this:
randomInt = ${Math.randomInt(0, q(node).children(Neos.Neos:Document).count()}
randomNode = ${q(node).children(Neos.Neos:Document).get(this.randomInt)}
Unfortunately the result is stored in the cache. That means that only after the cache get flushed a new node will be returned. How can I prevent this? I have already experimented with the cache rules a little bit, but I didn't come up with a solution yet.
The element that I want to use is on every page. That's why something like the unchached mode would be a really bad idea.
In my situation the output is only a array of strings. So I did following in my Fusion.
Generate "almost" a Array in Fusion
allMyStrings = Neos.Fusion:Loop {
items = ${q(node).children(Neos.Neos:Document).get()}
itemName = 'node'
itemRenderer = ${"'" + q(node).property('testString') + "'"}
#glue = ','
}
Pick a random array in JS
<p id='replaceMe'></p>
<script>
var quoteArray = [{allMyStrings -> f:format.raw()}]
var randomIndex = Math.floor(Math.random() * quoteArray.length);
var randomElement = quoteArray[randomIndex];
document.getElementById('replaceMe').outerHTML= '<p>' + randomElement + '</p>';
</script>
A bit hacky but it works and it don't harm the performance of the website

MailChimp Campaign Content Update

MailChimp campaign content docs - https://developer.mailchimp.com/documentation/mailchimp/reference/campaigns/content
I'm trying to replace some placeholders in a campaign content with actual values via the API. At first, I thought there might be some syntax errors or internal logic errors like non-unique mc:edits into a mc:repeatable that would get the HTML refused/declined by MailChimp, hence the update not taking place, however, that was not the case.
Tried replacing html with a simple <p>test</p> and it was still not working.
Here are a couple of local logs, I'll use xyz as my campaign id:
2018-02-26 16:26:13 [::1][9804][-][warning][application] calling GET /campaigns/xyz/content []
2018-02-26 16:26:13 [::1][9804][-][warning][application] got both plain_text and html versions of content
2018-02-26 16:26:13 [::1][9804][-][warning][application] calling PUT /campaigns/xyz/content {"html":"<p>test</p>"}
2018-02-26 16:26:14 [::1][9804][-][warning][application] got response [
'plain_text' => 'test' + other MailChimp stuff such as footer, that were appended automatically by MailChimp,
'html' => '<p>test</p>'
]
// calling GET immediately after PUT in order to see if any update occurred
2018-02-26 16:26:14 [::1][9804][-][warning][application] calling GET /campaigns/xyz/content []
2018-02-26 16:26:14 [::1][9804][-][warning][application] got updated html (my "test" paragraph + auto footer from MailChimp) and proper plain_text
Everything looks fine according to these, that means both versions updated as they were supposed to. However, on the next API/MailChimp dashboard request, it displays the old HTML content, preserving the update I've just made in the plain text version only.
No errors, nothing to look into. It could be any internal MailChimp behaviour.
PS: I know about Setting Mailchimp campaign content html not working or MailChimp API v3 campaign content template sections, but none of the answers provided to those are helpful.
PS2: I know I should contact MailChimp, but according to
Our MailChimp Support Team isn't trained at in-depth API troubleshooting. If you need a developer to help you configure something using the API, check out our great Experts Directory, which lists third-party MailChimp experts who can be hired to help out.
they don't provide support for API troubleshooting.
MailChimp doesn't allow updating the campaign's HTML content because the campaign type is based on a template.
In order to update the HTML content, the campaign has to be set to custom HTML instead of a template. You can check the type by sending a GET API request to /campaigns or /campaigns/{campaign_id} and finding the content_type attribute in the response (documentation).
Alternatively, in the dashboard, the type can be determined by editing the design of the email. Anything under 'Code your own' is HTML and templates are of course templates.
I'm not entirely sure why the first response to a PUT request on a template campaign shows the updated content, but changing the content type should let you update as you want to.
Hope this helps!
If anyone's still looking for an answer to this.
I managed to solve the issue several weeks ago without creating the campaign via API, but actually updating it.
I used placeholders like [product_card id=123], 3 cards per block/row, all repeatable, which are wrapped in a class that I named product-card. In the MailChimp dashboard, you may still see the placeholders, but on preview and any form of preview like thumbnail, it will display correctly.
On the server, I crawl through the campaign's content, "detect" section names based on how they seemed to me in MailChimp and update each section with the content that I want.
PHP snippet below, some Yii2 stuff, but mostly plain PHP. I use $preview to display a preview of how the template would look, I know it's not visible in the function.
/**
* #param $id - Id of the campaign
* #param $s - Whether to save or just preview
*
* #return bool
*/
function changeContent($id, $s = false)
{
$mcCampaign = new McCampaign();
$mcCampaign::$itemId = $id;
$content = $this->api->get("/campaigns/{$id}/content");
if (!isset($content['html'])) return false;
$template = $content['html'];
$forgedDom = new \DOMDocument();
$forgedDom->loadHTML($template);
$mcSections = [];
$finder = new \DOMXPath($forgedDom);
$nodes = $finder->query('//td[contains(#class, "product-card")]');
// disabling this shit in order to avoid strict errors
libxml_use_internal_errors(true);
$mcEditId = 1;
$mcEditIndex = 0;
foreach ($nodes as $key => $node) {
/** #var \DOMElement $node */
$textContent = $node->textContent;
if (!preg_match("#\[product_card id=\d+\]#", $textContent)) continue;
$productId = filter_var($textContent, FILTER_SANITIZE_NUMBER_INT);
$node->textContent = false;
$product = Product::findOne($productId);
$productDetails = $product ? $this->renderPartial('/partials/_mc-product', [
'product' => $product
]) : 'Product not found.';
if ($key != 0) {
if ($key % 3 == 0) {
$mcEditId = 1;
$mcEditIndex++;
} else {
$mcEditId++;
}
}
$mcSections["repeat_1:{$mcEditIndex}:product_card_{$mcEditId}"] = $productDetails;
$fragment = $forgedDom->createDocumentFragment();
$fragment->appendXML($productDetails);
$node->appendChild($fragment);
}
libxml_use_internal_errors(false);
$preview = $forgedDom->saveHTML();
// just in case
/* $preview = str_replace(["\n", "\t", "\r"], "", $preview); */
if ($s) {
if (!empty($mcSections)) {
$res = $this->api->put("/campaigns/{$id}/content", [
'template' => [
'id' => *template_id_here*,
'sections' => $mcSections
],
]);
// forcing Mc to rebuild cache
$this->api->get("/campaigns/{$id}/content");
Yii::$app->session->setFlash('success', 'Done.');
return $this->redirect(['campaign/index']);
} else {
Yii::$app->session->setFlash('error', 'Something went wrong.');
}
}
}

Prefill current date in Google Form with Google Apps Script

I have a Google Form to a spreadsheet and I need a date field pre-filled with the current date.
Something like an "onOpen trigger", which updates the date field or a date field with now().
Is this possible in Google Apps Script?
In your case, some of the points of using the Form remain unclear. Suppose that you have the ability to edit a link to the Form or your users agree to add the bookmarklet to their browser.
Common code
let id='1FAIpQLScx-1H1moCzBfkTEOZnVgBScbJeHZ4YE5E6IY2mNZvMuVcOXA';
window.open(
`https://docs.google.com/forms/d/e/${id}/viewform?usp=pp_url&entry.1000000=`+
(new Date()).toISOString().split('T')[0]
);
Bookmarklet
Just add the next string to the bookmarks bar
javascript:let id = '1FAIpQLScx-1H1moCzBfkTEOZnVgBScbJeHZ4YE5E6IY2mNZvMuVcOXA'; window.open(`https://docs.google.com/forms/d/e/${id}/viewform?usp=pp_url&entry.1000000=` + (new Date()).toISOString().split('T')[0]);
I put a static HTML page on S3 with a few line of Javascript for redirecting. See https://jsfiddle.net/barryku/3etd8qf0/
var formId = "1FAIpQLSc_acGSzp2VeJIyG0tNJwqcNdUPOweLROGenY0Fe56I635VGQ";
var dateField = "entry.1608430301";
var formsUrl = "https://docs.google.com/forms/d/e/"+ formId + "/viewform?usp=pp_url&" + dateField + "=$$today&entry.747437339=Yes";
var today = new Date();
var redirectUrl = formsUrl.replace("$$today", new Date(today.getTime() - (today.getTimezoneOffset() * 60000)).toISOString().split('T')[0]);
window.location.replace(redirectUrl);

How to add conditional elements in data-sly-list?

I currently have a data-sly-list that populates a JS array like this:
var infoWindowContent = [
<div data-sly-use.ed="Foo"
data-sly-list="${ed.allassets}"
data-sly-unwrap>
['<div class="info_content">' +
'<h3>${item.assettitle # context='unsafe'}</h3> ' +
'<p>${item.assettext # context='unsafe'} </p>' + '</div>'],
</div>
];
I need to add some logic into this array. If the assetFormat property is 'text/html' only then I want to print the <p> tag. If the assetFormat property is image/png then I want to print img tag.
I'm aiming for something like this. Is this possible to achieve?
var infoWindowContent = [
<div data-sly-use.ed="Foo"
data-sly-list="${ed.allassets}"
data-sly-unwrap>
['<div class="info_content">' +
'<h3>${item.assettitle # context='unsafe'}</h3> ' +
if (assetFormat == "image/png")
'<img src="${item.assetImgLink}</img>'
else if (assetFormat == "text/html")
'<p>${item.assettext # context='unsafe'}</p>'
+ '</div>'],
</div>
];
To answer your question quickly, yes you can have a condition (with data-sly-test) in your list as follows:
<div data-sly-list="${ed.allAssets}">
<h3>${item.assettitle # context='html'}</h3>
<img data-sly-test="${item.assetFormat == 'image/png'}" src="${item.assetImgLink}"/>
<p data-sly-test="${item.assetFormat == 'text/html'}">${item. assetText # context='html'}"</p>
</div>
But looking at what you're attempting to do, basically rendering that on the client-side rather than on the server, let me get a step back to find a better solution than using Sightly to generate JS code.
A few rules of thumb for writing good Sightly templates:
Try not to mix HTML, JS and CSS in the template: Sightly is on
purpose limited to HTML and therefore very poor to output JS or CSS.
The logic for generating a JS object should therefore be done in the
Use-API, by using some convenience APIs that are made fore that, like
JSONWriter.
Also avoid as much as possible any #context='unsafe', unless you filter that string somehow yourself. Each string that is not
escaped or filtered could be used in an XSS attack. This
is the case even if only AEM authors could have entered that string,
because they can be victim of an attack too. To be secure, a system
shouldn't hope for none of their users to get hacked. If you want to allow some HTML, use #context='html' instead.
A good way to pass information to JS is usually to use a data attribute.
<div class="info-window"
data-sly-use.foo="Foo"
data-content="${foo.jsonContent}"></div>
For the markup that was in your JS, I'd rather move that to the client-side JS, so that the corresponding Foo.java logic only builds the JSON content, without any markup inside.
package apps.MYSITE.components.MYCOMPONENT;
import com.adobe.cq.sightly.WCMUsePojo;
import org.apache.sling.commons.json.io.JSONStringer;
import com.adobe.granite.xss.XSSAPI;
public class Foo extends WCMUsePojo {
private JSONStringer content;
#Override
public void activate() throws Exception {
XSSAPI xssAPI = getSlingScriptHelper().getService(XSSAPI.class);
content = new JSONStringer();
content.array();
// Your code here to iterate over all assets
for (int i = 1; i <= 3; i++) {
content
.object()
.key("title")
// Your code here to get the title - notice the filterHTML that protects from harmful HTML
.value(xssAPI.filterHTML("title <span>" + i + "</span>"));
// Your code here to detect the media type
if ("text/html".equals("image/png")) {
content
.key("img")
// Your code here to get the asset URL - notice the getValidHref that protects from harmful URLs
.value(xssAPI.getValidHref("/content/dam/geometrixx/icons/diamond.png?i=" + i));
} else {
content
.key("text")
// Your code here to get the text - notice the filterHTML that protects from harmful HTML
.value(xssAPI.filterHTML("text <span>" + i + "</span>"));
}
content.endObject();
}
content.endArray();
}
public String getJsonContent() {
return content.toString();
}
}
A client-side JS located in a corresponding client library would then pick-up the data attribute and write the corresponding markup. Obviously, avoid inlining that JS into the HTML, or we'd be mixing again things that should be kept separated.
jQuery(function($) {
$('.info-window').each(function () {
var infoWindow = $(this);
var infoWindowHtml = '';
$.each(infoWindow.data('content'), function(i, content) {
infoWindowHtml += '<div class="info_content">';
infoWindowHtml += '<h3>' + content.title + '</h3>';
if (content.img) {
infoWindowHtml += '<img alt="' + content.img + '">';
}
if (content.text) {
infoWindowHtml += '<p>' + content.title + '</p>';
}
infoWindowHtml += '</div>';
});
infoWindow.html(infoWindowHtml);
});
});
That way, we moved the full logic of that info window to the client-side, and if it became more complex, we could use some client-side template system, like Handlebars. The server Java code needs to know nothing of the markup and simply outputs the required JSON data, and the Sightly template takes care of outputting the server-side rendered markup only.
Looking the at the example here, I would put this logic inside a JS USe-api to populate this Array.

How to overlay a cq widget so it is also available in SiteAdmin

The CQ.tagging.TagInputField provided two configuration parameter which won't work in combination:
tagsBasePath
namespaces
Using the OOTB facebook tags as example, I want to restric the dialog to only display the Favorite Teams. The Structure is this:
So I set tagBasePath to /etc/tags/facebook and namespaces to [favorite_teams]. This does what it is supposed to do and only shows the two teams in the dialog. But when you click on it, a JavaScript exceptions is thrown. The problem lies in the following method defined in /libs/cq/tagging/widgets/source/CQ.tagging.js
CQ.tagging.parseTag = function(tag, isPath) {
var tagInfo = {
namespace: null,
local: tag,
getTagID: function() {
return this.namespace + ":" + this.local;
}
};
// parse tag pattern: namespace:local
var colonPos = tag.indexOf(isPath ? '/' : ':');
if (colonPos > 0) {
// the first colon ":" delimits a namespace
// don't forget to trim the strings (in case of title paths)
tagInfo.namespace = tag.substring(0, colonPos).trim();
tagInfo.local = tag.substring(colonPos + 1).trim();
}
return tagInfo;
};
It does not respect the configurations set on the widget and returns a tagInfo where the namespace is null. I then overlayed the method in my authoring JavaScripts, but this is of course not working in the SiteAdmin as my custom JS are not included.
So, do I really have to overwrite the CQ.tagging.js below libs or can I somehow inject my overlay into the SiteAdmin so the PageProperties Dialog opened from there works as well?
UPDATE: I had a chat with Adobe support regarding this and it was pointed out that if you use tagsBasePath you need to place it somewhere else than below /etc/tags. But this won't work as well as the TagListServlet will return no tags as /etc/tags is also fixed in the TagManager as the tagsBasePath. I now overwrite the above mentioned js at its location, being well aware that I need to check it if we install a hotfix or an update. Is someone has a more elegant solution I'd be still thankful.