Inlining CSS when sending an email with Mailgun in Laravel

Standard

Since Laravel 4.2, it is possible to use external emails providers to send emails in your application: Mailgun and Mandrill. Before that I was using a nice plugin fedeisas/laravel-mail-css-inliner to inline CSS just before sending the email. Thanks to this, my views are very clean and my emails are still displayed properly in the various email clients and webmails. This plugin was taking advantage of SwiftMailer to inline the CSS when sending an email by registering a plugin. Unfortunately, it is not working with external providers because SwiftMailer is not used since an API call is made instead.

Extending some classes to fix this

I really wanted to inline my CSS before sending an email and I wanted a clean way to do this. A workaround that I have figured out is to extend two classes: Illuminate\Mail\MailServiceProvider and Illuminate\Mail\Transport\MailgunTransport.

I’ve created a new file located at app/lib/TeenQuotes/Mail/Transport/MailgunTransport.php. The goal was to edit the message before calling the Mailgun API.

namespace TeenQuotes\Mail\Transport;

use Swift_Transport;
use Swift_Mime_Message;
use GuzzleHttp\Post\PostFile;
use Swift_Events_EventListener;
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;

class MailgunTransport extends \Illuminate\Mail\Transport\MailgunTransport {

	/**
	 * {@inheritdoc}
	 */
	public function send(Swift_Mime_Message $message, &$failedRecipients = null)
	{
		$client = $this->getHttpClient();

		// Inline CSS here
		$converter = new CssToInlineStyles();
		$converter->setEncoding($message->getCharset());
		$converter->setUseInlineStylesBlock();
		$converter->setCleanup();

		if ($message->getContentType() === 'text/html' ||
			($message->getContentType() === 'multipart/alternative' && $message->getBody())
		) {
			$converter->setHTML($message->getBody());
			$message->setBody($converter->convert());
		}

		foreach ($message->getChildren() as $part) {
			if (strpos($part->getContentType(), 'text/html') === 0) {
				$converter->setHTML($part->getBody());
				$part->setBody($converter->convert());
			}
		}

		// Call the API
		$client->post($this->url, ['auth' => ['api', $this->key],
			'body' => [
				'to' => $this->getTo($message),
				'message' => new PostFile('message', (string) $message),
			],
		]);
	}
}

Since we have a new MailgunTransport, we need to use our custom MailgunTransport when sending an email. I have created a new file at app/lib/TeenQuotes/Mail/MailServiceProvider.php.

namespace TeenQuotes\Mail;

use TeenQuotes\Mail\Transport\MailgunTransport;

class MailServiceProvider extends \Illuminate\Mail\MailServiceProvider {

	/**
	 * Register the Mailgun Swift Transport instance.
	 *
	 * @param  array  $config
	 * @return void
	 */
	protected function registerMailgunTransport($config)
	{
		$mailgun = $this->app['config']->get('services.mailgun', array());

		$this->app->bindShared('swift.transport', function() use ($mailgun)
		{
			return new MailgunTransport($mailgun['secret'], $mailgun['domain']);
		});
	}
}

Not so much work, I just use my custom MailgunTransport that I have just created.

Replacing the Mail Provider

You need to update providers in app/config/app.php to replace the MailServiceProvider with our custom provider.

	'providers' => array(

		// Some others providers...
		'Illuminate\Log\LogServiceProvider',
		// Comment this 'Illuminate\Mail\MailServiceProvider', 
		// We add our new MailServiceProvider
		'TeenQuotes\Mail\MailServiceProvider',
		// Some more providers...
	),

Updating composer.json

We need some new plugins

	"require": {
		// Your plugins
		"tijsverkoyen/css-to-inline-styles": "1.2.*",
		"guzzlehttp/guzzle": "~4.0"
	},

And we need to update the autoload section to be able to load our custom library

	"autoload": {
		"classmap": [
			"app/commands",
			"app/controllers",
			"app/models",
			"app/database/migrations",
			"app/database/seeds",
			"app/exceptions.php",
			"app/tests/TestCase.php"
		],
		"psr-0": {
			"TeenQuotes": "app/lib"
		}
	},

A simple composer dump-autoload and you will be good! Do not forget to set your API key and your mail domain for Mailgun in app/config/services.php.

