Project

General

Profile

FrPlugin Tutorial » History » Revision 2

Revision 1 (Romain GEORGES, 2011-03-23 12:34) → Revision 2/3 (Robert Tome, 2013-03-21 10:16)

h1. Tutoriel du plugin Polls  

 Remarque : Pour suivre ce tutoriel, vous devez avoir installé Redmine devel r1786 ou supérieur. 

 {{>toc}} 

 h2. Créer un nouveau plugin 
 
 Vous devez préalablement initialiser la variable RAILS_ENV sur "production" dans votre session shell pour continuer le tutotiel, pour ce faire, tapez : 

 <pre> 
 $ export RAILS_ENV="production" 
 </pre> 

 Pour créer un plugin Redmine, il existe des generateur pour les taches essentielles.  

 La syntaxe des générateurs est : 

 <pre>ruby script/generate redmine_plugin <plugin_name></pre> 

 pour commencer, on va se positionner dans le répertoire de l'application Rails Redmine, puis créer la structure du plugin : 

   % ruby script/generate redmine_plugin Polls 

 La structure du plugin est créé dans le repertoire @vendor/plugins/redmine_polls@: 

 <pre> 
       create    vendor/plugins/redmine_polls/app/controllers 
       create    vendor/plugins/redmine_polls/app/helpers 
       create    vendor/plugins/redmine_polls/app/models 
       create    vendor/plugins/redmine_polls/app/views 
       create    vendor/plugins/redmine_polls/db/migrate 
       create    vendor/plugins/redmine_polls/lib/tasks 
       create    vendor/plugins/redmine_polls/assets/images 
       create    vendor/plugins/redmine_polls/assets/javascripts 
       create    vendor/plugins/redmine_polls/assets/stylesheets 
       create    vendor/plugins/redmine_polls/lang 
       create    vendor/plugins/redmine_polls/README 
       create    vendor/plugins/redmine_polls/init.rb 
       create    vendor/plugins/redmine_polls/lang/en.yml 
 </pre> 

 Editer @vendor/plugins/redmine_polls/init.rb@ pour adjuster les informations (attributs) du plugin (nom, auteur, description et version): 

 <pre><code class="ruby"> 
 require 'redmine' 

 Redmine::Plugin.register :redmine_polls do 
   name 'Polls plugin' 
   author 'John Smith' 
   description 'A plugin for managing polls' 
   version '0.0.1' 
 end 
 </code></pre> 

 Redemarrez l'application Redmine et ouvrez un navigateur sur l'URL http://localhost:3000/admin/plugins. 

 Après vous être loggué avec les droits d'administrateur Redmine, vous devriez voir la liste des plugins tel que : 

 !plugins_list1.png! 

 h2. Générer un modèle Rails de plugin Redmine 

 pour le moment le plugin ne persiste aucuns résultats 
 Nous allons donc créer un modèle Poll simple, tel que : 

 <pre> 
    ruby script/generate redmine_plugin_model <plugin_name> <model_name> [<fields>] 
 </pre> 

 La commande exacte pour notre cas sera : 

 <pre> 
    ruby script/generate redmine_plugin_model polls poll question:string yes:integer no:integer 
 </pre> 

 cette commande crée le modèle Rails Poll et les fichiers de migration correspondant 

 *Remarque : Il est possible que vous deviez renommer les fichiers de migration, les migrations avec des timestamps ne sont pas supportée par le moteur Redmine Actuelle, Si les migrations portent une numérotation avec des timestamps, renommez-les avec "001" "002", etc...  


    <pre>cd vendor/plugins/redmine_polls/db/migrate 
 mv    20091009211553_create_polls.rb 001_create_polls.rb 
 cd ../../../../..</pre> 

 Si vous avez déja executé une migration, vous devez mettre à jour les informations dans la table plugin_schema_info en adequation avec la nouvelle numérotation. 

 Migrer la base de données avec la commande : 

   rake db:migrate_plugins 

 Il est à noter que chaque plugin à son propre jeux de migration. 

 Nous allons ajouter des Votes via la console rails pour pouvoir faire des tests avec. 
 La console Rails permet l'ajout interactif d'enregistrement sur les modèles rails, c'est une shell Ruby Irb, qui pré-charge l'environnement de l'application rails depuis laquelle il est lancé. 
 Pour le moment nous allons juste créer un objet Poll 



 <pre> 
 script/console 
 >> Poll.create(:question => "Pouvez-vous voir ce vote ?") 
 >> Poll.create(:question => "Pouvez-vous voir cet autre vote ?") 
 >> exit 
 </pre> 

 Editez @vendor/plugins/redmine_polls/app/models/poll.rb@ dans le répertoire de votre plugin et ajoutez une méthode #vote qui sera appelée par votre controller: 

 <pre><code class="ruby"> 
 class Poll < ActiveRecord::Base 
   def vote(answer) 
     increment(answer == 'yes' ? :yes : :no) 
   end 
 end 
 </code></pre> 

 h2. Generer un controller 

 Pour le moment le plugins ne fait rien, donc nous allons créer un controller Rails pour celui-ci. 
 Nous pouvons utiliser le générateur Redmine prévue à cet effet : 

 <pre>ruby script/generate redmine_plugin_controller <plugin_name> <controller_name> [<actions>]</pre> 

 la commande exacte pour notre cas est : 

 <pre> 
 % ruby script/generate redmine_plugin_controller Polls polls index vote 
       exists    app/controllers/ 
       exists    app/helpers/ 
       create    app/views/polls 
       create    test/functional/ 
       create    app/controllers/polls_controller.rb 
       create    test/functional/polls_controller_test.rb 
       create    app/helpers/polls_helper.rb 
       create    app/views/polls/index.html.erb 
       create    app/views/polls/vote.html.erb 
 </pre> 

 un controlleur Rails    @PollsController@ avec 2 actions (@#index@ et @#vote@) vient d'être créé. 

 Editez @vendor/plugins/redmine_polls/app/controllers/polls_controller.rb@ dans le répertoire @redmine_polls@ pour implémenter ces deux actions. 

 <pre><code class="ruby"> 
 class PollsController < ApplicationController 
   unloadable 

   def index 
     @polls = Poll.find(:all) 
   end 

   def vote 
     poll = Poll.find(params[:id]) 
     poll.vote(params[:answer]) 
     if poll.save 
       flash[:notice] = 'Vote saved.' 
       redirect_to :action => 'index' 
     end 
   end 
 end 
 </code></pre> 

 Ensuite, editez la vue @vendor/plugins/redmine_polls/app/views/polls/index.html.erb@ qui va afficher les votes existants. 


 <pre> 
 <h2>Polls</h2> 

 <% @polls.each do |poll| %> 
   <p> 
   <%= poll[:question] %>? 
   <%= link_to 'Yes', {:action => 'vote', :id => poll[:id], :answer => 'yes'}, :method => :post %> (<%= poll[:yes] %>) / 
   <%= link_to 'No', {:action => 'vote', :id => poll[:id], :answer => 'no'}, :method => :post %> (<%= poll[:no] %>) 
   </p> 
 <% end %> 
 </pre> 

 Vous pouvez supprimer la vue @vendor/plugins/redmine_polls/app/views/polls/vote.html.erb@ comme il n'y a pas de rendu pour l'action correspondante. 

 Redemarreez le serveur Rails de Redmine et ouvrez votre navigateur sur l'URL http://localhost:3000/polls. 
 Vous devriez voir deux votes et vous pouvez les essailler :  

 !pools1.png! 

 Remarque : les resultats des votes sont remis à zero à chaque requête si vous ne lancez pas votre application en environnement "production", vue que le modèle Poll est stocké dans une variable de classe dans notre exemple.  


 h2. Traductions 

 L'emplacement des fichiers YAML d'internationalisation peut varier en fonction de votre version de Redmine : 

 |_. Version |_. Path| 
 | < 0.9 | @.../redmine_polls/lang@ | 
 | >= 0.9 | @.../redmine_polls/config/locales@ | 

 Si vous voulez que votre plugin marche dans les deux cas vous devez ajouter les fichiers dans les deux cas au bon format. 


 h2. Gestion des menus 

 Notre controller marche bien, mais nos utilisateurs n'en connaissent pas l'existence, il faut donc utilser l'API Redmine pour etendre les menus 
 Nous allons ajouter un une onglet au menu Application 
 So let's add a new item to the application menu. 

 h3. Gestion du menu application 

 Editez le fichier @vendor/plugins/redmine_polls/init.rb@ à la racine de votre plugin et ajoutez la ligne suivante dans le bloc ""registration"" : 

 <pre><code class="ruby"> 
 Redmine::Plugin.register :redmine_polls do 
   [...] 
  
   menu :application_menu, :polls, { :controller => 'polls', :action => 'index' }, :caption => 'Polls' 
 end 
 </code></pre> 

 la syntaxe est : 

   menu(menu_name, item_name, url, options={}) 

 Il y a cinq menu dans Redmine que vous pouvez étendre : 

 * @:top_menu@ - le menu en haut à gauche 
 * @:account_menu@ - le menu en haut à droite avec la gestion des comptes et connexions 
 * @:application_menu@ - Le menu affiché quand un utilisateur n'est dans aucuns projets. 
 * @:project_menu@ - Le menu affiché quand un utilisateur est dans un projet. 
 * @:admin_menu@ - le menu d'administration  

 les options sont disponibles sont : 

 * @:param@ - la clef parametre utilisée pour les id de projet (valeur par défaut @:id@) 
 * @:if@ - un Proc appelé avant de faire le rendu du menu,l'item est affiché que si le Proc retourne true 
 * @:caption@ - Le label du menu du type: 

   * un symbole de chaine de caratères localisée 
   * une chaine de caratères 
   * un Proc qui peut pendre le project_id en argument 

 * @:before@, @:after@ - specifie ou l'item du menu doit être inséré (ex. @:after => :activity@) 
 * @:first@, @:last@ - si ils sont true, l'item est positionné au début ou a la fin du menu (ex. @:last => true@) 
 * @:html@ - un hachage d'options html passés à @link_to@ pour le rendu du menuitem 

 Dans notre exemple, nous avons ajoutez un item au menu application, qui est vide par defaut. 

 Redemarrez l'application et allez à l'URL http://localhost:3000: 

 !application_menu.png! 

 Maintenant vous pouvez accéder aux votes en cliquant sur l'onglet Polls depuis l'ecran d'accueil de Redmine 

 h3. Etendre le menu projet 

 On va considérer, mantenant que les votes sont définis au niveau des projets (même si ce n'est pas le cas dans le modèle de notre exemple) 
 Donc, on veut ajouter l'onglet au menu Projet et plus au menu Application. 

 Editez le fichier @init.rb@ et replacez la dernière ligne que nous avons ajoutez par les deux lignes suivantes : 

 <pre><code class="ruby"> 
 Redmine::Plugin.register :redmine_polls do 
   [...] 

   permission :polls, {:polls => [:index, :vote]}, :public => true 
   menu :project_menu, :polls, { :controller => 'polls', :action => 'index' }, :caption => 'Polls', :after => :activity, :param => :project_id 
 end 
 </code></pre> 


 la deuxième ligne ajoute l'onglet Polls dans le menu Projet juste derrière l'onglet Activité. 
 la première ligne sert à déclarer que nos deux actions du @PollsController@ sont publiques. 
 Nous reviendrons plus tard sur les permissions. 

 Redémarrez encore l'application et entrez dans un de vos projets: 

 !http://www.redmine.org/attachments/3773/project_menu.png! 

 If you click the Polls tab, you should notice that the project menu is no longer displayed. 
 To make the project menu visible, you have to initialize the controller's instance variable @@project@. 

 Si vous cliquez sur l'onglet Polls, vous constatez que le menu projet n'est plus affiché. 
 Pour continuer à l'afficher, nous devons initier initi la variable d'instance @@project@ du controlleur. 

 Editez votre PollsController tel que: 

 <pre><code class="ruby"> 
 def index 
   @project = Project.find(params[:project_id]) 
   @polls = Poll.find(:all) # @project.polls 
 end 
 </code></pre> 

 L'id du projet est disponible dans @:project_id@ grace à l'option @:param => :project_id@ dans la déclaration du menu item précédante. 

 Maintenant, vous devriez continuer à voir le menu projet quand vous visualisez un vote : 



 !http://www.redmine.org/attachments/3774/project_menu_pools.png! 

 h2. Ajouter des nouvelles permissions 

 Pour le moment, tout le monde peut voter. 
 On peut rendre ceci configurable en ajoutant des permissions Redmine. 
 Nous allons definir deux permissions au niveau Projet, une pour visualiser et une pour voter. 
 Ces permissions ne seront donc plus publiques (l'option @:public => true@ disparait) 


 Editez @vendor/plugins/redmine_polls/init.rb@ pour remplacer la permission précedante avec les deux lignes suivantes : 

 <pre><code class="ruby"> 

   permission :view_polls, :polls => :index 
   permission :vote_polls, :polls => :vote 
 </code></pre> 

 Redemarrez l'application et allez à l'URL http://localhost:3000/roles/report: 


 !permissions1.png! 

 on voit donc deux permissions de plus ajoutable à votre role. 
 Biensur, il va falloir ajouter du code au PollsController, donc ces actions sont pour le moment protégé par les permissions de l'utilisateur courant. 

 Pour cela, nous avons juste à ajouter le filtre @:authorize@ et faire en sorte que la variable d'instance @project est initialisée avant d'appeler ce filtre   

 Voici à quoi ça doit ressembler dans l'action @#index@ : 


 <pre><code class="ruby"> 
 class PollsController < ApplicationController 
   unloadable 
  
   before_filter :find_project, :authorize, :only => :index 

   [...] 
  
   def index 
     @polls = Poll.find(:all) # @project.polls 
   end 

   [...] 
  
   private 
  
   def find_project 
     # @project variable must be set before calling the authorize filter 
     @project = Project.find(params[:project_id]) 
   end 
 end 
 </code></pre> 

 Récupérer le projet courant avant le @#vote@ peut-être fait de façon très similaire. 
 Après ça, voir un vote et voter ne sera accessible qu'aux administrateur ou aux utilisateurs que on un rôle approprié dnas le projet. 

 Pour traduire les symboles de vos permissions dans plusieurs langues, vous devez ajouter les labels de textes necessaires dans vos fichiers de langues tel que : 
 labels like this: 

 <pre><code class="ruby"> 

   permission_view_polls: Voir les votes 
   permission_vote_polls: Voter 

 </code></pre> 

 Dans cette exemple, on a créé le fichier fr.yml, mais on peut créer des fichiers pour toutes les langues que l'on souhaite. 

 On voit dans cette exemple, que le lable est le symbole de la permission @:view_polls@ et @:vote_polls@ préfixé de @permission_@ 


 Redemarrez votre application et pointer sur la section permissions. 

 h2. Créer un module de projets 

 Pour le moment, la fonctionnalité de vote est ajoutée à tous vos projets, mais on souhaite la rendre activable ou désactivable pour chaque projet. 
 On va donc créer un module de projet 'Polls'. 
 On fait ceci en inclant les permissions dans un bloc @#project_module@. 


 Editez @init.rb@ et changez la déclaration des permissions : 

 <pre><code class="ruby"> 
   project_module :polls do 
     permission :view_polls, :polls => :index 
     permission :vote_polls, :polls => :vote 
   end 
 </code></pre> 

 Redemarrez votre application et pointer sur un de vos projet dans sa configuration. 
 Cliquez sur l'onglet Modules, vous y verez le module Polls à la fin de la liste de modules, désactivé par defaut. 

 !modules.png! 

 Vous pouvez l'activer ou le désactiver pour chaque projet.  


 h2. Améliorer la vue du plugin 

 h3. Ajouter des Styles CSS 

 On va commencer par ajouter un CSS à notre vue  

 Créez un fichier nommé @voting.css@ dans le répertoire @vendor/plugins/redmine_polls/assets/stylesheets@ : 

 <pre> 
 a.vote { font-size: 120%; } 
 a.vote.yes { color: green; } 
 a.vote.no    { color: red; } 
 </pre> 

 Quand on redemarrer l'application, les plugin assets sont copié dans @public/plugin_assets/redmine_polls/@ 
 par le moteur de Rails pour les rendrent disponibles via votre serveur Web. 
 Donc toute modification des CSS des plugins impliques un redemarrage de Rails. 
 Ensuite, on va devoir ajouter les lignes suivantes dans la vue @vendor/plugins/redmine_polls/app/views/polls/index.html.erb@ pour que nos CSS soient pris en compte et incluent dans le header de cette vue par Redmine : 


 <pre> 
 <% content_for :header_tags do %> 
     <%= stylesheet_link_tag 'voting', :plugin => 'redmine_polls' %> 
 <% end %> 
 </pre> 

 Notez que l'option @:plugin => 'redmine_polls'@ est requise quand on appelle le helper @stylesheet_link_tag@. 

 Les Javascripts peuvent être inclut dans le plugin en utilisant le helper @javascript_include_tag@ de la même manière. 

 h3. Modifier le titre de la page 

 on peut modifier le titre HTML de la page depuis le vue elle-même grace au helper @html_title@  
 Exemple: 

   <% html_title "Polls" -%> 


 h2. Tester votre plugin 

 h3. test/test_helper.rb: 

 Voici le contenu de mon fichier de tests : 

 <pre> 
 require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper') 
 </pre> 

 h3. Exemple de test: 

 Contenu de requirements_controller_test.rb: 

 <pre> 
 require File.dirname(__FILE__) + '/../test_helper' 
 require 'requirements_controller' 

 class RequirementsControllerTest < ActionController::TestCase 
   fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details, 
            :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages, 
            :attachments, :custom_fields, :custom_values, :time_entries 

   def setup 
     @skill = Skill.new(:skill_name => 'Java') 
     @project = Project.find(1) 
     @request      = ActionController::TestRequest.new 
     @response     = ActionController::TestResponse.new 
     User.current = nil 
   end 

   def test_routing 
     assert_routing( 
       {:method => :get, :path => '/requirements'}, 
       :controller => 'requirements', :action => 'index' 
     ) 
   end 
 </pre> 

 h3. Initialisation de la base de test : 

 Il semble plus facile d'initialiser la base de tests directement par la commande rake : 

 <pre> 
 rake db:drop db:create db:migrate db:migrate_plugins redmine:load_default_data RAILS_ENV=test 
 </pre> 

 h3. Executer les tests: 

 Pour executer the requirements_controller_test.rb on utilise la commande suivante : 

 <pre> 
 rake test:engines:all PLUGIN=redmine_requirements 
 </pre> 

 h3. Tester avec des utilisateurs et des projets  

 If your plugin requires membership to a project, add the following to the beginning of your functional tests: 

 Si votre plugin requière de appartenances à un projet, ajouter les lignes suivantes pour que ce soit opérationnel : 

 <pre> 
 def setup 
   @request      = ActionController::TestRequest.new 
   @response     = ActionController::TestResponse.new 
   User.current = nil 
 end 

 def test_index 
   @request.session[:user_id] = 2 
   get :index, :project_id => 1 
   assert_response :success 
   assert_template :index 
 end 
 </pre>