Project

General

Profile

Patch #44660 » view_hooks.patch

Jimmy Westberg, 2026-10-22 18:00

View differences:

app/helpers/application_helper.rb
893 893
      end
894 894
    end
895 895
    html_title args.reverse.map {|s| (s.is_a?(Array) ? s.first : s).to_s}
896
    content_tag('h2', strings.join(' » ').html_safe)
896
    content_tag('h2', strings.join(' » ').html_safe) +
897
      call_hook(:view_layouts_base_title_after, :title => args)
897 898
  end
898 899

  
899 900
  # Sets the html title
......
1807 1808
  end
1808 1809

  
1809 1810
  def sidebar_content?
1810
    content_for?(:sidebar) || view_layouts_base_sidebar_hook_response.present?
1811
    content_for?(:sidebar) || view_layouts_base_sidebar_hook_response.present? ||
1812
      view_layouts_base_sidebar_top_hook_response.present?
1811 1813
  end
1812 1814

  
1813 1815
  def view_layouts_base_sidebar_hook_response
1814 1816
    @view_layouts_base_sidebar_hook_response ||= call_hook(:view_layouts_base_sidebar)
1815 1817
  end
1816 1818

  
1819
  # The CSS classes that plugins add to an element through +hook+, as a plain
1820
  # (not html_safe) String so that it is escaped in the attribute
1821
  def css_classes_from_hook(hook, context={})
1822
    String.new(call_hook(hook, context)).split.join(' ')
1823
  end
1824

  
1825
  def view_layouts_base_sidebar_top_hook_response
1826
    @view_layouts_base_sidebar_top_hook_response ||= call_hook(:view_layouts_base_sidebar_top)
1827
  end
1828

  
1817 1829
  def email_delivery_enabled?
1818 1830
    !!ActionMailer::Base.perform_deliveries
1819 1831
  end
app/helpers/groups_helper.rb
23 23
    tabs << {:name => 'general', :partial => 'groups/general', :label => :label_general}
24 24
    tabs << {:name => 'users', :partial => 'groups/users', :label => :label_user_plural} if group.givable?
25 25
    tabs << {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural}
26
    tabs
26
    Redmine::Tabs.add_to(:group, tabs, :group => group)
27 27
  end
28 28

  
29 29
  def render_principals_for_new_group_users(group, limit=100)
app/helpers/issues_helper.rb
522 522

  
523 523
  # Returns the textual representation of a single journal detail
524 524
  def show_detail(detail, no_html=false, options={})
525
    if (renderer = Redmine::JournalDetails.renderer_for(detail))
526
      return renderer.call(detail, self, no_html, options)
527
    end
528

  
525 529
    multiple = false
526 530
    show_diff = false
527 531
    no_details = false
......
758 762
            "'#{issue_path(@issue, :tab => 'changesets')}')"
759 763
        }
760 764
    end
761
    tabs
765
    Redmine::Tabs.add_to(:issue_history, tabs, :issue => @issue)
762 766
  end
763 767

  
764 768
  def issue_history_default_tab
app/helpers/projects_helper.rb
42 42
        {:name => 'activities', :action => :manage_project_activities,
43 43
         :partial => 'projects/settings/activities', :label => :label_time_tracking}
44 44
      ]
45
    # A plugin tab without a :permission needs the one of the project tab
46
    tabs = Redmine::Tabs.add_to(:project_settings, tabs, :project => @project).each {|tab| tab[:action] ||= :edit_project}
45 47
    tabs.
46 48
      select {|tab| User.current.allowed_to?(tab[:action], @project)}.
47 49
      select {|tab| tab[:module].nil? || @project.module_enabled?(tab[:module])}
app/helpers/settings_helper.rb
39 39
        {:name => 'repositories', :partial => 'settings/repositories',
40 40
         :label => :label_repository_plural}
41 41
      ]
42
    Redmine::Tabs.add_to(:administration_settings, tabs)
42 43
  end
43 44

  
44 45
  def render_settings_error(errors)
app/helpers/users_helper.rb
98 98
    if Group.givable.any?
99 99
      tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural}
100 100
    end
101
    tabs
101
    Redmine::Tabs.add_to(:user, tabs, :user => @user)
102 102
  end
103 103
end
app/views/boards/show.html.erb
10 10
                          {:controller => 'projects', :action => 'settings', :id => @project, :tab => 'boards'},
11 11
                          :class => 'icon icon-settings' if User.current.allowed_to?(:manage_boards, @project) %>
12 12

  
13
<%= call_hook(:view_boards_show_contextual, {:board => @board, :project => @project}) %>
13 14
</div>
14 15

  
15 16
<div id="add-message" style="display:none;">
app/views/context_menus/issues.html.erb
178 178
                              :method => :delete, :data => {:confirm => issues_destroy_confirmation_message(@issues)}, :class => 'icon icon-del', :disabled => !@can[:delete] %></li>
179 179
  <% end %>
180 180

  
181
  <%= render_object_menu_items(:issue_context_menu, @issues, @project, list: true) %>
181 182
  <%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
182 183
</ul>
app/views/documents/index.html.erb
1 1
<div class="contextual">
2 2
<%= link_to sprite_icon('add', l(:label_document_new)), new_project_document_path(@project), :class => 'icon icon-add',
3 3
            :onclick => 'showAndScrollTo("add-document", "document_title"); return false;' if User.current.allowed_to?(:add_documents, @project) %>
4
<%= call_hook(:view_documents_index_contextual, {:project => @project}) %>
4 5
</div>
5 6

  
6 7
<div id="add-document" style="display:none;">
app/views/documents/show.html.erb
5 5
<% if User.current.allowed_to?(:delete_documents, @project) %>
6 6
<%= delete_link document_path(@document) %>
7 7
<% end %>
8
<%= call_hook(:view_documents_show_contextual, {:document => @document, :project => @project}) %>
8 9
</div>
9 10

  
10 11
<h2><%= @document.title %></h2>
app/views/groups/_form.html.erb
1 1
<%= error_messages_for @group %>
2 2

  
3 3
<div class="box tabular">
4
  <%= call_hook(:view_groups_form_top, :group => @group, :form => f) %>
4 5
  <p><%= f.text_field :name, :required => true, :size => 60,
5 6
           :disabled => !@group.safe_attribute?('name')  %></p>
6 7
  <% unless @group.builtin? %>
......
16 17
  <% @group.custom_field_values.each do |value| %>
17 18
    <p><%= custom_field_tag_with_label :group, value %></p>
18 19
  <% end %>
20
  <%= call_hook(:view_groups_form_bottom, :group => @group, :form => f) %>
