Project

General

Profile

Feature #33820 » feature-33820.patch

Mizuki ISHIKAWA, 2020-08-06 04:04

View differences:

app/controllers/auto_completes_controller.rb
42 42
    render :json => format_issues_json(issues)
43 43
  end
44 44

  
45
  def wiki_pages
46
    q = params[:q].to_s.strip
47
    wiki = Wiki.find_by(project: @project)
48
    scope = (@project && wiki ? wiki.pages : WikiPage.all).reorder(id: :desc)
49
    wiki_pages =
50
      if q.present?
51
        scope.where("LOWER(#{WikiPage.table_name}.title) LIKE LOWER(?)", "%#{q}%").limit(10).to_a
52
      else
53
        scope.limit(10).to_a
54
      end
55
    render :json => format_wiki_pages_json(wiki_pages)
56
  end
57

  
45 58
  private
46 59

  
47 60
  def find_project
......
61 74
      }
62 75
    }
63 76
  end
77

  
78
  def format_wiki_pages_json(wiki_pages)
79
    wiki_pages.map {|wiki_page|
80
      {
81
        'id' => wiki_page.id,
82
        'label' => wiki_page.title.to_s.truncate(255),
83
        'value' => wiki_page.title
84
      }
85
    }
86
  end
64 87
end
app/helpers/custom_fields_helper.rb
85 85
      css += ' wiki-edit'
86 86
      data = {
87 87
        :auto_complete => true,
88
        :issues_url => auto_complete_issues_path(:project_id => custom_value.customized.project, :q => '')
88
        :issues_url => auto_complete_issues_path(:project_id => custom_value.customized.project, :q => ''),
89
        :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => custom_value.customized.project, :q => '')
89 90
      } if custom_value.customized&.try(:project)
90 91
    end
91 92
    custom_value.custom_field.format.edit_tag(
......
134 135
      css += ' wiki-edit'
135 136
      data = {
136 137
        :auto_complete => true,
137
        :issues_url => auto_complete_issues_path(:q => '')
138
        :issues_url => auto_complete_issues_path(:q => ''),
139
        :wiki_pages_url => auto_complete_wiki_pages_path(:q => '')
138 140
      }
139 141
    end
140 142
    custom_field.format.bulk_edit_tag(
app/views/documents/_form.html.erb
6 6
<p><%= f.text_area :description, :cols => 60, :rows => 15, :class => 'wiki-edit',
7 7
                  :data => {
8 8
                      :auto_complete => true,
9
                      :issues_url => auto_complete_issues_path(:project_id => @project, :q => '')
9
                      :issues_url => auto_complete_issues_path(:project_id => @project, :q => ''),
10
                      :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => @project, :q => '')
10 11
                  }
11 12
%></p>
12 13

  
app/views/issues/_edit.html.erb
32 32
      <%= f.text_area :notes, :cols => 60, :rows => 10, :class => 'wiki-edit',
33 33
            :data => {
34 34
                :auto_complete => true,
35
                :issues_url => auto_complete_issues_path(:project_id => @issue.project, :q => '')
35
                :issues_url => auto_complete_issues_path(:project_id => @issue.project, :q => ''),
36
                :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => @issue.project, :q => '')
36 37
            },
37 38
            :no_label => true %>
38 39
      <%= wikitoolbar_for 'issue_notes', preview_issue_path(:project_id => @project, :issue_id => @issue) %>
app/views/issues/_form.html.erb
38 38
                   :rows => [[10, @issue.description.to_s.length / 50].max, 20].min,
39 39
                   :data => {
40 40
                       :auto_complete => true,
41
                       :issues_url => auto_complete_issues_path(:project_id => @issue.project, :q => '')
41
                       :issues_url => auto_complete_issues_path(:project_id => @issue.project, :q => ''),
42
                       :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => @issue.project, :q => '')
42 43
                   },
43 44
                   :no_label => true %>
44 45
  <% end %>
app/views/issues/bulk_edit.html.erb
222 222
<%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit',
223 223
                  :data => {
224 224
                      :auto_complete => true,
225
                      :issues_url => auto_complete_issues_path(:project_id => @project, :q => '')
225
                      :issues_url => auto_complete_issues_path(:project_id => @project, :q => ''),
226
                      :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => @project, :q => '')
226 227
                  }
227 228
%>
228 229
<%= wikitoolbar_for 'notes' %>
app/views/journals/_notes_form.html.erb
7 7
          :rows => (@journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min),
8 8
          :data => {
9 9
              :auto_complete => true,
10
              :issues_url => auto_complete_issues_path(:project_id => @project, :q => '')
10
              :issues_url => auto_complete_issues_path(:project_id => @project, :q => ''),
11
              :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => @project, :q => '')
