feature-33820-v4.patch

Mizuki ISHIKAWA, 2021-02-25 01:40

Download (10.2 KB)

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
    if wiki.nil? || !User.current.allowed_to?(:view_wiki_pages, @project)
49
      render json: []
50
      return
51
    end
52

  
53
    scope = wiki.pages.reorder(id: :desc)
54
    wiki_pages =
55
      if q.present?
56
        scope.where("LOWER(#{WikiPage.table_name}.title) LIKE LOWER(?)", "%#{q}%").limit(10).to_a
57
      else
58
        scope.limit(10).to_a
59
      end
60
    render json: format_wiki_pages_json(wiki_pages)
61
  end
62

  
45 63
  private
46 64

  
47 65
  def find_project
......
61 79
      }
62 80
    end
63 81
  end
82

  
83
  def format_wiki_pages_json(wiki_pages)
84
    wiki_pages.map {|wiki_page|
85
      {
86
        'id' => wiki_page.id,
87
        'label' => wiki_page.title.to_s.truncate(255),
88
        'value' => wiki_page.title
89
      }
90
    }
91
  end
64 92
end
app/helpers/application_helper.rb
1804 1804

  
1805 1805
  def autocomplete_data_sources(project)
1806 1806
    {
1807
      issues: auto_complete_issues_path(:project_id => project, :q => '')
1807
      issues: auto_complete_issues_path(:project_id => project, :q => ''),
1808
      wiki_pages: auto_complete_wiki_pages_path(:project_id => project, :q => '')
1808 1809
    }
1809 1810
  end
1810 1811

  
config/routes.rb
46 46
  post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
47 47
  post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
48 48

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

  
53
  # Misc issue routes. TODO: move into resources
51 54
  match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
52 55
  match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
53 56
  match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
lib/redmine.rb
162 162
  end
163 163

  
164 164
  map.project_module :wiki do |map|
165
    map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index]}, :read => true
165
    map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index], :auto_complete => [:wiki_pages]}, :read => true
166 166
    map.permission :view_wiki_edits, {:wiki => [:history, :diff, :annotate]}, :read => true
167 167
    map.permission :export_wiki_pages, {:wiki => [:export]}, :read => true
168 168
    map.permission :edit_wiki_pages, :wiki => [:new, :edit, :update, :preview, :add_attachment], :attachments => :upload
public/javascripts/application.js
1113 1113
    };