If you want to use a different namespace of course you are free!

Laravel: fulltext selection and ordering

Standard

Yesterday I was looking for a way to do a FULLTEXT select using Laravel. It was not so easy. In this article I’m going to explain how to a FULLTEXT select and to order by this selection.

The migration

If you want to do a FULLTEXT search, you will need a FULLTEXT index on at least one column of your table. Warning: if you are using InnoDB as your table’s engine, you will need MySQL >= 5.6. If you are using MyISAM as your table’s engine, you are good to go for the index but you can’t use foreign keys.

I’m using InnoDB with MySQL 5.6, here is my code for the migration of the table.

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateQuotesTable extends Migration {

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::dropIfExists('quotes');

        Schema::create('quotes', function(Blueprint $table) {
            $table->engine = "InnoDB";
            $table->increments('id');
            $table->string('content', 500);
            $table->integer('user_id')->unsigned()->index();
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->tinyInteger('approved')->default(0);
            $table->timestamps();
        });

        // Here we create the FULLTEXT index
        DB::statement('ALTER TABLE quotes ADD FULLTEXT search(content)');
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        // Drop the index before dropping the table
        Schema::table('quotes', function($table) {
            $table->dropIndex('search');
        });
        Schema::drop('quotes');
    }

}

Nothing uncommon, note that you will have to use a DB::statement('ALTER TABLE quotes ADD FULLTEXT search(content)') to create the index.

Searching using the FULLTEXT index

Here it comes the fun part. Now that we have your index, let’s begin to use it. I want to get quotes based on a search on their content. I want pertinent results so I’ll advantage of the index.

My code is the following:

/**
 * @brief Function used to search for quotes using the FULLTEXT index on content
 *
 * @param  string $search Our search query
 * @return Collection Collection of Quote
 */
public static function searchQuotes($search)
{
    return Quote::
    select('id', 'content', 'user_id', 'approved', 'created_at', 'updated_at', DB::raw("MATCH(content) AGAINST(?) AS `rank`"))
    // $search will NOT be bind here
    // it will be bind when calling setBindings
    ->whereRaw("MATCH(content) AGAINST(?)", array($search))
    // I want to keep only published quotes
    ->where('approved', '=', 1)
    // Order by the rank column we got with our FULLTEXT index
    ->orderBy('rank', 'DESC')
    // Bind variables here
    // We really need to bind ALL variables here
    // question marks will be replaced in the query
    ->setBindings([$search, $search, 1])
    ->get();
}

I haven’t found a convenient way to select all columns from my table plus an additional one: the rank given by the FULLTEXT search. The tricky part here is really the binding. You need to bind all variables at the end of your query to make it work.

I’m not using the FULLTEXT search in BOOLEAN MODE here. If you need to do so, take a look at the official documentation: http://dev.mysql.com/doc/refman/5.0/en/fulltext-boolean.html. You will only need to add two strings to make it work.

Paginate posts correctly when they are random ordered

Standard

The problem

This is a common problem: you have entities in a category, you want to display them by pages because you have a lot of entities and you don’t want to have entities from page 1 in your page 2.

If you are using the ORDER BY RAND() function from MySQL, you will have a problem. MySQL splits up the data into pages of X posts each (paginates) and fails to include a new set of X posts on page 2 and so forth. In other words, because it is listing things in a random order, it just goes out and gets another X random posts. As a result, you will have some repeated posts instead of a new set of X random posts on page 2, etc.

The solution

Fortunately, there is a solution for this problem. You will be able to “remember” which random 10 posts were included on page 1, and then have a new set of 10 posts to put on pages 2, 3, etc. until all posts are displayed.

The MySQL RAND() function accepts a seed as an optional argument. Using a seed, it will return the same randomized result set each time. For example, if you want your posts to be random ordered, paginated with no repetition, you can write a query like this: SELECT * FROM posts ORDER BY RAND(42) to select posts.

If you do not want to have the same results for every user viewing the list, do not give an arbitrary value to the RAND function: generate a random number, store it in session and pass it to the MySQL RAND function when selecting posts.

You don’t write code for machines

Standard
double m[]= {7709179928849219.0, 771};
int main()
{
    m[1]--?m[0]*=2,main():printf((char*)m);    
}

