Redmineプラグインの作成

おそらく多くの人がRedmineプロジェクト管理システムについて聞いたことがあり、一部の人はRedmineプロジェクト管理システムを仕事で使用しているかもしれません。 Redmineは、有名なRuby on Railsフレームワークで書かれた、かなり柔軟なクロスプラットフォームシステムです。 そのようなほとんどのシステムと同様に、Redmineではサードパーティのプラグインを使用して機能を拡張できます。 現時点では、さまざまな好みや色に対応するこれらのプラグインがすでに1,000個以上あります。 それらの1つと、その例を使用してRedmineプラグインを作成する方法についてお話したいと思います。



リポジトリプラグイン-1つのzipアーカイブでプロジェクトリポジトリ(リポジトリ)からファイルとフォルダーをダウンロードできるプラグイン。 Redmineは、プラグインを作成するための視覚的なガイドを提供しています。

それでは、プラグインの作成を始めましょう。 以下に書かれていることはすべて、Linux OSを備えたWebサーバーにインストールされたredmineに関係しています。

まず、redmineがインストールされているディレクトリに移動します(たとえば、/ var / www / redmine):



$ cd /var/www/redmine







環境変数RAILS_ENVを設定します。



$ export RAILS_ENV="production"







これで、プラグインに必要なファイルを生成できます。



$ ruby script/generate redmine_plugin repository

create vendor/plugins/redmine_repository/app/controllers

create vendor/plugins/redmine_repository/app/helpers

create vendor/plugins/redmine_repository/app/models

create vendor/plugins/redmine_repository/app/views

create vendor/plugins/redmine_repository/db/migrate

create vendor/plugins/redmine_repository/lib/tasks

create vendor/plugins/redmine_repository/assets/images

create vendor/plugins/redmine_repository/assets/javascripts

create vendor/plugins/redmine_repository/assets/stylesheets

create vendor/plugins/redmine_repository/lang

create vendor/plugins/redmine_repository/README

create vendor/plugins/redmine_repository/init.rb

create vendor/plugins/redmine_repository/lang/en.yml







vendor / plugins / redmine_repository / init.rbファイルを編集して、指定します

これには、プラグインの名前、説明、作成者、およびプラグインが記述されているredmineの最小バージョンが含まれます。

 require 'redmine' require 'dispatcher' Redmine::Plugin.register :redmine_repository do name 'Redmine Repository plugin' author 'Sanny' description 'This is a reposirory plugin for Redmine' version '0.0.2' requires_redmine :version_or_higher => '1.1.2' end
      
      





プラグインを使用すると、リポジトリからダウンロードするファイルを選択できます。 したがって、既存のタイプのストレージにチェックボックスを追加する必要があります

画像

なぜなら リポジトリの既存のビューを変更する必要がある場合、最も簡単な方法は、リポジトリの外観を担当するファイルをプラグインの適切なディレクトリにコピーしてから、これらのファイルを編集することです。



$ cp app/views/repositories/_dir_lsit.rhtml vendor/plugins/redmine_repository/app/views/repositories

$ cp app/views/repositories/_dir_lsit_content.rhtml vendor/plugins/redmine_repository/app/views/repositories

$ cp app/views/repositories/show.rhtml vendor/plugins/redmine_repository/app/views/repositories







_dir_lsit.rhtmlファイルは、リポジトリファイルシステムツリーを表示するために拒否されます。 そこで、「ダウンロード」ボタンを追加してダウンロードします。

 <% if authorize_for('repositories', 'entries_operation') %> <div style="float: right;"> <%= submit_tag(l(:Download), :name => "download_entries") %> </div> <% end %>
      
      





ボタンが機能するには、form_tagのすべてのコードをラップします。

 <% form_tag({:action => "entries_operation"}, :method => :post, :id => "Entries") do %> ... <% if authorize_for('repositories', 'entries_operation') %> <div style="float: right;"> <%= submit_tag(l(:Download), :name => "download_entries") %> </div> <% end %> <p> </p> <% end %>
      
      





次に、リポジトリ表示の各エントリにチェックボックスを追加します。 これを行うには、_dir_lsit_content.rhtmlファイルに次の変更を加えます。

