Quick and simple wordpress blog backup by using a SHELL script
by Jamie Hall on August 31, 2010
I wanted to implement a quick way to back up my blog. (I know there are lots of solutions, but I wanted something quick and simple to setup)
I wrote this script on the server that I want to backup:
date="`eval date +%Y%m%d`";
mysqluser="user"
mysqlpass="password"
mysqldb="database"
backupsource="/srv/www/refineweb.co.uk/"
backupdest="/srv/backups/refineweb.co.uk/"
mysqldump -q -e -h localhost -u ${mysqluser} -p${mysqlpass} ${mysqldb} | gzip - > /${backupdest}${date}_db-backup.sql.gz
cd /${backupsource}
tar cf - . | gzip - > /${backupdest}${date}_files.tar.gz
echo "$date Backup complete" >> /var/log/backup_refineweb.log
On my desktop at home, I created a script which transfers the files from the server and then copies them to another location (3 seperate backup locations):
echo Backup Started `date` >> ~/backuplog
date="`eval date +%Y%m%d`";
files="${date}_files.tar.gz"
sql="${date}_db-backup.sql.gz"
# copy files from server
scp root@refineweb.co.uk:/var/backups/refineweb.co.uk/\{${files},${sql}\} /media/HALL-BACKUP/backup/refineweb.co.uk
# move to another location
cp /media/HALL-BACKUP/backup/refineweb.co.uk/${files} /home/jamie/backup/refineweb.co.uk
cp /media/HALL-BACKUP/backup/refineweb.co.uk/${sql} /home/jamie/backup/refineweb.co.uk
echo "$date Backup complete" >> /var/log/backup_refineweb.log
On the server I have a crontab that runs at 5am in the morning. On my linux desktop at home I have a crontab that runs at 6am in the morning. I have added a ssh key for my desktop to the server (when the cron job is run, it won’t ask for a password).
Although, there is no validation etc. to make sure the backup has run successfully. It’s a quick and easy way to back up your blog.
UPDATE: Storing emails in XML and sending them with sfMailer
by Jamie Hall on August 31, 2010
Hi all,
I have updated my previous post about sending emails which are stored in an XML file with sfMailer.
The class is better structured and it now includes i18n functionality.
Symfony – Storing email templates in XML for use with sfMailer
Thanks
Deploying a symfony project into production using capistrano
by Jamie Hall on August 30, 2010
Here is a simple(ish) way to deploy your symfony project from a staging server onto your production server. It will perform all of the basic tasks such as removing dev environments, timestamping file modification dates, symlinking release folder etc.
Here’s the deploy.rb (stored in my project config directory on my staging server):
set :repository, "http://example.com/svn/project/trunk"
# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
set :deploy_to, "/srv/www/"
# If there's no access to the repository from the production server, deploy via uploading tarball to the server
set :deploy_via, :export
# If you aren't using Subversion to manage your source code, specify
# your SCM below:
# set :scm, :subversion
role :app, "prodserver.example.com"
role :web, "prodserver.example.com"
# role :db, "your db-server here", :primary => true
set :user, "root"
# path to php executable
set :php, "/usr/bin/php"
# symfony application name (used for migrations)
set :sf_app, "frontend"
namespace (:deploy) do
desc <<-DESC
[internal] Overriding original task to fit to symfony project needs
DESC
task :finalize_update, :except => { :no_release => true } do
run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true)
run <<-CMD
rm -rf #{latest_release}/log &&
ln -s #{shared_path}/log #{latest_release}/log
CMD
run <<-CMD
rm -rf #{latest_release}/cache &&
ln -s #{shared_path}/cache #{latest_release}/cache
CMD
stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
asset_paths = %w(images css js).map { |p| "#{latest_release}/web/#{p}" }.join(" ")
run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" }
end
desc <<-DESC
Overriding original task to exclude restart
DESC
task :default do
update
end
desc <<-DESC
Overriding original task to use symfoy migrations (via sfMigrationsLightPlugin)
DESC
task :migrations do
update
sf.migrate
end
after "deploy:update", 'deploy:customize'
desc <<-DESC
All custom tasks will be here
DESC
task :customize do
# custmize it here
sf.symlinks
sf.remove_dev_environments
# clear cache
sf.cc
# do permissions
sf.batch_permissions
end
end
namespace (:sf) do
desc <<-DESC
Run the "symfony migrate" task
DESC
task :migrate do
run "cd #{current_path} && #{php} symfony migrate #{sf_app}"
end
desc <<-DESC
Run the "symfony cc" task
DESC
task :cc do
run "cd #{current_path} && rm -rf cache/*"
end
desc <<-DESC
Create symlink to symfony specific targets
DESC
task :symlinks do
# symlink to uploads
run "rm -rf #{current_path}/web/uploads"
run "ln -s #{shared_path}/uploads #{current_path}/web/uploads"
end
desc <<-DESC
Remove DEV environments
DESC
task :remove_dev_environments do
run "rm -rf #{current_path}/web/*_dev.php"
end
desc <<-DESC
Change permissions for batch jobs
DESC
task :batch_permissions do
run "chmod -R 777 /srv/www/current/batch"
end
desc <<-DESC
Disable symfony application
DESC
task :disable do
run "cd #{current_path} && #{php} symfony disable #{sf_app} prod"
end
desc <<-DESC
Enable symfony application
DESC
task :enable do
run "cd #{current_path} && #{php} symfony enable #{sf_app} prod"
end
end
What this script does is the following:
- Create a folder on the production server. The folder name will a timestamp.
- SVN export the project into the newly created folder.
- Remove the exported project log directory and symlink it to to /srv/www/shared/log (this way you’ll always have a continuous log and not news one generated all of the time)
- Remove the exported cache directory and symlink it to /srv/www/cache
- Find all images, javascripts, and css files and change the modification date of each file with a new timestamp.
- Symlink release folder to /srv/www/current
- Symlink uploads folder to /srv/www/shared/uploads
- Remove all the dev environments, such as frontend_dev.php
- Clear cache
- Change batch files permissions (custom, you can use it if you want) so cronjobs will run successfully
Here is the output log for more details:
* executing `deploy:update'
** transaction: start
* executing `deploy:update_code'
executing locally: "svn info http://example.com/svn/project/trunk -rHEAD"
* executing "svn export -q -r249 http://example.com/svn/project/trunk /srv/www/releases/20100830142937 && (echo 249 > /srv/www/releases/20100830142937/REVISION) "
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
* executing `deploy:finalize_update'
* executing "chmod -R g+w /srv/www/releases/20100830142937"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
* executing "rm -rf /srv/www/releases/20100830142937/log &&\\\n ln -s /srv/www/shared/log /srv/www/releases/20100830142937/log"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
* executing "rm -rf /srv/www/releases/20100830142937/cache &&\\\n ln -s /srv/www/shared/cache /srv/www/releases/20100830142937/cache"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
* executing "find /srv/www/releases/20100830142937/web/images /srv/www/releases/20100830142937/web/css /srv/www/releases/20100830142937/web/js -exec touch -t 201008301432.15 {} ';'; true"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
* executing `deploy:symlink'
* executing "rm -f /srv/www/current && ln -s /srv/www/releases/20100830142937 /srv/www/current"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
** transaction: commit
triggering after callbacks for `deploy:update'
* executing `deploy:customize'
* executing `sf:symlinks'
* executing "rm -rf /srv/www/current/web/uploads"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
* executing "ln -s /srv/www/shared/uploads /srv/www/current/web/uploads"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
* executing `sf:remove_dev_environments'
* executing "rm -rf /srv/www/current/web/*_dev.php"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
* executing `sf:cc'
* executing "cd /srv/www/current && rm -rf cache/*"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
* executing `sf:batch_permissions'
* executing "chmod -R 777 /srv/www/current/batch"
servers: ["prodserver.example.com"]
[prodserver.example.com] executing command
command finished
Happy deploying! If you would like some help then please do not hesitate to leave a comment and I will try my best to assist.
Using JaSig CAS with Symfony
by Jamie Hall on August 25, 2010
At work we use the SSO(single sign-on) library CAS to authenticate users accross our software stack. I didn’t like the code for phpCAS because it didn’t follow strict standards and it didn’t integrate very well with symfony, however, we do use it on some other php sites which aren’t running on symfony.
To get CAS integrated into symfony successfully, I created a class called sfCAS. This class handles validation of a ticket, sets the correct parameters etc.
*
* @author Jamie Hall
* @date 03-04-2009
* @description A library to use with symfony to validate a user into the SSO CAS system.
*/
class sfCAS extends sfCASRequiredFilter
{
public function __construct($context)
{
$this->ssl = true;
$this->server_uri = sfConfig::get("app_cas_server_domain");
$this->server_port = sfConfig::get("app_cas_server_port");
$this->server_location = sfConfig::get("app_cas_server_location");
$this->localhost = sfConfig::get("app_cas_server_localhost");
$this->request = $context->getRequest();
$this->controller = $context->getController();
$user = $context->getUser();
$this->_pt = null;
//Check if authenticated
if(!$user->isAuthenticated())
{
// check for ticket
$ticket = ($this->request->hasParameter("ticket") ? $this->request->getParameter("ticket") : null);
if(!empty($ticket))
{
if( preg_match('/^[SP]T-/',$ticket) )
{
$this->setPT($ticket);
}
}
}
}
/*
* Authenticate User to CAS
*/
public function authenticate()
{
if(empty($this->_pt))
{
// redirect to Login
$this->controller->redirect($this->getLoginURL());
}
else
{
if($this->validateTicket())
{
return true;
}
else
{
$this->controller->redirect($this->getLoginURL());
}
}
}
public function getLoginURL()
{
return $this->getURL() . "/login?service=" . urlencode($this->getService());
}
public function getLogoutURL()
{
return $this->getURL() . "/logout";
}
private function generateURL()
{
// generate the CAS URL
// remove ticket from service URL if exists
$service = $_SERVER['REQUEST_URI'];
$service = preg_replace('/&ticket=[^&]*/','',$service);
$service = preg_replace('/\?ticket=[^&;]*/','?',$service);
$service = preg_replace('/\?%26/','?',$service);
$service = preg_replace('/\?&/','?',$service);
$service = preg_replace('/\?$/','',$service);
$this->setService($service);
$url = ($this->ssl) ? "https://" : "http://";
$url .= sprintf("%s:%s/", $this->server_uri, $this->server_port);
$url .= (empty($this->server_location)) ? "" : $this->server_location;
$this->setURL($url);
}
private function setURL($url)
{
$this->url = $url;
}
public function getURL()
{
$this->generateURL();
return $this->url;
}
private function setService($service)
{
$this->service = $service;
}
public function getService()
{
return $this->localhost . $this->service;
}
public function getValidateURL()
{
return $this->getURL() . "/serviceValidate?service=" . urlencode($this->getService());
}
private function setPT($pt)
{
$this->_pt = $pt;
}
public function getPT()
{
return $this->_pt;
}
private function setUser($user)
{
$this->user = $user;
}
public function getUser()
{
return $this->user;
}
/*
*
* We have a ticket, but we need to validate it using proxyValidate on the CAS Server
*/
public function validateTicket()
{
// get validation URL and append the ticket
$url = $this->getValidateURL().'&ticket='.$this->getPT();
// new CURL object
$curl = $this->curlInit($url);
$result = curl_exec($curl);
// read the response of the CAS server into a DOM object
$dom = new DOMDocument();
$dom->formatOutput = true;
if (!($dom->loadXML($result)))
{
// read failed
return false;
}
if ($dom->getElementsByTagName("authenticationSuccess")->length != 0)
{
// authentication succeded, extract the user name
if ($dom->getElementsByTagName("user")->length == 0) {
return false;
}
$this->setUser($dom->getElementsByTagName("user")->item(0)->nodeValue);
return true;
}
else if ($dom->getElementsByTagName("authenticationFailure")->length != 0)
{
// authentication succeeded, extract the error code and message
return false;
}
}
private function curlInit($url)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
return $curl;
}
}
The class get’s it settings from the app.yml file in your app directory.
cas:
server_domain: cas.example.com
server_port: 9999
server_location: cas
server_localhost: http://localhost
You’ll also need a filter (I’ve called mine sfCASRequiredFilter):
* @author Jamie Hall
* @description The filter is executed on every action to check if a user is authenticated with CAS or needs to be authenticated
*/
class sfCASRequiredFilter extends sfBasicSecurityFilter
{
public function execute ($filterChain)
{
if ($this->isFirstCall())
{
if(!$this->getContext()->getUser()->isAuthenticated())
{
$sfCas = new sfCAS($this->getContext());
if($sfCas->authenticate())
{
$this->getContext()->getUser()->authenticate($sfCas->getUser());
}
}
}
// Execute next filter in the chain
$filterChain->execute();
}
}
Please make sure you enable the filter. And that’s all there is to it. When a user first goes into the application it will check if they’re authenticated, if they’re authenticated through CAS, they will then will be authenticated into the application, if they’re not authenticated in CAS they will be redirect to the CAS login. Once approved by CAS, the ticket will be validated, if valid, they will be authenticated through your system.
Symfony – Storing email templates in XML for use with sfMailer
by Jamie Hall on August 23, 2010
I wanted to come up with a cleaner solution than the current solution for sending emails in symfony. For example, I don’t like the way that if you want to send an email in an action, you have two different ways:
- Putting the template code in the action (yuk)
- Using a partial. Calling that partial content and then using it as the template for the email.
See ref: http://www.symfony-project.org/jobeet/1_4/Doctrine/en/16
Two problems: I don’t like seeing html code in the controller and if I want to use a task to send emails, using a partial is not a viable solution.
After thinking for a while, I decided the best approach would be to store the emails in an XML file in the data directory and create a MailTemplate wrapper class to insert the correct parameters for setting the content etc.
So here is what I did. I created an XML file with all of my email templates in theproject data directory. It has a bunch of configuration options. I’ll go into that later.
I then created a class called MailTemplate.class.php to extract an email template. Here is the class (stored in my project lib directory):
/*
* @author Jamie Hall
* @desccription Mail wrapper for using email templates in an
* XML file instead of php templates
* @date 31st August 2010
*/
class MailTemplate
{
protected $content_loaded = false;
protected $email;
protected $params = array();
protected $context = null;
protected $culture = null;
protected $content = array();
public function __construct($id, $context)
{
if(!$context instanceof sfContext) throw new sfException ("The context passed through is not a valid instance of sfContext");
$this->context = $context;
$file = sfConfig::get("sf_data_dir") . DIRECTORY_SEPARATOR . "emails.xml";
if(!file_exists($file) || !is_readable($file)) throw new sfException("Email XML file does not exist or is not readable");
$emails = new SimpleXMLElement(file_get_contents($file));
if($email = $emails->xpath(sprintf("/emails/email[@id='%s']", $id)))
{
// load email
$this->email = $email[0];
// load helpers
self::loadHelpers();
}
else
{
throw new sfException(sprintf("Could not find the email template '%s. Are you passing the correct email id?", $id));
}
}
/*
* @description check if a parameter which is being set is valid.
* return exception if parameter is not valid
*/
private function hasPHP()
{
return ($this->email->has_php=="true") ? true : false;
}
/*
* @description Check if the template is i18n enabled
*/
private function isInternationalised()
{
return ($this->email->i18n=="true") ? true : false;
}
/*
* @description If the template is i18n enabled, set the culture
*/
public function setCulture($culture)
{
if(self::isInternationalised())
{
$this->culture = $culture;
}
else
{
throw new sfException("This is not an internationalised template.");
}
}
public function loadHelpers()
{
$helpers = array();
if(count($this->email->helpers->helper) > 0 )
{
for ($i = 0; $i < count($this->email->helpers->helper); $i++)
{
$helpers[] = (string) $this->email->helpers->helper[$i];
}
// load the helpers, anyone know of a better way to do this?
$this->context->getConfiguration()->loadHelpers($helpers);
}
}
/*
* @description return an array of valid parameters for the email template
*/
public function getParameters()
{
$params = array();
for ($i = 0; $i < count($this->email->parameters->param); $i++)
{
$params[] = $this->email->parameters->param[$i];
}
return $params;
}
/*
* @description Set a parameter. If valid, add to params array.
*/
public function setParameter($param, $value)
{
if(self::isParameterValid($param))
{
$this->params[$param] = $value;
}
}
/*
* @description check if a parameter which is being set is valid.
* return exception if parameter is not valid
*/
private function isParameterValid($param)
{
for ($i = 0; $i < count($this->email->parameters->param); $i++)
{
if($this->email->parameters->param[$i] == $param)
{
return true;
}
}
throw new sfException(sprintf("Parameter name '%s' is not valid", $param));
}
/*
* @description Set the email content with the set parameters. If it has already been called
* once throw an exception, otherwise, set the content and flag content_loaded
* as true.
*/
public function setContent()
{
if($this->content_loaded) throw new sfException("Content has already been set");
$this->content['plain'] = self::setPlainContent();
$this->content['html'] = self::setHtmlContent();
$this->content_loaded = true;
}
/*
* @description Extract the parameter variables from the array, evaluate the plain content from the xml
* return the output back into the plain content variable
*/
private function setPlainContent()
{
if(isset($this->culture))
{
$culture = $this->culture;
$this->content['plain'] = $this->email->content->i18n->$culture->plain;
}
else
{
$this->content['plain'] = $this->email->content->plain;
}
if(self::hasPHP())
{
extract($this->params);
ob_start();
ob_implicit_flush(0);
try
{
eval(sprintf("?>%s<?", $this->content['plain']));
}
catch (sfException $e)
{
// need to end output buffering before throwing the exception #7596
ob_end_clean();
throw $e;
}
return ob_get_clean();
}
else
{
return $content['plain'];
}
}
/*
* @description Extract the parameter variables from the array, evaluate the html content from the xml
* return the output back into the html content variable
*/
private function setHtmlContent()
{
if(isset($this->culture))
{
$culture = $this->culture;
$this->content['html'] = $this->email->content->i18n->$culture->html;
}
else
{
$this->content['html'] = $this->email->content->html;
}
if(self::hasPHP())
{
extract($this->params);
ob_start();
ob_implicit_flush(0);
try
{
eval(sprintf("?>%s<?", $this->content['html']));
}
catch (sfException $e)
{
// need to end output buffering before throwing the exception #7596
ob_end_clean();
throw $e;
}
return ob_get_clean();
}
else
{
return $this->content['html'];
}
}
/*
* @description Return the email subject
*/
public function getSubject()
{
return $this->email->subject;
}
/*
* @description return the html content. If the content has not been set, throw exception.
*/
public function getHtmlContent()
{
if(!$this->content_loaded) throw new sfException("Content has not been loaded");
// file is saved in utf-8, however, to display any foreign characters correctly (such as in french) we need to decode
return utf8_decode ($this->content['html']);
}
/*
* @description return the plain content. If the content has not been set, throw exception.
*/
public function getPlainContent()
{
if(!$this->content_loaded) throw new sfException("Content has not been loaded");
return utf8_decode(trim($this->content['plain']));
}
}
And for the XML template file, (stored in the data directory) I have this:
<emails>
<email id="products-expired">
<description>We send this email when we want to inform a user when a product has expired</description>
<!-- If the content is dynamic (uses php) set value to true, otherwise false -->
<has_php>true</has_php>
<!-- if the email is internationalised -->
<i18n>false</i18n>
<!-- Template parameters which will be used in the content -->
<parameters>
<param>name</param>
<param>number_of_products</param>
<param>products</param>
</parameters>
<!-- Load any required helpers which will be used to display the email content, such as url, date, escaping etc. -->
<helpers>
<helper>Url</helper>
<helper>Helper</helper>
<helper>Asset</helper>
<helper>Tag</helper>
<helper>Escaping</helper>
</helpers>
<!-- Email subject -->
<subject>Product Expiration</subject>
<!-- Email Content -->
<content>
<html>
<![CDATA[
Dear <?php echo $name ?>, <br />
<p>
Homepage url: <?php echo url_for("@homepage", true)?>
You have <?php echo $number_of_products ?> products that are due to expire in under 30 days. Please log in to <?php echo link_to("Application", "@homepage", array('absolute' => true))?> to review the products.
<br /><br />
<ul>
<?php foreach($products as $product):?>
<li><?php echo $product->getName() ?></li>
<?endforeach;?>
</ul>
This is an automated email. Please do not reply.
</p>
]]>
</html>
<plain>
<![CDATA[
Dear <?php echo $name ?>, You have <?php echo $number_of_products ?> products that are due to expired in under 30 days. Please log in to example.com review the products. This is an automated email. Please do not reply.
]]>
</plain>
</content>
</email>
<email id="system-update">
<description>This is just a generic update email</description>
<has_php>false</has_php>
<subject>Update</subject>
<content>
<html>
<![CDATA[
Dear all,
<p>The system has been updated. Please login to <a href="example.com">example.com</a> to view all of the changes.</p>
]]>
</html>
<plain>
<![CDATA[
Dear all, the system has been updated. Please login to example.com to view all of the changes.
]]>
</plain>
</content>
</email>
</emails>
If you look in the XML file. You can set the helpers you want to use (for example if you’re using url_for in the content). You define all the valid parameters and also defined whether you’ll be using php in the content (if an email is just static content, then php is not needed).
To use an email template in an action for example, you would do the following:
{
$products = Doctrine::getTable("Product")->findAll();
// get email template
$mail = new MailTemplate("products-expired", $this->getContext());
$mail->setParameter("name", "Joe Bloggs");
$mail->setParameter("number_of_products", count($products));
$mail->setParameter("products", $products);
$mail->setContent();
// send the email using sfMailer
$message = $this->getMailer()->compose();
$message->setSubject($mail->getSubject());
$message->setTo("whoever@example.com");
$message->setFrom(array("jamie@example.com"=>"Automated Message"));
$message->setBody($mail->getHtmlContent(), 'text/html');
$message->addPart($mail->getPlainContent(), 'text/plain');
$this->getMailer()->send($message);
}
If you want to send an il8n email you’ll need the following XML markup:
<email id="message-received">
<description>We send this email to inform a user when they have received a message</description>
<has_php>true</has_php>
<i18n>true</i18n>
<parameters>
<param>name</param>
<param>received_from</param>
<param>date_received</param>
</parameters>
<helpers>
<helper>Url</helper>
<helper>Helper</helper>
<helper>Asset</helper>
<helper>Tag</helper>
<helper>Escaping</helper>
</helpers>
<subject>Message Received</subject>
<content>
<i18n>
<en_GB>
<html>
<![CDATA[
Dear <?php echo $name ?>, <br />
<p>
You have received a message from <?php echo $received_from ?> which was sent on <?php echo $date_received ?> <br />
Please login to <?php echo link_to("Application", "@homepage", array('absolute' => true))?> to view the message.
<br /><br />
This is an automated email. Please do not reply.
</p>
]]>
</html>
<plain>
<![CDATA[
Dear <?php echo $name ?>, You have received a message from <?php echo $received_from ?> which was sent at <?php echo $date_received ?>.
Please login to <?php echo url_for("@homepage", array('absolute' => true))?> to view the message. This is an automated email. Please do not reply.
]]>
</plain>
</en_GB>
<fr_FR>
<html>
<![CDATA[
Cher <?php echo $name ?>, <br />
<p>
Vous avez reçu un message de <?php echo $received_from ?>, envoyé le <?php echo $date_received ?> <br />
Merci de vous connecter à <?php echo link_to("Application", "@homepage", array('absolute' => true))?> afin de le lire. <br />
<br /><br />
Ceci est un courriel automatiquement généré, merci de ne pas y répondre.
</p>
]]>
</html>
<plain>
<![CDATA[
Cher <?php echo $name ?>, Vous avez reçu un message de <?php echo $received_from ?>, envoyé le<?php echo $date_received ?>. Merci de vous connecter à <?php echo url_for("@homepage", array('absolute' => true))?> afin de le lire.
Ceci est un courriel automatiquement généré, merci de ne pas y répondre.
]]>
</plain>
</fr_FR>
</i18n>
</content>
</email>
And use the following code to send an i18n email:
{
$template = new MailTemplate("message-received", $this->getContext());
$template->setCulture("fr_FR");
$template->setParameter("name", "Jamie Hall");
$template->setParameter("received_from", "Joe Bloggs");
$template->setParameter("date_received", "2010-12-12 10:10:10");
$template->setContent();
$message = $this->getMailer()->compose();
$message->setSubject($template->getSubject());
$message->setTo("jamie@example.com");
$message->setFrom(array("mail@example.com"=>"Automated Message"));
$message->setBody($template->getHtmlContent(), 'text/html');
$message->addPart($template->getPlainContent(), 'text/plain');
$this->getMailer()->send($message);
}
As simple as that.