I need your help. I'm writing a iCal .ics File-format with a php function.
If I open the .ics file with iCal it says the application says: "Calendar can’t read this calendar file. No events have been added to your calendar."
However if I validate the file with an online .ics validator it says everything should be fine except the line endings. The validator says this:
Your calendar is using an invalid newline format. Make sure to use
\r\n to end lines rather than just \n (RFC 2445 §4.1).
Congratulations; your calendar validated!
But I'm not sure if this is the "real" problem why my iCal can't read the file.
First off, I wonder how to change this line endings?
<?php
function wpse63611_events_feed_output(){
$filename = urlencode( 'My-Events-' . date('Y') . '.ics' );
// Start collecting output
ob_start();
header( 'Content-Description: File Transfer' );
header( 'Content-Disposition: attachment; filename=' . $filename );
header( 'Content-type: text/calendar' );
header( "Pragma: 0" );
header( "Expires: 0" );
?>
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//<?php get_bloginfo('name'); ?>//NONSGML Events //EN
CALSCALE:GREGORIAN
X-WR-CALNAME:<?php echo get_bloginfo('name');?> - Events
<?php
if ( have_posts() ):
$now = new DateTime();
$datestamp =$now->format('Ymd\THis\Z');
while( have_posts() ): the_post();
global $post;
$uid = md5(uniqid(mt_rand(), true))."#mydomain.com";
$start = unixToiCal(get_event_date($post, true, true), 2.0);
$end = unixToiCal(get_event_end_date($post, true, true), 2.0);
$summary = wpse63611_esc_ical_text(get_the_title());
$description = apply_filters('the_excerpt_rss', get_the_content());
$description = wpse63611_esc_ical_text($description); ?>
BEGIN:VEVENT
UID:<?php echo $uid; ?>
<?php echo "\r\n"; ?>
DTSTAMP:<?php echo $datestamp; ?>
<?php echo "\r\n"; ?>
DTSTART:<?php echo $start; ?>
<?php echo "\r\n"; ?>
DTEND:<?php echo $end; ?>
<?php echo "\r\n"; ?>
SUMMARY:<?php echo $summary; ?>
<?php echo "\r\n"; ?>
DESCRIPTION:<?php echo $description; ?>
<?php echo "\r\n"; ?>
END:VEVENT
<?php endwhile; endif; ?>
END:VCALENDAR
<?php
// Collect output and echo
$eventsical = ob_get_contents();
ob_end_clean();
echo $eventsical;
exit();
}
function unixToiCal( $uStamp = 0, $tzone = 0.0 ) {
$uStampUTC = $uStamp + ($tzone * 3600);
$stamp = date("Ymd\THis\Z", $uStampUTC);
return $stamp;
}
function wpse63611_esc_ical_text( $text='' ) {
$text = str_replace("\\", "", $text);
$text = str_replace("\r", "\r\n ", $text);
$text = str_replace("\n", "\r\n ", $text);
return $text;
}
?>
Can you see any problem with this? What could cause the calendar not to work?
UPDATE
Well, I fixed the line endings and the calendar validates fine now. So no errors when validating it, but I still can't get it working in iCal. When I open it it still says the calendar file is not readable. Here is the actual file that is generated by my script … http://cl.ly/383D3M3q3P32
looking at your ical file brings 2 topics:
as mentionned above, all your lines need to be ended by \r\n (i.e. you need to ensure
BEGIN:VCALENDAR
VERSION:2.0
..
are also ended properly
2.you need to escape commas in text (see RFC5545 §3.3.11: https://www.rfc-editor.org/rfc/rfc5545#section-3.3.11 )
you can also run those through online icalendar validators see this post answer: https://stackoverflow.com/a/4812081/1167333
You have the wpse63611_esc_ical_text() function to normalize output but you only apply it to some output fragments. The funny thing is that such function expects Unix-style input ("\n") but the whole mechanism relies in saving your source code as Windows-style ("\r\n"). Additionally, you sometimes call the function twice on the same text.
I believe the root problem is the you don't really know what a line ending is. When you hit your keyboard's Enter key, you'll actually get a different character depending on whether your computer runs Windows or some kind of Unix (such as Linux or MacOS). On Windows, you'll actually get two characters, represented as "\r\n" in PHP. On Unix, you'll get one character, represented as "\n" in PHP. If your editor is good enough, it'll allow you to save the file with the line ending of your choice, no matter what your computer runs. Check the "Save as" dialogue for further info.
Since you aren't actually typing the ICS file, you need to ensure that PHP generates the appropriate characters. The simplest way is to type and save the source code as you please and then convert the complete output once:
$output = strtr($output, array(
"\r\n" => "\r\n",
"\r" => "\r\n",
"\n" => "\r\n",
));
You'll probably need to clean up your code first.
Related
I'm trying to create an ajax function that retrieve an e-mail template and send it. This is my code so far:
<?php
add_action('wp_ajax_nopriv_test_function', 'test_function');
add_action('wp_ajax_test_function', 'test_function');
function test_function() {
$content = Array(
"name"=>$_POST['name'],
"email"=>$_POST['email'],
);
ob_start();
include("../emails/email_template.php");
$message = ob_get_contents();
ob_end_clean();
$headers[] = 'Content-Type: text/html; charset=UTF-8';
wp_mail($_POST['email'], "[E-mail subject] This is a test", $message, $headers);
$result['feedback'] = "All OK!";
}
wp_send_json($result);
die();
}
?>
The ajax call works. I get the response.feedback properly, even knowing it's not "All OK!".
You can analyze email_template.php by clicking here. It's basically a resposive e-mail template that receives some PHP variables from $contents. See line 313.
Unfortunately, I'm getting this error:
PHP Fatal error: Unknown: Cannot use output buffering in output buffering display handlers in Unknown on line 0, referer: http://example.com/.
I've searched here and in other sources, but didn't found an proper answer. Am i missing something? Is this even possible?
Thanks in advance :)
Sorry for posting this as an answer but I don't have enough points to post a comment. I have been following this question for weeks as I am extremely curious as to the critical difference between the working code and the non-working code. I absolutely don't understand what is wrong with the original solution. For my curiosity can you add the following code before the call to ob_start()
error_log( 'before ob_start(): ' . print_r( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), true ) );
The error you are getting should only occur if ob_start() is called in an output buffering callback. I really don't see how this can happen in your given code but maybe the backtrace will show something.
I know several weeks have passed and maybe you have lost interest in this problem but hopefully you are like me and still would like to understand why. I have looked at this code many times and cannot see any problem and would really like to understand what is wrong.
Thanks in advance for any consideration.
UPDATE:
I tested your code using the following plugin:
<?php
/*
Plugin Name: Gruby's Problem
*/
add_action('wp_ajax_nopriv_test_function', 'test_function');
add_action('wp_ajax_test_function', 'test_function');
function test_function() {
$content = Array(
"name"=>$_POST['name'],
"email"=>$_POST['email'],
);
ob_start();
include("email_template.php");
$message = ob_get_contents();
ob_end_clean();
$headers[] = 'Content-Type: text/html; charset=UTF-8';
wp_mail($_POST['email'], "[E-mail subject] This is a test", $message, $headers);
$result['feedback'] = "All OK!";
wp_send_json($result);
die();
}
add_shortcode( 'send_gruby_ajax_test', function() {
?>
<button id="gruby_send_ajax_test">Send Gruby Ajax Test</button>
<script>
jQuery( 'button#gruby_send_ajax_test' ).click( function() {
jQuery.post( '<?php echo admin_url( 'admin-ajax.php' ); ?>', { action: 'test_function', name: 'Magenta', email: 'xxx#xxx.com' }, function( r ) {
console.log( r );
} );
} );
</script>
<?php
} );
?>
I had to fix 2 problems - removed an extraneous '}' after the line
$result['feedback'] = "All OK!";
and changed line 313 in email_template.php
Your order has shipped, <?php echo $content['nome'];?>!
to
Your order has shipped, <?php echo $content['name'];?>!
After these changes the code worked as expected. So your original code does work in my environment. I am still curious as to how you are getting your code to fail. Can you run my plugin and see if it causes the same error as before. To test the plugin you need to create a page with post content:
[send_gruby_ajax_test]
This will display a button which when clicked will send the AJAX request for the 'test_function'.
Changed my code to:
<?php
add_action('wp_ajax_nopriv_test_function', 'test_function');
add_action('wp_ajax_test_function', 'test_function');
function test_function() {
ob_start();
$content = Array(
"nome"=>$_POST['nome_registro'],
"email"=>$_POST['email_registro'],
);?>
<?php include(ABS_PATH_DEFINED_ELSEWHERE."my-plugin/inc/emails/email_template.php"); ?>
<?php
$message = ob_get_contents();
ob_end_clean();
$headers[] = 'Content-Type: text/html; charset=UTF-8';
wp_mail($_POST['email'], "[E-mail subject] This is a test", $message, $headers);
$result['feedback'] = "All OK!";
}
wp_send_json($result);
die();
}
?>
And worked like a charm :)
Anyone has an explanation of why the previous code didn't worked?
In my PHP script I try to send utf8 characters to the google translate website for them to send me a translation of the text, but this doesn't work for UTF8 characters such as chinese, arabic and russian and I can't figure out why. If I try to translate 'как дела' to english I could use this link: https://translate.googleapis.com/translate_a/single?client=gtx&sl=ru&tl=en&dt=t&q=как дела
And it would return this: [[["how are you","как дела",,,1]],,"ru"]
A fine translation, exactly what I wanted, but if I try to recreate it in PHP I do this (I used bytes in the beginning because my future script will use bytes as starting point):
<?php
$bytes = array(1082,1072,1082,32,1076,1077,1083,1072); // bytes of: как дела
$str = "";
for($i = 0; $i < count($bytes); ++$i) {
$str .= json_decode('"\u' . '0' . strtoupper(dechex($bytes[$i])) . '"'); // returns string: как дела
}
$from = 'ru';
$to = 'en';
$url = 'https://translate.googleapis.com/translate_a/single?client=gtx&sl=' . $from . '&tl=' . $to . '&dt=t&q=' . $str;
$call = fopen($url,"r");
$contents = fread($call,2048);
print $contents;
?>
And it outputs: [[["RєR RєRґRμR ° \"° F","какдела",,,0]],,"ru"]
The output doesn't make sense, it appears that my PHP script send the string 'какдела' to translate to english for me. I read something about making UTF-8 characters readable for google in a URI (or url). It says I should transfer my bytes to UTF-8 code units and put them in my url. I didn't yet figure out how to transfer bytes to UTF-8 code units, but I first wanted to try if it worked. I started by converting my text 'как дела' to code units (with percents for URL) to test it myself. This resulted in the following link: https://translate.googleapis.com/translate_a/single?client=gtx&sl=ru&tl=en&dt=t&q=%D0%BA%D0%B0%D0%BA+%D0%B4%D0%B5%D0%BB%D0%B0
And when tested in browser it returns: [[["how are you","как дела",,,1]],,"ru"]
Again a fine translation, it appears it works so I tried to implement it in my script with the following code:
<?php
$from = 'ru';
$to = 'en';
$text = "%D0%BA%D0%B0%D0%BA+%D0%B4%D0%B5%D0%BB%D0%B0"; // code units of: как дела
$url = 'https://translate.googleapis.com/translate_a/single?client=gtx&sl=' . $from . '&tl=' . $to . '&dt=t&q=' . $text;
$call = fopen($url,"r");
$contents = fread($call,2048);
print $contents;
?>
This script outputs: [[["RєR Rє RґRμR ° \"° F","как дела",,,0]],,"ru"]
Again my script doesn't output what I want and what I get when I test these URL's in my own browser. I can't figure what I'm doing wrong and why google responds with a mess up of characters if I use the link in my PHP file.
Does someone know how to get the output I want? Thanks in advance!
Updated code to set strings in UTF-8, (not working)
I added a lot of settings at the top of the PHP file to make sure everything is in UTF8 format. Also I added a mb_convert_encoding halfway but the output keeps being wrong. The fopen function doesn't send the right UTF-8 string to google.
Output I get:
URL: https://translate.googleapis.com/translate_a/single?client=gtx&sl=ru&tl=en&dt=t&q=%D0%BA%D0%B0%D0%BA%20%D0%B4%D0%B5%D0%BB%D0%B0
Encoding: ASCII
File contents: [[["RєR Rє RґRμR ° \"° F","как дела",,,0]],,"ru"]
Code I use:
<?php
header('Content-Type: text/html; charset=utf-8');
$TYPO3_CONF_VARS['BE']['forceCharset'] = 'utf-8';
mb_internal_encoding('UTF-8');
mb_http_output('UTF-8');
mb_http_input('UTF-8');
mb_language('uni');
mb_regex_encoding('UTF-8');
ob_start('mb_output_handler');
$from = 'ru';
$to = 'en';
$text = rawurlencode('как дела');
$url = 'https://translate.googleapis.com/translate_a/single?client=gtx&sl=' . $from . '&tl=' . $to . '&dt=t&q=' . $text;
$url = mb_convert_encoding($url, "UTF-8", "ASCII");
$call = fopen($url,"r");
$contents = fread($call,2048);
print 'URL: ' . $url . '<br>';
print 'Encoding: ' . mb_detect_encoding($url) . '<br>';;
print 'File contents: ' . $contents;
?>
Solved! I got the hint from another not from these forums to look at this stackoverflow post about setting a user agent. After some more research I found that this answer was the solution to my problem. Now everything works fine!
I have a PHP mail script which is basically the following:
$result = mail($to, $subject, $message, $headers);
if(!$result) {
echo "Error";
} else {
echo "Success";
}
The $message is a HTML email that mostly renders fine in my email client except the images seem to only load sporadically.
The images are all like so:
<img src='http://www.mywebsite.com/media/twitter.png' />
I don't understand why some would load and some wouldn't, when they are all set up the same way.
I've read that it's better to embed images into the email as attachments but I'm unsure how to do this. It seems that you add a line like so:
<img src='cid:123456789'>
But what does this reference? How would I encode an image like this?
Any help would be appreciated!! Thanks
You would have to base64 encode the file.
I found a code example on github. I have not tested it myself but should give you a good nudge in the right direction...
$picture = file_get_contents($file);
$size = getimagesize($file);
// base64 encode the binary data, then break it into chunks according to RFC 2045 semantics
$base64 = chunk_split(base64_encode($picture));
echo '<img src="data:' . $size['mime'] . ';base64,' . "\n" . $base64 . '" ' . $size[3] . ' />', "\n";
Source : https://gist.github.com/jasny/3938108
Just as a side note. Are the images that you are using web optimised? Large images might be blocked by email clients, or just not downloaded by email clients.
Hope to get some help with a piece of code, I am using a theme for wordpress which sets the mail headers to text/html, this causes some problems with plain text mail ex. linebreaks don't show anymore.
I tried setting :
} else {
return 'text/plain';
}
but I don't know php very well so I don't know where to place it to make it work. I would like to set the text/plain for mails not defined.
this is the code for the wp header :
/**
* filter mail headers
*/
function wp_mail($compact) {
if (isset($_GET['action']) && $_GET['action'] == 'lostpassword') return $compact;
if ($compact['headers'] == '') {
//$compact['headers'] = 'MIME-Version: 1.0' . "\r\n";
$compact['headers'] = 'Content-type: text/html; charset=utf-8' . "\r\n";
$compact['headers'].= "From: " . get_option('blogname') . " < " . get_option('admin_email') . "> \r\n";
}
$compact['message'] = str_ireplace('[site_url]', home_url() , $compact['message']);
$compact['message'] = str_ireplace('[blogname]', get_bloginfo('name') , $compact['message']);
$compact['message'] = str_ireplace('[admin_email]', get_option('admin_email') , $compact['message']);
$compact['message'] = html_entity_decode($compact['message'], ENT_QUOTES, 'UTF-8');
$compact['subject'] = html_entity_decode($compact['subject'], ENT_QUOTES, 'UTF-8');
//$compact['message'] = et_get_mail_header().$compact['message'].et_get_mail_footer();
return $compact;
}
Instead of changing that, change your plain line breaks to html.
$message=nl2br($message); // of course use your var name.
That way you get to keep a standard format for email as well. plain text has nothing so special to need a separate header in this case. This function will convert all line breaks to html version.
Other than new lines most of your plain text will hold its formatting even in html because it has no special tags.
Here is how you will place it
function wp_mail($compact) {
// leave your existing code intact here, don't remove it.
$compact["message"]=nl2br($compact["message"]);
return $compact;
}
I'm using PHP and html in order to develop a simple mechanism that creates reports and send them by Email.
I use the function file_put_contents() and the function ob_get_contents() as a parameter in order to create an html file which I use to send by the mail.
I realized that if I use ob_get_contents() without using ob_start() it simply takes all the file and put it to an html file. This is not good for me since I want only parts of the file to be in the generated html. To be more clear my code looks something like this:
<html and php code I want to include in my html file>
.
.
<html and php code I don't want to include in my html file>
.
.
<html and php code I want to include in my html file>
.
.
<html and php code I don't want to include in my html file>
.
.
.
file_put_contents('report.html', ob_get_contents());
$message = file_get_contents('report.html');
mail($to, $subject, $message, $Headers);
So how do I choose only the parts I want to be included in report.html?
Thank you very much!
You are doing it unnecessarily difficult, you don't need external files to generate your report. Take a look at this:
<?php
$report = '';
// ...
// Code not included in your report
// ...
ob_start();
// ...
// HTML and PHP code you want in your report
// ...
$report .= ob_get_clean();
// ...
// Code not included in your report
// ...
ob_start();
// ...
// HTML and PHP code you want in your report
// ...
$report .= ob_get_clean();
// Mail it
mail($to, $subject, $report, $headers);
?>
EDIT: Regarding to OP's comment.
What you need is ob_get_flush() instead of ob_get_clean(). Both return the buffer contents as a string, but the first one dumps it to the script output while the second one empties the buffer instead.
May or May not Help
I always handle this the same way I load pages when using vanila PHP, with a snippet! The following is one I've kept around forever and used. It has 2 possible primary functions. One is to load a view (html page), the other is to get a page of html as a string for such things as inclusion in an email body.
For example:
// will load a page into the clients browser
// note the page location would indicate it will get the file "index.html" from "$_SERVER["DOCUMENT_ROOT"] . '/views/'"
loadView('/views/index.html');
// or, as would be more helpful to you
$msgHTML = loadView('/views/index.html', NULL, TRUE);
The TRUE param simply tells the function to only return a string and not echo anything to a client.
The NULL param you see there is for an array of data to be passed through. For instance, say you have an html page with a table you want populated for a database call. You would simply make your call, place your return in an array and then add to page.
$arr = array( 'key' => 'value' );
$msgHTML = loadView('/views/index.html', $arr, TRUE);
// then in the index.html
<div><?= $key; ?></div>
This makes it very easy to build anything HTML you need for an Email.
Snippet
if (!function_exists('loadView')) {
function loadView($file, $data=NULL, $get=FALSE) {
if (!empty($data)) extract($data);
ob_start();
if (is_file($file)) include($file);
$return = ob_get_clean();
if (!$get) echo($return);
return $return;
}
}
Thus you could do something like:
$htmlFirst = loadView('report.html', NULL, TRUE);
$msgFirst = 'Some message string here';
$htmlSecond = loadView('report2.html', NULL, TRUE);
$msgSecond = 'Some message string here';
$body = $htmlFirst . $msgFirst . $htmlSecond . $msgSecond;
mail($to, $subject, $body, $Headers);