Hello Community
I am trying to replace text in email templates using data from database.
This is my code where %<$receiver>% works and %<$sender>% doesnt.
If you have any knowledge what im doing wrong or hints what i should change i would appreciate it.
public static function getRowValue($type, $receiver, $sender, $job) {
$model = self::where('type', $type)->where('status', DEFAULT_TRUE)->first();
if ($model) {
$subject = $model->subject;
$page = "admin.templates.email.email";
$email = $receiver->email;
$model->content = html_entity_decode($model->content, ENT_COMPAT, 'UTF-8');
$sender_replaced = ($sender) ? str_replace('%<$sender>%',$sender->name, $model->content) : $model->content;
$receiver_replaced = ($receiver) ? str_replace('%<$receiver>%',$receiver->name,$sender_replaced) : $sender_replaced;
$job_replaced = ($job) ? str_replace('%<$job>%',$job->name,$receiver_replaced) : $receiver_replaced;
$message = $job_replaced;
$email_data = [];
$email_data['message'] = $message;
Helper::send_email($page,$subject,$email,$email_data);
return $model->message;
}
return "";
}
If you need additional informations please feel free to ask, hope my example will help someone else who is having hard time solving this issue.
<?= $email_data['message'] ?: "NO MESSAGE" ?></span></p>
Line how i call data in email template.
There was nothing wrong with this part of code, ive missed data flow logic in controllers where i was pulling out $sender and it hasnt been accepted by user.
I have to change data flow logic now to make it work.
Thanks all on help.
Related
I am trying to make sms sending functions in php but it not works from my ends error = env function I can use file_get_contents but this is not good idea to achieve this. I want to learn through bugs and mistakes. So stackoverflow is best for me as I believe.
Please now let me know how I make this correct.
Where I am doing mistake please correct me.
Thank you very much.
function sendMessage($route, $sender, $mobile, $message) {
$message = urlencode($message);
$url = sprintf("https://www.sms4india.com/send_sms?api_token=%s&route=%s&senderid=%s&number=%s&message=%s",
env('MY_API_HERE'), $route, $sender, $mobile, $message);
return $output;
}
sendMessage('4', 'WHTSAP', 'MOBILE_NO', 'HELLO WORLD');
I'm working on finishing a simple form for contest entry submissions. Normally we'd use the userforms SilverStripe extension (https://github.com/silverstripe/silverstripe-userforms) but that is a page type, and this form needs to be one section in a page type. I have got something setup right now that may not be be perfect but has done the job before. However, the inclusion of an image upload field is throwing me off.
Typically, outside of a CMS and without a plugin, I would use $_FILES['upload_field_name']['type or name or size'] to validate an upload field. But I am not sure I am able to use that here since I am working within a CMS.
This is what I have so far:
The jQuery + AJAX code:
(function ($) {
$(document).ready(function () {
var SubmitBtn = $('.SubmitEntry');
var FirstName = $('.FirstNameTxt');
var LastName = $('.LastNameTxt');
var EmailAddress = $('.EmailTxt');
var Image = $('.UploadImage');
SubmitBtn.on('click', function (e) {
var required = [FirstName, LastName, EmailAddress, Image];
var containsError = false;
e.preventDefault();
for (i = 0; i < required.length; i++) {
var input = required[i];
if ((input.val() == "")) {
containsError = true;
input.addClass('error-field');
$('.Error-Msg').show();
} else {
input.removeClass('error-field');
}
}
if (containsError == false) {
$('.Error-Msg').hide();
ajax_script();
}
});
function ajax_script() {
var form_data = {
firstname: FirstName.val(),
lastname: LastName.val(),
useremail: EmailAddress.val(),
image: Image.val()
};
$.ajax({
type: "POST",
url: "/home/ContestSubmission",
data: form_data,
dataType: "json"
}).done(function (response) {
for (var i = 0; i < response.length; i++) {
var status = response[i].status;
if (status == "error") {
var errorMessage = response[i].message;
if (response[i].field == "email") {
$('.Error-Msg').show();
$('.Error-Msg').html("<br />" + errorMessage.message);
EmailAddress.addClass("error-field");
}
/* else if image is not valid, show error message */
}
else if (status == "success") {
$('#contest-submissions')[0].reset();
alert("Thank you for your entry!");
}
}
});
}
});
}(jQuery));
$ = jQuery.noConflict();
This is the ContestSubmisson function located within Page.php:
public function ContestSubmission()
{
$fname = $this->getRequest()->postVar('firstname');
$lname = $this->getRequest()->postVar('lastname');
$useremail = $this->getRequest()->postVar('useremail');
$image = $this->getRequest()->postVar('image');
$errorField = "";
$return = array();
if (!filter_var($useremail, FILTER_VALIDATE_EMAIL)) {
$validatonStatus = "error";
$errorField = "email";
$errorList = "The email is not in the correct format. Please re-enter it.";
} /* else if $image is not valid i.e. size or file type */
else {
$contestEntry = new ContestEntrySubmission();
$contestEntry->FirstName = $fname;
$contestEntry->LastName = $lname;
$contestEntry->EmailAddress = $useremail;
$contestEntry->UploadedImage = $image;
$contestEntry->write();
$validatonStatus = "success";
$errorField = "";
$errorList = "";
$from = 'secret#secret.com';
$to = '[testing email address]';
$subject = 'Contest Entry Submission';
$body = "Below is the contest entry submission information:" . "<br /><br />";
$body .= "<strong>First Name:</strong> " . $fname . "<br/>" . "<strong>Last Name:</strong> " . $lname . "<br/>" . "<strong>Email:</strong> " . $useremail . "<br />" . "Image File: " . $image;
$email = new Email($from, $to, $subject, $body);
$email->replyTo($useremail);
$email->send();
}
$return[] = array(
"status" => $validatonStatus,
"field" => $errorField,
"message" => $errorList
);
return json_encode($return);
}
What is throwing me off is what to do with the image when it gets to the Page.php function shown above. Normally the setup above works fine in SilverStripe but now with the addition of an image upload field, I'm confused on what to change/add/edit to validate the file size and type. Examples I have found tend to deal with creating or working with an Upload Field inside of a page template type, which is not what I am looking for here.
Any suggestions would be great. I am just so used to not building these things inside a CMS. I may be making this much more difficult than needed therefore...any tips?
You mentioned that you just stick to JS, HTML, & PHP - that's cool because using the SilverStripe framework is all PHP! Learning how to make a custom form provides greater flexibility over UserForms, so it's handy to add to the ol' utility belt. It sounds like this is a good opportunity to learn something new, which is always fun :)
First and foremost, the tips to things you're missing is easy! There is a vast set of documentation for developers at https://docs.silverstripe.org/
I highly recommend the forms introduction section, which covers off basic form creation. Once you've defined the fields, SilverStripe will do all the rest for you (all the validation & all), leaving you just to deal with what happens after a valid submission.
Depending on your skill level, you may like to begin with one of the great tutorials - the text one is great and covers all the basics (even though it's quite old), you can access it from the tutorials link in the left menu. There's also a more modern video tutorial at http://www.silverstripe.org/learn/lessons/introduction-to-frontend-forms which can be useful for explaining some of the finer points that sometimes gets beginners a bit turned around. You choose which is best for you. There's also API documentation if you'd prefer to just jump straight into the deep end - you'll find that link on the top navigation.
It seems like you've got the front end part down pat, and SilverStripe doesn't really get in the way here at all. Plus it sounds like you're familiar with styling the UserForms, so that's a great bonus already. So at this point I'll assume that nothing here will be new to you on the front end - the default form rendering won't startle you.
Making the form itself is really easy. I'll use your example and try to cobble a very untested example using the inbuilt Form class (which is part of the Framework, not the CMS - a small but I think important distinction to make).
A form submits to itself, and takes care of all the validation for you. Neat! We'll use your contestSubmission function.
public function contestSubmission() {
return Form::create(
$this,
__FUNCTION__,
FieldList::create(
TextField::create('firstname', 'First Name'),
TextField::create('lastname', 'Last Name'),
EmailField::create('useremail', 'Email'),
FileField::create('image', 'Image')
),
FieldList::create(
FormAction::create('emailValidEntry', 'Submit Entry')
),
RequiredFields::create(array('firstname', 'lastname', 'useremail', 'image'))
);
}
You may be wondering what all this ClassName::create($stuff) business is. Think of it as new ClassName($stuff), but with the advantage that you can chain it (like how $('.selector') gives you a new jQuery object, and you can $('.selector').doThings().to().it() - using the ::create() method we can do the same in PHP, as opposed to creating into a variable then operating on it. A nice thing SilverStripe gives us :)
Once your form is all defined (including adjusting the required fields) you only need to worry about what to do with the info once it's collected, which removes a lot of the burden. Cool. We'll create another function for that: emailValidEntry (the name must match that given in the FormAction declared above) - it basically just contains the bit in your else block. I'll tidy it up for the Email bits to use templates too though.
public function emailValidEntry($data, $form) {
$contestEntry = ContestEntrySubmission::create();
$form->saveInto($contestEntry);
$contestEntry->write();
$data['ImageEmail'] = base64_encode(file_get_contents($data['UploadedImage']['tmp_name']));
Email::create(
'from#address', //from
'testing#address', //to
'Contest Entry Submission' //subject
)->setTemplate('ContestEntryEmail')
->populateTemplate(ArrayData::create($data))
->replyTo($data['EmailAddress'])
->attach($contestEntry->UploadedImage()->Filename)
->send();
$form->setMessage('Thank you for your submission!', 'good');
$this->redirectBack();
}
The 'do stuff with my form submission' method takes 2 arguements: an associative array of FieldName => Value, and the full instance of the Form object itself, with all the fields having their submitted values set.
OK, so there's a slight bit of complexity going on to get the base64 encode for the email embed, although I think attaching it is safer so I've shown that too.
The template ContestEntryEmail.ss (placed somewhere in your theme, maybe templates/emails or something) might look something like:
Below is the contest entry submission information: <br /><br />
<strong>First Name:</strong> $FirstName<br />
<strong>Last Name:</strong> $LastName<br />
<strong>Email:</strong> $EmailAddress<br />
<strong>Image File (see attached if not shown here):</strong><img src="$ImageEmail" alt="Submitted Image" />
Where $ImageEmail uses the base64 encoded image data to embed directly into the email.
But it seems like the recommended way would be to just attach it. From a quick google it seems that Outlook and Gmail both will block the inlined version shown in the template there.
Hopefully this is all rather not too TL;DR & helpful - good luck & happy form creating!
My top tip would be to check the MIME type of the image with PHP. This checks the actual file make-up to verify that it is in fact an image and not just a random file that has been renamed to have an image extension
Here is some sample code you could include
// Check for valid upload
if (($_FILES["picture"]["error"]) == "4") //no file detected
// error codes explained at http://php.net/manual/en/features.file-upload.errors.php
{
echo "<h3>No file has been detected. Please select a file to upload.</h3>";
exit();
}
if ($_FILES['picture']['error'] == "1" || $_FILES['picture']['error'] == "2" || ($_FILES['picture']['size'] > 71680)) //define your max_file_size 71680 is 70KB - not very big
//file too big
{
echo("<h3>Upload unsuccessful: File exceeds maximum allowed size</h3>");
echo("<p>Try compressing your photo or upload a different photo</p>");
exit();
}
//if file present and of acceptable size
if ($_FILES['picture']['error'] != "1" && $_FILES['picture']['error'] != "2" && $_FILES['picture']['error'] != "4" && $_FILES['picture']['size'] < 71680) {
//then check to see if the type of file is an image file
//check for mimetype as at http://php.net/manual/en/features.file-upload.php
$fileinfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$fileinfo->file($_FILES['picture']['tmp_name']),
array(
'png' => 'image/png','gif' => 'image/gif','jpg' => 'image/jpeg',
),
true
))
{
echo('<h3>The file chosen is not an acceptable image file (.jpg/.png or .gif)</h3>');
echo("Please upload a .jpg/.png or .gif image");
exit();
}
}
for our company intranet, I created a page with a Form to create an E-Mailing from the frontend (Create a New E-Mailing Page).
The page should be Live after saving the form.
I did it like this, but I think I made a mistake somewhere. Because KW1, KW2, Date and SendDate are only visible on the frontend if I go to the backend and click publish again.
public static $allowed_actions = array(
'MailingForm'
);
public function MailingForm() {
$date = new DateField('EndDate', 'Gültig bis');
$date->setConfig('showcalendar', true);
$sendDate = new DateField('SendDate', 'Versanddatum');
$sendDate->setConfig('showcalendar', true);
$fields = new FieldList(
new TextField('Title', 'Title'),
new TextField('KW1', 'Start KW'),
new TextField('KW2', 'End KW'),
$date,
$sendDate
);
$actions = new FieldList(
new FormAction('createMailing', 'Erstellen')
);
//$validator = new RequiredFields('Title');
return new Form($this, 'MailingForm', $fields, $actions);//, $validator);
}
public function createMailing($data, $form) {
$member = Member::currentUser();
$filter = new URLSegmentFilter();
$page = new Mailing();
$form->saveInto($page);
$page->PublisherID = $member->ID;
$page->AuthorID = $member->ID;
$page->ParentID = $this->ID;
$page->URLSegment = $filter->filter($page->Title);
$page->writeToStage('Stage');
$page->publish('Stage', 'Live');
// EMAIL BEG
$email = new Email();
$email->setTo('mail#mail.de');
$email->setFrom('intranet#mail.de');
$email->setSubject('Neues E-Mailing für '.$this->Title);
$messageBody = "
<p>Neues E-Mailing wurde angelegt und wartet auf Freischaltung</p>
<p><strong>Name:</strong> {$data['Title']}</p>
<p><strong>KWs:</strong> {$data['KW1']} - {$data['KW2']}</p>
<p><strong>Gültig bis:</strong> {$data['EndDate']}</p>
<p><strong>Versanddatum:</strong> {$data['SendDate']}</p>
";
$email->setBody($messageBody);
$email->send();
// EMAIL END
return $this->redirect($this->Parent()->URLSegment.'/'.$this->URLSegment.'/'.$page->URLSegment);
}
If I replace $page->writeToStage('Stage');
$page->publish('Stage', 'Live'); with $page->write() than the page isn't published if i add $page->write() to the other two than i receive this error
Can't find [Title of Page]/[Page ID] in stage Stage
Can someone help me here?
Thank you in advance
To break down the problem again
If I publish the Page with
$page->write();
$page->writeToStage('Stage');
$page->publish('Stage', 'Live');
than all data is submitted correctly but I receive the following error http://www.picbutler.de/bild/301819/erroroirpt.jpg and the page is only saved as live version. In the backend the page is than marked as "deleted from draft". So I think this is the right direction.
If I publish the Page with
$page->writeToStage('Stage');
$page->publish('Stage', 'Live');
I receive no error, the submitted data appears in the backend BUT NOT in the published version. I have to publish the Page again in the backend to make the data visible in frontend.
So any ideas how to fix this?
ok, a million tries later i got it! :)
For everybody else who get stuck on this.
You just need to restore the live page to stage after writing
$page->write();
$page->doRestoreToStage();
That's all :)
Many many thanks for your two lines of codes
$page->write();
$page->doRestoreToStage();
I have been struggling with SiteTree table for a last few days,trying to write a new record to this table.
One important point is never use SiteTree class directly otherwise it won't work
eg $mySiteTree = new SiteTree(); (Bad)
Create a new page extends SiteTree() , say, Mypage
$mypage = new Mypage();
$mypage->Title = 'My Page';
$mypage->URLSegment = 'testing';
$mypage->ShowInMenus = '1';
$mypage->Version = '1';
$mypage->ParentID = '24';
$mypage->write();
$mypage->doRestoreToStage();
Enjoy!!!!
I need to email all my users a daily product list in cakePHP 2.
I have the following code to get all the users emails.
$users = $this->User->find('all', array('fields' => array('email')));
foreach ($users as $user) {
$this->Email->reset();
$this->Email->from = '<no-reply#test.com.au>';
$this->Email->to = $user['email'];
$this->Email->subject = "Daily Products" ;
$this->Email->sendAs = 'html';
$this->Email->send();
}
Now I understand that I could use a html template for this and parse values to it but I really need a foreach loop inside the actual view itself and send the table of products.
What would be the best practise? cakePHP code in the controller or view to get the products?
Thanks
The best practice for this would be to use a shell to send the emails out. To avoid running out of memory you should read the users and their products in chunks and not all at the same time.
Inside the foreach loop you'll need to get the data for each user and set() it as any other variable it will be available in the html template then and you can render all the products there.
Here is some (shortened) code from a shell that processes data:
public function main() {
$this->loop();
}
public function loop() {
try {
while (true) {
if (!$this->poll()) {
$this->out(__('Nothing more to process, sleeping...'));
sleep($this->sleep);
}
}
} catch (Exception $e) {
$this->out($e->getMessage());
$this->log($e->getMessage(), 'processing');
}
}
public function poll() {
$this->out('Polling...');
$result = $this->Model->getFirstUnprocessed();
if ($result === false) {
return false;
}
// do something with the result here
return true;
}
This should be enough to give you an idea. For reading your users in chunks you would need to increment the offset in your find() options. In my case I just check if there is an unprocessed record and if yes i process and and wait a moment to do the next try.
The "view" for e-mails is actually an element. It's under Views/Elements/email. There are 2 folders there html and text, both meant to hold their respective templates.
You can do your foreach in there, then make sure to set the layout in your controller with:
$this->Email->sendAs = 'html'; // Can also be 'text' or 'both' (for multipart).
$this->Email->layout = 'foo'; // Would include Views/Elements/email/html/foo.ctp
Although the Email component is deprecated since CakePHP 2.0 and you should be using the CakeEmail component instead. See the book for more details on how to use that.
I am trying to figure out the best way to send emails from an external template file, at the moment I have a template file that looks like this:
Thank you, your order has been received, someone will review it and process it. No money has been taken from your account.
<?php
echo date('Y-m-d H:i:s');
?>
<pre>
<?php print_r($this->data); ?>
</pre>
And then my send method looks like this:
public function notify($template) {
// get the template from email folder
$path = $_SERVER['DOCUMENT_ROOT'].'templates/email/'.$template.'.php';
if(file_exists($path)) {
ob_start();
require_once($path);
$body = ob_get_contents();
ob_end_clean();
$subject = 'email send';
foreach($this->emailTo as $email)
new Mail($email,$subject,$body);
}
}
This all works fine when I call it like this:
$notifications = new notifications();
$notifications->setData(array('order' => $order->order));
$notifications->addEmail($order->order->email);
$notifications->notify('orderReceived');
However, if I try to make two calls to the "notify" method then the second email is blank, I know this is because the object buffer, but I cannot think of any other way to do it.
Thanks,
Ian
You are using require_once, so the file will only load once. Try require.
Also consider loading a pure text template and use str_replace to replace the variables in the template like this:
$template = "<pre>%DATA%</pre>";
$text = str_replace('%DATA%', $this->data, $template);
I would do this:
Template file
Thank you, your order has been received, someone will review it and process it. No money has been taken from your account.
%s
<pre>
%s
</pre>
Notify function
public function notify($template) {
// get the template from email folder
$path = $_SERVER['DOCUMENT_ROOT'].'templates/email/'.$template.'.php';
if (!file_exists($path)) {
// Return false if the template is missing
return FALSE;
}
// Create the message body and subject
$body = sprintf(file_get_contents($path), date('Y-m-d H:i:s'), print_r($this->data, TRUE));
$subject = 'email send';
// Send the mail(s)
foreach($this->emailTo as $email) {
new Mail($email, $subject, $body);
}
// Return true for success
return TRUE;
}
This will solve the problem - which could be solved anyway by changing require_once to require.
Using require_once means the template file will only be loaded once (clue's in the function name), so the second call will result in a blank body.