Problems with replacing CiviCRM tokens in API - php

I'm currently developing a CiviCRM extension, where I need to replace CiviCRM-Tokens (used in pdf and mailing generation) in html code.
I did a little bit of research in the core files and tried to recreate the behaviour in the PDFLetterCommon.php (/civicrm/CRM/Contact/Form/Task/PDFLetterCommon.php) where it replaces the tokens in the postProcess function.
Here is the original CiviCRM Code:
list($formValues, $categories, $html_message, $messageToken, $returnProperties) = self::processMessageTemplate($form);
$skipOnHold = isset($form->skipOnHold) ? $form->skipOnHold : FALSE;
$skipDeceased = isset($form->skipDeceased) ? $form->skipDeceased : TRUE;
foreach ($form->_contactIds as $item => $contactId) {
$params = array('contact_id' => $contactId);
list($contact) = CRM_Utils_Token::getTokenDetails($params,
$returnProperties,
$skipOnHold,
$skipDeceased,
NULL,
$messageToken,
'CRM_Contact_Form_Task_PDFLetterCommon'
);
...
}
And here is my version for testing:
(this code is located inside an api function in my extension)
$messageToken = CRM_Utils_Token::getTokens($params["html"]);
$returnProperties = array();
if (isset($messageToken['contact'])) {
foreach ($messageToken['contact'] as $key => $value) {
$returnProperties[$value] = 1;
}
}
$skipOnHold = FALSE;
$skipDeceased = TRUE;
$tokenParams = array("contact_id" => 67450);
list($contact) = CRM_Utils_Token::getTokenDetails($tokenParams,
$returnProperties,
$skipOnHold,
$skipDeceased,
NULL,
$messageToken,
'CRM_Contact_Form_Task_PDFLetterCommon'
);
I'm using the default values for $skipOnHold (false) and $skipDeceased (true) and also just passing one (existing) user id into the $params array ($tokenParams in my code).
Here is my problem:
My $messageToken and $returnProperties variables are being filled correctly via CiviCRM's core functions but when I pass them all into CRM_Utils_Token::getTokenDetails() the returned $contact variable holds an empty array.
I'm really out of ideas, I've been looking into CRM/Utils/Token.php where getTokenDetails() is located, but have been unsuccessful in finding the problem with my code.
Thanks in advance for any help!

Related

Mark email as read using microsoft graph api