You know what these lines print? They print C++Sucks.

Yes, really, you can give it a try if you want. If you want the explanation you can check this question on StackOverflow.

My point is that you don’t write code for machines. If you are happy when your code compiles or when it runs and prints what you expected, you are a fool. Of course it’s a success when your code does what you wanted to do, but this is the most basic thing you can expect from it.

Programming is difficult. Reading others people’s code is even more difficult. And yet you are going to do it everyday. So the next time you are going to write some code, or contribute to some code, keep in mind that your ultimate goal is not to make it work, but to write it in a way that should be understandable by other smart folks.

Providing a simple 2-step authentication for your app with Google Authenticator

Standard

In this article I will show how simple is it to code in PHP a 2-step authentication for your application thanks to the Google Authenticator application for your smartphone.

Requirements

You will need Composer for your computer and the Google Authenticator application on your smartphone.

Coding

Let’s start! Create an empty directory, and put this simple composer file inside.
Open a terminal in the directory you just created and run
Now you have the famous vendor directory with the package that we need. You can view it on GitHub here: https://github.com/mauroveron/laravel-google-authenticator.

Let’s create our main file. We are not going to code a entire connection system, I will just show you how you can code a 2-step authentication system. The user will have to download the Google Authenticator application, scan a barcode and then he will have a 6 digits code every 30 seconds from the application. You will ask for this code if he has previously entered his password successfully.

Put this file in the same directory.

If you want to test this code, run this command from your terminal:

Open your browser, go to http://localhost:8080, scan the barcode from the Google Authenticator application and refresh the page. The current code (given by the website) should follow what is written on your phone. You should have something like this in your Google Authenticator app.

Conclusion

And… That’s it! You know how to generate a secret key for a user, the barcode associated with it and you have a function to check if the user has entered the right code. Of course you will have some work to do if you want to implement it into your website: you will have to explain to your users how to install the app, how to scan the barcode, to save their secret key in your users table and add a step to your login process.

But it’s not so difficult 🙂

Neutralité du Net en France : la dangerosité d’imposer du filtrage aux hébergeurs

Standard

L’impact d’Hadopi sur le P2P en France

La “loi” Hadopi aurait un impact assez efficace envers les utilisateurs lambdas qui utilisent le protocole P2P pour s’échanger des contenus non libres de droit. C’est en tout ce qu’affirme cette étude récente (Investigating the reaction of BitTorrent content publishers to antipiracy actions) menée par plusieurs chercheurs internationaux, dont des chercheurs de l’Institut-Mines Télécom – Télécom SudParis.

Comme l’indique cette étude :

En comparant avec les autres uploadeurs, ceux situés en dehors de nos frontières, nous avons remarqué que le nombre d’éditeurs mettant en ligne des contenus depuis l’hexagone avait diminué de 46 % entre une première période située en avril-mai 2010 et une seconde, en octobre-décembre 2011

En revanche le nombre total de contenus partagés depuis la France a augmenté de 18 %.

Si on regarde plus précisément, l’activité des uploaders occasionnels, qui partagent temporairement et avec des connexions à Internet de faible capacité aurait chuté de 57 % entre 2010 et 2011. A contrario, les “uploaders professionnels”, qui mettent en partage des contenus pour alimenter des sites de torrents, seraient devenus plus actifs encore qu’auparavant. Ainsi, 29 des 100 uploaders les plus actifs de The Pirate Bay seraient originaires de France si on en croit leur adresse IP.

L’engouement pour OVH

Mais pourquoi un tel engouement pour la France ? Parce que OVH, premier hébergeur européen est très attractif pour les uploaders professionnels. En effet, OVH propose des serveurs dédiés que beaucoup de professionnels utilisent comme seedbox (un serveur dédié à la réception et l’émission de fichiers).

Et là, l’étude se met un doigt dans l’oeil. Elle pointe le laxisme d’OVH vis-à-vis de l’utilisation du P2P sur ses serveurs.

