MailChimp Campaign Content Update - rest

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.');
}
}
}

Related

RealURL with GET parameters

We have developed a typo3 plug in that searches for trucks. For SEO reasons, we are trying to use the realURL plug in to make the URLs friendlier to use.
On the front page we have several call to actions that link to the search page with certain search parameters. An example is bellow:
/search-results/?tx_fds_searchresults[type_name]=Trailer
This link works as expected. On the results page is a link to the listings page with more details. An example is bellow:
/listing/?tx_fds_listing[id]=119870
This link is not working. tx_fds_listing[id] is not being populated in the arguments passed to the plug in controller.
At first we thought it might be a config issue but again, it isn't present on other pages.
The ID is not a database object and may be a text string instead.
Edit:
I should add that it works fine with RealURL turned off.
We get the id as $id = $this->request->getArgument('id');
Edit 2:
Here is the error message from the logs.
[ALERT] request="28233e225150a" component="TYPO3.CMS.Frontend.ContentObject.Exception.ProductionExceptionHandler": Oops, an error occurred! Code: 201512141630381db91bba - {"exception":"exception 'TYPO3\\CMS\\Extbase\\Mvc\\Exception\\NoSuchArgumentException' with message 'An argument \"id\" does not exist for this request.'
I also tried renaming the variable to name, but that didn't work either.
I have a solution that solves the root cause of the problem, if not the specific issue.
So I had to add additional mapping to the realurl_conf.php file. For example to get the listing id:
$config['domain.com']['postVarSets'][3]['stock'] = array(array('GETvar' => 'tx_fds_listing[id]'));
This makes the effective URL:
/listing/stock/119870
This was the intended usage for the plugin, so this is a good result. I also added configuration for ajax and pdfs. This required modification to the typoscript that was not obvious.
PDF TS:
pdf = PAGE
pdf {
typeNum = 300
10 = USER_INT
10 {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
#vendorName = TYPO3
extensionName = Fds
pluginName = Listing
#controller = FDS
controller = Tx_Fds_Controller_FDSController
#action = listingPdf
switchableControllerActions.FDS.1 = listingPdf
}
config {
disableAllHeaderCode = 1
additionalHeaders = Content-type:application/pdf
xhtml_cleaning = 0
admPanel = 0
}
}
PDF RealURL Config:
$config['domain.com']['postVarSets'][3]['pdf'] = array('type' => 'single', 'keyValues' => array ('type' => 300));
PDF effective URL:
/listing/pdf/stock/119870

Use tt_address fields in direct_mail newsletter

I am using TYPO3 6.2.11, tt_address 2.3.5 and direct_mail 4.0.1 and sent me some test newsletters from a internal TYPO3-Page. Everything works fine.
Now, I want to send some data fields from my tt_address-table like name or title for example.
What's the name of the tt_address-MARKER, I'll use at my page content?
I also add the follwing to [basic.addRecipFields] at the direct_mail-Extension:
name,first_name,last_name,email,description,title
But nothing happens. I can't use tt_address-fields at my direct_mail newsletter. I hope someone can help me, thanks.
The other opportunity is to use fe_user-data for my newsletter (felogin). How can I use felogin-fields like passwordor username at my template?
You need to prefix the fields with USER_ and wrap the marker in ###. So e.g. if you'd like to use the e-mail address, you write ###USER_email###. You can find all possibilities in the Direct Mail documentation.
A note on sending the password: This would be a huge security risk but it's not possible anyway because passwords of fe_users are stored at least hashed (and nowadays also encrypted) in the database. But you can use the ###SYS_AUTHCODE### marker to generate an authentication code you can use in an "edit profile" extension to let the user update his subscription.
If you need fields from other sources or data you're calculating dynamically, you can also create an own extension and implement the Direct Mail mailMarkersHook.
ext_localconf.php:
// Direct Mail personalization hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/direct_mail']['res/scripts/class.dmailer.php']['mailMarkersHook']['userunilunewsletterrendering'] =
'My\Extension\Hook\DirectMail->mailMarkersHook';
EXT:extension/Classes/Hook/DirectMail.php:
<?php
namespace My\Extension\Hook;
class DirectMail {
public function mailMarkersHook($params, \DirectMailTeam\DirectMail\Dmailer $dmailer) {
$params['markers']['###USER_SALUTATION###'] = $this->getPersonalizedSalutation($params['row']);
return $params;
}
/**
* #param $row
* #return string
*/
protected function getPersonalizedSalutation($row) {
$personalizedSalutation = 'Dear Sir or Madam';
if (!empty($row['last_name']) && !empty($row['gender'])) {
if ($row['gender'] === 'm') {
$personalizedSalutation = 'Dear Mr. ' . $row['last_name'];
} elseif ($row['gender'] === 'f') {
$personalizedSalutation = 'Dear Ms. ' . $row['last_name'];
}
}
return $personalizedSalutation;
}
}

Symfony2 - Setting up a blog archive

I've been working on trying to setup a blog archive for a blog site where the use clicks on a date and the corresponding posts appear. (see image) I understand I need to retrieve all my blog posts and sort by date, but the steps after that are foggy to me. Taking that data then sorting it by month/year and passing it to a template is the part I am having trouble with.
Can someone shed some light on what I am doing wrong or provide a simple working example?
What I have thus far:
public function archiveAction()
{
$em = $this->getDoctrine()->getManager();
// $query = $em->getRepository('AcmeProjectBundle:Blog')
// ->findAll();
$blogs = $em->getRepository('AcmeProjectBundle:Blog')
->getLatestBlogs();
if (!$blogs) {
throw $this->createNotFoundException('Unable to find blog posts');
}
foreach ($blogs as $post) {
$year = $post->getCreated()->format('Y');
$month = $post->getCreated()->format('F');
$blogPosts[$year][$month][] = $post;
}
// exit(\Doctrine\Common\Util\Debug::dump($month));
return $this->render('AcmeProjectBundle:Default:archive.html.twig', array(
'blogPosts' => $blogPosts,
));
}
You want to tell your archiveAction which month was actually clicked, so you need to one or more parameters to it: http://symfony.com/doc/current/book/controller.html#route-parameters-as-controller-arguments (I would do something like /archive/{year}/{month}/ for my parameters, but it's up to you.) Then when someone goes you myblog.com/archive/2014/04, they would see those posts.
Next, you want to show the posts for that month. For this you'll need to use the Doctrine Query builder. Here's one SO answer on it, but you can search around for some more that pertain to querying for dates. Select entries between dates in doctrine 2

Form not passing through hidden field value correctly in Facebook, but ok elsewhere

I have a script that first checks if the form page is referred from a GET parameter
$page['is_referred'] = isset($_GET['rfr']) ? 1 : 0;
Then that set value is used as the value of a hidden field in a Symfony2 form
$form = $app['form.factory']->createBuilder('form', $data)
//....
->add('referred', 'hidden', array(
'data' => $page['is_referred'],
))
->getForm();
then
if ('POST' === $request->getMethod()) {
$form->bindRequest($request);
if ($form->isValid()) {
//...
$referred = $form->get('referred')->getData();
$msg_referer = "Thanks from Referrer";
$msg_noreferer = "Thanks";
if( 1 == $referred ){
$page['thanks'] = $msg_referer;
}else{
$page['thanks'] = $msg_noreferer;
}
//..
}
}
The hidden field is read okay on the main site when ?rfr is in the url, but when iframed in Facebook and ?rfr is present, the $msg_noreferer message is displayed instead. Any ideas as to why this happens?
EDIT: I should also mention that the field value is correctly set in Facebook when I check the source, but it just doesn't seem to post the data through correctly
I should also mention that the field value is correctly set in Facebook when I check the source, but it just doesn't seem to post the data through correctly
I’d say, the value is posted alright, but the form validation does not accept it, because the hidden field does not get added to the form on validation.
Have you checked this, if the code adding the hidden field to the form is actually being executed when handling the POST request received from the browser …?

KRL and Yahoo Local Search

I'm trying to use Yahoo Local Search in a Kynetx Application.
ruleset avogadro {
meta {
name "yahoo-local-ruleset"
description "use results from Yahoo local search"
author "randall bohn"
key yahoo_local "get-your-own-key"
}
dispatch { domain "example.com"}
global {
datasource local:XML <- "http://local.yahooapis.com/LocalSearchService/V3/localsearch";
}
rule add_list {
select when pageview ".*" setting ()
pre {
ds = datasource:local("?appid=#{keys:yahoo_local()}&query=pizza&zip=#{zip}&results=5");
rs = ds.pick("$..Result");
}
append("body","<ul id='my_list'></ul>");
always {
set ent:pizza rs;
}
}
rule add_results {
select when pageview ".*" setting ()
foreach ent:pizza setting pizza
pre {
title = pizza.pick("$..Title");
}
append("#my_list", "<li>#{title}</li>");
}
}
The list I wind up with is
. [object Object]
and 'title' has
{'$t' => 'Pizza Shop 1'}
I can't figure out how to get just the title. It looks like the 'text content' from the original XML file turns into {'$t' => 'text content'} and the '$t' give problems to pick().
When XML datasources and datasets get converted into JSON, the text value within an XML node gets assigned to $t. You can pick the text of the title by changing your pick statement in the pre block to
title = pizza.pick("$..Title.$t");
Try that and see if that solves your problem.
Side notes on things not related to your question to consider:
1) Thank you for sharing the entire ruleset, what problem you were seeing and what you expected. Made answering your question much easier.
2) The ruleset identifier should not be changed from what AppBuilder or the command-line gem generate for you. Your identifier that is currently
ruleset avogadro {
should look something more like
ruleset a60x304 {
3) You don't need the
setting ()
in the select statement unless you have a capture group in your regular expression
Turns out that pick("$..Title.$t") does work. It looks funny but it works. Less funny than a clown hat I guess.
name = pizza.pick("$..Title.$t");
city = pizza.pick("$..City.$t");
phone = pizza.pick("$..Phone.$t");
list_item = "<li>#{name}/#{city} #{phone}</li>"
Wish I had some pizza right now!