の代わりに

 <% if entry.is_dir? %> <span class="expander" onclick="<%= remote_function :url => {:action => 'show', :id => @project, :path => to_path_param(ent_path), :rev => @rev, :depth => (depth + 1), :parent_id => tr_id}, :method => :get, :update => { :success => tr_id }, :position => :after, :success => "scmEntryLoaded('#{tr_id}')", :condition => "scmEntryClick('#{tr_id}')"%>"> </span> <% end %>
      
      





書きます

 <% if entry.is_dir? %> <span class="expander" onclick="<%= remote_function :url => {:action => 'show', :id => @project, :path => to_path_param(entry.path), :rev => @rev, :depth => (depth + 1), :parent_id => tr_id, :p arent_val => entry.path}, :method => :get, :update => { :success => tr_id }, :position => :after, :success => "scmEntryLoaded('#{tr_id}')", :complete => "checkBranch('#{tr_id}', getParentNodeChecked('#{params[:parent_val]}'))", :condition => "scmEntryClick('#{tr_id}')"%>"> </span> <span><%= check_box_tag("folders[]", entry.path, false, :id => params[:parent_id], :onclick => "checkBranch('#{tr_id}', this.checked);" ) if authorize_for('repositories', 'entries_operation') %></span> <% else %> <span style="padding-left: 8px"> </span> <span><%= check_box_tag("files[]", entry.path, false, :id => params[:parent_id]) if authorize_for('repositories', 'entries_operation') %></span> <% end %>
      
      





ここでは2つのJavaScript関数が使用されます。

getParentNodeChecked(parentVal)



-このツリーノードの親のチェックボックスの状態を決定します。

checkBranch(parentId, checked)



-ツリーノードの子孫のチェックボックスを親としての状態に設定します(大まかに言うと、ディレクトリを選択すると、このディレクトリ内のすべてのサブディレクトリとファイルが選択されます)。

新しいアセット/ javascripts / repository.jsファイルに作成します

 function getParentNodeChecked(parentVal) { var form = document.getElementById('Entries'); for (var i=0;i<form.elements.length;i++) { var e = form.elements[i]; if(e.type=='checkbox' && e.value == parentVal) return e.checked; } } function checkBranch(parentId, checked) { var allchecked = true; var has_check_tree = false; var form = document.getElementById('Entries'); for (var i=0;i<form.elements.length;i++) { var e = form.elements[i]; if(e.type=='checkbox') { if(e.id == parentId) { e.checked = !checked; e.click(); } allchecked = allchecked && e.checked; if(e.name == "check_tree") has_check_tree = true; } } if(has_check_tree) form.check_tree.checked = allchecked; }
      
      





repository.jsを新しいリポジトリマッピング(show.rhtmlファイル内)に接続します。

 ... <% content_for :header_tags do %> <%= stylesheet_link_tag "scm" %> <%= javascript_include_tag "repository.js", :plugin => "redmine_repository" %> <% end %>
      
      





選択したフォルダとファイルをアーカイブする機能を追加することは残っています。 実装はルビ言語で行われます。 さらに、zipアーカイブをサポートするには、rubyzipパッケージをインストールする必要があります(gem install ruby​​zip)。

アーカイブに直接関与するRepositoryZipクラスを作成しましょう(ファイルapp / helpers / repository_zip.rb):

 require 'zip/zip' require 'zip/zipfilesystem' class RepositoryZip attr_reader :file_count def initialize() @zip = Tempfile.new(["repository_zip",".zip"]) @zip_file = Zip::ZipOutputStream.new(@zip.path) @file_count = 0 end def finish @zip_file.close unless @zip_file.nil? @zip.path unless @zip.nil? end def close @zip_file.close unless @zip_file.nil? @zip.close unless @zip.nil? end def add_file(file, cat) @zip_file.put_next_entry(file) @zip_file.write(cat) @file_count += 1 end def add_folder(folder) @zip_file.put_next_entry(folder + "/") end end
      
      





