Créer une application : l’interface
Article mis à jour le 26/10/2015
Dans le développement d’une application l’interface représente toujours une grosse partie du travail. Que nous propose Laravel 5 à ce sujet ? A vrai dire pas grand chose. Pour les linuxiens il existe Elixir qui permet d’utiliser Gulp. Je ne vous en parlerai pas parce que j’utilise Windows et je n’ai donc aucune expérience sur le sujet.
J’ai choisi pour l’application d’utiliser Bootstrap en utilisant ses fichiers LESS pour adapter le visuel. D’autre part j’ai adopté l’approche de html5boilerplate pour l’organisation.
Laravel 5 a un dossier de ressources et des sous-dossiers :
On a 3 catégories de données :
- assets : ici on va mettre nos fichiers LESS de Bootstrap
- lang : ici c’est pour les fichiers de langue, pour l’application on va ajouter le français à l’anglais préexistant
- views : ici on a toutes les vues de l’application
Un autre aspect concerne le choix de l’éditeur en ligne et du gestionnaire de médias. Le choix est assez vaste. J’ai opté pour CKEditor (avec le plugin codesnippet) pour l’éditeur et Filemanager pour la gestion des médias. On va voir dans cet article comment s’effectue leur intégration.
Pour les templates j’en ai choisi deux gratuits sur le site startbootstrap. Pour le front-end : Business Casual avec cet aspect :
Je l’ai prévu en deux langues : français et anglais.
Pour le back-end : SB Admin avec cet aspect :
Bootstrap
J’aime bien Bootstrap, il est simple et puissant. Il est devenu quasiment un standard. J’ai créé un cours que je maintiens régulièrement à jour.
La meilleure manière d’adapter Bootstrap à ses besoins est d’utiliser LESS. J’ai rassemblé tous les fichiers utiles dans le dossier resources/assets :
Dans le dossier bootstrap figure l’original du dossier less du framework. Il contient donc tous les fichiers originels non modifiés :
Il est ainsi facile de faire une mise à jour.
De la même façon on trouve les fichiers LESS des icônes de Font Awesome dans un dossier spécifique :
On trouve ensuite deux dossiers, un pour le front-end et un pour le back-end :
C’est là que figurent l’adaptation de Bootstrap pour l’application. Les fichiers _main.less sont des copies modifiées du fichier boostrap.less. La compilation du framework s’effectue avec ces fichiers. pour le front-end on ajoute deux fichiers :
@import "business.less"; @import "final.less";
Le fichier business.less est le template du front-end et final.less comporte quelques adaptations.
De la même façon on importe le template pour le back-end :
@import "sb-admin.less";
Il suffit ensuite de placer les fichiers compilés dans le dossier public :
On les appelle directement des templates, par exemple pour le front-end (resources/views/front/template.blade.php):
{!! HTML::style('css/main_front.css') !!}
Pour le Javascript html5boilerplate prévoit un fichier plugins.js. Il suffit d’ajouter le code de Bootstrap :
// Avoid `console` errors in browsers that lack a console. (function() { var method; var noop = function () {}; var methods = [ 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn' ]; var length = methods.length; var console = (window.console = window.console || {}); while (length--) { method = methods[length]; // Only stub undefined methods. if (!console[method]) { console[method] = noop; } } }()); /*! * Bootstrap v3.2.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires // All Bootstrap script there
Il faut placer ce fichier dans le dossier public :
Le fichier main.js comporte un peu de code particulier pour l’application.
Le dossier js/vendor comporte les scripts préconisés par html5boilerplate ainsi que jQuery :
Avec cette organisation il est facile d’apporter des modifications et de mettre à jour avec une nouvelle version de Bootstrap. Ce n’est évidemment pas la seule manière de procéder mais je l’ai trouvée efficace à l’usage.
CKEditor
CKEditor est un éditeur très puissant, fiable, facile à paramétrer, et qui comporte une multitude de plugins. Pour l’utiliser j’ai opté pour une configuration particulière parce que je voulais ajouter le plugin codesnippet :
Il suffit ensuite d’ajouter les langues qu’on veut :
Et on a plus qu’à charger le tout ! Ensuite on place tout ça dans le dossier public :
On peut aussi utiliser un CDN pour utiliser CKEditor mais je n’ai pas trouvé l’intégration de plugins vraiment explicite dans ce cas alors j’ai préféré tout charger.
L’intégration dans une vue s’effectue en chargeant la librairie :
{!! HTML::script('ckeditor/ckeditor.js') !!}
Puis en prévoyant un textarea :
{!! Form::control('textarea', 0, 'content', $errors, trans('back/blog.content')) !!}
Il faut ensuite prévoir la configuration :
var config = { codeSnippet_theme: 'Monokai', language: '{{ config('app.locale') }}', height: 100, filebrowserBrowseUrl: '{!! url($url) !!}', toolbarGroups: [ { name: 'clipboard', groups: [ 'clipboard', 'undo' ] }, { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] }, { name: 'links' }, { name: 'insert' }, { name: 'forms' }, { name: 'tools' }, { name: 'document', groups: [ 'mode', 'document', 'doctools' ] }, { name: 'others' }, //'/', { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] }, { name: 'styles' }, { name: 'colors' } ] }; config['height'] = 400; CKEDITOR.replace( 'content', config);
Le résultat est sympathique :
On trouve le bouton pour insérer du code du plugin codesnippet :
Je ne vais pas entrer dans le détail de la configuration. Vous pouvez trouver toutes les explications sur le site.
Filemanager
Pour utiliser Filemanager on pourrait charger tous les fichiers sur le site et de les placer dans le dossier public comme on l’a fait pour CKEditor. C’est ce que j’avais fait dans un premier temps puis je me suis dit qu’il serait peut-être judicieux de créer un package. C’est ce que j’ai fait :
Du coup l’intégration devient très facile, il suffit dans un premier temps de prévoir la référence dans composer.json :
"require": { "php": ">=5.5.9", "laravel/framework": "5.2.*", "laravelcollective/html": "5.2.*", "bestmomo/filemanager": "1.1.*" },
Ensuite il faut faire un composer update.
Il faut ensuite ajouter la référence du service provider dans la configuration (config/app.php) :
App\Providers\RouteServiceProvider::class, App\Services\Html\HtmlServiceProvider::class, Bestmomo\Filemanager\FilemanagerServiceProvider::class,
Pour terminer il faut tout publier avec php artisan vendor:publish. On se retrouve avec un nouveau dossier :
Si vous voulez comprendre comment ça fonctionne regardez le fichier filemanager.config.js :
Toute la configuration se trouve dans ce fichier. Toutes les informations concernant ce fichier se trouvent ici. Pour l’application j’ai juste modifié ces lignes :
"serverRoot": true, "fileRoot": "/", "baseUrl": false,
La chose la plus importante à gérer avec ce genre d’outil est la sécurité. Regardez le fichier filemanager/connectors/php/default.config.php :
// Laravel init require getcwd() . '/../../../../bootstrap/autoload.php'; $app = require_once getcwd() . '/../../../../bootstrap/app.php'; $kernel = $app->make('Illuminate\Contracts\Http\Kernel'); $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); $id = $app['encrypter']->decrypt($_COOKIE[$app['config']['session.cookie']]); $app['session']->driver()->setId($id); $app['session']->driver()->start(); // Folder path $folderPath = config('filemanager.folder_path'); // Check if user in authentified if(!$app['auth']->check()) { $laravelAuth = false; } else { // Check if user has all access if($app['auth']->user()->accessMediasAll()) { $laravelAuth = true; } elseif(method_exists($app['auth']->user(), 'accessMediasFolder')) { // Check if user has access to one folder if($app['auth']->user()->accessMediasFolder()) { // Folder name with user id $folderPath .= 'user' . $app['auth']->id(); $laravelAuth = true; } else { $laravelAuth = false; } } else { $laravelAuth = false; } } /** * Check if user is authorized * * * @return boolean true if access granted, false if no access */ function auth() { return $GLOBALS['laravelAuth']; } $fm = new Filemanager(); $fm->setFileRoot($folderPath, true); ?>
En gros on crée une application, on crée le kernel, on récupère le cookie, on lance les sessions. On a alors tout ce qu’il faut pour vérifier l’utilisateur.
On fait appel à des méthodes présentes dans le modèle User pour les droits d’accès :
/** * Check media all access * * @return bool */ public function accessMediasAll() { return $this->role->slug == 'admin'; } /** * Check media access one folder * * @return bool */ public function accessMediasFolder() { return $this->role->slug != 'user'; }
On va ainsi renseigner la variable $laravelAuth. On a deux cas :
- Si c’est un utilisateur qui a accès à tout (un administrateur), on se contente de mettre la valeur true,
- Si c’est un utilisateur seulement autorisé dans son dossier personnel (un rédacteur) on détermine le nom de son dossier à partir de son id. On vérifie si ce dossier existe et si ce n’est pas le cas on le crée. Il suffit ensuite d’informer filemanager du dossier avec la méthode setFileRoot. Ainsi chaque rédacteur sera bien cantonné à son dossier personnel.
Vous pouvez vérifier que la sécurité fonctionne bien en supprimant tous les cookies de votre navigateur et en lançant l’url …/filemanager/index.html :
L’intégration dans CKEditor est d’une grande facilité puisqu’il suffit de déclarer la bonne url :
var config = { ... filebrowserBrowseUrl: '{!! url($url) !!}',
L’url est récupérée dans la vue avec la variable $url.
Si vous regardez dans le dossier des images de Filemanager :
Vous voyez un dossier user2 qui contient les images de ce rédacteur (avec l’id 2) qui ne doit avoir accès qu’à ce dossier.
Le gestionnaire est accessible avec le bouton « Explorer le serveur » de la fenêtre de CKEditor :
Pour l’accès direct aux médias à partir de l’administration j’ai inclus Filemanager dans une i-frame :
<div class="iframe-responsive-wrapper"> <img class="iframe-ratio" src="data:image/gif;base64,R0lGODlhEAAJAIAAAP///wAAACH5BAEAAAAALAAAAAAQAAkAAAIKhI+py+0Po5yUFQA7"/> <iframe scrolling="no" src="{!! url($url) !!}" width="640" height="360" frameborder="2" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe> </div>
Avec un visuel plutôt sympathique :
Vous avez là les grandes lignes de la création de l’interface de l’application. Nous aurons sans doute l’occasion dans les prochains articles de détailler certains points…
12 commentaires
Saradimi
Bonjour cher Bestmomo,
Après de longues heures de prise de tête je me décide à t’écrire pour te demander de l’aide…
Après avoir implémenter le package « Filemanager », l’upload de fichiers et la création de dossiers fonctionnent parfaitement. Par contre, il est impossible de supprimer quoi que soit.
Quand je tente une suppression, j’ai droit à un joli « no way »
Il me semble que ma config est correcte :
« fileRoot »: « / »,
‘folder_path’ => « /uploads/files/ »
Si tu pouvais m’éclairer de tes lumières, je t’en serait très reconnaissant 🙂
Merci d’avance
bestmomo
Bonjour,
Essaie plutôt comme ça :
‘folder_path’ => « uploads/files/ »
Saradimi
Merci de ta réponse mais j’ai déjà essayé.
D’ailleurs, j’ai une indication supplémentaire pour mon pb, avec la config suivante :
« fileRoot »: « / »,
‘folder_path’ => « uploads/files/ »
L’upload de fichier, la création de dossier la génération de vignettes etc… tout fonctionne sauf la suppression.
J’ai fait un test en retournant le chemin du fichier utilisé dans la fonction delete de la classe :
if(!$this->has_permission(‘delete’) || !$this->is_valid_path($current_path)) {
$this->error($current_path);
}
et voici le chemin que j’obtient :
/chemin/vers/monsite/public/uploads/files/uploads/files/assistance-technique.jpg
Comme tu peux le voir, la portion « uploads/files » est dupliquée et je ne comprends pas pourquoi.
C’est pour cela que la suppression ne fonctionne pas.
bestmomo
J’ai apporté des modifications il y a quelques semaines, est-ce que tu as bien la dernière version des fichiers ? Parce que j’avais justement des soucis avec les url et j’ai modifié la classe de filemanager pour m’en sortir.
Saradimi
Je te confirme bien que j’ai la dernière version.
Par contre, j’ai apparemment trouvé une solution en bricolant dans la classe filemanager au niveau de la fonction getFullPath (ligne 1097), j’ai dé-commenté les 2 lignes suivantes :
$dynPart = str_replace($_SERVER[‘DOCUMENT_ROOT’], », $this->path_to_files);
$full_path = $this->path_to_files . rawurldecode(str_replace ( $dynPart , » , $path));
Après cela, tout semble fonctionner…
bizaaaaare 🙂
pimous
Bonjour,
Tout d’abord merci pour ton travail que je trouve très utile 🙂 Cependant, j’ai beau suivre tes instructions, je n’arrive pas à intégrer le filemanager dans le CkEditor … (mon ckeditor et le filemanager fonctionnent bien séparément par contre). Pourrais-tu détailler cette étape ? Quand tu dis « L’intégration dans CKEditor est d’une grande facilité puisqu’il suffit de déclarer la bonne url : », tu parles de quelle url ? Oui, je crois que j’ai manqué quelque chose …
bestmomo
Bonjour,
L’intégration dans CKEditor se fait dans la partie configuration à cette ligne :
filebrowserBrowseUrl: ‘{!! url($url) !!}’,
Dans mon cas j’ai l’url ‘filemanager/index.html’
GrCOTE7
Bon, ça roule (y) :
dans le filemanager.blade.php, ajout de ‘public/’. :
et on garde l’avantage du .htaccess tout en ayant le plugin opérationnel (y)
Bien-sûr, garder l’idée pour le blog/template.blade.php du cas d’utilisation du plugin dans le ckeditor ;’)…
Encore Bravos pour votre présent blog 🙂 ! (Que vois-je ?!? Sous WP, et pas L5…? Hou la honte !!! Lol 😉
Encore Bravos 🙂 !
@++
GrCOTE7
Une good news BestMomo…
G trouv la faille…
En fait, elle était ‘indirecte’, je m’eexplique.
Devoir pointer une url publique sur mon.domaine.com/public m’ennuyait, donc, j’utilisait un .htaccess à la racine du site:
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
Ainsi, le chemin /public devenait invisible…
Et comme le soucis était lié au chemin, alors, possible que c’était un effet indésirable…
Note, avec la Debugbar lorsqu’on est logué en GreatRedactor/redac à got.cote-des-annonces.com, on peut voir le chemin appelé dans l’iframe qui était bien:
filemanager/index.html?exclusiveFolder=greatredactor
(Bref, conformément à l’usage du plugin…)
Une fois enlevé le .htaccess (Qui à priori n’avait eût comme seul autre conséquence que de devoir mettre un ../ au début des url des image utilisées dans les posts du blog), les fichiers et dossiers du filemanager se sont enfin affichés…
Bion, j’men va voir si on peut avoir le .htaccess et la modif dans le plugin qui le rendra compatible… ;-)…
GrCOTE7
Oki… Mais au-delà du bleuté, dans la grande zone est inscrit tel quel: « No way. »
A noter, je viens de poser en réel dans (un dossier test) mon appli pour études de vos tutos… ( got.cote-des-annonces.com dans lequel le GreatRedactor a encore les mêmes accès) et même en réel, même symptômes…
( Notons au passage que simple copie des fichiers + monter la BdD + chmod -R 777 storage ont suffit pour rendre opérationnelle une install locale (y) )
ça doit être un point de détail, mais encore faut-il arriver à mettre le doigt dessus…
GrCOTE7
Bonjour,
J’ai refait une installation de A à Z depuis le dépôt GitHub ce jour…
Globalement, impeccable, et tout fonctionne parfaitement… Excepté ce plugin, il est bien présent, et même semble bien gérer la gestion des cookies et donc, la sécurité de base (Avec le lien direct: url/filemanager/index.html )
Par contre, et cela en interdit donc l’usage, dans l’iframe, les bouton Upload et New folder sont grisés (En fait, bleutés ;-)) que l’on soit le GreatRedactor ou l’Admin…
A noter quer cette install « étude » est sous Windows afin de limité tout soucis de chmod, etc…
… Une piste ? … Cas déjà rencontré ?
bestmomo
Salut,
Mes deux boutons sont bleutés aussi mais ils fonctionnent chez moi, et je suis aussi sous Windows…