Merge pull request #3162 from NobodysNightmare/feature/autocomplete_scopes

Change auto completion scopes
pull/3168/merge
ulferts 9 years ago
commit c294fe90d4
  1. 6
      app/assets/javascripts/application.js.erb
  2. 2
      app/assets/stylesheets/content/_in_place_editing.lsg
  3. 62
      app/controllers/work_packages/auto_completes_controller.rb
  4. 3
      app/views/journals/_notes_form.html.erb
  5. 2
      app/views/messages/_form.html.erb
  6. 1
      app/views/news/_form.html.erb
  7. 3
      app/views/news/show.html.erb
  8. 1
      app/views/project_associations/_form.html.erb
  9. 7
      app/views/projects/form/attributes/_description.html.erb
  10. 8
      app/views/projects/form/attributes/_summary.html.erb
  11. 1
      app/views/reportings/edit.html.erb
  12. 1
      app/views/wiki/edit.html.erb
  13. 1
      app/views/wiki/new.html.erb
  14. 3
      app/views/work_packages/_edit.html.erb
  15. 3
      app/views/work_packages/_form.html.erb
  16. 3
      app/views/work_packages/bulk/edit.html.erb
  17. 3
      app/views/work_packages/moves/new.html.erb
  18. 4
      frontend/app/helpers/auto-complete-helper.js
  19. 2
      frontend/app/helpers/index.js
  20. 7
      frontend/app/helpers/path-helper.js
  21. 3
      frontend/app/templates/components/activity_comment.html
  22. 3
      frontend/app/templates/components/inplace_editor/editable/textarea.html
  23. 3
      frontend/app/templates/work_packages/inplace_editor/custom/editable/wiki_textarea.html
  24. 3
      frontend/app/templates/work_packages/tabs/_user_activity.html
  25. 3
      frontend/app/ui_components/activity-comment-directive.js
  26. 3
      frontend/app/work_packages/directives/inplace_editor/custom/editable/inplace-editor-wiki-textarea-directive.js
  27. 2
      frontend/app/work_packages/tabs/add-work-package-relation-directive.js
  28. 3
      frontend/app/work_packages/tabs/user-activity-directive.js
  29. 60
      spec/controllers/work_packages/auto_completes_controller_spec.rb

@ -344,7 +344,7 @@ function observeParentIssueField(url) {
updateElement: function(value) {
document.getElementById('issue_parent_id').value = value.id;
},
parameters: 'scope=all'
parameters: 'scope=relatable'
});
}
@ -358,7 +358,7 @@ function observeWorkPackageParentField(url) {
updateElement: function(value) {
document.getElementById('work_package_parent_id').value = value.id;
},
parameters: 'scope=all'
parameters: 'scope=relatable'
});
}
@ -372,7 +372,7 @@ function observeRelatedIssueField(url) {
updateElement: function(value) {
document.getElementById('relation_to_id').value = value.id;
},
parameters: 'scope=all'
parameters: 'scope=relatable'
});
}

@ -122,7 +122,7 @@
</div>
<div class="jstEditor">
<textarea wiki-toolbar="" class="focus-input inplace-edit--textarea " preview-toggle="togglePreview()" name="value" title="Description: Edit" data-wp_autocomplete_url="/work_packages/auto_complete.json?project_id=undefined" aria-multiline="true" tabindex="0" aria-hidden="false" aria-disabled="false" aria-invalid="false" rows="5">
<textarea wiki-toolbar="" class="focus-input inplace-edit--textarea " preview-toggle="togglePreview()" name="value" title="Description: Edit" aria-multiline="true" tabindex="0" aria-hidden="false" aria-disabled="false" aria-invalid="false" rows="5">
</textarea>
</div>

