You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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插件的补丁逻辑上:

  1. 原Redmine代码中,render_issuequery_block明确向_issues.erb传递了:block => block局部变量,理论上模板中所有位置都能访问该变量。
  2. 插件补丁通过alias_method_chain重写了render_block方法,虽然代码中调用render_block_without_reporter(block, user)参数传递正确,但补丁过程中意外破坏了模板渲染的局部变量上下文,导致模板中Ruby字符串插值#{block}无法访问到局部变量(而<%%= block %>的直接输出逻辑未受影响)。
  3. 这种差异是因为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 21:20:33