how to send scheduled emails with sendGrid and symfony - symfony

I'm building an application with symfony3 in which I have an EmailService basin on SendGridService.
Sending emails is okay, but I want to schedule my emails.
This is SendGridEmailService :
<?php
namespace AppBundle\Services;
use SendGrid;
use Swift_Attachment;
use Swift_Mailer;
use Swift_Message;
use Swift_SmtpTransport;
use Twig_Environment;
class SendGirdEmailService
{
/**
* Library to facilitate email messages being sent out, sendMail deprecated in symfony 1.2
*
* #param string $partial - Array with html and text partials ie array('text'=>'textPartial', 'html'=>'htmlPartial')
* #param array $parameters - Array we will pass into the partials
* #param string $mailFrom - Email source
* #param string $mailTo - Email destination
* #param string $subject - The subject of the email message
* #param array $sgHeaders - What we will be placing in the SMTPAPI header. Must be null or a non-empty array
* #param array $attachments - Email contains the attachments
*/
public static function sendEmail($partials, $parameters, $mailFrom, $mailTo, $subject, $sgHeaders = null, $attachments = null)
{
// verify we have username/password to send out emails - IMPORTANT
/* if (!sfconfig::has('app_sendgrid_username') or !sfconfig::has('app_sendgrid_password')) {
throw new sfException('SMTP username/password is required to send email out');
}*/
$text = null;
$html = null;
if (is_array($partials)) {
// load libraries
//sfContext::getInstance()->getConfiguration()->loadHelpers('Partial');
if (isset($partials['text'])) {
$text = $partials['text'];
}
if (isset($partials['html'])) {
$html = $partials['html'];
}
}
if ($text === null and $html === null) {
throw new sfException('A text and/or HTML partial must be given');
}
try {
/*
* Load connection for mailer
*/
$connection = Swift_SmtpTransport::newInstance('smtp.sendgrid.net', 465, 'ssl')->setUsername('xxxxxx')->setPassword('xxxxxxx');
// setup connection/content
$mailer = Swift_Mailer::newInstance($connection);
$message = Swift_Message::newInstance()->setSubject($subject)->setTo($mailTo);
if ($text and $html) {
$message->setBody($html, 'text/html');
$message->addPart($text, 'text/plain');
} else if ($text) {
$message->setBody($text, 'text/plain');
} else {
$message->setBody($html, 'text/html');
}
// if contains SMTPAPI header add it
if (null !== $sgHeaders) {
$message->getHeaders()->addTextHeader('X-SMTPAPI', json_encode($sgHeaders));
}
// update the from address line to include an actual name
if (is_array($mailFrom) and count($mailFrom) == 2) {
$mailFrom = array(
$mailFrom['email'] => $mailFrom['name']
);
}
// add attachments to email
if ($attachments !== null and is_array($attachments)) {
foreach ($attachments as $attachment) {
$attach = Swift_Attachment::fromPath($attachment['file'], $attachment['mime'])->setFilename($attachment['filename']);
$message->attach($attach);
}
}
// Send
$message->setFrom($mailFrom);
$mailer->send($message);
}
catch (Exception $e) {
throw new sfException('Error sending email out - ' . $e->getMessage());
}
}
}
And this is the function I'm using to send my emails:
SendGirdEmailService::sendEmail(array(
'text' => $htmlContent,
'html' => $htmlContent
),
null,
$this->from,
$to,
$subject,
$sgHeaders = null,
$attachments = null
);
How can I set the parameters to send emails after one hour for example?

I found solution by setting an array with 'sent_at' index with timestamp value like :
SendGirdEmailService::sendEmail(array(
'text' => $htmlContent,
'html' => $htmlContent,
),
null,
$this->from,
$to,
$subject,
$sgHeaders =
array('send_at' => strtotime('+1 day', date_timestamp_get(new \DateTime()) ) ),
$attachments = null
);

Related

Twilio - Laravel issue - Credentials are required to create a Client