@ -30,43 +30,65 @@
require 'rack/utils'
class WorkPackages::AutoCompletesController < ApplicationController
before_filter :find_project
def index
@work_packages = []
q = params[:q].to_s
scope = determine_scope
if scope.nil?
render_404
return
end
if q.present?
query = (params[:scope] == 'all' && Setting.cross_project_work_package_relations?) ? WorkPackage : @project.work_packages
query_term = params[:q].to_s
@work_packages = []
# query for exact ID matches first, to make an exact match the first result of autocompletion
if query_term =~ /\A\d+\z/
@work_packages |= scope.visible.find_all_by_id(query_term.to_i)
end
@work_packages |= query.visible.find_all_by_id(q.to_i) if q =~ /\A\d+\z/
sql_query = ["LOWER(#{WorkPackage.table_name}.subject) LIKE :q OR
CAST(#{WorkPackage.table_name}.id AS CHAR(13)) LIKE :q",
{ q: "%#{query_term.downcase}%" }]
@work_packages |= query.visible.find(:all,
limit: 10,
order: "#{WorkPackage.table_name}.id ASC",
conditions: ["LOWER(#{WorkPackage.table_name}.subject) LIKE :q OR CAST(#{WorkPackage.table_name}.id AS CHAR(13)) LIKE :q", { q: "%#{q.downcase}%" }])
end
@work_packages |= scope.visible
.where(sql_query)
.order("#{WorkPackage.table_name}.id ASC") # :id does not work because...
.limit(10)
respond_to do |format|
format.html { render layout: false }
format.any(:xml, :json) { render request.format.to_sym => wp_hash_with_string }
format.any(:xml, :json) { render request.format.to_sym => wp_hashes_with_string }
end
end
private
def wp_hash_with_string
@work_packages.map do |wp|
Hash[wp.attributes.map do |key, value|
[key, Rack::Utils.escape_html(value)]
end << ['to_s', Rack::Utils.escape_html(wp.to_s)]]
def wp_hashes_with_string
@work_packages.map do |work_package|
wp_hash = Hash.new
work_package.attributes.each { |key, value| wp_hash[key] = Rack::Utils.escape_html(value) }
wp_hash['to_s'] = Rack::Utils.escape_html(work_package.to_s)
wp_hash
end
end
def find_project
project_id = (params[:work_package] && params[:work_package][:project_id]) || params[:project_id]
@project = Project.find(project_id)
return nil unless project_id
Project.find(project_id)
rescue ActiveRecord::RecordNotFound
render_404
nil
end
def determine_scope
project = find_project
if params[:scope] == 'relatable'
return nil unless project
Setting.cross_project_work_package_relations? ? WorkPackage.scoped : project.work_packages
elsif params[:scope] == 'all' || project.nil?
WorkPackage.scoped
else
project.work_packages
end
end
end

@ -35,8 +35,7 @@ See doc/COPYRIGHT.rdoc for more details.
<% rows = @journal.notes.blank? ? 10 : [[10, @journal.notes.length / 50].max, 100].min %>
<%= text_area_tag :notes, @journal.notes,
:class => 'wiki-edit',
:rows => rows,
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json) %>
:rows => rows %>
<%= javascript_tag("enable_textarea_auto_completion(jQuery('#notes'));") %>
<%= call_hook(:view_journals_notes_form_after_notes, { :journal => @journal}) %>

@ -45,7 +45,7 @@ See doc/COPYRIGHT.rdoc for more details.
</div>
<% end %>
<div class="form--field -required">
<%= f.text_area :content, required: true, label: l(:description_message_content), class: 'wiki-edit', data: {:'ng-non-bindable' => '' }, 'data-wp_autocomplete_url' => work_packages_auto_complete_path(project_id: @project, format: :json) %>
<%= f.text_area :content, required: true, label: l(:description_message_content), class: 'wiki-edit', data: {:'ng-non-bindable' => '' } %>
<%= wikitoolbar_for(replying ? 'reply_content' : 'message_content') %>
</div>
<%= render :partial => 'attachments/form' %>

@ -35,7 +35,6 @@ See doc/COPYRIGHT.rdoc for more details.
</div>
<div class="form--field">
<%= f.text_area :description, :required => true, :cols => 60, :rows => 15, :class => 'wiki-edit',
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json),
:'ng-non-bindable' => '' %>
</div>
<%= wikitoolbar_for 'news_description' %>