19 21
</div>
app/views/groups/show.html.erb
1 1
<div class="contextual">
2 2
<%= link_to(sprite_icon('edit', l(:button_edit)), edit_group_path(@group), :class => 'icon icon-edit') if User.current.admin? %>
3
<%= call_hook(:view_groups_show_contextual, {:group => @group}) %>
3 4
</div>
4 5

  
5 6
<h2><%= @group.name %></h2>
app/views/issues/_action_menu.html.erb
7 7
<%= watcher_link(@issue, User.current) %>
8 8
<%= link_to sprite_icon('copy', l(:button_copy)), project_copy_issue_path(@project, @issue),
9 9
            :class => 'icon icon-copy ' if User.current.allowed_to?(:copy_issues, @project) && Issue.allowed_target_projects.any? %>
10
<%= call_hook(:view_issues_show_contextual, {:issue => @issue, :project => @project}) %>
10 11
<%= actions_dropdown do %>
11 12
  <%= copy_object_url_link(issue_url(@issue, only_path: false)) %>
13
  <%= render_object_menu_items(:issue_actions, @issue, @project) %>
12 14
  <%= link_to sprite_icon('del', l(:button_delete_object, object_name: l(:label_issue)).capitalize), issue_path(@issue),
13 15
              :data => {:confirm => issues_destroy_confirmation_message(@issue)},
14 16
              :method => :delete, :class => 'icon icon-del ' if @issue.deletable? %>
17
  <%= call_hook(:view_issues_show_actions_menu, {:issue => @issue, :project => @project}) %>
15 18
<% end %>
16 19
</div>
app/views/issues/_form.html.erb
28 28
<% if @issue.safe_attribute? 'subject' %>
29 29
<p><%= f.text_field :subject, :size => 80, :maxlength => 255, :required => true %></p>
30 30
<% end %>
31
<%= call_hook(:view_issues_form_after_subject, { :issue => @issue, :form => f }) %>
31 32

  
32 33
<% if @issue.safe_attribute? 'description' %>
33 34
<p>
......
44 45
</p>
45 46
<%= wikitoolbar_for 'issue_description', preview_issue_path(:project_id => @issue.project, :issue_id => @issue.id) %>
46 47
<% end %>
48
<%= call_hook(:view_issues_form_after_description, { :issue => @issue, :form => f }) %>
47 49

  
48 50
<div id="attributes" class="attributes">
49 51
  <%= render :partial => 'issues/attributes' %>
50 52
</div>
53
<%= call_hook(:view_issues_form_after_attributes, { :issue => @issue, :form => f }) %>
51 54

  
52 55
<%= call_hook(:view_issues_form_details_bottom, { :issue => @issue, :form => f }) %>
53 56
<% end %>
app/views/issues/_form_custom_fields.html.erb
1 1
<% custom_field_values = @issue.editable_custom_field_values %>
2 2
<% custom_field_values_full_width = custom_field_values.select { |value| value.custom_field.full_width_layout? } %>
3 3
<% custom_field_values -= custom_field_values_full_width %>
4
<%= call_hook(:view_issues_form_custom_fields_top, { :issue => @issue, :custom_field_values => custom_field_values }) %>
4 5

  
5 6
<% if custom_field_values.present? %>
6 7
<div class="splitcontent">
......
22 23
  <p><%= custom_field_tag_with_label :issue, value, :required => @issue.required_attribute?(value.custom_field_id) %></p>
23 24
  <%= wikitoolbar_for "issue_custom_field_values_#{value.custom_field_id}", preview_issue_path(:project_id => @issue.project, :issue_id => @issue.id) if value.custom_field.full_text_formatting? %>
24 25
<% end %>
26
<%= call_hook(:view_issues_form_custom_fields_bottom, { :issue => @issue, :custom_field_values => custom_field_values }) %>
app/views/issues/_list.html.erb
35 35
      </td>
36 36
    </tr>
37 37
  <% end %>
38
  <tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
38
  <tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %> <%= css_classes_from_hook(:view_issues_list_row_css_classes, {:issue => issue, :query => query}) %>">
39 39
    <td class="checkbox hide-when-print"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
40 40
    <% query.inline_columns.each do |column| %>
41 41
    <%= content_tag('td', column_content(column, issue), :class => column.css_classes) %>
......
54 54
  </tr>
55 55
  <% end -%>
56 56
  <% end -%>
57
  <%= call_hook(:view_issues_list_row_after, {:issue => issue, :query => query, :colspan => query.inline_columns.size + 2}) %>
57 58
  <% end -%>
58 59
  </tbody>
59 60
</table>
app/views/issues/index.html.erb
2 2
  <% if User.current.allowed_to?(:add_issues, @project, :global => true) && (@project.nil? || Issue.allowed_target_trackers(@project).any?) %>
3 3
    <%= link_to sprite_icon('add', l(:label_issue_new)), _new_project_issue_path(@project), :class => 'icon icon-add new-issue' %>
4 4
  <% end %>
5
  <%= call_hook(:view_issues_index_contextual, {:project => @project, :query => @query}) %>
5 6
  <%= actions_dropdown do %>
6 7
    <% if @project %>
7 8
      <%= link_to sprite_icon('summary', l(:field_summary)), project_issues_report_path(@project), :class => 'icon icon-stats' %>
......
14 15
    <%= link_to_if_authorized sprite_icon('settings', l(:label_settings)),
15 16
                              {:controller => 'projects', :action => 'settings', :id => @project, :tab => 'issues'},
16 17
                              :class => 'icon icon-settings' if User.current.allowed_to?(:edit_project, @project) %>
18
    <%= call_hook(:view_issues_index_actions_menu, {:project => @project, :query => @query}) %>
17 19
  <% end %>
18 20
</div>
19 21

  
app/views/issues/show.html.erb
4 4
<% if @issue.is_private? %>
5 5
  <span class="badge badge-private private"><%= l(:field_is_private) %></span>
6 6
<% end %>
7
<%= call_hook(:view_issues_show_top, :issue => @issue) %>
7 8

  
8 9
<div class="<%= @issue.css_classes %> details">
9 10
  <% if @prev_issue_id || @next_issue_id %>
......
143 144
  <%= render :partial => 'relations' %>
144 145
<% end %>
145 146
<% end %>
146

  
147
<%= call_hook(:view_issues_show_details_end, :issue => @issue) %>
147 148
</div>
149
<%= call_hook(:view_issues_show_bottom, :issue => @issue) %>
148 150

  
149 151
<%= render partial: 'action_menu_edit' if User.current.wants_comments_in_reverse_order? %>
150 152

  
app/views/layouts/base.html.erb
88 88
</nav>
89 89

  
90 90
<div id="header">
91
    <%= call_hook :view_layouts_base_header_top %>
91 92

  
92 93
    <a href="#" class="mobile-toggle-button js-flyout-menu-toggle-button"></a>
93 94

  
......
123 124
        </div>
124 125
    </div>
125 126
    <% end %>
127
    <%= call_hook :view_layouts_base_header_bottom %>
126 128
</div>
127 129

  
128 130
<div id="main" class="<%= sidebar_content? ? 'collapsiblesidebar' : 'nosidebar' %>">
......
136 138
          <%= javascript_tag "$('#sidebar-switch-panel').css('visibility', 'visible');" %>