11 12
          }
12 13
    %>
13 14
    <% if @journal.safe_attribute? 'private_notes' %>
app/views/messages/_form.html.erb
27 27
                :accesskey => accesskey(:edit),
28 28
                :data => {
29 29
                    :auto_complete => true,
30
                    :issues_url => auto_complete_issues_path(:project_id => @project, :q => '')
30
                    :issues_url => auto_complete_issues_path(:project_id => @project, :q => ''),
31
                    :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => @project, :q => '')
31 32
                }
32 33
%></p>
33 34
<%= wikitoolbar_for 'message_content', preview_board_message_path(:board_id => @board, :id => @message) %>
app/views/news/_form.html.erb
6 6
<p><%= f.text_area :description, :required => true, :cols => 60, :rows => 15, :class => 'wiki-edit',
7 7
                   :data => {
8 8
                       :auto_complete => true,
9
                       :issues_url => auto_complete_issues_path(:project_id => @project, :q => '')
9
                       :issues_url => auto_complete_issues_path(:project_id => @project, :q => ''),
10
                       :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => @project, :q => '')
10 11
                   }
11 12
%></p>
12 13
<p id="attachments_form"><label><%= l(:label_attachment_plural) %></label><%= render :partial => 'attachments/form', :locals => {:container => @news} %></p>
app/views/news/show.html.erb
56 56
    <%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit',
57 57
                  :data => {
58 58
                    :auto_complete => true,
59
                    :issues_url => auto_complete_issues_path(:project_id => @project, :q => '')
59
                    :issues_url => auto_complete_issues_path(:project_id => @project, :q => ''),
60
                    :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => @project, :q => '')
60 61
                  }
61 62
    %>
62 63
    <%= wikitoolbar_for 'comment_comments', preview_news_path(:project_id => @project, :id => @news) %>
app/views/wiki/edit.html.erb
17 17
                  :class => 'wiki-edit',
18 18
                  :data => {
19 19
                      :auto_complete => true,
20
                      :issues_url => auto_complete_issues_path(:project_id => @project, :q => '')
20
                      :issues_url => auto_complete_issues_path(:project_id => @project, :q => ''),
21
                      :wiki_pages_url => auto_complete_wiki_pages_path(:project_id => @project, :q => '')
21 22
                  }
22 23
%>
23 24

  
config/routes.rb
43 43
  post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
44 44
  post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
45 45

  
46
  # Misc issue routes. TODO: move into resources
46
  # Auto complate routes
47 47
  match '/issues/auto_complete', :to => 'auto_completes#issues', :via => :get, :as => 'auto_complete_issues'
48
  match '/wiki_pages/auto_complete', :to => 'auto_completes#wiki_pages', :via => :get, :as => 'auto_complete_wiki_pages'
49

  
48 50
  match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
49 51
  match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
50 52
  match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
lib/redmine.rb
154 154
  end
155 155

  
156 156
  map.project_module :wiki do |map|
157
    map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index]}, :read => true
157
    map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index], :auto_complete => [:wiki_pages]}, :read => true
158 158
    map.permission :view_wiki_edits, {:wiki => [:history, :diff, :annotate]}, :read => true
159 159
    map.permission :export_wiki_pages, {:wiki => [:export]}, :read => true
160 160
    map.permission :edit_wiki_pages, :wiki => [:new, :edit, :update, :preview, :add_attachment], :attachments => :upload
public/javascripts/application.js
1040 1040
    // do not attach if Tribute is already initialized
1041 1041
    if (element.dataset.tribute === 'true') {return;}
1042 1042

  
1043
    const issuesUrl = element.dataset.issuesUrl;
1044
    const usersUrl = element.dataset.usersUrl;
1045

  
1046 1043
    const remoteSearch = function(url, cb) {
1047 1044
      const xhr = new XMLHttpRequest();
1048 1045
      xhr.onreadystatechange = function ()
......
1060 1057
      xhr.send();
1061 1058
    };