@ -95,8 +95,7 @@ See doc/COPYRIGHT.rdoc for more details.
<%= form_for([@news, Comment.new], :html => { :id => "add_comment_form", :style => "display:none;" }) do %>
<div class="box">
<%= label_tag 'comment_comments', Journal.human_attribute_name(:notes), :class => 'hidden-for-sighted' %>
<%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit',
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json) %>
<%= text_area 'comment', 'comments', :cols => 80, :rows => 15, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'comment_comments' %>
</div>
<p><%= submit_tag l(:button_add), class: 'button -highlight' %></p>

@ -28,7 +28,6 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<div class="form--field -required">
<%= f.text_area(:description, :class => 'timelines-project-association-description wiki-edit', :rows => 10,
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json),
:'ng-non-bindable' => '') %>
</div>
<%= wikitoolbar_for 'project_association_description' %>

@ -27,17 +27,10 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<%
project = form.object
autocomplete_path = work_packages_auto_complete_path(project_id: project,
format: :json)
%>
<div class="form--field">
<%= form.text_area :description,
rows: 5,
class: 'wiki-edit',
:'data-wp_autocomplete_url' => autocomplete_path,
:'ng-non-bindable' => '' %>
</div>
<%= wikitoolbar_for 'project_description' %>

@ -27,17 +27,9 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<%
project = form.object
autocomplete_path = work_packages_auto_complete_path(project_id: project,
format: :json)
%>
<div class="form--field">
<%= form.text_area :summary,
rows: 2,
class: 'wiki-edit',
:'data-wp_autocomplete_url' => autocomplete_path,
:'ng-non-bindable' => '' %>
</div>

@ -58,7 +58,6 @@ See doc/COPYRIGHT.rdoc for more details.
<div class="form--field">
<%= f.text_area(:reported_project_status_comment, :class => 'wiki-edit', :rows => 10,
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json),
:'ng-non-bindable' => '', label: Reporting.human_attribute_name(:reported_project_status_comment)) %>
</div>
<%= wikitoolbar_for 'reporting_reported_project_status_comment' %>

@ -33,7 +33,6 @@ See doc/COPYRIGHT.rdoc for more details.
<div class="form--field -required -vertical">
<%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit),
:value => format_text(@content, :text, :attachments => @content.page.attachments, :edit => true),
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json),
:'ng-non-bindable' => '' %>
</div>
<div class="form--field">

@ -49,7 +49,6 @@ See doc/COPYRIGHT.rdoc for more details.
</div>
<div class="form--field -required -vertical">
<%= f.text_area :text, :cols => 100, :rows => 25, :class => 'wiki-edit', :accesskey => accesskey(:edit),
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json),
:'ng-non-bindable' => '' %>
</div>

@ -57,8 +57,7 @@ See doc/COPYRIGHT.rdoc for more details.
<%= f.text_area :journal_notes, :cols => 60,
:rows => 10,
:class => 'wiki-edit',
:label => Journal.human_attribute_name(:notes),
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => project, :format => :json) %>
:label => Journal.human_attribute_name(:notes) %>
<%= wikitoolbar_for 'work_package_journal_notes' %>
</div>

@ -57,8 +57,7 @@ See doc/COPYRIGHT.rdoc for more details.
<%= f.text_field :subject, required: true %>
</div>
<div class="form--field -vertical">
<%= f.text_area :description, accesskey: accesskey(:edit), class: 'wiki-edit', rows: 10, :'ng-non-bindable' => '',
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(project_id: work_package.project, format: :json) %>
<%= f.text_area :description, accesskey: accesskey(:edit), class: 'wiki-edit', rows: 10, :'ng-non-bindable' => '' %>
</div>
<div id="attributes" class="attributes">

@ -140,8 +140,7 @@ See doc/COPYRIGHT.rdoc for more details.
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= Journal.human_attribute_name(:notes) %></legend>
<%= label_tag 'notes', Journal.human_attribute_name(:notes), :class => 'hidden-for-sighted' %>
<%= styled_text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit',
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @project, :format => :json) %>
<%= styled_text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'notes' %>
<p><%= send_notification_option %></p>
</fieldset>