137 139
        <% end %>
138 140
        <div id="sidebar-wrapper">
141
          <%= view_layouts_base_sidebar_top_hook_response %>
139 142
          <%= yield :sidebar %>
140 143
          <%= view_layouts_base_sidebar_hook_response %>
141 144
        </div>
......
146 149
        <% if @project && !@project.active? %>
147 150
          <p class="warning"><span class="icon icon-lock"><%= sprite_icon('lock', l(:text_project_closed)) %></span></p>
148 151
        <% end %>
152
        <%= call_hook :view_layouts_base_content_top %>
149 153
        <%= yield %>
150 154
        <%= call_hook :view_layouts_base_content %>
151 155
        <div style="clear:both;"></div>
152 156
    </div>
153 157
</div>
154 158
<div id="footer">
159
    <%= call_hook :view_layouts_base_footer_top %>
155 160
    Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url, :target => '_blank', :rel => 'noopener' %> &copy; 2006-2026 Jean-Philippe Lang
161
    <%= call_hook :view_layouts_base_footer_bottom %>
156 162
</div>
157 163

  
158 164
<div id="ajax-indicator" style="display:none;">
app/views/messages/show.html.erb
18 18
          :data => { :confirm => l(:text_are_you_sure) },
19 19
          :class => 'icon icon-del'
20 20
        ) if @message.destroyable_by?(User.current) %>
21
    <%= call_hook(:view_messages_show_contextual, {:topic => @topic, :message => @message, :project => @project}) %>
21 22
  </div>
22 23

  
23 24
  <h2><%= avatar(@topic.author) %><%= @topic.subject %></h2>
app/views/news/index.html.erb
4 4
            :class => 'icon icon-add add-news-link',
5 5
            :onclick => 'showAndScrollTo("add-news", "news_title"); return false;') if User.current.allowed_to?(:manage_news, @project, global: true) %>
6 6
<%= watcher_link(@project.enabled_module('news'), User.current) if @project && User.current.logged? %>
7
<%= call_hook(:view_news_index_contextual, {:project => @project}) %>
7 8
</div>
8 9

  
9 10
<div id="add-news" style="display:none;">
app/views/news/show.html.erb
7 7
            :accesskey => accesskey(:edit),
8 8
            :onclick => '$("#edit-news").show(); return false;') if User.current.allowed_to?(:manage_news, @project) %>
9 9
<%= delete_link news_path(@news) if User.current.allowed_to?(:manage_news, @project) %>
10
<%= call_hook(:view_news_show_contextual, {:news => @news, :project => @project}) %>
10 11
</div>
11 12

  
12 13
<h2><%= avatar(@news.author) %> <%=h @news.title %></h2>
app/views/projects/_form.html.erb
2 2

  
3 3
<div class="box tabular">
4 4
<!--[form:project]-->
5
<%= call_hook(:view_projects_form_top, :project => @project, :form => f) %>
5 6
<p><%= f.text_field :name, :required => true, :size => 60 %></p>
6 7

  
7 8
<p><%= f.textarea :description, :rows => 8, :class => 'wiki-edit', :data => wiki_textarea_stimulus_attributes %></p>
......
42 43
<%= hidden_field_tag 'project[enabled_module_names][]', '' %>
43 44
</fieldset>
44 45
<% end %>
46
<%= call_hook(:view_projects_form_bottom, :project => @project, :form => f) %>
45 47
<!--[eoform:project]-->
46 48

  
47 49
<% unless @project.identifier_frozen? %>
app/views/projects/_list.html.erb
40 40
      <% end %>
41 41
    </tr>
42 42
  <% end %>
43
  <tr id="project-<%= entry.id %>" class="<%= cycle('odd', 'even') %> <%= 'hascontextmenu' if @admin_list %> <%= entry.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>">
43
  <tr id="project-<%= entry.id %>" class="<%= cycle('odd', 'even') %> <%= 'hascontextmenu' if @admin_list %> <%= entry.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %> <%= css_classes_from_hook(:view_projects_list_row_css_classes, {:project => entry, :query => @query}) %>">
44 44
    <% if @admin_list %>
45 45
      <% if !entry.scheduled_for_deletion? %>
46 46
        <td class="checkbox hide-when-print"><%= check_box_tag("ids[]", entry.id, false, :id => nil) %></td>
......
59 59
      <% end %>
60 60
    <% end %>
61 61
  </tr>
62
  <%= call_hook(:view_projects_list_row_after, {:project => entry, :query => @query, :colspan => @query.inline_columns.size + (@admin_list ? 2 : 0)}) %>
62 63
<% end -%>
63 64
</tbody>
64 65
</table>
app/views/projects/index.html.erb
2 2
  <%= form_tag({}, :method => :get) do %>
3 3
  <% end %>
4 4
  <%= render_project_action_links %>
5
  <%= call_hook(:view_projects_index_contextual, {:query => @query}) %>
5 6
</div>
6 7

  
7 8
<h2><%= @query.new_record? ? l(:label_project_plural) : @query.name %></h2>
app/views/projects/show.html.erb
1 1
<div class="contextual">
2 2
  <%= bookmark_link @project %>
3 3

  
4
  <%= call_hook(:view_projects_show_contextual, {:project => @project}) %>
4 5
  <%= actions_dropdown do %>
5 6
  <% if User.current.allowed_to?(:add_subprojects, @project) %>
6 7
    <%= link_to sprite_icon('add', l(:label_subproject_new)), new_project_path(:parent_id => @project), :class => 'icon icon-add' %>
......
18 19
  <%= link_to_if_authorized sprite_icon('settings', l(:label_settings)),
19 20
                            {:controller => 'projects', :action => 'settings', :id => @project},
20 21
                            :class => 'icon icon-settings' if User.current.allowed_to?(:edit_project, @project) %>
22
  <%= render_object_menu_items(:project_actions, @project, @project) %>
23
  <%= call_hook(:view_projects_show_actions_menu, {:project => @project}) %>
21 24
  <% end %>
22 25
</div>
23 26

  
app/views/repositories/_navigation.html.erb
22 22
  <%= link_to sprite_icon('reload', l(:button_fetch_changesets)),
23 23
              {:action => :fetch_changesets, :id => @project, :repository_id => @repository.identifier_param},
24 24
              :class => 'icon icon-reload', :method => :post if User.current.allowed_to?(:manage_repository, @project) && !Setting.autofetch_changesets? %>
25
  <%= call_hook(:view_repositories_navigation_actions_menu, {:repository => @repository, :project => @project}) %>
