Commit 65ff2c7a427bdc4417ac2cdab7f7c27a39890e99

Authored by Michel Felipe
Committed by Leandro Santos
1 parent 60716a4f

Each tags is defined in a task, store it in database. Added to the logged user l…

…ike a tagger of tasks. jQuery plugin 'inputosaurus.js' was customize and a pull request was made in your github repo
app/controllers/my_profile/tasks_controller.rb
@@ -41,33 +41,18 @@ class TasksController < MyProfileController @@ -41,33 +41,18 @@ class TasksController < MyProfileController
41 41
42 def close 42 def close
43 failed = {} 43 failed = {}
44 - save = false  
45 44
46 if params[:tasks] 45 if params[:tasks]
47 params[:tasks].each do |id, value| 46 params[:tasks].each do |id, value|
48 decision = value[:decision] 47 decision = value[:decision]
49 -  
50 - if value[:task].is_a?(Hash) && value[:task][:tag_list]  
51 - 48 + if request.post? && VALID_DECISIONS.include?(decision) && id && decision != 'skip'
52 task = profile.find_in_all_tasks(id) 49 task = profile.find_in_all_tasks(id)
53 - task.tag_list = value[:task][:tag_list]  
54 - value[:task].delete('tag_list')  
55 -  
56 - save = true  
57 - end  
58 -  
59 - if request.post?  
60 - if VALID_DECISIONS.include?(decision) && id && decision != 'skip'  
61 - task ||= profile.find_in_all_tasks(id)  
62 - begin  
63 - task.update_attributes(value[:task])  
64 - task.send(decision)  
65 - rescue Exception => ex  
66 - message = "#{task.title} (#{task.requestor ? task.requestor.name : task.author_name})"  
67 - failed[ex.message] ? failed[ex.message] << message : failed[ex.message] = [message]  
68 - end  
69 - elsif save  
70 - task.save! 50 + begin
  51 + task.update_attributes(value[:task])
  52 + task.send(decision)
  53 + rescue Exception => ex
  54 + message = "#{task.title} (#{task.requestor ? task.requestor.name : task.author_name})"
  55 + failed[ex.message] ? failed[ex.message] << message : failed[ex.message] = [message]
71 end 56 end
72 end 57 end
73 end 58 end
@@ -104,4 +89,24 @@ class TasksController &lt; MyProfileController @@ -104,4 +89,24 @@ class TasksController &lt; MyProfileController
104 @ticket = Ticket.find(:first, :conditions => ['(requestor_id = ? or target_id = ?) and id = ?', profile.id, profile.id, params[:id]]) 89 @ticket = Ticket.find(:first, :conditions => ['(requestor_id = ? or target_id = ?) and id = ?', profile.id, profile.id, params[:id]])
105 end 90 end
106 91
  92 + def save_tags
  93 +
  94 + if request.post? && params[:tag_list]
  95 + result = {
  96 + success: false
  97 + }
  98 +
  99 + ActsAsTaggableOn.remove_unused_tags = true
  100 +
  101 + task = Task.find_by_id params[:task_id]
  102 + save = user.tag(task, with: params[:tag_list], on: :tags)
  103 +
  104 + if save
  105 + result[:success] = true
  106 + end
  107 + end
  108 +
  109 + render json: result
  110 + end
  111 +
107 end 112 end
app/models/person.rb
@@ -16,6 +16,8 @@ class Person &lt; Profile @@ -16,6 +16,8 @@ class Person &lt; Profile
16 acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)} 16 acts_as_trackable :after_add => Proc.new {|p,t| notify_activity(t)}
17 acts_as_accessor 17 acts_as_accessor
18 18
  19 + acts_as_tagger
  20 +
