Redmine插件更新后出现ActionView模板错误:block变量未定义
Redmine「我的页面」区块模板报错分析与修复
问题现象
Redmine 3.4.6.stable版本中,「我的页面」的app/views/my/blocks/_issues.erb模板代码如下:
<div class="contextual"> <%= link_to_function l(:label_options), "$('##{block}-settings').toggle();", :class => 'icon-only icon-settings', :title => l(:label_options) %> </div>
更新部分插件后,该模板无法正常渲染,日志抛出如下错误:
ActionView::Template::Error (undefined local variable or method `block' for #<#<Class:0x00007fd303e3a430>:0x00007fd2fc2b3e38> Did you mean? @blocks): 1: <div class="contextual"> 2: <%= link_to_function l(:label_options), "$('##{block}-settings').toggle();", :class => 'icon-only icon-settings', :title => l(:label_options) %> 3: </div>
同一模板内其他使用block变量的代码可正常运行:
<div id="<%= block %>-settings" style="display:none;"> <%= form_tag(my_page_path, :remote => true) do %> <div class="box"> <%= render_query_columns_selection(query, :name => "settings[#{block}][columns]") %> </div> <p> <%= submit_tag l(:button_save) %> <%= link_to_function l(:button_cancel), "$('##{block}-settings').toggle();" %> </p> <% end %> </div>
系统环境
Environment: Redmine version 3.4.6.stable Ruby version 2.5.1-p57 (2018-03-29) [x86_64-linux-gnu] Rails version 4.2.11.3 Environment production Database adapter PostgreSQL
调用栈及核心代码
错误调用栈
app/views/my/blocks/_issues.erb:2:in `_app_views_my_blocks__issues_erb___4510516419606634452_70176136873380' app/helpers/my_helper.rb:133:in `render_issuequery_block' app/helpers/my_helper.rb:64:in `render_block_content' app/helpers/my_helper.rb:35:in `render_block' plugins/redmine_reporter/lib/redmine_reporter/patches/my_helper_patch.rb:19:in `render_block_with_reporter'
插件补丁代码(my_helper_patch.rb)
def render_block_with_reporter(block, user) return '' if %w(report_by_issues report_by_spent_time).include?(block) && !user.allowed_to?(:view_time_entries, nil, global: true) render_block_without_reporter(block, user) # Line 19 end
MyHelper核心方法
render_block(app/helpers/my_helper.rb:35)
# Renders a single block def render_block(block, user) content = render_block_content(block, user) # Line 35 if content.present? handle = content_tag('span', '', :class => 'sort-handle', :title => l(:button_move)) close = link_to(l(:button_delete), {:action => "remove_block", :block => block}, :remote => true, :method => 'post', :class => "icon-only icon-close", :title => l(:button_delete)) content = content_tag('div', handle + close, :class => 'contextual') + content content_tag('div', content, :class => "mypage-box", :id => "block-#{block}") end end
render_block_content(app/helpers/my_helper.rb:64)
# Renders a single block content def render_block_content(block, user) unless block_definition = Redmine::MyPage.find_block(block) Rails.logger.warn("Unknown block \"#{block}\" found in #{user.login} (id=#{user.id}) preferences") return end settings = user.pref.my_page_settings(block) if partial = block_definition[:partial] begin render(:partial => partial, :locals => {:user => user, :settings => settings, :block => block}) rescue ActionView::MissingTemplate Rails.logger.warn("Partial \"#{partial}\" missing for block \"#{block}\" found in #{user.login} (id=#{user.id}) preferences") return nil end else send "render_#{block_definition[:name]}_block", block, settings # line 64 end end
render_issuequery_block(app/helpers/my_helper.rb:133)
def render_issuequery_block(block, settings) query = IssueQuery.visible.find_by_id(settings[:query_id]) if query query.column_names = settings[:columns] if settings[:columns].present? query.sort_criteria = settings[:sort] if settings[:sort].present? issues = query.issues(:limit => 10) # Line 132 render :partial => 'my/blocks/issues', :locals => {:query => query, :issues => issues, :block => block, :settings => settings} else queries = IssueQuery.visible.sorted render :partial => 'my/blocks/issue_query_selection', :locals => {:queries => queries, :block => block, :settings => settings} end end
问题原因
从代码和调用栈分析,问题出在redmine_reporter插件的补丁逻辑上:
- 原Redmine代码中,
render_issuequery_block明确向_issues.erb传递了:block => block局部变量,理论上模板中所有位置都能访问该变量。 - 插件补丁通过
alias_method_chain重写了render_block方法,虽然代码中调用render_block_without_reporter(block, user)参数传递正确,但补丁过程中意外破坏了模板渲染的局部变量上下文,导致模板中Ruby字符串插值#{block}无法访问到局部变量(而<%%= block %>的直接输出逻辑未受影响)。 - 这种差异是因为ERB中
#{}属于字符串插值的Ruby代码上下文,而<%%= %>是模板输出上下文,插件补丁的拦截逻辑可能只影响了前者的变量传递。
修复方案
方案1:修改模板代码(临时修复)
将报错行的字符串插值改为ERB嵌套输出,绕开上下文问题:
<%= link_to_function l(:label_options), "$('#<%= block %>-settings').toggle();", :class => 'icon-only icon-settings', :title => l(:label_options) %>
或者拼接字符串:
<%= link_to_function l(:label_options), "$('#" + block + "-settings').toggle();", :class => 'icon-only icon-settings', :title => l(:label_options) %>
方案2:修复插件补丁
修改redmine_reporter插件的my_helper_patch.rb,确保调用原方法时上下文完全一致:
def render_block_with_reporter(block, user) return '' if %w(report_by_issues report_by_spent_time).include?(block) && !user.allowed_to?(:view_time_entries, nil, global: true) # 使用super调用原方法,避免上下文丢失 super(block, user) end
方案3:回滚插件版本
如果暂时无法修复插件,回滚到之前正常工作的版本,排查具体是哪次插件更新引发的问题。
内容的提问来源于stack exchange,提问作者gakera
相关产品推荐
相关产品推荐