25 26
<% end %>
26 27

  
27 28
<%= form_tag({:action => controller.action_name,
app/views/timelog/index.html.erb
2 2
<%= link_to sprite_icon('time-add', l(:button_log_time)),
3 3
            _new_time_entry_path(@project, @query.filtered_issue_id),
4 4
            :class => 'icon icon-time-add' if User.current.allowed_to?(:log_time, @project, :global => true) %>
5
<%= call_hook(:view_timelog_index_contextual, {:project => @project, :query => @query}) %>
5 6
<%= actions_dropdown do %>
6 7
  <% if User.current.allowed_to?(:import_time_entries, @project, :global => true) && User.current.allowed_to?(:log_time, @project, :global => true) %>
7 8
    <%= link_to sprite_icon('import', l(:button_import)), new_time_entries_import_path(:project_id => @project), :class => 'icon icon-import' %>
......
10 11
  <%= link_to_if_authorized sprite_icon('settings', l(:label_settings)),
11 12
                            {:controller => 'projects', :action => 'settings', :id => @project, :tab => 'activities'},
12 13
                            :class => 'icon icon-settings' if User.current.allowed_to?(:manage_project_activities, @project) %>
14
  <%= call_hook(:view_timelog_index_actions_menu, {:project => @project, :query => @query}) %>
13 15
<% end %>
14 16
</div>
15 17

  
app/views/trackers/_form.html.erb
4 4
<div class="splitcontentleft">
5 5
<div class="box tabular">
6 6
<!--[form:tracker]-->
7
<%= call_hook(:view_trackers_form_top, :tracker => @tracker, :form => f) %>
7 8
<p><%= f.text_field :name, :required => true %></p>
8 9
<p><%= f.select :default_status_id,
9 10
        IssueStatus.sorted.map {|s| [s.name, s.id]},
......
42 43
<p><label for="copy_workflow_from"><%= l(:label_copy_workflow_from) %></label>
43 44
<%= select_tag(:copy_workflow_from, content_tag("option") + options_from_collection_for_select(@trackers, :id, :name, params[:copy_workflow_from] || @copy_from.try(:id))) %></p>
44 45
<% end %>
46
<%= call_hook(:view_trackers_form_bottom, :tracker => @tracker, :form => f) %>
45 47
<!--[eoform:tracker]-->
46 48
</div>
47 49
<%= submit_tag l(@tracker.new_record? ? :button_create : :button_save), class: "mobile-hide" %>
app/views/users/_form.html.erb
2 2

  
3 3
<div id="user_form">
4 4
<!--[form:user]-->
5
<%= call_hook(:view_users_form_top, :user => @user, :form => f) %>
5 6
<div class="splitcontent">
6 7
<div class="splitcontentleft">
7 8
<fieldset class="box tabular">
......
60 61
  </p>
61 62
  <% end -%>
62 63
</fieldset>
64
<%= call_hook(:view_users_form_after_authentication, :user => @user, :form => f) %>
63 65
<p class="mobile-hide">
64 66
  <% if @user.new_record? %>
65 67
    <%= submit_tag l(:button_create) %>
......
75 77
  <legend><%=l(:field_mail_notification)%></legend>
76 78
  <%= render :partial => 'users/mail_notifications' %>
77 79
</fieldset>
80
<%= call_hook(:view_users_form_after_mail_notifications, :user => @user, :form => f) %>
78 81

  
79 82
<fieldset class="box">
80 83
  <legend><%=l(:label_auto_watch_on)%></legend>
......
86 89
  <%= render :partial => 'users/preferences' %>
87 90
  <%= call_hook(:view_users_form_preferences, :user => @user, :form => f) %>
88 91
</fieldset>
92
<%= call_hook(:view_users_form_bottom, :user => @user, :form => f) %>
89 93
</div>
90 94
</div>
91 95
</div>
app/views/users/_list.html.erb
31 31
      </td>
32 32
    </tr>
33 33
  <% end %>
34
  <tr id="user-<%= user.id %>" class="hascontextmenu <%= cycle("odd", "even") %> <%= user.css_classes %>">
34
  <tr id="user-<%= user.id %>" class="hascontextmenu <%= cycle("odd", "even") %> <%= user.css_classes %> <%= css_classes_from_hook(:view_users_list_row_css_classes, {:user => user, :query => @query}) %>">
35 35
    <td class="checkbox hide-when-print"><%= check_box_tag("ids[]", user.id, false, id: nil) %></td>
36 36
    <% @query.inline_columns.each do |column| %>
37 37
      <% if column.name == :login %>
......
56 56
  </tr>
57 57
  <% end -%>
58 58
  <% end -%>
59
  <%= call_hook(:view_users_list_row_after, {:user => user, :query => @query, :colspan => @query.inline_columns.size + 2}) %>
59 60
<% end -%>
60 61
</tbody>
61 62
</table>
app/views/users/edit.html.erb
8 8

  
9 9
<%=
10 10
  page_title = title [l(:label_user_plural), users_path], @user.login
11
  page_title.insert(page_title.rindex(' ') + 1, avatar(@user).to_s)
11
  page_title.insert(page_title.rindex(' ', page_title.index('</h2>')) + 1, avatar(@user).to_s)
12 12
%>
13 13

  
14 14
<%= render_tabs user_settings_tabs %>
app/views/users/index.html.erb
1 1
<div class="contextual">
2 2
<%= link_to sprite_icon('add', l(:label_user_new)), new_user_path, :class => 'icon icon-add' %>
3
  <%= call_hook(:view_users_index_contextual, {:query => @query}) %>
3 4
  <%= actions_dropdown do %>
4 5
    <% if User.current.allowed_to?(:import_users, nil, :global => true) %>
5 6
      <%= link_to sprite_icon('import', l(:button_import)), new_users_import_path, :class => 'icon icon-import' %>
6 7
    <% end %>
8
    <%= call_hook(:view_users_index_actions_menu, {:query => @query}) %>
7 9
  <% end %>
8 10
</div>
9 11

  
app/views/users/show.html.erb
1 1
<div class="contextual">
2 2
<%= link_to(sprite_icon('edit', l(:button_edit)), edit_user_path(@user), :class => 'icon icon-edit') if User.current.admin? && @user.logged? %>
3
<%= render_object_menu_items(:user_actions, @user) %>
4
<%= call_hook(:view_users_show_contextual, {:user => @user}) %>
3 5
</div>
4 6

  
5 7
<h2><%= avatar_edit_link(@user, :size => "50") %> <%= @user.name %></h2>
app/views/versions/_form.html.erb
2 2
<%= error_messages_for 'version' %>
3 3

  
4 4
<div class="box tabular">
5
<%= call_hook(:view_versions_form_top, :version => @version, :form => f) %>
5 6
<p><%= f.text_field :name, :maxlength => 60, :size => 60, :required => true %></p>
6 7
<p><%= f.text_field :description, :size => 60 %></p>
7 8
<% unless @version.new_record? %>
......
17 18
<% @version.visible_custom_field_values.each do |value| %>
18 19
  <p><%= custom_field_tag_with_label :version, value %></p>
19 20
<% end %>
20

  
21
<%= call_hook(:view_versions_form_bottom, :version => @version, :form => f) %>
21 22
</div>
app/views/versions/index.html.erb
1 1
<div class="contextual">
2 2
  <%= link_to(sprite_icon('add', l(:label_version_new)), new_project_version_path(@project),
3 3
              :class => 'icon icon-add') if User.current.allowed_to?(:manage_versions, @project) %>
4
  <%= call_hook(:view_versions_index_contextual, {:project => @project}) %>
4 5
  <%= actions_dropdown do %>
5 6
  <%= link_to_if_authorized sprite_icon('settings', l(:label_settings)),
6 7
                            {:controller => 'projects', :action => 'settings', :id => @project, :tab => 'versions'},
7 8
                            :class => 'icon icon-settings' if User.current.allowed_to?(:manage_versions, @project) %>
9
  <%= call_hook(:view_versions_index_actions_menu, {:project => @project}) %>
8 10
  <% end %>
9 11
</div>
10 12

  
app/views/versions/show.html.erb
3 3
<%= link_to_if_authorized(sprite_icon('edit', l(:button_edit_associated_wikipage, :page_title => @version.wiki_page_title)), { :controller => 'wiki', :action => 'edit', :project_id => @version.project, :id => Wiki.titleize(@version.wiki_page_title)}, :class => 'icon icon-edit') unless @version.wiki_page_title.blank? || @version.project.wiki.nil? %>
4 4
<%= delete_link version_path(@version, :back_url => url_for(:controller => 'versions', :action => 'index', :project_id => @version.project)) if User.current.allowed_to?(:manage_versions, @version.project) %>
5 5
<%= link_to_new_issue(@version, @project) %>
6
<%= render_object_menu_items(:version_actions, @version, @version.project) %>
6 7
<%= call_hook(:view_versions_show_contextual, { :version => @version, :project => @project }) %>
7 8
</div>
8 9

  
app/views/wiki/show.html.erb
7 7
<% end %>
8 8
<% end %>
9 9

  
10
  <%= call_hook(:view_wiki_show_contextual, {:page => @page, :project => @project}) %>
10 11
  <%= actions_dropdown do %>
11 12
    <%= link_to_if_authorized(sprite_icon('history', l(:label_history)), { :action => 'history', :id => @page.title}, :class => 'icon icon-history') %>
12 13

  
......
24 25
    <% if User.current.allowed_to?(:edit_wiki_pages, @project) %>
25 26
      <%= link_to sprite_icon('add', l(:label_wiki_page_new)), new_project_wiki_page_path(@project, :parent => @page.title), :remote => true, :class => 'icon icon-add' %>
26 27
    <% end %>
28
    <%= call_hook(:view_wiki_show_actions_menu, {:page => @page, :project => @project}) %>
27 29
  <% end %>
28 30
</div>
29 31

  
doc/HOOKS
1
= Redmine hooks
2

  
3
Every hook that Redmine calls, with the file it is called in and the keys of its
4
context (besides :project, :controller, :request and :hook_caller, which view hooks
5
always get). A plugin implements a hook as a method of a Redmine::Hook::Listener
6
(controller and model hooks) or Redmine::Hook::ViewListener (view hooks), or with
7
render_on. A hook listed here is kept; it is removed only after a deprecation notice
8
in this file and in the changelog. test/unit/lib/redmine/hook_list_test.rb checks
9
that every hook called in app/ and lib/ is listed here, and the other way round.
10

  
11
Since: the version the hook appeared in; - for hooks older than this list.
12
Context: the keys given on the line of the call (see the file for the rest).
13

  
14
after_plugins_loaded
15
  Since: -
16
  Context: -
17
  Called in: lib/redmine/plugin_loader.rb
18

  
19
another_hook
20
  Since: -
21
  Context: :foo
22
  Called in: lib/redmine/hook.rb
23

  
24
controller_account_success_authentication_after
25
  Since: -
26
  Context: :user
27
  Called in: app/controllers/account_controller.rb
28

  
29
controller_custom_fields_edit_after_save
30
  Since: -
31
  Context: :params, :custom_field
32
  Called in: app/controllers/custom_fields_controller.rb
33

  
34
controller_custom_fields_new_after_save
35
  Since: -
36
  Context: :params, :custom_field
37
  Called in: app/controllers/custom_fields_controller.rb
38

  
39
controller_issues_bulk_edit_after_save
40
  Since: -
41
  Context: :params, :issue, :journal
42
  Called in: app/controllers/issues_controller.rb
43

  
44
controller_issues_bulk_edit_before_save
45
  Since: -
46
  Context: :params, :issue
47
  Called in: app/controllers/issues_controller.rb
48

  
49
controller_issues_edit_after_save
50
  Since: 7.1.0
51
  Context: -
52
  Called in: app/controllers/issues_controller.rb
53

  
54
controller_issues_edit_before_save
55
  Since: 7.1.0
56
  Context: -
57
  Called in: app/controllers/issues_controller.rb
58

  
59
controller_issues_new_after_save
60
  Since: -
61
  Context: :params, :issue
62
  Called in: app/controllers/issues_controller.rb
63

  
64
controller_issues_new_before_save
65
  Since: -
66
  Context: :params, :issue
67
  Called in: app/controllers/issues_controller.rb
68

  
69
controller_journals_edit_post
70
  Since: -
71
  Context: :journal, :params
72
  Called in: app/controllers/journals_controller.rb
73

  
74
controller_messages_new_after_save
75
  Since: -
76
  Context: :params, :message
77
  Called in: app/controllers/messages_controller.rb
78

  
79
controller_messages_reply_after_save
80
  Since: -
81
  Context: :params, :message
82
  Called in: app/controllers/messages_controller.rb
83

  
84
controller_time_entries_bulk_edit_before_save
85
  Since: 7.1.0
86
  Context: -
87
  Called in: app/controllers/timelog_controller.rb
88

  
89
controller_timelog_edit_before_save
90
  Since: -
91
  Context: -
92
  Called in: app/controllers/timelog_controller.rb
93

  
94
controller_wiki_edit_after_save
95
  Since: -
96
  Context: :params, :page
97
  Called in: app/controllers/wiki_controller.rb
98

  
99
controller_wiki_show_before_render
100
  Since: -
101
  Context: :content, :format
102
  Called in: app/controllers/wiki_controller.rb
103

  
104
helper_issues_show_detail_after_setting
105
  Since: -
106
  Context: -
107
  Called in: app/helpers/issues_helper.rb
108

  
109
helper_queries_column_content
110
  Since: -
111
  Context: -
112
  Called in: app/helpers/queries_helper.rb
113

  
114
helper_queries_column_value
115
  Since: -
116
  Context: -
117
  Called in: app/helpers/queries_helper.rb
118

  
119
model_changeset_scan_commit_for_issue_ids_pre_issue_update
120
  Since: -
121
  Context: -
122
  Called in: app/models/changeset.rb
123

  
124
model_project_copy_before_save
125
  Since: -
126
  Context: -
127
  Called in: app/models/project.rb
128

  
129
some_hook
130
  Since: -
131
  Context: -
132
  Called in: lib/redmine/hook.rb
133

  
134
view_account_left_bottom
135
  Since: -
136
  Context: :user
137
  Called in: app/views/users/show.html.erb
138

  
139
view_account_login_bottom
140
  Since: -
141
  Context: -
142
  Called in: app/views/account/login.html.erb
143

  
144
view_account_login_top
145
  Since: -
146
  Context: -
147
  Called in: app/views/account/login.html.erb
148

  
149
view_account_right_bottom
150
  Since: -
151
  Context: :user
152
  Called in: app/views/users/show.html.erb
153

  
154
view_admin_projects_sidebar_queries_bottom
155
  Since: -
156
  Context: -
157
  Called in: app/views/admin/projects.html.erb
158

  
159
view_boards_show_contextual
160
  Since: 7.1.0
161
  Context: :board, :project
162
  Called in: app/views/boards/show.html.erb
163

  
164
view_calendars_show_bottom
165
  Since: -
166
  Context: :year, :month, :project, :query
167
  Called in: app/views/calendars/show.html.erb
168

  
169
view_custom_fields_form_upper_box
170
  Since: -
171
  Context: :custom_field, :form
172
  Called in: app/views/custom_fields/_form.html.erb
173

  
174
view_documents_index_contextual
175
  Since: 7.1.0
176
  Context: :project
177
  Called in: app/views/documents/index.html.erb
178

  
179
view_documents_show_contextual
180
  Since: 7.1.0
181
  Context: :document, :project
182
  Called in: app/views/documents/show.html.erb
183

  
184
view_groups_form_bottom
185
  Since: 7.1.0
186
  Context: :group, :form
187
  Called in: app/views/groups/_form.html.erb
188

  
189
view_groups_form_top
190
  Since: 7.1.0
191
  Context: :group, :form
192
  Called in: app/views/groups/_form.html.erb
193

  
194
view_groups_memberships_table_header
195
  Since: -
196
  Context: :group
197
  Called in: app/helpers/principal_memberships_helper.rb
198

  
199
view_groups_memberships_table_row
200
  Since: -
201
  Context: :group, :membership
202
  Called in: app/helpers/principal_memberships_helper.rb
203

  
204
view_groups_show_contextual
205
  Since: 7.1.0
206
  Context: :group
207
  Called in: app/views/groups/show.html.erb
208

  
209
view_issue_statuses_form
210
  Since: -
211
  Context: :issue_status
212
  Called in: app/views/issue_statuses/_form.html.erb
213

  
214
view_issues_bulk_edit_details_bottom
215
  Since: -
216
  Context: :issues
217
  Called in: app/views/issues/bulk_edit.html.erb
218

  
219
view_issues_context_menu_end
220
  Since: -
221
  Context: :issues, :can, :back
222
  Called in: app/views/context_menus/issues.html.erb
223

  
224
view_issues_context_menu_start
225
  Since: -
226
  Context: :issues, :can, :back
227
  Called in: app/views/context_menus/issues.html.erb
228

  
229
view_issues_edit_notes_bottom
230
  Since: -
231
  Context: :issue, :notes, :form
232
  Called in: app/views/issues/_edit.html.erb
233

  
234
view_issues_edit_top
235
  Since: -
236
  Context: :issue, :form
237
  Called in: app/views/issues/_edit.html.erb
238

  
239
view_issues_form_after_attributes
240
  Since: 7.1.0
241
  Context: :issue, :form
242
  Called in: app/views/issues/_form.html.erb
243

  
244
view_issues_form_after_description
245
  Since: 7.1.0
246
  Context: :issue, :form
247
  Called in: app/views/issues/_form.html.erb
248

  
249
view_issues_form_after_subject
250
  Since: 7.1.0
251
  Context: :issue, :form
252
  Called in: app/views/issues/_form.html.erb
253

  
254
view_issues_form_custom_fields_bottom
255
  Since: 7.1.0
256
  Context: :issue, :custom_field_values
257
  Called in: app/views/issues/_form_custom_fields.html.erb
258

  
259
view_issues_form_custom_fields_top
260
  Since: 7.1.0
261
  Context: :issue, :custom_field_values
262
  Called in: app/views/issues/_form_custom_fields.html.erb
263

  
264
view_issues_form_details_bottom
265
  Since: -
266
  Context: :issue, :form
267
  Called in: app/views/issues/_form.html.erb
268

  
269
view_issues_form_details_top
270
  Since: -
271
  Context: :issue, :form
272
  Called in: app/views/issues/_form.html.erb
273

  
274
view_issues_history_changeset_bottom
275
  Since: -
276
  Context: :changeset
277
  Called in: app/views/issues/tabs/_changesets.html.erb
278

  
279
view_issues_history_journal_bottom
280
  Since: -
281
  Context: :journal
282
  Called in: app/views/issues/tabs/_history.html.erb
283

  
284
view_issues_history_time_entry_bottom
285
  Since: -
286
  Context: :time_entry
287
  Called in: app/views/issues/tabs/_time_entries.html.erb
288

  
289
view_issues_index_actions_menu
290
  Since: 7.1.0
291
  Context: :project, :query
292
  Called in: app/views/issues/index.html.erb
293

  
294
view_issues_index_bottom
295
  Since: -
296
  Context: :issues, :project, :query
297
  Called in: app/views/issues/index.html.erb
298

  
299
view_issues_index_contextual
300
  Since: 7.1.0
301
  Context: :project, :query
302
  Called in: app/views/issues/index.html.erb
303

  
304
view_issues_list_row_after
305
  Since: 7.1.0
306
  Context: :issue, :query, :colspan
307
  Called in: app/views/issues/_list.html.erb
308

  
309
view_issues_new_top
310
  Since: -
311
  Context: :issue
312
  Called in: app/views/issues/new.html.erb
313

  
314
view_issues_show_actions_menu
315
  Since: 7.1.0
316
  Context: :issue, :project
317
  Called in: app/views/issues/_action_menu.html.erb
318

  
319
view_issues_show_bottom
320
  Since: 7.1.0
321
  Context: :issue
322
  Called in: app/views/issues/show.html.erb
323

  
324
view_issues_show_contextual
325
  Since: 7.1.0
326
  Context: :issue, :project
327
  Called in: app/views/issues/_action_menu.html.erb
328

  
329
view_issues_show_description_bottom
330
  Since: -
331
  Context: :issue
332
  Called in: app/views/issues/show.html.erb
333

  
334
view_issues_show_details_bottom
335
  Since: -
336
  Context: :issue
337
  Called in: app/views/issues/show.html.erb
338

  
339
view_issues_show_details_end
340
  Since: 7.1.0
341
  Context: :issue
342
  Called in: app/views/issues/show.html.erb
343

  
344
view_issues_show_top
345
  Since: 7.1.0
346
  Context: :issue
347
  Called in: app/views/issues/show.html.erb
348

  
349
view_issues_sidebar_issues_bottom
350
  Since: -
351
  Context: -
352
  Called in: app/views/issues/_sidebar.html.erb
353

  
354
view_issues_sidebar_planning_bottom
355
  Since: -
356
  Context: -
357
  Called in: app/views/issues/_sidebar.html.erb
358

  
359
view_issues_sidebar_queries_bottom
360
  Since: -
361
  Context: -
362
  Called in: app/views/issues/_sidebar.html.erb
363

  
364
view_journals_notes_form_after_notes
365
  Since: -
366
  Context: :journal
367
  Called in: app/views/journals/_notes_form.html.erb
368

  
369
view_journals_update_js_bottom
370
  Since: -
371
  Context: :journal
372
  Called in: app/views/journals/update.js.erb
373

  
374
view_layouts_base_body_bottom
375
  Since: -
376
  Context: -
377
  Called in: app/views/layouts/base.html.erb
378

  
379
view_layouts_base_body_top
380
  Since: -
381
  Context: -
382
  Called in: app/views/layouts/base.html.erb
383

  
384
view_layouts_base_content
385
  Since: -
386
  Context: -
387
  Called in: app/views/layouts/base.html.erb
388

  
389
view_layouts_base_content_top
390
  Since: 7.1.0
391
  Context: -
392
  Called in: app/views/layouts/base.html.erb
393

  
394
view_layouts_base_footer_bottom
395
  Since: 7.1.0
396
  Context: -
397
  Called in: app/views/layouts/base.html.erb
398

  
399
view_layouts_base_footer_top
400
  Since: 7.1.0
401
  Context: -
402
  Called in: app/views/layouts/base.html.erb
403

  
404
view_layouts_base_header_bottom
405
  Since: 7.1.0
406
  Context: -
407
  Called in: app/views/layouts/base.html.erb
408

  
409
view_layouts_base_header_top
410
  Since: 7.1.0
411
  Context: -
412
  Called in: app/views/layouts/base.html.erb
413

  
414
view_layouts_base_html_head
415
  Since: -
416
  Context: -
417
  Called in: app/views/layouts/base.html.erb
418

  
419
view_layouts_base_sidebar
420
  Since: -
421
  Context: -
422
  Called in: app/helpers/application_helper.rb
423

  
424
view_layouts_base_sidebar_top
425
  Since: 7.1.0
426
  Context: -
427
  Called in: app/helpers/application_helper.rb
428

  
429
view_layouts_base_title_after
430
  Since: 7.1.0
431
  Context: :title
432
  Called in: app/helpers/application_helper.rb
433

  
434
view_messages_show_contextual
435
  Since: 7.1.0
436
  Context: :topic, :message, :project
437
  Called in: app/views/messages/show.html.erb
438

  
439
view_my_account
440
  Since: -
441
  Context: :user, :form
442
  Called in: app/views/my/account.html.erb
443

  
444
view_my_account_contextual
445
  Since: -
446
  Context: :user
447
  Called in: app/views/my/account.html.erb
448

  
449
view_my_account_preferences
450
  Since: -
451
  Context: :user, :form
452
  Called in: app/views/my/account.html.erb
453

  
454
view_my_page_contextual
455
  Since: -
456
  Context: :user
457
  Called in: app/views/my/page.html.erb
458

  
459
view_my_page_splitcontent
460
  Since: -
461
  Context: :user
462
  Called in: app/views/my/page.html.erb
463

  
464
view_news_index_contextual
465
  Since: 7.1.0
466
  Context: :project
467
  Called in: app/views/news/index.html.erb
468

  
469
view_news_show_contextual
470
  Since: 7.1.0
471
  Context: :news, :project
472
  Called in: app/views/news/show.html.erb
473

  
474
view_projects_copy_only_items
475
  Since: -
476
  Context: :project, :f
477
  Called in: app/views/projects/copy.html.erb
478

  
479
view_projects_form
480
  Since: -
481
  Context: :project, :form
482
  Called in: app/views/projects/_form.html.erb
483

  
484
view_projects_form_bottom
485
  Since: 7.1.0
486
  Context: :project, :form
487
  Called in: app/views/projects/_form.html.erb
488

  
489
view_projects_form_top
490
  Since: 7.1.0
491
  Context: :project, :form
492
  Called in: app/views/projects/_form.html.erb
493

  
494
view_projects_index_contextual
495
  Since: 7.1.0
496
  Context: :query
497
  Called in: app/views/projects/index.html.erb
498

  
499
view_projects_list_row_after
500
  Since: 7.1.0
501
  Context: :project, :query, :colspan
502
  Called in: app/views/projects/_list.html.erb
503

  
504
view_projects_roadmap_version_bottom
505
  Since: -
506
  Context: :version
507
  Called in: app/views/versions/index.html.erb
508

  
509
view_projects_settings_members_table_header
510
  Since: -
511
  Context: :project
512
  Called in: app/views/members/_members.html.erb
513

  
514
view_projects_settings_members_table_row
515
  Since: -
516
  Context: :project, :member
517
  Called in: app/views/members/_members.html.erb
518

  
519
view_projects_show_actions_menu
520
  Since: 7.1.0
521
  Context: :project
522
  Called in: app/views/projects/show.html.erb
523

  
524
view_projects_show_contextual
525
  Since: 7.1.0
526
  Context: :project
527
  Called in: app/views/projects/show.html.erb
528

  
529
view_projects_show_left
530
  Since: -
531
  Context: :project
532
  Called in: app/views/projects/show.html.erb
533

  
534
view_projects_show_right
535
  Since: -
536
  Context: :project
537
  Called in: app/views/projects/show.html.erb
538

  
539
view_projects_show_sidebar_bottom
540
  Since: -
541
  Context: :project
542
  Called in: app/views/projects/show.html.erb
543

  
544
view_projects_sidebar_queries_bottom
545
  Since: -
546
  Context: -
547
  Called in: app/views/projects/_sidebar.html.erb
548

  
549
view_reports_issue_report_split_content_left
550
  Since: -
551
  Context: :project
552
  Called in: app/views/reports/issue_report.html.erb
553

  
554
view_reports_issue_report_split_content_right
555
  Since: -
556
  Context: :project
557
  Called in: app/views/reports/issue_report.html.erb
558

  
559
view_repositories_navigation_actions_menu
560
  Since: 7.1.0
561
  Context: :repository, :project
562
  Called in: app/views/repositories/_navigation.html.erb
563

  
564
view_repositories_show_contextual
565
  Since: -
566
  Context: :repository, :project
567
  Called in: app/views/repositories/annotate.html.erb
568
  Called in: app/views/repositories/changes.html.erb
569
  Called in: app/views/repositories/entry.html.erb
570
  Called in: app/views/repositories/show.html.erb
571

  
572
view_search_index_options_content_bottom
573
  Since: -
574
  Context: -
575
  Called in: app/views/search/index.html.erb
576

  
577
view_settings_general_form
578
  Since: -
579
  Context: -
580
  Called in: app/views/settings/_general.html.erb
581

  
582
view_time_entries_bulk_edit_details_bottom
583
  Since: -
584
  Context: :time_entries
585
  Called in: app/views/timelog/bulk_edit.html.erb
586

  
587
view_time_entries_context_menu_end
588
  Since: -
589
  Context: :time_entries, :can, :back
590
  Called in: app/views/context_menus/time_entries.html.erb
591

  
592
view_time_entries_context_menu_start
593
  Since: -
594
  Context: :time_entries, :can, :back
595
  Called in: app/views/context_menus/time_entries.html.erb
596

  
597
view_timelog_edit_form_bottom
598
  Since: -
599
  Context: :time_entry, :form
600
  Called in: app/views/timelog/_form.html.erb
601

  
602
view_timelog_index_actions_menu
603
  Since: 7.1.0
604
  Context: :project, :query
605
  Called in: app/views/timelog/index.html.erb
606

  
607
view_timelog_index_contextual
608
  Since: 7.1.0
609
  Context: :project, :query
610
  Called in: app/views/timelog/index.html.erb
611

  
612
view_trackers_form_bottom
613
  Since: 7.1.0
614
  Context: :tracker, :form
615
  Called in: app/views/trackers/_form.html.erb
616

  
617
view_trackers_form_top
618
  Since: 7.1.0
619
  Context: :tracker, :form
620
  Called in: app/views/trackers/_form.html.erb
621

  
622
view_users_form
623
  Since: -
624
  Context: :user, :form
625
  Called in: app/views/users/_form.html.erb
626

  
627
view_users_form_after_authentication
628
  Since: 7.1.0
629
  Context: :user, :form
630
  Called in: app/views/users/_form.html.erb
631

  
632
view_users_form_after_mail_notifications
633
  Since: 7.1.0
634
  Context: :user, :form
635
  Called in: app/views/users/_form.html.erb
636

  
637
view_users_form_bottom
638
  Since: 7.1.0
639
  Context: :user, :form
640
  Called in: app/views/users/_form.html.erb
641

  
642
view_users_form_preferences
643
  Since: -
644
  Context: :user, :form
645
  Called in: app/views/users/_form.html.erb
646

  
647
view_users_form_top
648
  Since: 7.1.0
649
  Context: :user, :form
650
  Called in: app/views/users/_form.html.erb
651

  
652
view_users_index_actions_menu
653
  Since: 7.1.0
654
  Context: :query
655
  Called in: app/views/users/index.html.erb
656

  
657
view_users_index_contextual
658
  Since: 7.1.0
659
  Context: :query
660
  Called in: app/views/users/index.html.erb
661

  
662
view_users_list_row_after
663
  Since: 7.1.0
664
  Context: :user, :query, :colspan
665
  Called in: app/views/users/_list.html.erb
666

  
667
view_users_memberships_table_header
668
  Since: -
669
  Context: :user
670
  Called in: app/helpers/principal_memberships_helper.rb
671

  
672
view_users_memberships_table_row
673
  Since: -
674
  Context: :user, :membership
675
  Called in: app/helpers/principal_memberships_helper.rb
676

  
677
view_users_show_contextual
678
  Since: 7.1.0
679
  Context: :user
680
  Called in: app/views/users/show.html.erb
681

  
682
view_users_sidebar_queries_bottom
683
  Since: -
684
  Context: -
685
  Called in: app/views/users/index.html.erb
686

  
687
view_versions_form_bottom
688
  Since: 7.1.0
689
  Context: :version, :form
690
  Called in: app/views/versions/_form.html.erb
691

  
692
view_versions_form_top
693
  Since: 7.1.0
694
  Context: :version, :form
695
  Called in: app/views/versions/_form.html.erb
696

  
697
view_versions_index_actions_menu
698
  Since: 7.1.0
699
  Context: :project
700
  Called in: app/views/versions/index.html.erb
701

  
702
view_versions_index_contextual
703
  Since: 7.1.0
704
  Context: :project
705
  Called in: app/views/versions/index.html.erb
706

  
707
view_versions_show_bottom
708
  Since: -
709
  Context: :version
710
  Called in: app/views/versions/show.html.erb
711

  
712
view_versions_show_contextual
713
  Since: -
714
  Context: :version, :project
715
  Called in: app/views/versions/show.html.erb
716

  
717
view_welcome_index_left
718
  Since: -
719
  Context: -
720
  Called in: app/views/welcome/index.html.erb
721

  
722
view_welcome_index_right
723
  Since: -
724
  Context: -
725
  Called in: app/views/welcome/index.html.erb
726

  
727
view_wiki_show_actions_menu
728
  Since: 7.1.0
729
  Context: :page, :project
730
  Called in: app/views/wiki/show.html.erb
731

  
732
view_wiki_show_contextual
733
  Since: 7.1.0
734
  Context: :page, :project
735
  Called in: app/views/wiki/show.html.erb
736

  
737
view_wiki_show_sidebar_bottom
738
  Since: -
739
  Context: :wiki, :page
740
  Called in: app/views/wiki/_sidebar.html.erb
741

  
lib/redmine/journal_details.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20
module Redmine
21
  # Registry of the renderers for journal details that plugins write with a
22
  # property of their own (journal_details.property), so that a plugin does not
23
  # have to override IssuesHelper#show_detail.
24
  #
25
  # A renderer responds to +call+ with the detail, the view and the options of
26
  # show_detail, and returns the line as HTML or, when +no_html+ is true, as text
27
  # (the text is also used in emails):
28
  #
29
  #   class APlugin::JournalDetail
30
  #     def self.call(detail, view, no_html, options)
31
  #       label = no_html ? 'Sample' : view.content_tag('strong', 'Sample')
32
  #       view.l(:text_journal_set_to, :label => label, :value => detail.value)
33
  #     end
34
  #   end
35
  #
36
  #   Redmine::Plugin.register :a_plugin do
37
  #     journal_detail :property => 'a_plugin', :renderer => APlugin::JournalDetail
38
  #   end
39
  #
40
  # A renderer registered with a :prop_key is used for that key only and comes
41
  # before one registered for the whole property.
42
  module JournalDetails
43
    @renderers = {}
44

  
45
    class << self
46
      def register(property, renderer, prop_key: nil)
47
        unless renderer.respond_to?(:call)
48
          raise ArgumentError, "Invalid renderer for journal detail property '#{property}'"
49
        end
50

  
51
        @renderers[[property.to_s, prop_key&.to_s]] = renderer
52
      end
53

  
54
      # Returns the renderer for +detail+, or nil when none is registered
55
      def renderer_for(detail)
56
        property = detail.property.to_s
57
        @renderers[[property, detail.prop_key.to_s]] || @renderers[[property, nil]]
58
      end
59

  
60
      def clear
61
        @renderers.clear
... This diff was truncated because it exceeds the maximum size that can be displayed.
(1-1/4)