19 scope :members_of, lambda { |resources| 21 scope :members_of, lambda { |resources|
20 resources = [resources] if !resources.kind_of?(Array) 22 resources = [resources] if !resources.kind_of?(Array)
21 conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ') 23 conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ')
app/views/tasks/_task.html.erb
@@ -64,7 +64,7 @@ @@ -64,7 +64,7 @@
64 64
65 <div class="formfieldline"> 65 <div class="formfieldline">
66 <div class="formfield tag-list-fields"> 66 <div class="formfield tag-list-fields">
67 - <%= labelled_text_field(_('Tags'),"tasks[#{task.id}][task][tag_list]", task.tag_list.to_s, :size => 36, :class => 'tag-list') %> 67 + <%= labelled_text_field(_('Tags'),"tasks[#{task.id}][task][tag_list]", task.tags_from(nil).to_s, :size => 36, :class => 'tag-list','data-submit-values'=>"{'task_id':'#{task.id}'}") %>
68 </div> 68 </div>
69 </div> 69 </div>
70 70
app/views/tasks/index.html.erb
@@ -75,7 +75,13 @@ @@ -75,7 +75,13 @@
75 <script> 75 <script>
76 jQuery('.tag-list').inputosaurus({ 76 jQuery('.tag-list').inputosaurus({
77 autoCompleteSource: <%= "'/myprofile/#{profile.identifier}/cms/search_tags'," %> 77 autoCompleteSource: <%= "'/myprofile/#{profile.identifier}/cms/search_tags'," %>
78 - activateFinalResult : true 78 + activateFinalResult: true,
  79 + submitTags: {
  80 + url: <%= "'/myprofile/#{profile.identifier}/tasks/save_tags'" %>,
  81 + success: function(response){
  82 + console.dir(response);
  83 + }
  84 + }
79 }) 85 })
80 </script> 86 </script>
81 87
public/javascripts/inputosaurus.js
1 /** 1 /**
2 - * Inputosaurus Text 2 + * Inputosaurus Text
3 * 3 *
4 * Must be instantiated on an <input> element 4 * Must be instantiated on an <input> element
5 * Allows multiple input items. Each item is represented with a removable tag that appears to be inside the input area. 5 * Allows multiple input items. Each item is represented with a removable tag that appears to be inside the input area.
@@ -30,7 +30,7 @@ @@ -30,7 +30,7 @@
30 // 30 //
31 // 'change' - triggered whenever a tag is added or removed (should be similar to binding the the change event of the instantiated input 31 // 'change' - triggered whenever a tag is added or removed (should be similar to binding the the change event of the instantiated input
32 // 'keyup' - keyup event on the newly created input 32 // 'keyup' - keyup event on the newly created input
33 - 33 +
34 // while typing, the user can separate values using these delimiters 34 // while typing, the user can separate values using these delimiters
35 // the value tags are created on the fly when an inputDelimiter is detected 35 // the value tags are created on the fly when an inputDelimiter is detected
36 inputDelimiters : [',', ';'], 36 inputDelimiters : [',', ';'],
@@ -56,12 +56,15 @@ @@ -56,12 +56,15 @@
56 // manipulate and return the input value after parseInput() parsing 56 // manipulate and return the input value after parseInput() parsing
57 // the array of tag names is passed and expected to be returned as an array after manipulation 57 // the array of tag names is passed and expected to be returned as an array after manipulation
58 parseHook : null, 58 parseHook : null,
59 - 59 +
60 // define a placeholder to display when the input is empty 60 // define a placeholder to display when the input is empty
61 placeholder: null, 61 placeholder: null,
62 - 62 +
63 // when you check for duplicates it check for the case 63 // when you check for duplicates it check for the case
64 - caseSensitiveDuplicates: false 64 + caseSensitiveDuplicates: false,
  65 +
  66 + //Ajax options to send tags
  67 + submitTags: null
65 }, 68 },
66 69
67 _create: function() { 70 _create: function() {
@@ -69,7 +72,7 @@ @@ -69,7 +72,7 @@
69 els = {}, 72 els = {},
70 o = widget.options, 73 o = widget.options,
71 placeholder = o.placeholder || this.element.attr('placeholder') || null; 74 placeholder = o.placeholder || this.element.attr('placeholder') || null;
72 - 75 +
73 this._chosenValues = []; 76 this._chosenValues = [];
74 77
75 // Create the elements 78 // Create the elements
@@ -77,11 +80,11 @@ @@ -77,11 +80,11 @@
77 els.input = $('<input type="text" />'); 80 els.input = $('<input type="text" />');
78 els.inputCont = $('<li class="inputosaurus-input inputosaurus-required"></li>'); 81 els.inputCont = $('<li class="inputosaurus-input inputosaurus-required"></li>');
79 els.origInputCont = $('<li class="inputosaurus-input-hidden inputosaurus-required">'); 82 els.origInputCont = $('<li class="inputosaurus-input-hidden inputosaurus-required">');
80 - 83 +
81 // define starting placeholder 84 // define starting placeholder
82 - if (placeholder) { 85 + if (placeholder) {
83 o.placeholder = placeholder; 86 o.placeholder = placeholder;
84 - els.input.attr('placeholder', o.placeholder); 87 + els.input.attr('placeholder', o.placeholder);
85 if (o.width) { 88 if (o.width) {
86 els.input.css('min-width', o.width - 50); 89 els.input.css('min-width', o.width - 50);
87 } 90 }
@@ -90,11 +93,11 @@ @@ -90,11 +93,11 @@
90 o.wrapperElement && o.wrapperElement.append(els.ul); 93 o.wrapperElement && o.wrapperElement.append(els.ul);
91 this.element.replaceWith(o.wrapperElement || els.ul); 94 this.element.replaceWith(o.wrapperElement || els.ul);
92 els.origInputCont.append(this.element).hide(); 95 els.origInputCont.append(this.element).hide();
93 - 96 +
94 els.inputCont.append(els.input); 97 els.inputCont.append(els.input);
95 els.ul.append(els.inputCont); 98 els.ul.append(els.inputCont);
96 els.ul.append(els.origInputCont); 99 els.ul.append(els.origInputCont);
97 - 100 +
98 o.width && els.ul.css('width', o.width); 101 o.width && els.ul.css('width', o.width);
99 102
100 this.elements = els; 103 this.elements = els;
@@ -130,8 +133,8 @@ @@ -130,8 +133,8 @@
130 var auto = $(this).data('ui-autocomplete') || $(this).data('autocomplete'); 133 var auto = $(this).data('ui-autocomplete') || $(this).data('autocomplete');
131 var menu = auto.menu, 134 var menu = auto.menu,
132 $menuItems; 135 $menuItems;
133 -  
134 - 136 +
  137 +
135 // zIndex will force the element on top of anything (like a dialog it's in) 138 // zIndex will force the element on top of anything (like a dialog it's in)
136 menu.element.zIndex && menu.element.zIndex($(this).zIndex() + 1); 139 menu.element.zIndex && menu.element.zIndex($(this).zIndex() + 1);
137 menu.element.width(widget.elements.ul.outerWidth()); 140 menu.element.width(widget.elements.ul.outerWidth());
@@ -241,7 +244,7 @@ @@ -241,7 +244,7 @@
241 244
242 widget.elements.input.width(txtWidth < maxWidth ? txtWidth : maxWidth); 245 widget.elements.input.width(txtWidth < maxWidth ? txtWidth : maxWidth);
243 }, 246 },
244 - 247 +
245 // resets placeholder on representative input 248 // resets placeholder on representative input
246 _resetPlaceholder: function () { 249 _resetPlaceholder: function () {
247 var placeholder = this.options.placeholder, 250 var placeholder = this.options.placeholder,
@@ -266,7 +269,7 @@ @@ -266,7 +269,7 @@
266 ev.preventDefault(); 269 ev.preventDefault();
267 lastTag.find('a').focus(); 270 lastTag.find('a').focus();
268 } 271 }
269 - 272 +
270 }, 273 },
271 274
272 _editTag : function(ev) { 275 _editTag : function(ev) {
@@ -285,6 +288,7 @@ @@ -285,6 +288,7 @@
285 v.key === tagKey && (tagName = v.value); 288 v.key === tagKey && (tagName = v.value);
286 }); 289 });
287 290
  291 + widget.beforeEditValue = widget.element.val();
288 widget.elements.input.val(tagName); 292 widget.elements.input.val(tagName);
289 293
290 widget._removeTag(ev); 294 widget._removeTag(ev);
@@ -295,7 +299,7 @@ @@ -295,7 +299,7 @@
295 var widget = ev.data.widget; 299 var widget = ev.data.widget;
296 switch(ev.which){ 300 switch(ev.which){
297 301
298 - case $.ui.keyCode.BACKSPACE: 302 + case $.ui.keyCode.BACKSPACE:
299 ev && ev.preventDefault(); 303 ev && ev.preventDefault();
300 ev && ev.stopPropagation(); 304 ev && ev.stopPropagation();
301 $(ev.currentTarget).trigger('click'); 305 $(ev.currentTarget).trigger('click');
@@ -409,11 +413,18 @@ @@ -409,11 +413,18 @@
409 }, 413 },
410 414
411 _setValue : function(value) { 415 _setValue : function(value) {
412 - var val = this.element.val(); 416 +
  417 + if(arguments[1] && (arguments[1].type == 'dblclick' && value == '')){
  418 + return false;
  419 + }
  420 + var val = this.element.val().replace(/,\s*/g,',');
