mirror of
https://github.com/redmine/redmine.git
synced 2025-10-26 07:46:17 +01:00
Auto complete wiki page links (#33820).
Patch by Mizuki ISHIKAWA. git-svn-id: http://svn.redmine.org/redmine/trunk@20755 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -42,6 +42,24 @@ class AutoCompletesController < ApplicationController
|
||||
render :json => format_issues_json(issues)
|
||||
end
|
||||
|
||||
def wiki_pages
|
||||
q = params[:q].to_s.strip
|
||||
wiki = Wiki.find_by(project: @project)
|
||||
if wiki.nil? || !User.current.allowed_to?(:view_wiki_pages, @project)
|
||||
render json: []
|
||||
return
|
||||
end
|
||||
|
||||
scope = wiki.pages.reorder(id: :desc)
|
||||
wiki_pages =
|
||||
if q.present?
|
||||
scope.where("LOWER(#{WikiPage.table_name}.title) LIKE LOWER(?)", "%#{q}%").limit(10).to_a
|
||||
else
|
||||
scope.limit(10).to_a
|
||||
end
|
||||
render json: format_wiki_pages_json(wiki_pages)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_project
|
||||
@@ -61,4 +79,14 @@ class AutoCompletesController < ApplicationController
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def format_wiki_pages_json(wiki_pages)
|
||||
wiki_pages.map {|wiki_page|
|
||||
{
|
||||
'id' => wiki_page.id,
|
||||
'label' => wiki_page.title.to_s.truncate(255),
|
||||
'value' => wiki_page.title
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1804,7 +1804,8 @@ module ApplicationHelper
|
||||
|
||||
def autocomplete_data_sources(project)
|
||||
{
|
||||
issues: auto_complete_issues_path(:project_id => project, :q => '')
|
||||
issues: auto_complete_issues_path(:project_id => project, :q => ''),
|
||||
wiki_pages: auto_complete_wiki_pages_path(:project_id => project, :q => '')
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
@@ -46,8 +46,11 @@ Rails.application.routes.draw do
|
||||
post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
|
||||
post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
|
||||
|
||||
# Misc issue routes. TODO: move into resources
|
||||
# Auto complate routes
|
||||
match '/issues/auto_complete', :to => 'auto_completes#issues', :via => :get, :as => 'auto_complete_issues'
|
||||
match '/wiki_pages/auto_complete', :to => 'auto_completes#wiki_pages', :via => :get, :as => 'auto_complete_wiki_pages'
|
||||
|
||||
# Misc issue routes. TODO: move into resources
|
||||
match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
|
||||
match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
|
||||
match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
|
||||
|
||||
@@ -162,7 +162,7 @@ Redmine::AccessControl.map do |map|
|
||||
end
|
||||
|
||||
map.project_module :wiki do |map|
|
||||
map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index]}, :read => true
|
||||
map.permission :view_wiki_pages, {:wiki => [:index, :show, :special, :date_index], :auto_complete => [:wiki_pages]}, :read => true
|
||||
map.permission :view_wiki_edits, {:wiki => [:history, :diff, :annotate]}, :read => true
|
||||
map.permission :export_wiki_pages, {:wiki => [:export]}, :read => true
|
||||
map.permission :edit_wiki_pages, :wiki => [:new, :edit, :update, :preview, :add_attachment], :attachments => :upload
|
||||
|
||||
@@ -1113,24 +1113,45 @@ function inlineAutoComplete(element) {
|
||||
};
|
||||
|
||||
const tribute = new Tribute({
|
||||
trigger: '#',
|
||||
values: function (text, cb) {
|
||||
if (event.target.type === 'text' && $(element).attr('autocomplete') != 'off') {
|
||||
$(element).attr('autocomplete', 'off');
|
||||
collection: [
|
||||
{
|
||||
trigger: '#',
|
||||
values: function (text, cb) {
|
||||
if (event.target.type === 'text' && $(element).attr('autocomplete') != 'off') {
|
||||
$(element).attr('autocomplete', 'off');
|
||||
}
|
||||
remoteSearch(getDataSource('issues') + text, function (issues) {
|
||||
return cb(issues);
|
||||
});
|
||||
},
|
||||
lookup: 'label',
|
||||
fillAttr: 'label',
|
||||
requireLeadingSpace: true,
|
||||
selectTemplate: function (issue) {
|
||||
return '#' + issue.original.id;
|
||||
},
|
||||
noMatchTemplate: function () {
|
||||
return '<span style:"visibility: hidden;"></span>';
|
||||
}
|
||||
},
|
||||
{
|
||||
trigger: '[[',
|
||||
values: function (text, cb) {
|
||||
remoteSearch(getDataSource('wiki_pages') + text, function (wikiPages) {
|
||||
return cb(wikiPages);
|
||||
});
|
||||
},
|
||||
lookup: 'label',
|
||||
fillAttr: 'label',
|
||||
requireLeadingSpace: true,
|
||||
selectTemplate: function (wikiPage) {
|
||||
return '[[' + wikiPage.original.value + ']]';
|
||||
},
|
||||
noMatchTemplate: function () {
|
||||
return '<span style:"visibility: hidden;"></span>';
|
||||
}
|
||||
}
|
||||
remoteSearch(getDataSource('issues') + text, function (issues) {
|
||||
return cb(issues);
|
||||
});
|
||||
},
|
||||
lookup: 'label',
|
||||
fillAttr: 'label',
|
||||
requireLeadingSpace: true,
|
||||
selectTemplate: function (issue) {
|
||||
return '#' + issue.original.id;
|
||||
},
|
||||
noMatchTemplate: function () {
|
||||
return '<span style:"visibility: hidden;"></span>';
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
tribute.attach(element);
|
||||
|
||||
@@ -28,7 +28,8 @@ class AutoCompletesControllerTest < Redmine::ControllerTest
|
||||
:member_roles,
|
||||
:members,
|
||||
:enabled_modules,
|
||||
:journals, :journal_details
|
||||
:journals, :journal_details,
|
||||
:wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
|
||||
|
||||
def test_issues_should_not_be_case_sensitive
|
||||
get(
|
||||
@@ -196,4 +197,76 @@ class AutoCompletesControllerTest < Redmine::ControllerTest
|
||||
assert_equal 10, json.count
|
||||
assert_equal Issue.last.id, json.first['id'].to_i
|
||||
end
|
||||
|
||||
def test_wiki_pages_should_not_be_case_sensitive
|
||||
get(
|
||||
:wiki_pages,
|
||||
params: {
|
||||
project_id: 'ecookbook',
|
||||
q: 'pAgE'
|
||||
}
|
||||
)
|
||||
assert_response :success
|
||||
assert_include 'Page_with_an_inline_image', response.body
|
||||
end
|
||||
|
||||
def test_wiki_pages_should_return_json
|
||||
get(
|
||||
:wiki_pages,
|
||||
params: {
|
||||
project_id: 'ecookbook',
|
||||
q: 'Page_with_an_inline_image'
|
||||
}
|
||||
)
|
||||
assert_response :success
|
||||
json = ActiveSupport::JSON.decode(response.body)
|
||||
assert_kind_of Array, json
|
||||
issue = json.first
|
||||
assert_kind_of Hash, issue
|
||||
assert_equal 4, issue['id']
|
||||
assert_equal 'Page_with_an_inline_image', issue['value']
|
||||
assert_equal 'Page_with_an_inline_image', issue['label']
|
||||
end
|
||||
|
||||
def test_wiki_pages_without_view_wiki_pages_permission_should_not_return_pages
|
||||
Role.anonymous.remove_permission! :view_wiki_pages
|
||||
get :wiki_pages, params: { project_id: 'ecookbook', q: 'Page_with_an_inline_image' }
|
||||
|
||||
assert_response :success
|
||||
json = ActiveSupport::JSON.decode(response.body)
|
||||
assert_equal 0, json.count
|
||||
end
|
||||
|
||||
def test_wiki_pages_without_project_id_params_should_not_return_pages
|
||||
get :wiki_pages, params: { project_id: '' }
|
||||
assert_response :success
|
||||
json = ActiveSupport::JSON.decode(response.body)
|
||||
assert_equal 0, json.count
|
||||
end
|
||||
|
||||
def test_wiki_pages_should_return_json_content_type_response
|
||||
get(
|
||||
:wiki_pages,
|
||||
params: {
|
||||
project_id: 'ecookbook',
|
||||
q: 'Page_with_an_inline_image'
|
||||
}
|
||||
)
|
||||
assert_response :success
|
||||
assert_include 'application/json', response.headers['Content-Type']
|
||||
end
|
||||
|
||||
def test_wiki_pages_without_q_params_should_return_last_10_pages
|
||||
# There are 8 pages generated by fixtures
|
||||
# and we need three more to test the 10 limit
|
||||
wiki = Wiki.find_by(project: Project.first)
|
||||
3.times do |n|
|
||||
WikiPage.create(wiki: wiki, title: "test#{n}")
|
||||
end
|
||||
get :wiki_pages, params: { project_id: 'ecookbook' }
|
||||
assert_response :success
|
||||
json = ActiveSupport::JSON.decode(response.body)
|
||||
assert_equal 10, json.count
|
||||
assert_equal wiki.pages[-2].id, json.first['id'].to_i
|
||||
end
|
||||
end
|
||||
|
||||
@@ -24,7 +24,7 @@ class InlineAutocompleteSystemTest < ApplicationSystemTestCase
|
||||
:trackers, :projects_trackers, :enabled_modules, :issue_statuses, :issues,
|
||||
:enumerations, :custom_fields, :custom_values, :custom_fields_trackers,
|
||||
:watchers, :journals, :journal_details, :versions,
|
||||
:workflows
|
||||
:workflows, :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions
|
||||
|
||||
def test_inline_autocomplete_for_issues
|
||||
log_user('jsmith', 'jsmith')
|
||||
@@ -129,4 +129,26 @@ class InlineAutocompleteSystemTest < ApplicationSystemTestCase
|
||||
|
||||
page.has_css?('.tribute-container li', minimum: 1)
|
||||
end
|
||||
|
||||
def test_inline_autocompletion_of_wiki_page_links
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'issues/new'
|
||||
|
||||
fill_in 'Description', :with => '[['
|
||||
|
||||
within('.tribute-container') do
|
||||
assert page.has_text? 'Child_1_1'
|
||||
assert page.has_text? 'Page_with_sections'
|
||||
end
|
||||
|
||||
fill_in 'Description', :with => '[[page'
|
||||
within('.tribute-container') do
|
||||
assert page.has_text? 'Page_with_sections'
|
||||
assert page.has_text? 'Another_page'
|
||||
assert_not page.has_text? 'Child_1_1'
|
||||
|
||||
first('li').click
|
||||
end
|
||||
assert_equal '[[Page_with_sections]] ', find('#issue_description').value
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user