I am receiving the following error
[2018-12-18 12:12:46] local.ERROR: Credentials are required to create a Client {"exception":"[object] (Twilio\Exceptions\ConfigurationException(code: 0): Credentials are required to create a Client at C:\wamp64\www\_javid\javid\vendor\twilio\sdk\Twilio\Rest\Client.php:157)
I will include the code below and the source i used to create it. I would like to add, this was all working correctly the other evening.
Today, i merely added a new function to handle the saving of messages to the database. Then i started receiving the above error. Naturally i reverted my changes but still the same error.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Illuminate\Support\Facades\Auth;
use JWTAuth;
use App\Item;
use Log;
use Twilio\Rest\Client;
class MessagingController extends Controller
{
protected $client;
public function __construct(Client $client){
$this->client = $client;
}
/**
* Show the form for creating a notification.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return view('notifications.create');
}
public function sendMessage(request $request){
$details = $request->only('membershipNumber', 'countryCode', 'message');
$user = User::where('membership_number', $details['membershipNumber'])->with('mobile_number')->first();
if(count($user)>0){
$this->messageSaveToDatabase($details, $user);
$this->messageSendToMobile($details, $user);
$this->messageSendToEmail($details, $user);
return response([
'status' => 'success',
'msg' => __('messages.success'),
'response' => $details
], 200);
} else {
return response([
'status' => 'error',
'msg' => __('messages.error')
], 200);
}
}
protected function messageSaveToDatabase($details, $user){
}
protected function messageSendToMobile($details, $user, $imageUrl = null){
$lineBreak = "\n\n";
$phoneNumber = $user->mobile_number->country_code.decrypt($user->mobile_number->number);
$message = "Hi member #".$details['membershipNumber'].$lineBreak.
$details['message'];
$twilioPhoneNumber = config('services.twilio')['phoneNumber'];
$messageParams = array(
'from' => $twilioPhoneNumber,
'body' => $message
);
if ($imageUrl) {
$messageParams['mediaUrl'] = $imageUrl;
}
$this->client->messages->create(
$phoneNumber,
$messageParams
);
}
protected function messageSendToEmail($details, $user){
}
}
I have checked the TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN, these are both correct.
The code was taken from the following guide, i stripped out the subscriber part. Guide from Twilio
one more thing, I found the following Here which suggests i need to do something like this $client = new Client($keySid, $keySecret, $accountSid); but the guide above, does not do this, plus it all worked like this also.
Any help or suggestions would be great, i'm running out of hair to pull out :(
After a little more googling and some re-working, I found a working solution
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Illuminate\Support\Facades\Auth;
use Twilio\Rest\Client;
class MessagingController extends Controller
{
protected function messageSendToMobile($details, $message, $user, $imageUrl = null){
$accountSid = env('TWILIO_ACCOUNT_SID');
$authToken = env('TWILIO_AUTH_TOKEN');
$twilioNumber = env('TWILIO_PHONE_NUMBER');
$lineBreak = "\n\n";
$to = $user->mobile_number->country_code.decrypt($user->mobile_number->number);
$client = new Client($accountSid, $authToken);
try {
$client->messages->create(
$to,
[
"body" => $message,
"from" => $twilioNumber
]
);
Log::info('Message sent to ' . $twilioNumber);
} catch (TwilioException $e) {
Log::error(
'Could not send SMS notification.' .
' Twilio replied with: ' . $e
);
}
}
}

JWT web token does not give back user infos

I have a problem with my API. After my login, I have a token, but unfortunately I can not read the information of my user with this token.
my code to have the token (that works):
/**
* #Rest\View()
* #Rest\Post("/api/createToken")
*/
public function createTokenAction(Request $request)
{
// reception payload
$username = $request->request->get('email');
$password = $request->request->get('password');
$user = $this->getDoctrine()
->getRepository('ApplicationSonataUserBundle:User')
->findOneBy(['email' => $username]);
// check user
if (!$user) {
$response = new JsonResponse('User not found');
$response->setStatusCode(Response::HTTP_NOT_FOUND);
return $response;
}
//check password
$encoder_service = $this->get('security.encoder_factory');
$encoder = $encoder_service->getEncoder($user);
$valid = $encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt());
if (!$valid) {
$response = new JsonResponse('User Password is invalid');
$response->setStatusCode(Response::HTTP_NOT_FOUND);
return $response;
}
// store data inside my token
$token = $this->get('lexik_jwt_authentication.encoder')
->encode([
'id' => $user->getId(),
'email' => $user->getEmail(),
'exp' => time() + 3600 // 1 hour expiration
]);
$view = View::create($token);
$view->setFormat('json');
return $view;
}
then I try to use this code to read the information of my token:
/**
* #Rest\View()
* #Rest\Get("/api/user")
*/
public function getUsersAction(Request $request)
{
$response = $this->get('lexik_jwt_authentication.jwt_manager')->create($this->getUser());
$view = View::create($response);
$view->setFormat('json');
return $view;
}
but nothing does .. I do not understand why .. :/
If someone would have a track to offer me please

Call symfony console command with sudo from Controller

I'm implementing a web interface using symfony which allow some system restricted command.
To better separate the logic of my code, I've create some console command like:
app/console system:do-restricted --option
And then I call the command from controller like this:
$status = $console->run(new ArrayInput([
'command' => 'system:do-restricted',
'--option' => true
]), $output = new BufferedOutput());
Is there a way to allow sudo of the console command?
I think the only way is to reconvert the above command to the shell form and use Process, in which case, there is a simple way to convert InputArray to command and stdout to OutputBaffer (+ansi colors)?
In the end, I've implemented this class to be used in place of symfony's Console\Application:
<?php
namespace Acme\Model;
use Symfony\Component\Process\Process;
use Symfony\Component\Console\Input\ArrayInput;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\Console\Output\OutputInterface;
final class Console implements LoggerAwareInterface
{
use LoggerAwareTrait;
private $consoleCommand;
public function __construct($consoleCommand = 'sudo app/console')
{
$this->consoleCommand = $consoleCommand;
}
/**
* Create a process for console command.
*
* #param string $command
* #param array[] $argv Same syntax as symfony ArrayInput
*
* #see Symfony\Component\Console\Input\ArrayInput
*/
public function process($command, array $argv = [])
{
$console = escapeshellcmd($this->consoleCommand);
$command = escapeshellarg($command);
$options = [];
$arguments = [];
foreach ($argv as $name => $value) {
if ('--' === substr($name, 0, 2)) {
if (false === $value) {
continue;
}
$option = $name;
if (is_string($value)) {
$option .= '='.$value;
}
$options[] = escapeshellarg($option);
} else {
$arguments[] = escapeshellarg($value);
}
}
$process = new Process(
$console.' '
.$command.' '
.implode(' ', $options).' '
.implode(' ', $arguments)
);
if ($this->logger) {
$this->logger->info(sprintf('Created process for command: %s', $process->getCommandLine()));
}
return $process;
}
/**
* Run a console command.
*
* #param string $command One of the 'app/console' commands
* #param array[] $argv Assoc array '--opt' => true/false, '--opt' => 'value' or 'arg_name' => 'arg_value'
* #param OutputInterface|null $output Output object
*
* #see Symfony\Component\Console\Input\ArrayInput
*/
public function run($command, array $argv = [], OutputInterface $output = null)
{
if ($output->isDecorated()) {
$argv['--ansi'] = true;
}
$process = $this->process($command, $argv);
$callable = null;
if (null !== $output) {
$callable = function ($type, $line) use ($output) {
$output->writeln($line);
};
}
$exitCode = $process->run($callable);
if ($this->logger) {
$this->logger->info(sprintf('Command returned: %d', $exitCode), ['output' => $output]);
}
return $exitCode;
}
}
And then I call it like this:
$status = $this->console->run(
'balancer:cluster:copy-config',
['--option' => true, '--opt-val' => 'value', 'arg1' => 'value1'],
$output = new BufferedOutput()
);

How to implement a nice solution for multilang entity slug based routes in Symfony2

I'd like to create a simple bundle to handle some multilingual pages in a website with translated slugs.
Based on translatable, sluggable and i18nrouting
implemented an entity (Page) with title, content, slug fields + locale property as the doc says
created a new Page set its title and content then translated it by $page->setTranslatableLocale('de'); and set those fields again with the german values, so that the data in the tables looks fine, they are all there
implemented the controller with type hinting signature: public function showAction(Page $page)
generated some urls in the template by: {{ path("page_show", {"slug": "test", "_locale": "en"}) }} and {{ path("page_show", {"slug": "test-de", "_locale": "de"}) }}, routes are generated fine, they look correct (/en/test and /de/test-de)
clicking on them:
Only the "en" translation works, the "de" one fails:
MyBundle\Entity\Page object not found.
How to tell Symfony or the Doctrine or whatever bundle to use the current locale when retrieving the Page? Do I have to create a ParamConverter then put a custom DQL into it the do the job manually?
Thanks!
Just found another solution which I think is much nicer and i'm going to use that one!
Implemented a repository method and use that in the controller's annotation:
#ParamConverter("page", class="MyBundle:Page", options={"repository_method" = "findTranslatedOneBy"})
public function findTranslatedOneBy(array $criteria, array $orderBy = null)
{
$page = $this->findOneBy($criteria, $orderBy);
if (!is_null($page)) {
return $page;
}
$qb = $this->getEntityManager()
->getRepository('Gedmo\Translatable\Entity\Translation')
->createQueryBuilder('t');
$i = 0;
foreach ($criteria as $name => $value) {
$qb->orWhere('t.field = :n'. $i .' AND t.content = :v'. $i);
$qb->setParameter('n'. $i, $name);
$qb->setParameter('v'. $i, $value);
$i++;
}
/** #var \Gedmo\Translatable\Entity\Translation[] $trs */
$trs = $qb->groupBy('t.locale', 't.foreignKey')->getQuery()->getResult();
return count($trs) == count($criteria) ? $this->find($trs[0]->getForeignKey()) : null;
}
It has one disadvantage there is no protection against same translated values ...
I found out a solution which i'm not sure the best, but works.
Implemented a PageParamConverter:
class PageParamConverter extends DoctrineParamConverter
{
const PAGE_CLASS = 'MyBundle:Page';
public function apply(Request $request, ParamConverter $configuration)
{
try {
return parent::apply($request, $configuration);
} catch (NotFoundHttpException $e) {
$slug = $request->get('slug');
$name = $configuration->getName();
$class = $configuration->getClass();
$em = $this->registry->getManagerForClass($class);
/** #var \Gedmo\Translatable\Entity\Translation $tr */
$tr = $em->getRepository('Gedmo\Translatable\Entity\Translation')
->findOneBy(['content' => $slug, 'field' => 'slug']);
if (is_null($tr)) {
throw new NotFoundHttpException(sprintf('%s object not found.', $class));
}
$page = $em->find($class, $tr->getForeignKey());
$request->attributes->set($name, $page);
}
return true;
}
public function supports(ParamConverter $configuration)
{
$name = $configuration->getName();
$class = $configuration->getClass();
return parent::supports($configuration) && $class == self::PAGE_CLASS;
}
}
TranslationWalker nicely gets the entity in active locale:
class PagesRepository extends \Doctrine\ORM\EntityRepository
{
public function findTranslatedBySlug(string $slug)
{
$queryBuilder = $this->createQueryBuilder("p");
$queryBuilder
->where("p.slug = :slug")
->setParameter('slug', $slug)
;
$query = $queryBuilder->getQuery();
$query->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
return $query->getSingleResult();
}
}
And in controller
/**
* #Entity("page", expr="repository.findTranslatedBySlug(slug)")
* #param $page
*
* #return Response
*/
public function slug(Pages $page)
{
// thanks to #Entity annotation (Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity)
// Pages entity is automatically retrieved by slug
return $this->render('content/index.html.twig', [
'page' => $page
]);
}

in zend framework 2 how to save fieldset data to database

I just start to study zend framwork2 , and read the document about how to use fieldset http://zf2.readthedocs.org/en/latest/modules/zend.form.collections.html
I can use tablegateway insert product data into database.but don't know how to insert data to brand table and I don't know how to link product and brand . thank you very much!!!!!
Many people has the same problem and rlandas wrote and uploaded a working code to github
i post the code of the controller in case the url changes. but take a look at the complete module in github
<?php
namespace Product\Controller;
use Product\Table\ProductTable;
use Product\Entity\Product as ProductEntity;
use Product\Form\CreateProduct;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Http\PhpEnvironment\Response;
use Zend\View\Model\ViewModel;
class ManageController extends AbstractActionController
{
public function indexAction ()
{
$product = $this->getProductTable();
$products = $product->getAllOrderByName();
$view = new ViewModel();
$view->setVariable('products', $products);
return $view;
}
public function viewAction ()
{
if ($id = $this->params('id')) {
$product = $this->getProductTable()
->getByProductId($id);
}
$view = new ViewModel();
$view->setVariable('product', $product);
return $view;
}
public function addAction ()
{
$form = new CreateProduct();
$product = $this->getServiceLocator()->get('Product\Entity\Product');
$form->bind($product);
$data = array(
'product' => array(
'name' => 'product name ' . mt_rand(1, 1000),
'price' => mt_rand(100.000, 5000.999) / 100,
'brand' => array(
'name' => 'My brand ' . mt_rand(1, 200),
'url' => 'http://www.mybrand.com'
),
'categories' => array(
array('name' => 'Sony'),
array('name' => 'Panasonic'),
array('name' => 'Phillips')
)
)
);
$form->populateValues($data);
// action viewscript
$view = new ViewModel(array(
'form' => $form
));
// do Post/Redirect/Get (PRG) strategy to stop user refresh/back button
$prg = $this->prg($this->getRequest()->getRequestUri(), true);
if ($prg instanceof Response) {
return $prg;
}
// this is when the user first arrives to this url, display the form
else if ($prg === false) {
return $view;
}
// lets retrieve the post data stored in the PRG session
$post = $prg;
// validate the form
$form->setData($post);
if(!$form->isValid())
return $view;
// if data are valid, then save
// save the brand
$brand = $product->getBrand();
$brandTable = $this->getBrandTable();
$brand = $brandTable->save($brand);
$brandId = $brandTable->getLastInsertValue();
$product->setBrandId($brandId);
// save the categories
$categoryTable = $this->getCategoryTable();
$categoryTable->persist($product->getCategories())->flush();
$categoryIds = implode(",", $categoryTable->getEntityIds());
$product->setCategoryIds($categoryIds);
// save the product
$productTable = $this->getProductTable();
$product = $productTable->save($product);
$this->redirect()->toRoute('product');
return $view;
}
public function editAction ()
{
$form = new CreateProduct();
$product = $this->getServiceLocator()->get('Product\Entity\Product');
$form->bind($product);
// action viewscript
$view = new ViewModel(array(
'form' => $form
));
$productTable = $this->getProductTable();
if ($id = $this->params('id')) {
$product = $this->getProductTable()->getByProductId($id);
// get the brands
$brand = $this->getBrandTable()->getByBrandId($product->getBrandId());
$product->setBrand($brand);
// get the categories
$categoryIds = explode(",", $product->getCategoryIds());
$categories = $this->getCategoryTable()->getAllByCategoryId($categoryIds);
$product->setCategories($categories);
$form->bind($product);
}
// do Post/Redirect/Get (PRG) strategy to stop user refresh/back button
$prg = $this->prg($this->getRequest()->getRequestUri(), true);
if ($prg instanceof Response) {
return $prg;
}
// this is when the user first arrives to this url, display the form
else if ($prg === false) {
return $view;
}
// lets retrieve the post data stored in the PRG session
$post = $prg;
// validate the form
$form->setData($post);
if(!$form->isValid())
return $view;
\Zend\Debug\Debug::dump(__METHOD__.' '.__LINE__);
\Zend\Debug\Debug::dump($post);
\Zend\Debug\Debug::dump($product);
return $view;
}
/**
*
* #return \Product\Table\ProductTable
*/
public function getProductTable ()
{
$sm = $this->getServiceLocator();
$table = $sm->get('Product\Table\ProductTable');
return $table;
}
/**
*
* #return \Product\Table\BrandTable
*/
public function getBrandTable ()
{
return $this->getServiceLocator()
->get('Product\Table\BrandTable');
}
/**
*
* #return \Product\Table\CategoryTable
*/
public function getCategoryTable ()
{
return $this->getServiceLocator()
->get('Product\Table\CategoryTable');
}
}

Resources