I have written a custom Drupal 8 module that will send article notifications to our Mailchimp subscribers.
Problem
Currently, the module loops over each of the article 'tags' and sends out a separate campaign for each. Consequently, if someone is subscribed two of an article's tags, they will receive two emails.
Solution
Instead, I'd like each person to receive a maximum of one email notification. I think that the best way to achieve this is to create a dynamic segment. Similar to the query posted here. However, I have been getting various errors, and can't find much help here and here.
Code
<?php
function mailchimp_notification_process(EntityInterface $node)
{
global $base_url;
$settings = Settings::get('mailchimp_audience');
$content = (object) array(
'subject_line' => 'XXX',
'title' => 'Notification',
'from_name' => 'XXX',
'reply_to' => 'noreply#gmail.com'
);
$template_content = [
'html' => [
'value' => '<table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">email content removed for brevity</table>',
'format' => 'mailchimp_campaign'
]
];
$mc_campaign = mailchimp_get_api_object('MailchimpCampaigns');
$lists = mailchimp_campaign_get_list_segments($settings, NULL);
if (
$node->isPublished() &&
($node->bundle() == 'article' || $node->bundle() == 'resource') &&
$node->field_send_notification->getString() == 1 &&
isset($node->field_send_notification_to->entity)
) {
$all_tags = [];
// loop over each of the loops to send notifications to
foreach ($node->field_send_notification_to as $category) {
$tags = array_filter($lists, function ($list) use ($category) {
return $list->name == $category->entity->getName();
});
if ($tags && is_array($tags)) {
$tags = reset($tags);
array_push($all_tags, $tags->id);
\Drupal::logger('mailchimp_notification')->notice('all_tags: ' . json_encode($all_tags));
}
}
$recipients = (object) [
'list_id' => $settings,
'segment_opts' => [
'match' => 'all',
'conditions' => [
[
'condition_type' => 'Tags',
'field' => 'tags-123',
'op' => 'interestcontainsall',
'value' => $all_tags,
]
]
]
];
\Drupal::logger('mailchimp_notification')->notice('recipients: ' . json_encode($recipients));
$campaign_id = mailchimp_campaign_save_campaign($template_content, $recipients, $content, "", "");
if ($campaign_id) {
$mc_campaign->send($campaign_id);
}
}
}
Related
I've created a custom module in Drupal 8 that grab some data from an API, and puts them in the Drupal DB creating a new table.
I want to add this data as the contents of a specific content type.
How can I do that?
here is my code :
<?php
/**
* Implements hook_cron().
*/
function ods_cron() {
$message = 'Cron run: ' . date('Y-m-d H:i:s');
$ods = \Drupal::service('ods.ods');
$conf = \Drupal::service('ods.ods_configuration_request');
if ($conf->isDevelopment()) {
// Development
$response_bond = beforeSendRequest($conf->devUrlExternalBond(), 'GET');
$response_mf = beforeSendRequest($conf->devUrlExternalMutualFund(), 'GET');
} else {
// Production
$parameters_bond = [
'headers' => $conf->headers(),
'authorization' => $conf->basicAuthorization(),
'data_post' => $conf->bodyBond(),
];
$parameters_mf = [
'headers' => $conf->headers(),
'authorization' => $conf->basicAuthorization(),
'data_post' => $conf->bodyMutualFund(),
];
$response_bond = beforeSendRequest($conf->urlExternalBond(), 'POST', $parameters_bond);
$response_mf = beforeSendRequest($conf->urlExternalMutualFund(), 'POST', $parameters_mf);
}
$raw_result_bond = json_decode($response_bond);
$raw_result_mf = json_decode($response_mf);
// Development
if ($conf->isDevelopment()) {
$raw_result_bond = json_decode($raw_result_bond[0]->field_bonds);
$raw_result_mf = json_decode($raw_result_mf[0]->field_api);
}
$BondsProductList = $raw_result_bond->BondsProductInqRs->BondsProductList;
$MFProductInqList = $raw_result_mf->MFProductInqRs->MFProductInqList;
// Bond data store to internal
if ($BondsProductList !== null) {
$bond_datas = [];
foreach ($BondsProductList as $row => $content) {
$bond_datas[] = [
'AskPrice' => number_format($content->AskPrice, 1, '.', ','),
'BidPrice' => number_format($content->BidPrice, 1, '.', ','),
'BuySettle' => number_format($content->BuySettle, 1, '.', ','),
'CouponFreqCode' => $content->CouponFreqCode,
'CouponFreqID' => number_format($content->CouponFreqID),
'CouponRate' => number_format($content->CouponRate, 2, '.', ','),
'IDCurrency' => $content->IDCurrency,
'LastCoupon' => $content->LastCoupon,
'MaturityDate' => $content->MaturityDate,
'MinimumBuyUnit' => number_format($content->MinimumBuyUnit),
'MultipleOfUnit' => number_format($content->MultipleOfUnit),
'NextCoupon' => $content->NextCoupon,
'Penerbit' => $content->Penerbit,
'ProductCode' => $content->ProductCode,
'ProductName' => $content->ProductName,
'ProductAlias' => $content->ProductAlias,
'RiskProfile' => $content->RiskProfile,
'SellSettle' => $content->SellSettle
];
}
$insert_data = $ods->setData(
'bond',
[
'AskPrice', 'BidPrice', 'BuySettle', 'CouponFreqCode', 'CouponFreqID', 'CouponRate', 'IDCurrency',
'LastCoupon', 'MaturityDate', 'MinimumBuyUnit', 'MultipleOfUnit', 'NextCoupon', 'Penerbit',
'ProductCode', 'ProductName', 'ProductAlias', 'RiskProfile', 'SellSettle'
],
$bond_datas
);
if ($insert_data) {
// make response as JSON File and store the file
$ods->makeJsonFile($bond_datas, 'feeds/bonds', 'bond.json');
}
}
// Mutual Fund data store to internal
if ($MFProductInqList !== null) {
$mf_datas = [];
foreach ($MFProductInqList as $row => $content) {
$mf_datas[] = [
'ProductCode' => $content->ProductCode,
'ProductName' => $content->ProductName,
'ProductCategory' => $content->ProductCategory,
'ProductType' => $content->ProductType,
'Currency' => $content->Currency,
'Performance1' => $content->field_1_tahun_mf,
'Performance2' => $content->Performance2,
'Performance3' => $content->Performance3,
'Performance4' => $content->Performance4,
'Performance5' => $content->Performance5,
'UrlProspektus' => $content->UrlProspektus,
'UrlFactSheet' => $content->UrlFactSheet,
'UrlProductFeatureDocument' => $content->UrlProductFeatureDocument,
'RiskProfile' => $content->RiskProfile,
'FundHouseName' => $content->FundHouseName,
'NAVDate' => $content->NAVDate,
'NAVValue' => $content->NAVValue
];
}
$insert_data_mf = $ods->setData(
'mutual_fund',
[
'ProductCode', 'ProductName', 'ProductCategory', 'ProductType', 'Currency', 'Performance1', 'Performance2', 'Performance3',
'Performance4', 'Performance5', 'UrlProspektus', 'UrlFactSheet', 'UrlProductFeatureDocument', 'RiskProfile', 'FundHouseName',
'NAVDate', 'NAVValue'
],
$mf_datas
);
if ($insert_data_mf) {
// make response as JSON File and store the file
$ods->makeJsonFile($mf_datas, 'feeds/mf', 'mutual_fund.json');
}
}
// console log
\Drupal::logger('ods')->notice($message);
}
So can I store the data to pristine drupal 8 table?
First, you need to create the content type in the Drupal 8 backend going to Structure > Content type.
Second you can add a node programmatically like this
use Drupal\node\Entity\Node;
$node = Node::create(array(
'type' => 'your_content_type',
'title' => 'your title',
'langcode' => 'en',
'uid' => '1',
'status' => 1,
'body'=> 'your body',
));
$node->save();
Rather than using a plug-in I am in the process of creating my own schema markup for a home page on a WordPress site which makes use of Advananced Custom Fields (ACF) for some of the content relevant to this challenge. My aim is to give me a little more granular control over what is output and as a little personal challenge :)
So far I have successfully created a basic schema, but I have become stuck where I need to create a nested entity for a list of services.
currently I have this in my functions.php file:
function schema() {
$schema = array(
'#context' => "http://schema.org",
'#type' => "ProfessionalService",
'name' => get_bloginfo('name'),
'url' => get_home_url(),
// ... and so on
);
if ( have_rows('services_list') ) {
$schema['itemListElement'] = array();
while (have_rows('services_list')) : the_row();
$services = array(
'#type' => 'Offer',
'itemOffered' => array (
'#type' => 'Service',
'name' => get_sub_field('service')
)
);
array_push($schema['itemListElement'], $services);
endwhile;
}
echo '<script type="application/ld+json">' . json_encode($schema) . '</script>';
}
add_action('wp_head', 'schema');
Result:
{
"#context":"http:\/\/schema.org",
"#type":"ProfessionalService",
"name":"Name of Company",
"url":"http:\/\/www.whatever.com",
and so on...
"itemListElement": [
{
"#type":"Offer",
"itemOffered": {
"#type":"Service",
"name":"Service One"
}
},
{
"#type":"Offer",
"itemOffered": {
"#type":"Service",
"name":"Service Two"
}
},
... more services
]
}
This resulting markup is great, but I need to nest the itemListElement so that it outputs like so:
"hasOfferCatalog": {
"#type": "OfferCatalog",
"name": "Some Services",
"itemListElement": [
{
"#type":"Offer",
"itemOffered": {
"#type":"Service",
"name":"Service One"
}
},
{
"#type":"Offer",
"itemOffered": {
"#type":"Service",
"name":"Service Two"
}
},
... more services
I can't for the life of me work out how this is done. My current best effort is to add it like so:
if ( have_rows('services_list') ) {
'hasOfferCatalog' => array(
'#type' => 'OfferCatalog',
'name' => 'Tree Surgery'
$schema['itemListElement'] = array();
while (have_rows('services_list')) : the_row();
$services = array(
'#type' => 'Offer',
'itemOffered' => array (
'#type' => 'Service',
'name' => get_sub_field('service')
)
);
array_push($schema['itemListElement'], $services);
endwhile;
)
}
However this is not working at all. If anyone can point me in the right direction to the nested entities working in this context I'd be really grateful.
I did eventually manage to solve my issue. I never got around to posting this at he time, but since here's been a little interest here's what I did.
My 'best effort' was almost there, but I needed to make a few small adjustments. Sadly I sorted this out sometime ago and I forget the resource I used to put things right, but I hope this might be of help to someone else.
if ( have_rows('services_list') ) {
$schema['hasOfferCatalog'] = array();
$catalog = array(
'#type' => 'OfferCatalog',
'name' => 'Tree Surgery'
);
if ( have_rows('services_list') ) {
$catalog['itemListElement'] = array();
while (have_rows('services_list')) : the_row();
$services = array(
'#type' => 'Offer',
'itemOffered' => array (
'#type' => 'Service',
'name' => get_sub_field('service')
)
);
array_push($catalog['itemListElement'], $services);
endwhile;
array_push($schema['hasOfferCatalog'], $catalog);
}
}
Fr a little bit of context I have this all placed in my functions.php file and is put together like so:
function schema() {
$schema = array(
'#context' => "http://schema.org",
'#type' => "ProfessionalService",
'name' => get_bloginfo('name'),
'url' => get_home_url(),
'telephone' => '+00 0000 00000',
'address' => array(
'#type' => 'PostalAddress',
'streetAddress' => 'XXXXX',
'postalCode' => 'XXX XXX',
'addressLocality' => 'XXXXXX',
'addressRegion' => 'XXXXXXX',
'addressCountry' => 'XXXXXXXXXXXX'
),
'logo' => get_stylesheet_directory_uri() . '/path/to/your/image.svg',
'image' => get_stylesheet_directory_uri() . '/path/to/your/image.svg'
);
if ( have_rows('services_list') ) {
$schema['hasOfferCatalog'] = array();
$catalog = array(
'#type' => 'OfferCatalog',
'name' => 'Tree Surgery'
);
if ( have_rows('services_list') ) {
$catalog['itemListElement'] = array();
while (have_rows('services_list')) : the_row();
$services = array(
'#type' => 'Offer',
'itemOffered' => array (
'#type' => 'Service',
'name' => get_sub_field('service')
)
);
array_push($catalog['itemListElement'], $services);
endwhile;
array_push($schema['hasOfferCatalog'], $catalog);
}
}
echo '<script type="application/ld+json">' . json_encode($schema) . '</script>';
}
add_action('wp_head', 'schema');
It seems to do the job.
To be very specific - there is CRM system written in Codeigniter called Rise. I would like to make (automatically) an expense entry ( call save() method of an Expenses class ) each time someone logs in ( inside save_timelog() method of a Projects class ) time manually.
Expense Controller:
function save() {
validate_submitted_data(array(
"id" => "numeric",
"expense_date" => "required",
"category_id" => "required",
"amount" => "required"
));
$id = $this->input->post('id');
$target_path = get_setting("timeline_file_path");
$files_data = move_files_from_temp_dir_to_permanent_dir($target_path, "expense");
$has_new_files = count(unserialize($files_data));
$data = array(
"expense_date" => $this->input->post('expense_date'),
"title" => $this->input->post('title'),
"description" => $this->input->post('description'),
"category_id" => $this->input->post('category_id'),
"amount" => unformat_currency($this->input->post('amount')),
"project_id" => $this->input->post('expense_project_id'),
"user_id" => $this->input->post('expense_user_id'),
"files" => $files_data
);
<.. ETC. CHECKING FILES ..>
$save_id = $this->Expenses_model->save($data, $id);
if ($save_id) {
echo json_encode(array("success" => true, "data" => $this->_row_data($save_id), 'id' => $save_id, 'message' => lang('record_saved')));
} else {
echo json_encode(array("success" => false, 'message' => lang('error_occurred')));
}
}
Projects Controller:
function save_timelog() {
$this->access_only_team_members();
$id = $this->input->post('id');
$start_time = $this->input->post('start_time');
$end_time = $this->input->post('end_time');
$note = $this->input->post("note");
$task_id = $this->input->post("task_id");
if (get_setting("time_format") != "24_hours") {
$start_time = convert_time_to_24hours_format($start_time);
$end_time = convert_time_to_24hours_format($end_time);
}
$start_date_time = $this->input->post('start_date') . " " . $start_time;
$end_date_time = $this->input->post('end_date') . " " . $end_time;
$start_date_time = convert_date_local_to_utc($start_date_time);
$end_date_time = convert_date_local_to_utc($end_date_time);
$data = array(
"project_id" => $this->input->post('project_id'),
"start_time" => $start_date_time,
"end_time" => $end_date_time,
"note" => $note ? $note : "",
"task_id" => $task_id ? $task_id : 0,
);
if (!$id) {
//insert mode
$data["user_id"] = $this->input->post('user_id') ? $this->input->post('user_id') : $this->login_user->id;
} else {
//edit mode
//check edit permission
$this->check_timelog_updte_permission($id);
}
$save_id = $this->Timesheets_model->save($data, $id);
if ($save_id) {
echo json_encode(array("success" => true, "data" => $this->_timesheet_row_data($save_id), 'id' => $save_id, 'message' => lang('record_saved')));
} else {
echo json_encode(array("success" => false, 'message' => lang('error_occurred')));
}
}
So now what I'm trying to do is inside Projects controller, save_timelog() method just below these lines:
<...>
if (!$id) {
//insert mode
$data["user_id"] = $this->input->post('user_id') ? $this->input->post('user_id') : $this->login_user->id;
} else {
//edit mode
//check edit permission
$this->check_timelog_updte_permission($id);
}
/* CREATING A SAMPLE ARRAY WITH STATIC DATA FOR AN EXAMPLE EXPENSE ENTRY */
$a = array(
"expense_date" => '2018-03-13',
"title" => 'Cat Food',
"description" => 'Sheba, Felix, KiteKat',
"category_id" => '85',
"amount" => '500',
"project_id" => '84',
"user_id" => '10',
"files" => $files_data
);
/* TRYING TO SAVE/SEND EXAMPLE ARRAY TO Expenses Class save() method (?) */
$b = $this->Expenses_model->save($a);
/* RESULT (?) */
$save_id = $this->Timesheets_model->save($data, $id);
if ($save_id) {
echo json_encode(
array(
array(
"success" => true,
"data" => $this->_timesheet_row_data($save_id),
'id' => $save_id,
'message' => lang('record_saved')
),
array(
"success" => true,
"data" => _row_data($b),
'id' => $save_id,
'message' => lang('record_saved')
)
)
);
} else {
echo json_encode(array("success" => false, 'message' => lang('error_occurred')));
}
<.. Closing save_timelog() method ..>
However it surely doesn't work and all I get is "POST http://rise.test/index.php/projects/save_timelog 500 (Internal Server Error)".
I also load Expenses model and Expenses categories model in Projects _construct():
Projects Controller:
public function __construct() {
parent::__construct();
$this->load->model("Project_settings_model");
$this->load->model("Expense_categories_model");
$this->load->model("Expenses_model");
}
I also contacted developers of Rise with following question/answer:
Me:
In Projects controller save_timelog() method I just want to call
Expenses controller save() method, and if save_timelog() is successful
I would like to save an Expense ( $this->Expenses_model->save($data,
$id); ) with appropriate data. Could be static values for now for
$data array in save() method - just to find out it's working.
Rise Devs:
Hi, You are doing almost right. Just remove the 2nd parameter $id. It
should be used only for update. $this->Expenses_model->save($data)
Would really appreciate any help and directions! Thanks.
I need to reply to incoming emails including the attachment they contain.
I wrote this code, but for some reason when I check my debug log and mandrill API logs the attachment isn't included into the request.
Where's my fault?
if ($message['attachments'])
{
$mail=
[
'html' => $mail->msg->html,
'text' => $mail->msg->text,
'subject' => $mail->msg->subject,
'from_email' => 'test#test.com',
'from_name' => $mail->msg->from_name,
'to' => [
[
'email' => 'test#test.com',
'name' => 'test#test.com',
'type' => 'to'
]
],
'headers' => [
'Reply-To' => $mail->msg->from_email
],
];
//just some sample data for testing
foreach ($message['attachment'] as $attachment)
{
$mail['attachments']['name'] ='sample.png';
$mail['attachments']['type'] ='image/png';
$mail['attachments']['content'] ='iVBORw0KGgoAAAANSUhEUgAAABwAAAASCAMAAAB/2U7WAAAABlBMVEUAAAD///+l2Z/dAAAASUlEQVR4XqWQUQoAIAxC2/0vXZDrEX4IJTRkb7lobNUStXsB0jIXIAMSsQnWlsV+wULF4Avk9fLq2r8a5HSE35Q3eO2XP1A1wQkZSgETvDtKdQAAAABJRU5ErkJggg==';
}
$async = false;
$ip_pool = 'Main Pool';
$v = var_export( $mail, true);
file_put_contents('phplog.txt', 'gesendet: ' . $v, FILE_APPEND);
$result = $mandrill->messages->send($mail, $async, $ip_pool, $send_at);
}
Below line looks suspicious. If you check https://mandrillapp.com/api/docs/messages.html here. You can clearly see attachments is a multidimensional array.
//just some sample data for testing
foreach ($message['attachment'] as $attachment)
{
$mail['attachments']['name'] ='sample.png';
$mail['attachments']['type'] ='image/png';
$mail['attachments']['content'] ='iVBORw0KGgoAAAANSUhEUgAAABwAAAASCAMAAAB/2U7WAAAABlBMVEUAAAD///+l2Z/dAAAASUlEQVR4XqWQUQoAIAxC2/0vXZDrEX4IJTRkb7lobNUStXsB0jIXIAMSsQnWlsV+wULF4Avk9fLq2r8a5HSE35Q3eO2XP1A1wQkZSgETvDtKdQAAAABJRU5ErkJggg==';
}
At least it should be like following, but still you don't use $attachment in your code. But the correct usage like following.
foreach ($message['attachment'] as $key => $attachment)
{
$mail['attachments'][$key]['name'] ='sample.png';
$mail['attachments'][$key]['type'] ='image/png';
$mail['attachments'][$key]['content'] ='iVBORw0KGgoAAAANSUhEUgAAABwAAAASCAMAAAB/2U7WAAAABlBMVEUAAAD///+l2Z/dAAAASUlEQVR4XqWQUQoAIAxC2/0vXZDrEX4IJTRkb7lobNUStXsB0jIXIAMSsQnWlsV+wULF4Avk9fLq2r8a5HSE35Q3eO2XP1A1wQkZSgETvDtKdQAAAABJRU5ErkJggg==';
}
If you want to try in more clear way please use as following :
$attachment = [];
$attachment['name'] ='sample.png';
$attachment['type'] ='image/png';
$attachment['content'] ='iVBORw0KGgoAAAANSUhEUgAAABwAAAASCAMAAAB/2U7WAAAABlBMVEUAAAD///+l2Z/dAAAASUlEQVR4XqWQUQoAIAxC2/0vXZDrEX4IJTRkb7lobNUStXsB0jIXIAMSsQnWlsV+wULF4Avk9fLq2r8a5HSE35Q3eO2XP1A1wQkZSgETvDtKdQAAAABJRU5ErkJggg==';
$mail['attachments'][] = $attachment;
UPDATED ACCORDING TO COMMENT :
things look fine, just change your if statement if ($message['attachments']) to if (is_array($message['attachments']) && count($message['attachments']) > 1) then after if statment place $message['attachments'] = array_values($message['attachments']);
so loop like the following one. Just pay attention to $attachment I added to semantic keys to it, you can change according to your needs. I don't know where is your source you getting file, static or file upload etc. I added as an example.
foreach ($message['attachment'] as $key => $attachment)
{
$mail['attachments'][$key]['name'] =$attachment['fileName'];
$mail['attachments'][$key]['type'] =$attachment['mimeType'];
$mail['attachments'][$key]['content'] = chunk_split(base64_encode(file_get_contents($attachment['filePath']))); ;
}
I am working on a plugin for Elgg that keeps track of device ids that are sent to the application when logging in from your mobile phone. For this, I would like to store these device ids in the database and would like to use ElggObjects for this.
This is what I do now:
function initialize() {
$androidTokens = elgg_get_entities(array(
'type' => 'object',
'subtype' => 'androidTokens',
'limit' => 0
));
$iosTokens = elgg_get_entities(array(
'type' => 'object',
'subtype' => 'iosTokens',
'limit' => 0
));
if ($androidTokens == 0) {
$tokenObject = new ElggObject();
$tokenObject->subtype = 'androidTokens';
$tokenObject->tags = array();
$tokenObject->save();
}
if ($iosTokens == 0) {
$tokenObject = new ElggObject();
$tokenObject->subtype = 'iosTokens';
$tokenObject->tags = array();
$tokenObject->save();
}
}
So this generates two ElggObjects that hold ids for android and for ios devices, stored in the metadata field tags. This array of tags can however not be retrieved anymore. When I do:
$tokenObject = elgg_get_entities(array(
'type' => 'object',
'subtype' => $os.'Tokens',
'limit' => 0
));
$tokens = $tokenObject->tags
tokens remains empty. Does someone know what I am doing wrong? Am I using the Elgg objects wrong?
I think the reason you're running into issues there is that elgg_get_entities returns an array of entities.
Am I correct in assuming that you'll only ever have one of each token object subtype? (One for iOS and one for Android?)
If so, I would modify your code as follows:
function initialize() {
$androidTokens = elgg_get_entities(array(
'type' => 'object',
'subtype' => 'androidTokens',
'limit' => 1 // only expecting one entity
));
$iosTokens = elgg_get_entities(array(
'type' => 'object',
'subtype' => 'iosTokens',
'limit' => 1 // only expecting one entity
));
if (count($androidTokens) == 0) {
$tokenObject = new ElggObject();
$tokenObject->subtype = 'androidTokens';
$tokenObject->tags = array();
$tokenObject->save();
}
if (count($iosTokens) == 0) {
$tokenObject = new ElggObject();
$tokenObject->subtype = 'iosTokens';
$tokenObject->tags = array();
$tokenObject->save();
}
}
Later, when grabbing the entity:
$tokenObject = elgg_get_entities(array(
'type' => 'object',
'subtype' => $os.'Tokens',
'limit' => 1 // only grab one
));
$tokens = $tokenObject[0]->tags; // get tag data for first (and only) entity