@ -113,8 +113,7 @@ See doc/COPYRIGHT.rdoc for more details.
<fieldset class="form--fieldset">
<legend class="form--fieldset-legend"><%= Journal.human_attribute_name(:notes) %></legend>
<%= label_tag 'notes', Journal.human_attribute_name(:notes), :class => 'hidden-for-sighted' %>
<%= styled_text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit',
:'data-wp_autocomplete_url' => work_packages_auto_complete_path(:project_id => @target_project, :format => :json) %>
<%= styled_text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'notes' %>
</fieldset>
<%= call_hook(:view_work_packages_move_bottom, :work_packages => @work_packages, :target_project => @target_project, :copy => !!@copy) %>

@ -26,7 +26,7 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
module.exports = function($http) {
module.exports = function($http, PathHelper) {
var getAtWhoParameters = function(url) {
return {
at: '#',
@ -57,7 +57,7 @@ module.exports = function($http) {
return {
enableTextareaAutoCompletion: function(textareas) {
angular.forEach(textareas, function(textarea) {
var url = angular.element(textarea).data('wp_autocomplete_url');
var url = PathHelper.workPackageJsonAutoCompletePath();
if (url !== undefined && url.length > 0) {
angular.element(textarea).atwho(getAtWhoParameters(url));

@ -28,7 +28,7 @@
angular.module('openproject.helpers')
.constant('CUSTOM_FIELD_PREFIX', 'cf_')
.service('AutoCompleteHelper', ['$http', require('./auto-complete-helper')])
.service('AutoCompleteHelper', ['$http', 'PathHelper', require('./auto-complete-helper')])
.service('CustomFieldHelper', ['CUSTOM_FIELD_PREFIX', 'I18n', require(
'./custom-field-helper')])
.service('PathHelper', require('./path-helper'))

@ -126,9 +126,6 @@ module.exports = function() {
workPackagesBulkDeletePath: function() {
return PathHelper.workPackagesPath() + '/bulk';
},
workPackageAutoCompletePath: function(projectId, workPackageId) {
return '/work_packages/auto_complete?escape=false&id=' + workPackageId + '&project_id=' + projectId;
},
workPackageJsonAutoCompletePath: function() {
return '/work_packages/auto_complete.json';
},
@ -284,10 +281,6 @@ module.exports = function() {
staticWorkPackagesAutocompletePath: function(projectId) {
return PathHelper.staticBase + '/work_packages/auto_complete.json?project_id=' + projectId;
},
staticWorkPackageAutoCompletePath: function(projectId, workPackageId) {
return PathHelper.staticBase
+ PathHelper.workPackageAutoCompletePath(projectId, workPackageId);
},
staticProjectWikiPath: function(projectId) {
return PathHelper.staticProjectPath(projectId) + '/wiki';
},

@ -9,8 +9,7 @@
ng-model="$parent.activity.comment"
required
placeholder="{{ $parent.title }}"
ng-disabled="$parent.processingComment"
data-wp_autocomplete_url="{{ autocompletePath }}">
ng-disabled="$parent.processingComment">
</textarea>
<button class="button"
ng-click="$parent.createComment()"

@ -4,6 +4,5 @@
ng-disabled="fieldController.state.isBusy"
ng-required="editPaneController.isRequired"
ng-model="fieldController.writeValue.raw"
title="{{ fieldController.editTitle }}"
data-wp_autocomplete_url="{{ autocompletePath }}">
title="{{ fieldController.editTitle }}">
</textarea>

@ -8,8 +8,7 @@
ng-disabled="fieldController.state.isBusy"
ng-required="editPaneController.isRequired"
ng-model="fieldController.writeValue.raw"
title="{{ fieldController.editTitle }}"
data-wp_autocomplete_url="{{customEditorController.autocompletePath }}">
title="{{ fieldController.editTitle }}">
</textarea>
<div class="inplace-edit--preview" ng-if="customEditorController.isPreview && !fieldController.state.isBusy">
<span ng-bind-html="customEditorController.previewHtml"></span>

@ -33,8 +33,7 @@
ng-model="activity.props.comment.raw"
ng-bind-html="activity.props.comment.html"
rows="4"
required
data-wp_autocomplete_url="{{ autocompletePath }}">
required>
</textarea>
</div>
<div>

@ -38,8 +38,7 @@ module.exports = function($timeout,
require: '^?exclusiveEdit',
scope: {
workPackage: '=',
activities: '=',
autocompletePath: '@'
activities: '='
},
templateUrl: '/templates/components/activity_comment.html',
link: function(scope, element, attrs, exclusiveEditController) {

@ -37,8 +37,7 @@ module.exports = function(TextileService, EditableFieldsState, $sce, AutoComplet
controller: function($scope) {
this.isPreview = false;
this.previewHtml = '';
this.autocompletePath = '/work_packages/auto_complete.json?project_id=' +
EditableFieldsState.workPackage.embedded.project.props.id;
this.autocompletePath = '/work_packages/auto_complete.json';
this.togglePreview = function() {
this.isPreview = !this.isPreview;

@ -39,7 +39,7 @@ module.exports = function($http, PathHelper) {
}
var params = {
q: term,
scope: 'all',
scope: 'relatable',
escape: false,
id: scope.handler.workPackage.props.id,
'project_id': scope.handler.workPackage.embedded.project.props.id

@ -45,8 +45,7 @@ module.exports = function($uiViewScroll,
workPackage: '=',
activity: '=',
activityNo: '=',
inputElementId: '=',
autocompletePath: '@'
inputElementId: '='
},
link: function(scope, element, attrs, exclusiveEditController) {
exclusiveEditController.addEditable(scope);

@ -181,7 +181,7 @@ describe WorkPackages::AutoCompletesController, type: :controller do
end
end
describe '#cross_project_work_package_relations' do
describe 'in different projects' do
let(:project_2) {
FactoryGirl.create(:project,
parent: project)
@ -203,38 +203,74 @@ describe WorkPackages::AutoCompletesController, type: :controller do
work_package_4
end
context 'with scope all and cross project relations' do
let(:expected_values) { work_package_4 }
context 'with cross project relations' do
let(:project_id) { project.id }
before do
allow(Setting).to receive(:cross_project_work_package_relations?).and_return(true)
get :index,
project_id: project.id,
project_id: project_id,
q: work_package_4.id,
scope: 'all'
scope: scope
end
context 'with scope "relatable"' do
let(:scope) { 'relatable' }
let(:expected_values) { work_package_4 }
it_behaves_like 'successful response'
it_behaves_like 'contains expected values'
context 'without project_id' do
let(:project_id) { nil }
it 'returns HTTP Not Found' do
expect(response.status).to eql(404)
end
end
end
it_behaves_like 'successful response'
context 'with scope "all"' do
let(:scope) { 'all' }
let(:expected_values) { work_package_4 }
it_behaves_like 'successful response'
it_behaves_like 'contains expected values'
it_behaves_like 'contains expected values'
end
end
context 'with scope all but w/o cross project relations' do
context 'without cross project relations' do
before do
allow(Setting).to receive(:cross_project_work_package_relations?).and_return(false)
get :index,
project_id: project.id,
q: work_package_4.id,
scope: 'all'
scope: scope
end
it_behaves_like 'successful response'
context 'with scope "relatable"' do
let(:scope) { 'relatable' }
let(:expected_values) { work_package_4 }
it_behaves_like 'successful response'
subject { assigns(:work_packages) }
subject { assigns(:work_packages) }
it { is_expected.to eq([]) }
end
it { is_expected.to eq([]) }
context 'with scope "all"' do
let(:scope) { 'all' }
let(:expected_values) { work_package_4 }
it_behaves_like 'successful response'
it_behaves_like 'contains expected values'
end
end
end
end

Loading…
Cancel
Save