1062 1059

  
1063
    const tribute = new Tribute({
1064
      trigger: '#',
1065
      values: function (text, cb) {
1066
        if (event.target.type === 'text' && $(element).attr('autocomplete') != 'off') {
1067
          $(element).attr('autocomplete', 'off');
1060
    const AutoCompleteIssueId =
1061
      {
1062
        trigger: '#',
1063
        values: function (text, cb) {
1064
          if (event.target.type === 'text' && $(element).attr('autocomplete') != 'off') {
1065
            $(element).attr('autocomplete', 'off');
1066
          }
1067
          remoteSearch(element.dataset.issuesUrl + text, function (issues) {
1068
            return cb(issues);
1069
          });
1070
        },
1071
        lookup: 'label',
1072
        fillAttr: 'label',
1073
        requireLeadingSpace: true,
1074
        selectTemplate: function (issue) {
1075
          return '#' + issue.original.id;
1076
        },
1077
        noMatchTemplate: function () {
1078
          return '<span style:"visibility: hidden;"></span>';
1068 1079
        }
1069
        remoteSearch(issuesUrl + text, function (issues) {
1070
          return cb(issues);
1071
        });
1072
      },
1073
      lookup: 'label',
1074
      fillAttr: 'label',
1075
      requireLeadingSpace: true,
1076
      selectTemplate: function (issue) {
1077
        return '#' + issue.original.id;
1078
      },
1079
      noMatchTemplate: function () {
1080
        return '<span style:"visibility: hidden;"></span>';
1081 1080
      }
1082
    });
1081
    const AutoCompleteWikiPageTitle =
1082
      {
1083
        trigger: '[[',
1084
        values: function (text, cb) {
1085
          remoteSearch(element.dataset.wikiPagesUrl + text, function (wikiPages) {
1086
            return cb(wikiPages);
1087
          });
1088
        },
1089
        lookup: 'label',
1090
        fillAttr: 'label',
1091
        requireLeadingSpace: true,
1092
        selectTemplate: function (wikiPage) {
1093
          return '[[' + wikiPage.original.value + ']]';
1094
        },
1095
        noMatchTemplate: function () {
1096
          return '<span style:"visibility: hidden;"></span>';
1097
        }
1098
      }
1099

  
1100
    const tribute = new Tribute({
1101
      collection: [
1102
        AutoCompleteIssueId,
1103
        ($(element).closest('.jstEditor').length && element.dataset.wikiPagesUrl ? AutoCompleteWikiPageTitle : {})
1104
      ]});
1083 1105

  
1084 1106
    tribute.attach(element);
1085 1107
}
1086 1108

  
1087

  
1088 1109
$(document).ready(setupAjaxIndicator);
1089 1110
$(document).ready(hideOnLoad);
1090 1111
$(document).ready(addFormObserversForDoubleSubmit);
test/functional/auto_completes_controller_test.rb
196 196
    assert_equal 10, json.count
197 197
    assert_equal Issue.last.id, json.first['id'].to_i
198 198
  end
199

  
200
  def test_wiki_pages_should_not_be_case_sensitive
201
    get(
202
      :wiki_pages,
203
      params: {
204
        project_id: 'ecookbook',
205
        q: 'pAgE'
206
      }
207
    )
208
    assert_response :success
209
    assert_include "Page_with_an_inline_image", response.body
210
  end
211

  
212
  def test_wiki_pages_without_project_should_search_all_projects
213
    get(:wiki_pages, params: {q: 'pAgE'})
214
    assert_response :success
215
    assert_include "Page_with_an_inline_image", response.body # project eCookbook
216
    assert_include "Start_page", response.body # project OnlineStore
217
  end
218

  
219
  def test_wiki_pages_should_return_json
220
    get(
221
      :wiki_pages,
222
      params: {
223
        project_id: 'ecookbook',
224
        q: 'Page_with_an_inline_image'
225
      }
226
    )
227
    assert_response :success
228
    json = ActiveSupport::JSON.decode(response.body)
229
    assert_kind_of Array, json
230
    issue = json.first
231
    assert_kind_of Hash, issue
232
    assert_equal 4, issue['id']
233
    assert_equal 'Page_with_an_inline_image', issue['value']
234
    assert_equal 'Page_with_an_inline_image', issue['label']
235
  end
236

  
237
  def test_wiki_pages_should_return_json_content_type_response
238
    get(
239
      :wiki_pages,
240
      params: {
241
        project_id: 'ecookbook',
242
        q: 'Page_with_an_inline_image'
243
      }
244
    )
245
    assert_response :success
246
    assert_include 'application/json', response.headers['Content-Type']
247
  end
248

  
249
  def test_wiki_pages_without_q_params_should_return_last_10_pages
250
    # There are 8 pages generated by fixtures
251
    # and we need three more to test the 10 limit
252
    wiki = Wiki.find_by(project: Project.first)
253
    3.times do |n|
254
      WikiPage.create(wiki: wiki, title: "test#{n}")
255
    end
256
    get :wiki_pages, params: { project_id: 'ecookbook' }
257

  
258
    assert_response :success
259
    json = ActiveSupport::JSON.decode(response.body)
260

  
261
    assert_equal 10, json.count
262
    assert_equal wiki.pages[-2].id, json.first['id'].to_i
263
  end
199 264
end
(3-3/6)