I am writing a script that will use Microsoft Graph api ( using this library https://github.com/microsoftgraph/msgraph-sdk-php )
I managed to connect and search for the specific email, download the attachment but now I need to mark the email as read and set the flag but I have no idea how.
So far I have used this tutorial ( https://learn.microsoft.com/en-us/graph/tutorials/php?tabs=aad ) in order to connect and read the emails.
public static function getInbox() {
$token = GraphHelper::getUserToken();
GraphHelper::$userClient->setAccessToken($token);
// Only request specific properties
$select = '$select=from,isRead,receivedDateTime,subject,hasAttachments';
// Sort by received time, newest first
$orderBy = '$orderBy=receivedDateTime DESC';
$filter = '$filter=isRead eq false';
$requestUrl = '/me/mailFolders/inbox/messages?'.$filter.'&'.$select.'&'.$orderBy;
$messages = GraphHelper::$userClient->createCollectionRequest('GET', $requestUrl)
->setReturnType(Model\Message::class)
->setPageSize(100)
->getPage();
foreach ($messages as $message) {
if(strpos($message->getSubject(), 'XML')!==false ){
print('Message: '.$message->getSubject().PHP_EOL);echo PHP_EOL;
$expand="microsoft.graph.itemattachment/item";
$requestUrl = '/me/messages/'.$message->getId().'/attachments/?$expand= '.$expand;
$docDatas = GraphHelper::$userClient->createCollectionRequest('GET', $requestUrl)
->setReturnType(Model\Message::class)
->setPageSize(1)
->getPage();
$dat = $docDatas[0]->getProperties();
//parseXmlOrder(base64_decode($dat['contentBytes']));
$sendBody = array( 'isRead' => true );
var_dump( GraphHelper::$userClient->createRequest('PATCH', '/me/messages/'.$message->getId())
->attachBody($sendBody)
->execute() );
}
}
}
This is the code I have at the moment. Right at the end of the function I am trying to set the isRead attribute.
If someone could give me some advice where I am going wrong that would be amazing and help me stop banging my head against the wall.
Thanks,
Turns out giving readwrite persmissions does help. I only had read permissions.

Solr search engine need to restart after adding docs

I am working with solr 6.6.0 using solr PHP client. I am adding the docs using below code and it is working properly :
foreach ($data as $key => $value) {
$docs['doc_no'.$i]['id'] = $value['id'];
$docs['doc_no'.$i]['name'] = $value['name'];
$docs['doc_no'.$i]['sub_title'] = strip_tags($value['sub_title']);
$docs['doc_no'.$i]['small_image'] = $value['small_image'];
$docs['doc_no'.$i]['project_type'] = $value['project_type'];
$docs['doc_no'.$i]['project_status'] = $value['project_status'];
$docs['doc_no'.$i]['logo'] = $value['logo'];
$docs['doc_no'.$i]['price'] = $value['price'];
$docs['doc_no'.$i]['url'] = $value['url'];
$docs['doc_no'.$i]['flat_type_desc'] = $value['flat_type_desc'];
$docs['doc_no'.$i]['project_config'] = $value['project_config'];
$docs['doc_no'.$i]['address'] = $value['address'];
$docs['doc_no'.$i]['location'] = $value['location'];
$i++;
}
//print_r($docs);exit;
$documents = array();
foreach($docs as $item => $fields) {
$part = new Apache_Solr_Document();
foreach ( $fields as $key => $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $data ) {
$part->setMultiValue( $key, $data );
}
}
else{
$part->$key = $value;
}
}
$documents[] = $part;
}
try {
$solr->addDocuments( $documents );
$solr->commit();
$solr->optimize();
}
catch ( Exception $e ) {
echo $e->getMessage();
}
After executing the above code I have to manually restart the solr through cmd line and then it gets reflected, I want to ask that is every time when I add any docs in solr then I have to restart the solr manually ? Is there any other way to restart the solr automatically as soon as I have the data in docs.
Any help will be appreciated Thanks in advance.
For the submitted documents to be visible in the index, you have to issue a commit - and ask for a new reader to be opened (this is usually handled for you, so that's not usually necessary. How you do exactly that in the Drupal framework I have no idea about, but I'm guessing your Solr client has a commit method or something similar. I tried searching for the API docs, but came up empty except for the _Document class.
After a commit has been issued the index changes will be visible within a few seconds, or in the case of a soft commit (where the changes aren't persisted to disk before later) almost instantly.
You can also ask for a commitWithin interval when submitting documents, but that would also depend on how the client you're using works for how you include that parameter.

php - same source code for command line or web with arguments

If you are calling php page from web, you can give as
../../somepage.php?myid=1&trackno=2&anotherparam=3
and then you can use $_REQUEST or $_GET to retrieve the information
In command line, you can use
$options = getopt("a:b:c:"); to get the options that are passed through arguments
How to make sure, same source works either in web or in command line?
Let say your requests are like following;
WEB: http://domain.com/somepage.php?myid=1&trackno=2&anotherparam=3
CLI: php /path/to/this/php/file/somepage.php 1 2 3
You can use following php code;
<?php
if (!empty($_REQUEST)) {
$myid = $_REQUEST["myid"];
$trackno = $_REQUEST["trackno"];
$anotherparam = $_REQUEST["anotherparam"];
} else if (!empty($argv)) {
$myid = $argv[1];
$trackno = $argv[2];
$anotherparam = $argv[3];
} else {
die("Invalid request!");
}
You have already know how to handle web requests, you can refer here for more detail about $argv. Simply,
$argv[0] => scriptname(somepage.php),
$argv[1] => first param, ...,
$argv[n] => (n-1)th param
Edit:
In order to not miss order of commandline arguments, you can use naming conventions like;
php somepage.php myid_1 anotherparam_2 trackno_3
and you can use following to handle this;
foreach ($argv as $k => $v) {
if ($k == 0) continue;
$temp = explode("_", $v);
${$temp[0]} = $temp[1];
}
Simply,
myid_3 becomes $myid = 3;
variable names hidden in the values so you don't need to know about sequences

Wordpress Ajax setcookie WAMP/LAMP disparities

In a wordpress themed-plugin for e-commerce I have two ajax/php scripts setting cookies (both in the same directory) The ajax calls are set from the same "cart.js" The first script sets/updates an anonymous cart cookie when a cart is either created or updated. The second checks if customer/user exists, or creates one anew and --in either case -- logs them in if not already, before the cart gets passed to PayPal. As such, upon returning from paypal the customer/user (now logged in) is presented with an overview / review of the status of their orders (new and old).
On my WAMP develpment stack, this works flawlessly, while on the hosted (linux) installation
the cart_cookie script works as expected, while the checkout/customer_cookie throws...
[14-May-2013 02:08:50]
PHP Warning: session_start()
[<a href='function.session-start'>function.session-start</a>]:
Cannot send session cache limiter - headers already sent
(output started at /home2/alternam/public_html/demo/wp-content/themes/AM_Wallaby_Kids/checkout.php:2)
in /home2/alternam/public_html/demo/wp-content/plugins/cat-man/catalog-manager.php on line 23
Subsequently, the user is NOT logged in, and the cart is not converted (updated with relevant customer data). Wish I saw a way to pare this down to a minimum, but as I have no earthly idea why the two scripts behave so differently across platforms I'll apologize in advance for the lengthy post and include them both in their entirety below, and ask if anyone can see some obvious reason for the disparity? Thanks for your patience.
P.S. Both WAMP and LAMP stacks running php 5.2
cart_add.php (works both WAMP && LAMP)
<?php
ob_start();
require_once(preg_replace("/wp-content.*/","wp-load.php",__FILE__));
ob_end_clean();
$ud_cart = $product_name = $product_url = $reset = "";
$_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);# Sanitize Post Input
foreach ($_POST as $key => $val)
{ if(!is_array($val)) $$key = html_entity_decode($val,ENT_QUOTES);
else $$key = $val;
}
if($ud_cart)
{ $reset =1;
$amt_cart[0] = $cart_id;
if(#$items) foreach($items as $key => $item )$amt_cart[] = $item;
$amt_cart = serialize($amt_cart);
}
if($reset)
{ // Initiated from the cart (tpl_cart.php on page load) to remove out-of-stock items
// from OLD CARTS -- where items have gone out-of-stock since cart created -- OR
// to simply update/remove cart items upon user request (user clicks Update|Remove)
if($amt_cart)
{ $amt_cart = stripslashes($amt_cart);
setcookie(AMART_CART, $amt_cart, time()+60*60*24*90, COOKIEPATH, COOKIE_DOMAIN);
}
exit;
}
// Create Cart, and add, update, or remove Cart-Items from within the catalog gallery && product detail pages
$add = array("product_id" => $product_id, "product_name" => $product_name, "product_type" => $product_type,"product_url" => $product_url,"qty" => $qty);
$update = "";
if( isset( $_COOKIE[AMART_CART] ) )
{ $amt_cart = stripslashes($_COOKIE[AMART_CART]);
$amt_cart = unserialize($amt_cart);
foreach($amt_cart as $key => $item)
{ if($key == 0 ) $amt_cart_id = $item;
else
{ foreach($item as $attr => $value)
{ if($product_id != $value) continue;
else
{ $update = 1;
if($qty == 0 )
{ unset($amt_cart[$key]);
break;
} else $amt_cart[$key]['qty'] = $qty;
}
}
}
}
if(!$update) $amt_cart[] = $add;
setcookie(AMART_CART, serialize($amt_cart), time()+60*60*24*90, COOKIEPATH, COOKIE_DOMAIN);
}
else
{ unset($_SESSION[STORE_ID]['dest_zip'], $_SESSION[STORE_ID]['dest_ctry']);
$amt_cart[0] = uniqid(AMART_CART);
$amt_cart[] = $add;
setcookie(AMART_CART, serialize($amt_cart), time()+60*60*24*90, COOKIEPATH, COOKIE_DOMAIN);
}
?>
checkout.php (works on WAMP, fails on LAMP)
<?php
// TPL CART POSTS VIA AJAX CALL IN cart.js
ob_start();
require_once(preg_replace("/wp-content.*/","wp-load.php",__FILE__));
ob_end_clean();
$_POST = filter_input_array(INPUT_POST, FILTER_SANITIZE_STRING);
global $current_user, $wpfx;
$buyer_address1 = $buyer_address2 = $buyer_city = $buyer_region = $buyer_postal_code = $buyer_country = $buyer_ctry_code ="";
foreach ($_POST as $key => $val) $$key = $val;
foreach($buyer as $key => $val) $$key = $val;
$user_is_admin = current_user_can('manage_options');
if(!is_user_logged_in() || $user_is_admin )
{ if($userID = email_exists($email))
{ $user_info = $user_info = get_userdata($userID);
$user_login = $user_info->user_login;
$display_name = $user_info->display_name;
$welcome = "Welcome Back $display_name!";
}
if(#$welcome)
{ if(!$user_is_admin )
{ if(!$user_cnfm) die($welcome);
$auth = get_object_vars(wp_authenticate($user_login, $user_pass));
if(array_key_exists('errors',$auth)) die("Password Error");
wp_set_auth_cookie( $userID, true);
wp_set_current_user($userID, $user_login);
}
update_user_meta( $userID, 'customer', 1);
}
else
{ $buyer_name = "$buyer_first $buyer_last";
$ship_to_name = "$first_name $last_name";
if($ship_to_self)
{ foreach ( $ctry_opts as $key=>$value ) if (strcasecmp($country, $value) == 0) $buyer_country = $key;
$buyer_address1 = $address1;
$buyer_address2 = $address2;
$buyer_city = $city;
$buyer_region = $state;
$buyer_postal_code = $zip;
$buyer_ctry_code =strtolower($country);
} else foreach ( $ctry_opts as $key=>$value ) if ($buyer_ctry_code == $value) $buyer_country = $key;
$userdata = $user_cookie = array(
'user_login' => $email,
'user_email'=> $email,
'user_pass'=>$user_pass,
'first_name'=>$buyer_first,
'last_name'=>$buyer_last,
'display_name' =>$buyer_name,
'address1' => $buyer_address1,//null if not ship to self
'address2' => $buyer_address2,//null if not ship to self
'city' => $buyer_city,//google guess if not ship to self
'region' => $buyer_region,//google guess if not ship to self
'postal_code' => $buyer_postal_code,//null if not ship to self
'country' => $buyer_country,//google guess if not ship to self
'ctry_code' => $buyer_ctry_code,//google guess if not ship to self
'customer' => '1'
);
$userID = wp_insert_user( $userdata );
if(!$user_is_admin)
{ wp_set_auth_cookie( $userID, true);
wp_set_current_user($userID, $email);
}
unset($user_cookie['user_login'],$user_cookie['user_pass'],$user_cookie['display_name']);
setcookie('AMART_CUSTOMER', serialize($user_cookie), time()+60*60*24*180, COOKIEPATH, COOKIE_DOMAIN);
}
}
if(is_user_logged_in())
{ if(!$user_is_admin) $userID = $current_user->ID;
$cart_id = $item_name;
$cart = $wpdb->get_row("SELECT * FROM {$wpfx}amt_carts WHERE cart_id = '$cart_id'", ARRAY_A);
if( $cart['host_checkout'] && isset($store_options->paypal_live) && $store_options->paypal_live !=='false')
$host_checkout = true;
$ship_to = serialize( array('first_name' => $first_name,'last_name' => $last_name,'address1' => $address1,'address2' => $address2,'city' => $city, 'state' => $state,'postal_code' => $zip,'country' =>$country));
$attributes = array('ship_to' => $ship_to, 'customer_id'=>$userID, 'checkout_date'=>$now);
$where = array('cart_id' => $cart_id);
$wpdb->update("{$wpfx}amt_carts", $attributes,$where);
}
?>
This is, I believe, due to a difference in PHP configuration. I cannot tell you more without knowing the full list of POST variables, besides broad guidelines.
This is what I assume is causing the issue:
You're assigning $_POST[key] to $key. This is fine
You're then looping through $buyers. If $_POST["buyers"] was not set or not an array, this will throw a notice.
A notice is an echo. You are not in an output buffering context, which therefore causes the headers to also be sent. If the headers are sent and you later send a cookie or other header info, you get the warning you are getting.
One of your dev environments probably has error_reporting set to E_NONE. Check this in the PHP information. If set to E_NONE, your code will work. If not, you will get that message. Consider always checking for the existence and correct type of your functions. A good way to do so concisely is as follows:
foreach (((array)$buyers) as $v) {
if $buyers is undefined, you will get an empty array. If buyers had one element you will have an array with one element. Otherwise, you will get the array you had.
Okay...
As much as Sébastien's answer made good sense (and was even correct in some regard), and grateful as I am to him for pointing out that Notice level errors are essentially an echo causing headers to be sent, and for demonstrating a new way (to me) of ensuring an array exists in a foreach loop... the problem actually appears to stem from the differences between Linux and Windows newline characters, which I discovered when opening the hosted "cart_add.php" (from FileZilla ftp ) for editing in Notepad++. I was surprised to find extra newlines for every line of code. (Not the way the file was written). On a whim, I highlighted the first doubled newline, and replaced all with \n. Uploading the file (which had worked before on either stack) cart_add.php subsequently returned an error indicating something like no such function - phpob_start.
Aha!
On my windows box, coding in Notepad++ and comparing files in Beyond Compare, the hosted (Linux) files appear to be exact copies of the local source files, however file sizes tell a different story (local / windows files invariably larger) which I suspect is owed to the difference in newline characters (\n\r) in windows, and (\n) on linux.
Now, the problem I face is
Gaining an understanding of why replacing (in Notepad++) all doubled newlines with \n (Linux standard) cased the script to break.
Establishing a means of safeguarding against this issue in the future
but perhaps that is a matter for a separate post?

Yii::app()->lang doesn't work sometimes with LimeSurvey

I am working on a custom script to automatically send out invites and reminders. I have everything working fine up until a point. My function to send invites looks like this:
function sendInvites($iSurveyID) {
$oSurvey = Survey::model()->findByPk($iSurveyID);
if (!isset($oSurvey)) {
die("could not load survey");
}
if(!tableExists("{{tokens_$iSurveyID}}")) {
die("survey has no tokens or something");
}
$SQLemailstatuscondition = "emailstatus = 'OK'";
$SQLremindercountcondition = '';
$SQLreminderdelaycondition = '';
$iMaxEmails = (int)Yii::app()->getConfig("maxemails");
$iMaxReminders = 1;
if(!is_null($iMaxReminders)) {
$SQLremindercountcondition = "remindercount < " . $iMaxReminders;
}
$oTokens = Tokens_dynamic::model($iSurveyID);
$aResultTokens = $oTokens->findUninvited(false, $iMaxEmails, true, $SQLemailstatuscondition, $SQLremindercountcondition, $SQLreminderdelaycondition);
if (empty($aResultTokens)) {
die("No tokens to send invites to");
}
$aResult = emailTokens($iSurveyID, $aResultTokens, 'invite');
}
I also have a simple little file that starts up Yii:
Yii::createApplication('LSYii_Application', APPPATH . 'config/config' . EXT);
Yii::app()->loadHelper('admin/token');
Yii::app()->loadHelper('common');
Everything works as expected up until I actually try to send emails to the tokens. I've tracked the problem down to the following, on of the functions called by emailTokens has this in it:
$clang = Yii::app()->lang;
$aBasicTokenFields=array('firstname'=>array(
'description'=>$clang->gT('First name'),
'mandatory'=>'N',
'showregister'=>'Y'
),
The Yii::app()->lang part seems to be causing issues because then php is unable to call the gT method. However, when LimeSurvey is running "properly" this never happens. I can't even seem to find where "lang" is in the LimeSurvey source.
What can I do to make it work?
Why do you make it so hard on yourself and not use the RemoteControl2 API ?
See http://manual.limesurvey.org/wiki/RemoteControl_2_API#invite_participants
On that page you will also find a PHP example script.
maybe
Yii::import('application.libraries.Limesurvey_lang');
$clang = new Limesurvey_lang($oTokens->language);

Categories