次に、redmineに存在するRepositoriesControllerクラスにいくつかのメソッド(ファイルlib / repositories_controller_patch.rb)を追加して「パッチ」します。 この「パッチ」のロジックについては説明しませんが、コードを示します。

 require 'tree' require_dependency 'application_controller' require_dependency 'repositories_controller' require_dependency 'repository_zip' module RepositoriesControllerPatch def self.included(base) # :nodoc: base.extend(ClassMethods) base.send(:include, InstanceMethods) base.class_eval do unloadable #  unloadable      end end module ClassMethods end module InstanceMethods def entries_operation selected_folders = params[:folders].nil? ? [] : params[:folders] selected_files = params[:files].nil? ? [] : params[:files] if selected_folders.empty? && selected_files.empty? flash[:warning] = l(:warning_no_entries_selected) redirect_to :action => "show", :id => @project, :path => @path return end # make a selected files and folders tree selected_tree = Tree::TreeNode.new(".", "root") selected_files.each do |file| folder = Pathname.new(file).dirname.to_s selected_tree_node = selected_tree if !folder.match(/^\.+$/) folder.split("/").each do if selected_tree_node[folder].nil? selected_tree_node = selected_tree_node.add(Tree::TreeNode.new(folder, "folder")) else selected_tree_node = selected_tree_node[folder] end end selected_tree_node << Tree::TreeNode.new(file, "file") else selected_tree << Tree::TreeNode.new(file, "file") end end selected_folders.each do |folder| selected_tree_node = selected_tree folder.split("/").each do if selected_tree_node[folder].nil? selected_tree_node = selected_tree_node.add(Tree::TreeNode.new(folder, "folder")) else selected_tree_node = selected_tree_node[folder] end end selected_tree_node << Tree::TreeNode.new(file, "file") else selected_tree << Tree::TreeNode.new(file, "file") end end selected_folders.each do |folder| selected_tree_node = selected_tree folder.split("/").each do if selected_tree_node[folder].nil? selected_tree_node = selected_tree_node.add(Tree::TreeNode.new(folder, "folder")) else selected_tree_node = selected_tree_node[folder] end end end begin if !params[:email_entries].blank? email_entries(selected_tree) else download_entries(selected_tree) end rescue => e flash[:warning] = l(:error_in_getting_files) + " (" + e.message + ")" redirect_to :action => "show", :id => @project end end def download_entries(selected_tree) zip = RepositoryZip.new zip_entries(zip, selected_tree) send_file(zip.finish, :filename => filename_for_content_disposition(@project.name + "-" + DateTime.now.strftime("%y%m%d%H%M%S") + ".zip"), :type => "application/zip", :disposition => "attachment") ensure zip.close unless zip.nil? end def zip_entries(zip, selected_tree) selected_tree.children.each do |node| if node.content == "file" zip.add_file(node.name, @repository.cat(node.name, @rev)) else zip.add_folder(node.name) if node.hasChildren? # add selected subfolders zip_entries(zip, node) else # add all subfolders with files entries = @repository.entries(node.name, @rev) entries.each do |entry| node << Tree::TreeNode.new(entry.path, entry.is_dir? ? "folder" : "file") end end zip_entries(zip, node) end end zip end end # of InstaceMethods end # of module RepositoriesController.send(:include, RepositoriesControllerPatch)
      
      





entries_operation



メソッドentries_operation



、アーカイブ用のファイルとフォルダーのリストを作成します。

download_entries



メソッドは、ユーザーによるアーカイブのダウンロードを担当します。

zip_entries



メソッドzip_entries



、選択したファイルとフォルダーのアーカイブを担当します。



それだけです。 Redmineシステムのユーザーにファイルをダウンロードする権限とプラグインのローカライズを追加することのみが残ります。 init.rbファイルを編集して権限を追加します。

 require 'redmine' require 'dispatcher' require 'repositories_controller_patch' Redmine::Plugin.register :redmine_repository do name 'Redmine Repository plugin' author 'Sanny' description 'This is a reposirory plugin for Redmine' version '0.0.2' requires_redmine :version_or_higher => '1.1.2' end Redmine::AccessControl.map do |map| map.project_module :repository do |map| map.permission :operations, :repositories => [:entries_operation] end end
      
      





ローカライズファイルはconfig / localesディレクトリにあります。 ロシア語の場合、ru.ymlファイルを作成します。

 ru: Download: "Download" warning_no_entries_selected: "  " error_in_getting_files: "    " permission_operations: "    "
      
      





redmineを再起動し、システムのさまざまなユーザー向けに1つのアーカイブにファイルをダウンロードする権限を設定して使用します。




All Articles