Nous avons contacté OVH pour avoir quelques informations sur sa popularité parmi les éditeurs BitTorrent professionnels, et avons appris qu’OVH ne surveillait pas activement ses clients sauf si une violation est rapportée par un tiers et que le client ne cesse pas son activité. Une telle stratégie de surveillance passive est inhabituelle. Ces dernières années la plupart des hébergeurs ont adopté des politiques de surveillance strictes pour empêcher la distribution de contenus protégés par les droits d’auteur depuis leurs serveurs à travers des applications P2P.

Pourtant OVH respecte scrupuleusement la loi en ne surveillant pas l’usage que font ses clients des serveurs dédiés loués. En France, l’article 6.7 de la loi pour la confiance dans l’économie numérique indique que les sociétés d’hébergement de données :

ne sont pas soumises à une obligation générale de surveiller les informations qu’elles transmettent ou stockent, ni à une obligation générale de rechercher des faits ou des circonstances révélant des activités illicites.

Une riposte graduée pour les hébergeurs ?

Mireille Imbert-Quaretta, présidente de la commission de protection des droits de l’Hadopi, ne semble pas être en accord avec ceci et propose la mise en place riposte graduée à l’encontre des hébergeurs dans des propositions d’amendement formulées au ministère de la Culture. Concrètement, il s’agirait d’obliger les hébergeurs à filtrer pro-activement ce qu’ils stockent, et à les mettre en garde en cas d’infractions. Puis s’ils refusent d’améliorer leurs technologies et pratiques de filtrage, l’autorité publique pourrait décider de rendre public le comportement de cette plateforme dans le cadre d’une procédure d’alerte, laquelle pourrait aller jusqu’à demander le blocage de noms de domaine ou serveurs.

Une absurdité sans nom.

La dangerosité d’une obligation de filtrage imposée aux hébergeurs

Si jamais une obligation de filtrage était imposée aux hébergeurs, ceci serait extrêmement dangereux. Avant d’être dangereux, ceci serait extrêmement difficile à mettre en place techniquement :

  • OVH loue des centaines de milliers de serveurs dans le monde ;
  • La loi ne pourrait s’appliquer qu’aux résidents français ;
  • Comment déterminer qu’un contenu mis en ligne ou téléchargé est libre de droit automatiquement (c’est-à-dire grâce à un système informatique capable d’être efficace) ? Plusieurs millions de fichiers sont échangés sur les centaines de milliers de serveurs de l’infrastructure d’OVH au quotidien.

Et puis surtout ceci serait extrêmement dangereux. En demandant aux hébergeurs de filtrer le contenu qui est stocké sur les serveurs qu’ils louent, on leur donne des droits qui sont réservés à la justice. Un intermédiaire technique serait alors en droit (et en devoir d’après la loi) de déterminer, lui seul, quel fichier peut et ne peut pas être stocké sur ses serveurs. Ceci pourrait engendrer des dérives importantes voire catastrophiques :

  • L’hébergeur incapable de proposer un système informatique pouvant déterminer automatiquement si un fichier est libre de droit ou non interdit à ses clients de modifier des fichiers en dehors des heures de bureau, entre 8h et 18h. Tout nouvel envoi de fichier vers un serveur devra être validé humainement, ce qui peut prendre plusieurs heures voir plusieurs jours.
  • Votre hébergeur ne partage pas les mêmes convictions politiques que vous et décide de censurer ou d’altérer le contenu de l’article politique faisant controverse que vous avez rédigé et que vous voulez mettre en ligne.
  • Votre hébergeur ayant l’obligation de prêter attention à ce que vous faites sur votre serveur s’aperçoit que vous développez un outil qui pourrait être très utile pour son fonctionnement. Sans vous mettre au courant, il fait une copie de votre travail.

Imaginez que quelqu’un s’amuse à censurer, ou pire, à modifier les mots que vous formulez quand vous parlez à quelqu’un, face à face. Plutôt gênant non ? Et bien ceci pourrait être encore plus grave : votre droit de publier ce qui vous chante pourrait être remis en cause.

La neutralité technologique et la neutralité du réseau ne sont pas des fantaisies techniques. Ces principes sont fondamentaux pour la protection de nos droits.

Raildar

Standard

RaildarL’open data a le vent en poupe en ce moment, et on ne peut qu’applaudir les initiatives des institutions qui font le choix de mettre à la disposition de la communauté une partie de leurs données. Dernièrement c’est le gouvernement français qui a montré l’exemple en refaisant complètement la plateforme data.gouv.fr regroupant les données provenant des services publics français.