1114 1114

  
1115 1115
    const tribute = new Tribute({
1116
      trigger: '#',
1117
      values: function (text, cb) {
1118
        if (event.target.type === 'text' && $(element).attr('autocomplete') != 'off') {
1119
          $(element).attr('autocomplete', 'off');
1116
      collection: [
1117
        {
1118
          trigger: '#',
1119
          values: function (text, cb) {
1120
            if (event.target.type === 'text' && $(element).attr('autocomplete') != 'off') {
1121
              $(element).attr('autocomplete', 'off');
1122
            }
1123
            remoteSearch(getDataSource('issues') + text, function (issues) {
1124
              return cb(issues);
1125
            });
1126
          },
1127
          lookup: 'label',
1128
          fillAttr: 'label',
1129
          requireLeadingSpace: true,
1130
          selectTemplate: function (issue) {
1131
            return '#' + issue.original.id;
1132
          },
1133
          noMatchTemplate: function () {
1134
            return '<span style:"visibility: hidden;"></span>';
1135
          }
1136
        },
1137
        {
1138
          trigger: '[[',
1139
          values: function (text, cb) {
1140
            remoteSearch(getDataSource('wiki_pages') + text, function (wikiPages) {
1141
              return cb(wikiPages);
1142
            });
1143
          },
1144
          lookup: 'label',
1145
          fillAttr: 'label',
1146
          requireLeadingSpace: true,
1147
          selectTemplate: function (wikiPage) {
1148
            return '[[' + wikiPage.original.value + ']]';
1149
          },
1150
          noMatchTemplate: function () {
1151
            return '<span style:"visibility: hidden;"></span>';
1152
          }
1120 1153
        }
1121
        remoteSearch(getDataSource('issues') + text, function (issues) {
1122
          return cb(issues);
1123
        });
1124
      },
1125
      lookup: 'label',
1126
      fillAttr: 'label',
1127
      requireLeadingSpace: true,
1128
      selectTemplate: function (issue) {
1129
        return '#' + issue.original.id;
1130
      },
1131
      noMatchTemplate: function () {
1132
        return '<span style:"visibility: hidden;"></span>';
1133
      }
1154
      ]
1134 1155
    });
1135 1156

  
1136 1157
    tribute.attach(element);
test/functional/auto_completes_controller_test.rb
28 28
           :member_roles,
29 29
           :members,
30 30
           :enabled_modules,
31
           :journals, :journal_details
31
           :journals, :journal_details,
32
           :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
32 33

  
33 34
  def test_issues_should_not_be_case_sensitive
34 35
    get(
......
196 197
    assert_equal 10, json.count
197 198
    assert_equal Issue.last.id, json.first['id'].to_i
198 199
  end
200

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

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

  
231
  def test_wiki_pages_without_view_wiki_pages_permission_should_not_return_blank_array
232
    Role.anonymous.remove_permission! :view_wiki_pages
233
    get :wiki_pages, params: { project_id: 'ecookbook', q: 'Page_with_an_inline_image' }
234

  
235
    assert_response :success
236
    json = ActiveSupport::JSON.decode(response.body)
237
    assert_equal 0, json.count
238
  end
239

  
240
  def test_wiki_pages_without_project_id_params_should_not_return_blank_array
241
    get :wiki_pages, params: { project_id: '' }
242
    assert_response :success
243
    json = ActiveSupport::JSON.decode(response.body)
244
    assert_equal 0, json.count
245
  end
246

  
247
  def test_wiki_pages_should_return_json_content_type_response
248
    get(
249
      :wiki_pages,
250
      params: {
251
        project_id: 'ecookbook',
252
        q: 'Page_with_an_inline_image'
253
      }
254
    )
255
    assert_response :success
256
    assert_include 'application/json', response.headers['Content-Type']
257
  end
258

  
259
  def test_wiki_pages_without_q_params_should_return_last_10_pages
260
    # There are 8 pages generated by fixtures
261
    # and we need three more to test the 10 limit
262
    wiki = Wiki.find_by(project: Project.first)
263
    3.times do |n|
264
      WikiPage.create(wiki: wiki, title: "test#{n}")
265
    end
266
    get :wiki_pages, params: { project_id: 'ecookbook' }
267
    assert_response :success
268
    json = ActiveSupport::JSON.decode(response.body)
269
    assert_equal 10, json.count
270
    assert_equal wiki.pages[-2].id, json.first['id'].to_i
271
  end
199 272
end
test/system/inline_autocomplete_test.rb
24 24
           :trackers, :projects_trackers, :enabled_modules, :issue_statuses, :issues,
25 25
           :enumerations, :custom_fields, :custom_values, :custom_fields_trackers,
26 26
           :watchers, :journals, :journal_details, :versions,
27
           :workflows
27
           :workflows, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
28 28

  
29 29
  def test_inline_autocomplete_for_issues
30 30
    log_user('jsmith', 'jsmith')
......
129 129

  
130 130
    page.has_css?('.tribute-container li', minimum: 1)
131 131
  end
132

  
133
  def test_inline_autocompletion_of_wiki_page_links
134
    log_user('jsmith', 'jsmith')
135
    visit 'issues/new'
136

  
137
    fill_in 'Description', :with => '[['
138

  
139
    within('.tribute-container') do
140
      assert page.has_text? 'Child_1_1'
141
      assert page.has_text? 'Page_with_sections'
142
    end
143

  
144
    fill_in 'Description', :with => '[[page'
145
    within('.tribute-container') do
146
      assert page.has_text? 'Page_with_sections'
147
      assert page.has_text? 'Another_page'
148
      assert_not page.has_text? 'Child_1_1'
149

  
150
      first('li').click
151
    end
152
    assert_equal '[[Page_with_sections]] ', find('#issue_description').value
153
  end
132 154
end