插件教程 » 历史记录 » 修订版104
上一页 | 修订版104/119 (差异) | 下一页
Go MAEDA, 2018-01-09 10:15
移除了“不可加载” (#20513) 并修复了一些拼写错误。
插件教程¶
本教程基于Redmine 2.x。您可以在此处查看本教程的Redmine 1.x先前的版本。
假设您熟悉Ruby on Rails框架。
注意:Redmine 3.x (Rails 4) 脚本¶
此维基在Redmine 2.x (Rails 3)上使用ruby script/rails
。
在Redmine 3.x (Rails 4)上,您需要使用ruby bin/rails
或rails
。
- 目录
- 插件教程
创建新插件¶
您可能需要设置RAILS_ENV变量才能使用以下命令
$ export RAILS_ENV="production"
在Windows上
$ set RAILS_ENV=production
可以使用Redmine插件生成器创建新插件。
此生成器的语法是
bundle exec ruby bin/rails generate redmine_plugin <plugin_name>
因此,打开命令提示符并“cd”到您的redmine目录,然后执行以下命令
$ bundle exec ruby script/rails generate redmine_plugin Polls create plugins/polls/app create plugins/polls/app/controllers create plugins/polls/app/helpers create plugins/polls/app/models create plugins/polls/app/views create plugins/polls/db/migrate create plugins/polls/lib/tasks create plugins/polls/assets/images create plugins/polls/assets/javascripts create plugins/polls/assets/stylesheets create plugins/polls/config/locales create plugins/polls/test create plugins/polls/README.rdoc create plugins/polls/init.rb create plugins/polls/config/routes.rb create plugins/polls/config/locales/en.yml create plugins/polls/test/test_helper.rb
插件结构在plugins/polls
中创建。编辑plugins/polls/init.rb
以调整插件信息(名称、作者、描述和版本)
Redmine::Plugin.register :polls do
name 'Polls plugin'
author 'John Smith'
description 'A plugin for managing polls'
version '0.0.1'
end
然后重新启动应用程序并将浏览器指向https://127.0.0.1:3000/admin/plugins。
登录后,您应该在插件列表中看到您的新插件
注意:对插件中init.rb
文件的任何更改都需要重新启动应用程序,因为它不会在每个请求中重新加载。
生成模型¶
目前插件不存储任何内容。让我们为插件创建一个简单的Poll模型。语法是
bundle exec ruby script/rails generate redmine_plugin_model <plugin_name> <model_name> [field[:type][:index] field[:type][:index] ...]
因此,转到命令提示符并运行
$ bundle exec ruby script/rails generate redmine_plugin_model polls poll question:string yes:integer no:integer create plugins/polls/app/models/poll.rb create plugins/polls/test/unit/poll_test.rb create plugins/polls/db/migrate/001_create_polls.rb
这创建了一个Poll模型和相应的迁移文件001_create_polls.rb
在plugins/polls/db/migrate
class CreatePolls < ActiveRecord::Migration
def change
create_table :polls do |t|
t.string :question
t.integer :yes, :default => 0
t.integer :no, :default => 0
end
end
end
您可以调整您的迁移文件(例如,默认值...),然后使用以下命令迁移数据库
$ bundle exec rake redmine:plugins:migrate Migrating polls (Polls plugin)... == CreatePolls: migrating ==================================================== -- create_table(:polls) -> 0.0410s == CreatePolls: migrated (0.0420s) ===========================================
请注意,每个插件都有自己的迁移集合。
让我们在控制台中添加一些投票,这样我们就有东西可以工作了。控制台是您可以交互式地工作和检查Redmine环境的地方,非常适合探索。但现阶段我们只需要创建两个投票对象。
bundle exec ruby script/rails console [rails 3] rails console >> Poll.create(:question => "Can you see this poll") >> Poll.create(:question => "And can you see this other poll") >> exit
编辑您的插件目录中的 plugins/polls/app/models/poll.rb
文件,以添加一个将被我们的控制器调用的 #vote 方法
class Poll < ActiveRecord::Base
def vote(answer)
increment(answer == 'yes' ? :yes : :no)
end
end
生成控制器¶
目前,插件没有任何功能。所以让我们为我们的插件创建一个控制器。
我们可以使用插件控制器生成器来完成这个任务。语法是
bundle exec ruby script/rails generate redmine_plugin_controller <plugin_name> <controller_name> [<actions>]
所以回到命令提示符,运行
$ bundle exec ruby script/rails generate redmine_plugin_controller Polls polls index vote create plugins/polls/app/controllers/polls_controller.rb create plugins/polls/app/helpers/polls_helper.rb create plugins/polls/test/functional/polls_controller_test.rb create plugins/polls/app/views/polls/index.html.erb create plugins/polls/app/views/polls/vote.html.erb
创建了一个名为 PollsController
的控制器,包含 2 个操作(#index
和 #vote
)。
编辑 plugins/polls/app/controllers/polls_controller.rb
来实现这两个操作。
class PollsController < ApplicationController
def index
@polls = Poll.all
end
def vote
poll = Poll.find(params[:id])
poll.vote(params[:answer])
if poll.save
flash[:notice] = 'Vote saved.'
end
redirect_to :action => 'index'
end
end
然后编辑 plugins/polls/app/views/polls/index.html.erb
,它将显示现有的投票。
<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 %>
由于 #vote
动作没有进行渲染,所以您可以删除 plugins/polls/app/views/polls/vote.html.erb
。
添加路由¶
Redmine 不提供默认的通配符路由(':controller/:action/:id'
)。插件必须在它们正确的 config/routes.rb
文件中声明所需的路由。所以编辑 plugins/polls/config/routes.rb
以添加这两个操作的 2 个路由。
get 'polls', :to => 'polls#index'
post 'post/:id/vote', :to => 'polls#vote'
您可以在这里找到有关Rails路由的更多信息:https://guides.rubyonrails.net.cn/routing.html。
现在,重新启动应用程序,并将您的浏览器指向 https://127.0.0.1:3000/polls。
您应该能看到两个投票,并且应该能够为它们投票。
国际化¶
翻译文件必须存储在 config/locales 中,例如 plugins/polls/config/locales/
。
扩展菜单¶
我们的控制器工作得很好,但用户必须知道URL才能查看投票。使用Redmine插件API,您可以扩展标准菜单。
所以让我们向应用程序菜单添加一个新的项目。
扩展应用程序菜单¶
编辑插件目录根目录下的 plugins/polls/init.rb
,在插件注册块的末尾添加以下行
Redmine::Plugin.register :redmine_polls do
[...]
menu :application_menu, :polls, { :controller => 'polls', :action => 'index' }, :caption => 'Polls'
end
语法是
menu(menu_name, item_name, url, options={})
您可以扩展以下五个菜单
:top_menu
- 左上角的菜单:account_menu
- 包含登录/注销链接的右上角菜单:application_menu
- 用户不在项目内时显示的主要菜单:project_menu
- 用户在项目内时显示的主要菜单:admin_menu
- 显示在管理页面上的菜单(只能在设置之后、插件之前插入)
可用的选项是
:param
- 用于项目ID的参数键(默认为:id
):if
- 在渲染项目之前被调用的Proc,只有当它返回true时才显示项目:caption
- 菜单标题,可以是- 本地化字符串Symbol
- 字符串
- 可以接受项目作为参数的Proc
:before
,:after
- 指定菜单项应插入的位置(例如:after => :activity
):first
,:last
- 如果设置为true,则该项将保持在菜单的起始/结束位置(例如:last => true
):html
- 要传递给link_to
的html选项的hash,用于渲染菜单项
在我们的示例中,我们已向应用程序菜单添加了一个空的项目。
重新启动应用程序并转到 https://127.0.0.1:3000。
现在您可以通过点击欢迎屏幕上的投票标签来访问投票。
扩展项目菜单¶
现在,让我们考虑将投票定义在项目级别(即使在我们示例投票模型中不是这种情况)。因此,我们希望将“投票”标签页添加到项目菜单中。
打开init.rb
文件,用以下2行代码替换之前添加的行
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
第二行将我们的“投票”标签页添加到项目菜单,紧接在“活动”标签页之后。第一行是必需的,并声明我们的PollsController
中的2个操作是公开的。我们稍后会更详细地解释这一点。重新启动应用程序,并前往您的一个项目。
如果您点击“投票”标签页(第3位),应该会注意到项目菜单不再显示。
为了使项目菜单可见,您必须初始化控制器的实例变量@project
。
编辑您的PollsController以实现这一点
def index
@project = Project.find(params[:project_id])
@polls = Poll.find(:all) # @project.polls
end
由于在上面的菜单项声明中使用了:param => :project_id
选项,因此项目id在:project_id
参数中可用。
现在,当查看投票时,您应该能看到项目菜单。
从菜单中删除项目¶
要删除菜单中的项目,您可以像以下示例那样使用delete_menu_item
。
Redmine::Plugin.register :redmine_polls do
[...]
delete_menu_item :top_menu, :my_page
delete_menu_item :top_menu, :help
delete_menu_item :project_menu, :overview
delete_menu_item :project_menu, :activity
delete_menu_item :project_menu, :news
end
添加新权限¶
目前,任何人都可以为投票投票。让我们通过更改权限声明使其更具可配置性。
我们将声明两个基于项目的权限,一个用于查看投票,另一个用于投票。这些权限不再是公开的(移除了:public => true
选项)。
编辑plugins/polls/init.rb
,用以下2行代码替换之前的权限声明
permission :view_polls, :polls => :index
permission :vote_polls, :polls => :vote
重新启动应用程序,并前往https://127.0.0.1:3000/roles/permissions
现在,您可以将这些权限分配给您现有的角色。
当然,需要在PollsController中添加一些代码,以便根据当前用户的权限实际保护操作。为此,我们只需要添加:authorize
过滤器,并确保在调用此过滤器之前,Herve Harster实例变量被正确设置。
以下是如何为#index
操作进行操作的示例
class PollsController < ApplicationController
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
在#vote
操作之前检索当前项目可以以类似的方式进行。
在此之后,查看和投票将仅限于管理员用户或具有项目适当角色的用户。
如果您希望以多语言方式显示权限的符号,则需要在一个语言文件中添加必要的文本标签。
只需在plugins/polls/config/locales
中创建一个*.yml文件(例如en.yml
),并填充如下的标签
"en":
permission_view_polls: View Polls
permission_vote_polls: Vote Polls
在这个例子中,创建的文件是en.yml
,但所有其他支持的语言文件也是可能的。
如上例所示,标签由权限符号:view_polls
和:vote_polls
组成,前面添加了额外的permission_
。
重新启动您的应用程序,并指向权限部分。
创建项目模块¶
目前,投票功能已添加到所有项目中。但是,您可能只想为某些项目启用投票。
因此,让我们创建一个“投票”项目模块。这是通过将权限声明包裹在对#project_module
的调用中实现的。
编辑init.rb
,更改权限声明
project_module :polls do
permission :view_polls, :polls => :index
permission :vote_polls, :polls => :vote
end
重新启动应用程序,并前往您的一个项目设置。
点击“模块”标签页。您应该在模块列表的末尾看到“投票”模块(默认情况下禁用)
您现在可以按项目级别启用/禁用投票。
改进插件视图¶
添加样式表¶
让我们从向插件视图添加样式表开始。
在plugins/polls/assets/stylesheets
目录下创建一个名为voting.css
的文件
a.vote { font-size: 120%; }
a.vote.yes { color: green; }
a.vote.no { color: red; }
启动应用程序时,插件资源会自动复制到public/plugin_assets/polls/
,以便通过您的Web服务器使用。因此,对插件样式表或javascripts的任何更改都需要重新启动应用程序。
引入的类需要由链接使用。因此,在文件plugins/polls/app/views/polls/index.html.erb
中更改链接声明为
<%= link_to 'Yes', {:action => 'vote', :id => poll[:id], :answer => 'yes' }, :method => :post, :class => 'vote yes' %> (<%= poll.yes %>)
<%= link_to 'No', {:action => 'vote', :id => poll[:id], :answer => 'no' }, :method => :post, :class => 'vote no' %> (<%= poll.no %>)
然后,在index.html.erb
文件的末尾追加以下行,以便Redmine将样式表包含在页面标题中
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'voting', :plugin => 'polls' %>
<% end %>
注意,调用stylesheet_link_tag
辅助函数时需要:plugin => 'polls'
选项。
可以使用相同的javascript_include_tag
辅助函数将javascripts包含在插件视图中。
设置页面标题¶
您可以使用html_title
辅助函数在视图中设置HTML标题。
示例
<% html_title "Polls" %>
使用钩子¶
视图中的钩子¶
Redmine视图中的钩子允许您将自定义内容插入常规Redmine视图中。例如,查看源代码:tags/2.0.0/app/views/projects/show.html.erb#L52可以看到有2个钩子可用:一个名为:view_projects_show_left
,用于向左侧添加内容;另一个名为:view_projects_show_right
,用于向右侧添加内容。
要使用一个或多个视图钩子,您需要创建一个继承自Redmine::Hook::ViewListener
的类,并实现您想要使用的钩子名称的方法。要向项目概述中添加一些内容,请向您的插件添加一个类,并在init.rb
中要求它,然后实现与钩子名称匹配的方法。
为我们的插件创建一个名为plugins/polls/lib/polls_hook_listener.rb
的文件,其内容如下
class PollsHookListener < Redmine::Hook::ViewListener
def view_projects_show_left(context = {})
return content_tag("p", "Custom content added to the left")
end
def view_projects_show_right(context = {})
return content_tag("p", "Custom content added to the right")
end
end
将此行添加到plugins/polls/init.rb
require_dependency 'polls_hook_listener'
重新启动Redmine,查看项目的概览标签。您应该在概览的左侧和右侧看到字符串。
您还可以使用render_on
辅助函数渲染一个部分。在我们的插件中,您必须在plugins/polls/lib/polls_hook_listener.rb
中替换刚刚创建的内容,如下所示
class PollsHookListener < Redmine::Hook::ViewListener
render_on :view_projects_show_left, :partial => "polls/project_overview"
end
通过创建文件app/views/polls/_project_overview.html.erb
将部分添加到您的插件中。其内容(使用类似“钩子消息!”的文本)将追加到项目概览的左侧。别忘了重新启动Redmine。
控制器中的钩子¶
待办事项
使您的插件可配置¶
与Redmine注册的每个插件都会显示在管理员/插件页面。通过Settings控制器提供基本配置机制的支持。通过在插件init.rb文件中添加“settings”方法来启用此功能。
Redmine::Plugin.register :redmine_polls do
[ ... ]
settings :default => {'empty' => true}, :partial => 'settings/poll_settings'
end
添加此方法将完成两件事。首先,它将在管理员/插件列表中为插件描述块添加一个“配置”链接。点击此链接将加载通用的插件配置模板视图,该视图将渲染引用的:partial。调用settings方法还将为插件在Setting模块中添加支持。Setting模型将基于插件名称存储和检索序列化哈希。此哈希通过插件方法名访问,形式为plugin_<插件名称>。对于此示例,可以通过调用Setting.plugin_redmine_polls来访问哈希。
通过settings方法传递给settings方法的:partial哈希键引用的视图将作为插件配置视图的一部分加载。基本页面布局受插件配置视图的限制:声明一个表单并生成提交按钮。部分在表单中的表div内部拉入。将显示并可以修改插件的配置设置,通过标准HTML表单元素进行修改。
注意:如果两个插件具有相同的设置部分名称,则第一个将覆盖第二个的设置页面。因此,请确保为您的设置部分提供一个独特的名称。
当页面提交时,settings_controller将取由'settings'引用的参数散列,并将其直接以序列化格式存储在Setting.plugin_redmine_polls中。每次生成页面时,Setting.plugin_redmine_polls的当前值将分配给局部变量settings。
<table>
<tbody>
<tr>
<th>Notification Default Address</th>
<td>
<input type="text" id="settings_notification_default"
value="<%= settings['notification_default'] %>"
name="settings[notification_default]" >
</td>
</tr>
</tbody>
</table>
在上面的示例中,配置表单不是使用Rails表单辅助程序创建的。这是因为没有@settings模型,只有设置散列。表单辅助程序将尝试使用模型访问器方法访问属性,但这些方法不存在。例如,调用@settings.notification_default将失败。此表单设置的值可以访问为Setting.plugin_redmine_polls['notification_default']。
最后,在settings方法调用中的:default是为了注册一个值,如果为此插件在设置表中没有存储任何内容,则将从Setting.plugin_redmine_polls调用中返回此值。
测试您的插件¶
test/test_helper.rb¶
以下是我的测试辅助文件的内容
require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')
示例测试¶
polls_controller_test.rb
的内容
require File.expand_path('../../test_helper', __FILE__)
class PollsControllerTest < ActionController::TestCase
fixtures :projects
def test_index
get :index, :project_id => 1
assert_response :success
assert_template 'index'
end
end
运行测试¶
如有必要,初始化测试数据库
$ rake db:drop db:create db:migrate redmine:plugins:migrate redmine:load_default_data RAILS_ENV=test
要执行polls_controller_test.rb
$ bundle exec ruby plugins\polls\test\functionals\polls_controller_test.rb
带有权限的测试¶
如果您的插件需要加入一个项目,请在功能测试的开始处添加以下内容
def test_index
@request.session[:user_id] = 2
...
end
如果您的插件需要特定的权限,您可以将该权限添加到用户角色中,如下所示(在固定值中查找适合用户的角色)
def test_index
Role.find(1).add_permission! :my_permission
...
end
您可以像这样启用/禁用特定模块
def test_index
Project.find(1).enabled_module_names = [:mymodule]
...
end
参考文件层次结构¶
以下是本教程和钩子中提到的所有文件和目录的简单列表。这有助于确保使用标准路径,并且对新手了解文件应放置的位置也很有用。
plugins/PLUGIN/README.rdoc plugins/PLUGIN/init.rb plugins/PLUGIN/app/ plugins/PLUGIN/app/controllers/ plugins/PLUGIN/app/controllers/CONTROLLER_controller.rb plugins/PLUGIN/app/helpers/ plugins/PLUGIN/app/helpers/CONTROLLER_helper.rb plugins/PLUGIN/app/models/ plugins/PLUGIN/app/models/MODEL.rb plugins/PLUGIN/app/views/ plugins/PLUGIN/app/views/CONTROLLER/ plugins/PLUGIN/app/views/CONTROLLER/_PARTIAL.html.erb plugins/PLUGIN/app/views/CONTROLLER/CONTROLLER-ACTION.html.erb plugins/PLUGIN/app/views/hooks/ plugins/PLUGIN/app/views/hooks/_HOOK.html.erb plugins/PLUGIN/app/views/settings/ plugins/PLUGIN/app/views/settings/_MODEL_settings.html.erb plugins/PLUGIN/assets/ plugins/PLUGIN/assets/images/ plugins/PLUGIN/assets/javascripts/ plugins/PLUGIN/assets/stylesheets/ plugins/PLUGIN/assets/stylesheets/voting.css plugins/PLUGIN/config/ plugins/PLUGIN/config/locales/ plugins/PLUGIN/config/locales/en.yml plugins/PLUGIN/config/routes.rb plugins/PLUGIN/db/ plugins/PLUGIN/db/migrate/ plugins/PLUGIN/db/migrate/001_create_MODELs.rb plugins/PLUGIN/lib/ plugins/PLUGIN/lib/PLUGIN_hook_listener.rb plugins/PLUGIN/lib/PLUGIN/ plugins/PLUGIN/lib/PLUGIN/hooks.rb plugins/PLUGIN/lib/PLUGIN/MODEL_patch.rb plugins/PLUGIN/lib/tasks/ plugins/PLUGIN/test/ plugins/PLUGIN/test/test_helper.rb plugins/PLUGIN/test/functional/ plugins/PLUGIN/test/functional/CONTROLLER_controller_test.rb plugins/PLUGIN/test/unit/ plugins/PLUGIN/test/unit/MODEL_test.rb