Récemment, la SNCF s’est lancée partiellement dans l’open data. Guillaume Pepy a du faire partie de l’initiative, lui qui a promis que de nouveaux outils innovants seraient édités prochainement par la SNCF. Pour autant, on ne peut pas encore dire que la SNCF met à la disposition de ses usagers des outils appropriés pour visualiser ses données publiques.

Spyou a décidé de prendre les devants et propose depuis le mois de décembre 2013 un outil très intéressant nommé Raildar (on notera le jeu de mots !) qui permet de suivre les trains circulant en France en quasi temps réel. Comme indiqué sur le wiki de Raildar,

Les données proviennent d’une collecte permanente et (très) régulière des informations de circulation disponibles sur divers sites officiels (infolignes, gares-en-mouvement, …) et nous les avons rapproché des informations théoriques fournies par les API SNCF officielles pour créer notre propre API mêlant données théoriques de circulation et informations temps réel.

Le résultat est bluffant. Et comme un lien vaut mieux qu’une longue description, je vous donne ceci : raildar.fr.

EasyPHPCharts

Standard

Area Chart
Last week I released on GitHub a quite simple PHP class to easily build beautiful charts from www.chartjs.org. The source code can be found on GitHub here: github.com/AntoineAugusti/EasyPHPCharts. Do not hesitate to submit a pull request if you found a bug or a typo in my code. I’m quite sure that the code isn’t perfect yet.

A documentation can be found at GitHub Pages : antoineaugusti.github.io/EasyPHPCharts/. If you consider that the documentation is not detailed enough, please e-mail me! If you want to implement new features, please take a loot at the ChartJS Documentation : www.chartjs.org/docs/.

I hope you’ll be able to draw beautiful charts 🙂

Kids Can’t Use Computers… And This Is Why It Should Worry You

Standard

Kids and computersIl y a quelques jours, Marc Scott, un enseignant en informatique a publié un article sur Coding 2 Learn intitulé “Kids Can’t Use Computers… And This Is Why It Should Worry You” et qui a depuis fait beaucoup parlé de lui.

Dans cet article Marc relate les situations auxquelles il est confronté au quotidien : des élèves, des professeurs qui viennent lui demander de l’aide parce qu’ils rencontrent un problème avec leur ordinateur. Il s’étonne que ces personnes n’arrivent pas à régler ces soucis, assez classiques et qui peuvent être résolus avec un peu de bon sens ou au pire à l’aide d’une recherche sur un moteur de recherche. Il en arrive à la conclusion que la plupart des personnes ne savent pas utiliser un ordinateur, et que ceci est inquiétant vu la place de l’informatique dans notre société.

J’ai trouvé cet article absolument passionnant. Tout d’abord parce qu’il est bien écrit, ensuite parce qu’au-delà de la critique dans un premier temps, Marc donne des pistes sur ce qui a pu mener à ceci et des moyens pour essayer d’enrailler le phénomène.

L’article original peut être lu ici : coding2learn.org/blog/2013/07/29/kids-cant-use-computers/.
Une traduction française par Nathalie Pauchet peut être lue ici : lunatopia.fr/blog/les-gamins-ne-savent-pas-utiliser-les-ordinateurs.

Étude des données de Teen Quotes

Standard

Teen Quotes ?

Teen Quotes est un projet sur lequel je travaille depuis novembre 2010. En quelques mots, Teen Quotes est un site regroupant des citations du quotidien des adolescents à propos de leurs centres d’intérêts : l’école, les amis, les premiers amours, les premières peines. Teen Quotes est intégralement en anglais, a déjà enregistré plus de 1,5 M visiteurs et est présent sur Twitter, Facebook, le web, le web mobile et l’App Store.

Miam, des données !

Poussé par la réalisation d’un projet pour mon cursus à l’INSA de Rouen, j’ai décidé de prendre le temps de faire une analyse plus complètes des données associées à Teen Quotes. Teen Quotes étant déjà un projet ouvert (le code source est majoritairement libre de consultation), il apparait comme logique que l’étude de ces données soit publique.

Sans plus attendre, voici les liens pour consulter cette étude :