413 421
414 if(val !== value){ 422 if(val !== value){
415 this.element.val(value); 423 this.element.val(value);
416 this._trigger('change'); 424 this._trigger('change');
  425 + if($.isPlainObject(this.options.submitTags)){
  426 + this._sendTags();
  427 + }
417 } 428 }
418 }, 429 },
419 430
@@ -439,7 +450,7 @@ @@ -439,7 +450,7 @@
439 }, 450 },
440 451
441 _removeTag : function(ev) { 452 _removeTag : function(ev) {
442 - var $closest = $(ev.currentTarget).closest('li'), 453 + var $closest = $(ev.currentTarget).closest('li'),
443 key = $closest.data('ui-inputosaurus') || $closest.data('inputosaurus'), 454 key = $closest.data('ui-inputosaurus') || $closest.data('inputosaurus'),
444 indexFound = false, 455 indexFound = false,
445 widget = (ev && ev.data.widget) || this; 456 widget = (ev && ev.data.widget) || this;
@@ -453,7 +464,7 @@ @@ -453,7 +464,7 @@
453 464
454 indexFound !== false && widget._chosenValues.splice(indexFound, 1); 465 indexFound !== false && widget._chosenValues.splice(indexFound, 1);
455 466
456 - widget._setValue(widget._buildValue()); 467 + widget._setValue(widget._buildValue(),ev);
457 468
458 $(ev.currentTarget).closest('li').remove(); 469 $(ev.currentTarget).closest('li').remove();
459 widget.elements.input.focus(); 470 widget.elements.input.focus();
@@ -477,7 +488,7 @@ @@ -477,7 +488,7 @@
477 var delim = this.options.outputDelimiter, 488 var delim = this.options.outputDelimiter,
478 val = this.element.val(), 489 val = this.element.val(),
479 values = []; 490 values = [];
480 - 491 +
481 values.push(val); 492 values.push(val);
482 delim && (values = val.split(delim)); 493 delim && (values = val.split(delim));
483 494
@@ -515,9 +526,37 @@ @@ -515,9 +526,37 @@
515 526
516 this.elements.ul.replaceWith(this.element); 527 this.elements.ul.replaceWith(this.element);
517 528
  529 + },
  530 +
  531 + _sendTags: function(){
  532 +
  533 + var requestTags = this.options.submitTags;
  534 + if(this.element.val() != '' && this.beforeEditValue != this.element.val()) {
  535 +
  536 + opts = $.extend({
  537 + url: requestTags.url,
  538 + dataType: 'json',
  539 + data:{},
  540 + type:'POST'
  541 + },requestTags);
  542 +
  543 + var extraValues = this.element.data('submit-values');
  544 + if(typeof extraValues == 'string' && /\{*/.test(extraValues)){
  545 + extraValues = $.parseJSON(extraValues.replace(/\'/g,'\"'));
  546 + }
  547 +
  548 + if(!$.isEmptyObject(extraValues)){
  549 + for(v in extraValues){
  550 + opts.data[v] = extraValues[v];
  551 + }
  552 + }
  553 +
  554 + opts.data.tag_list = this.element.val();
  555 +
  556 + $.ajax(opts);
  557 + }
518 } 558 }
519 }; 559 };
520 560
521 $.widget("ui.inputosaurus", inputosaurustext); 561 $.widget("ui.inputosaurus", inputosaurustext);
522 -})(jQuery);  
523 - 562 +})($ || jQuery);