Commit 0e570857957ddb3de09172751101754f95a4cb43

Authored by Dmitriy Zaporozhets
2 parents d1a5e370 ef05423f

Merge branch 'feature/select2-user-ajax'

@@ -113,6 +113,7 @@ group :assets do @@ -113,6 +113,7 @@ group :assets do
113 gem "therubyracer" 113 gem "therubyracer"
114 114
115 gem 'chosen-rails', "0.9.8" 115 gem 'chosen-rails', "0.9.8"
  116 + gem 'select2-rails'
116 gem 'jquery-atwho-rails', "0.1.7" 117 gem 'jquery-atwho-rails', "0.1.7"
117 gem "jquery-rails", "2.1.3" 118 gem "jquery-rails", "2.1.3"
118 gem "jquery-ui-rails", "2.0.2" 119 gem "jquery-ui-rails", "2.0.2"
@@ -384,6 +384,9 @@ GEM @@ -384,6 +384,9 @@ GEM
384 seed-fu (2.2.0) 384 seed-fu (2.2.0)
385 activerecord (~> 3.1) 385 activerecord (~> 3.1)
386 activesupport (~> 3.1) 386 activesupport (~> 3.1)
  387 + select2-rails (3.3.1)
  388 + sass-rails (>= 3.2)
  389 + thor (~> 0.14)
387 selenium-webdriver (2.30.0) 390 selenium-webdriver (2.30.0)
388 childprocess (>= 0.2.5) 391 childprocess (>= 0.2.5)
389 multi_json (~> 1.0) 392 multi_json (~> 1.0)
@@ -534,6 +537,7 @@ DEPENDENCIES @@ -534,6 +537,7 @@ DEPENDENCIES
534 sass-rails (~> 3.2.5) 537 sass-rails (~> 3.2.5)
535 sdoc 538 sdoc
536 seed-fu 539 seed-fu
  540 + select2-rails
537 settingslogic 541 settingslogic
538 shoulda-matchers (= 1.3.0) 542 shoulda-matchers (= 1.3.0)
539 sidekiq 543 sidekiq
app/assets/javascripts/application.js
@@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
17 //= require bootstrap 17 //= require bootstrap
18 //= require modernizr 18 //= require modernizr
19 //= require chosen-jquery 19 //= require chosen-jquery
  20 +//= require select2
20 //= require raphael 21 //= require raphael
21 //= require g.raphael-min 22 //= require g.raphael-min
22 //= require g.bar-min 23 //= require g.bar-min
app/assets/javascripts/md5.js 0 → 100644
@@ -0,0 +1,211 @@ @@ -0,0 +1,211 @@
  1 +function md5 (str) {
  2 + // http://kevin.vanzonneveld.net
  3 + // + original by: Webtoolkit.info (http://www.webtoolkit.info/)
  4 + // + namespaced by: Michael White (http://getsprink.com)
  5 + // + tweaked by: Jack
  6 + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  7 + // + input by: Brett Zamir (http://brett-zamir.me)
  8 + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  9 + // - depends on: utf8_encode
  10 + // * example 1: md5('Kevin van Zonneveld');
  11 + // * returns 1: '6e658d4bfcb59cc13f96c14450ac40b9'
  12 + var xl;
  13 +
  14 + var rotateLeft = function (lValue, iShiftBits) {
  15 + return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
  16 + };
  17 +
  18 + var addUnsigned = function (lX, lY) {
  19 + var lX4, lY4, lX8, lY8, lResult;
  20 + lX8 = (lX & 0x80000000);
  21 + lY8 = (lY & 0x80000000);
  22 + lX4 = (lX & 0x40000000);
  23 + lY4 = (lY & 0x40000000);
  24 + lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
  25 + if (lX4 & lY4) {
  26 + return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
  27 + }
  28 + if (lX4 | lY4) {
  29 + if (lResult & 0x40000000) {
  30 + return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
  31 + } else {
  32 + return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
  33 + }
  34 + } else {
  35 + return (lResult ^ lX8 ^ lY8);
  36 + }
  37 + };
  38 +
  39 + var _F = function (x, y, z) {
  40 + return (x & y) | ((~x) & z);
  41 + };
  42 + var _G = function (x, y, z) {
  43 + return (x & z) | (y & (~z));
  44 + };
  45 + var _H = function (x, y, z) {
  46 + return (x ^ y ^ z);
  47 + };
  48 + var _I = function (x, y, z) {
  49 + return (y ^ (x | (~z)));
  50 + };
  51 +
  52 + var _FF = function (a, b, c, d, x, s, ac) {
  53 + a = addUnsigned(a, addUnsigned(addUnsigned(_F(b, c, d), x), ac));
  54 + return addUnsigned(rotateLeft(a, s), b);
  55 + };
  56 +
  57 + var _GG = function (a, b, c, d, x, s, ac) {
  58 + a = addUnsigned(a, addUnsigned(addUnsigned(_G(b, c, d), x), ac));
  59 + return addUnsigned(rotateLeft(a, s), b);
  60 + };
  61 +
  62 + var _HH = function (a, b, c, d, x, s, ac) {
  63 + a = addUnsigned(a, addUnsigned(addUnsigned(_H(b, c, d), x), ac));
  64 + return addUnsigned(rotateLeft(a, s), b);
  65 + };
  66 +
  67 + var _II = function (a, b, c, d, x, s, ac) {
  68 + a = addUnsigned(a, addUnsigned(addUnsigned(_I(b, c, d), x), ac));
  69 + return addUnsigned(rotateLeft(a, s), b);
  70 + };
  71 +
  72 + var convertToWordArray = function (str) {
  73 + var lWordCount;
  74 + var lMessageLength = str.length;
  75 + var lNumberOfWords_temp1 = lMessageLength + 8;
  76 + var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
  77 + var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
  78 + var lWordArray = new Array(lNumberOfWords - 1);
  79 + var lBytePosition = 0;
  80 + var lByteCount = 0;
  81 + while (lByteCount < lMessageLength) {
  82 + lWordCount = (lByteCount - (lByteCount % 4)) / 4;
  83 + lBytePosition = (lByteCount % 4) * 8;
  84 + lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition));
  85 + lByteCount++;
  86 + }
  87 + lWordCount = (lByteCount - (lByteCount % 4)) / 4;
  88 + lBytePosition = (lByteCount % 4) * 8;
  89 + lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
  90 + lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
  91 + lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
  92 + return lWordArray;
  93 + };
  94 +
  95 + var wordToHex = function (lValue) {
  96 + var wordToHexValue = "",
  97 + wordToHexValue_temp = "",
  98 + lByte, lCount;
  99 + for (lCount = 0; lCount <= 3; lCount++) {
  100 + lByte = (lValue >>> (lCount * 8)) & 255;
  101 + wordToHexValue_temp = "0" + lByte.toString(16);
  102 + wordToHexValue = wordToHexValue + wordToHexValue_temp.substr(wordToHexValue_temp.length - 2, 2);
  103 + }
  104 + return wordToHexValue;
  105 + };
  106 +
  107 + var x = [],
  108 + k, AA, BB, CC, DD, a, b, c, d, S11 = 7,
  109 + S12 = 12,
  110 + S13 = 17,
  111 + S14 = 22,
  112 + S21 = 5,
  113 + S22 = 9,
  114 + S23 = 14,
  115 + S24 = 20,
  116 + S31 = 4,
  117 + S32 = 11,
  118 + S33 = 16,
  119 + S34 = 23,
  120 + S41 = 6,
  121 + S42 = 10,
  122 + S43 = 15,
  123 + S44 = 21;
  124 +
  125 + str = this.utf8_encode(str);
  126 + x = convertToWordArray(str);
  127 + a = 0x67452301;
  128 + b = 0xEFCDAB89;
  129 + c = 0x98BADCFE;
  130 + d = 0x10325476;
  131 +
  132 + xl = x.length;
  133 + for (k = 0; k < xl; k += 16) {
  134 + AA = a;
  135 + BB = b;
  136 + CC = c;
  137 + DD = d;
  138 + a = _FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
  139 + d = _FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
  140 + c = _FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
  141 + b = _FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
  142 + a = _FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
  143 + d = _FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
  144 + c = _FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
  145 + b = _FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
  146 + a = _FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
  147 + d = _FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
  148 + c = _FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
  149 + b = _FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
  150 + a = _FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
  151 + d = _FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
  152 + c = _FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
  153 + b = _FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
  154 + a = _GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
  155 + d = _GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
  156 + c = _GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
  157 + b = _GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
  158 + a = _GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
  159 + d = _GG(d, a, b, c, x[k + 10], S22, 0x2441453);
  160 + c = _GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
  161 + b = _GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
  162 + a = _GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
  163 + d = _GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
  164 + c = _GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
  165 + b = _GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
  166 + a = _GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
  167 + d = _GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
  168 + c = _GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
  169 + b = _GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
  170 + a = _HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
  171 + d = _HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
  172 + c = _HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
  173 + b = _HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
  174 + a = _HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
  175 + d = _HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
  176 + c = _HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
  177 + b = _HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
  178 + a = _HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
  179 + d = _HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
  180 + c = _HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
  181 + b = _HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
  182 + a = _HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
  183 + d = _HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
  184 + c = _HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
  185 + b = _HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
  186 + a = _II(a, b, c, d, x[k + 0], S41, 0xF4292244);
  187 + d = _II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
  188 + c = _II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
  189 + b = _II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
  190 + a = _II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
  191 + d = _II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
  192 + c = _II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
  193 + b = _II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
  194 + a = _II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
  195 + d = _II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
  196 + c = _II(c, d, a, b, x[k + 6], S43, 0xA3014314);
  197 + b = _II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
  198 + a = _II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
  199 + d = _II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
  200 + c = _II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
  201 + b = _II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
  202 + a = addUnsigned(a, AA);
  203 + b = addUnsigned(b, BB);
  204 + c = addUnsigned(c, CC);
  205 + d = addUnsigned(d, DD);
  206 + }
  207 +
  208 + var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
  209 +
  210 + return temp.toLowerCase();
  211 +}
app/assets/javascripts/users_select.js.coffee 0 → 100644
@@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
  1 +$ ->
  2 + userFormatResult = (user) ->
  3 + avatar = gon.gravatar_url
  4 + avatar = avatar.replace('%{hash}', md5(user.email))
  5 + avatar = avatar.replace('%{size}', '24')
  6 +
  7 + markup = "<div class='user-result'>"
  8 + markup += "<div class='user-image'><img class='avatar s24' src='" + avatar + "'></div>"
  9 + markup += "<div class='user-name'>" + user.name + "</div>"
  10 + markup += "<div class='user-username'>" + user.username + "</div>"
  11 + markup += "</div>"
  12 + markup
  13 +
  14 + userFormatSelection = (user) ->
  15 + user.name
  16 +
  17 + $('.ajax-users-select').select2
  18 + placeholder: "Search for a user"
  19 + multiple: $('.ajax-users-select').hasClass('multiselect')
  20 + minimumInputLength: 0
  21 + ajax: # instead of writing the function to execute the request we use Select2's convenient helper
  22 + url: "/api/" + gon.api_version + "/users.json"
  23 + dataType: "json"
  24 + data: (term, page) ->
  25 + search: term # search term
  26 + per_page: 10
  27 + private_token: gon.api_token
  28 +
  29 + results: (data, page) -> # parse the results into the format expected by Select2.
  30 + # since we are using custom formatting functions we do not need to alter remote JSON data
  31 + results: data
  32 +
  33 + initSelection: (element, callback) ->
  34 + id = $(element).val()
  35 + if id isnt ""
  36 + $.ajax(
  37 + "/api/" + gon.api_version + "/users/" + id + ".json",
  38 + dataType: "json"
  39 + data:
  40 + private_token: gon.api_token
  41 + ).done (data) ->
  42 + callback data
  43 +
  44 +
  45 + formatResult: userFormatResult # omitted for brevity, see the source of this page
  46 + formatSelection: userFormatSelection # omitted for brevity, see the source of this page
  47 + dropdownCssClass: "ajax-users-dropdown" # apply css that makes the dropdown taller
  48 + escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
  49 + m
  50 +
app/assets/javascripts/utf8_encode.js 0 → 100644
@@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
  1 +function utf8_encode (argString) {
  2 + // http://kevin.vanzonneveld.net
  3 + // + original by: Webtoolkit.info (http://www.webtoolkit.info/)
  4 + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  5 + // + improved by: sowberry
  6 + // + tweaked by: Jack
  7 + // + bugfixed by: Onno Marsman
  8 + // + improved by: Yves Sucaet
  9 + // + bugfixed by: Onno Marsman
  10 + // + bugfixed by: Ulrich
  11 + // + bugfixed by: Rafal Kukawski
  12 + // + improved by: kirilloid
  13 + // + bugfixed by: kirilloid
  14 + // * example 1: utf8_encode('Kevin van Zonneveld');
  15 + // * returns 1: 'Kevin van Zonneveld'
  16 +
  17 + if (argString === null || typeof argString === "undefined") {
  18 + return "";
  19 + }
  20 +
  21 + var string = (argString + ''); // .replace(/\r\n/g, "\n").replace(/\r/g, "\n");
  22 + var utftext = '',
  23 + start, end, stringl = 0;
  24 +
  25 + start = end = 0;
  26 + stringl = string.length;
  27 + for (var n = 0; n < stringl; n++) {
  28 + var c1 = string.charCodeAt(n);
  29 + var enc = null;
  30 +
  31 + if (c1 < 128) {
  32 + end++;
  33 + } else if (c1 > 127 && c1 < 2048) {
  34 + enc = String.fromCharCode(
  35 + (c1 >> 6) | 192,
  36 + ( c1 & 63) | 128
  37 + );
  38 + } else if (c1 & 0xF800 != 0xD800) {
  39 + enc = String.fromCharCode(
  40 + (c1 >> 12) | 224,
  41 + ((c1 >> 6) & 63) | 128,
  42 + ( c1 & 63) | 128
  43 + );
  44 + } else { // surrogate pairs
  45 + if (c1 & 0xFC00 != 0xD800) { throw new RangeError("Unmatched trail surrogate at " + n); }
  46 + var c2 = string.charCodeAt(++n);
  47 + if (c2 & 0xFC00 != 0xDC00) { throw new RangeError("Unmatched lead surrogate at " + (n-1)); }
  48 + c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
  49 + enc = String.fromCharCode(
  50 + (c1 >> 18) | 240,
  51 + ((c1 >> 12) & 63) | 128,
  52 + ((c1 >> 6) & 63) | 128,
  53 + ( c1 & 63) | 128
  54 + );
  55 + }
  56 + if (enc !== null) {
  57 + if (end > start) {
  58 + utftext += string.slice(start, end);
  59 + }
  60 + utftext += enc;
  61 + start = end = n + 1;
  62 + }
  63 + }
  64 +
  65 + if (end > start) {
  66 + utftext += string.slice(start, stringl);
  67 + }
  68 +
  69 + return utftext;
  70 +}
app/assets/stylesheets/application.scss
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 *= require jquery.ui.gitlab 5 *= require jquery.ui.gitlab
6 *= require jquery.atwho 6 *= require jquery.atwho
7 *= require chosen 7 *= require chosen
  8 + *= require select2
8 *= require_self 9 *= require_self
9 */ 10 */
10 11
@@ -14,7 +15,7 @@ @@ -14,7 +15,7 @@
14 @import "gitlab_bootstrap.scss"; 15 @import "gitlab_bootstrap.scss";
15 16
16 @import "common.scss"; 17 @import "common.scss";
17 -@import "ref_select.scss"; 18 +@import "selects.scss";
18 19
19 @import "sections/header.scss"; 20 @import "sections/header.scss";
20 @import "sections/nav.scss"; 21 @import "sections/nav.scss";
app/assets/stylesheets/common.scss
@@ -554,3 +554,4 @@ img.emoji { @@ -554,3 +554,4 @@ img.emoji {
554 .appear-data { 554 .appear-data {
555 display: none; 555 display: none;
556 } 556 }
  557 +
app/assets/stylesheets/ref_select.scss
@@ -1,90 +0,0 @@ @@ -1,90 +0,0 @@
1 -/** Branch/tag selector **/  
2 -.project-refs-form {  
3 - margin: 0;  
4 - span {  
5 - background:none !important;  
6 - position:static !important;  
7 - width:auto !important;  
8 - height:auto !important;  
9 - }  
10 -}  
11 -.project-refs-select {  
12 - width: 120px;  
13 -}  
14 -  
15 -.project-refs-form .chzn-container {  
16 - position: relative;  
17 - top: 0;  
18 - left: 0;  
19 - margin-right: 10px;  
20 -  
21 - .chzn-drop {  
22 - min-width: 400px;  
23 - .chzn-results {  
24 - max-height: 300px;  
25 - }  
26 - .chzn-search input {  
27 - min-width: 365px;  
28 - }  
29 - }  
30 -}  
31 -  
32 -/** Fix for Search Dropdown Border **/  
33 -.chzn-container {  
34 - .chzn-search {  
35 - input:focus {  
36 - @include box-shadow(none);  
37 - }  
38 - }  
39 -  
40 - .chzn-drop {  
41 - margin: 7px 0;  
42 - min-width: 200px;  
43 - border: 1px solid #bbb;  
44 - @include border-radius(0);  
45 -  
46 - .chzn-results {  
47 - margin-top: 5px;  
48 - max-height: 300px;  
49 -  
50 - .group-result {  
51 - color: $style_color;  
52 - border-bottom: 1px solid #EEE;  
53 - padding: 8px;  
54 - }  
55 - .active-result {  
56 - @include border-radius(0);  
57 -  
58 - &.highlighted {  
59 - background: $hover;  
60 - color: $style_color;  
61 - }  
62 - &.result-selected {  
63 - background: #EEE;  
64 - border-left: 4px solid #CCC;  
65 - }  
66 - }  
67 - }  
68 -  
69 - .chzn-search {  
70 - @include bg-gray-gradient;  
71 - input {  
72 - min-width: 165px;  
73 - border-color: #CCC;  
74 - }  
75 - }  
76 - }  
77 -  
78 - .chzn-single {  
79 - @include bg-light-gray-gradient;  
80 -  
81 - div {  
82 - background: transparent;  
83 - border-left: none;  
84 - }  
85 -  
86 - span {  
87 - font-weight: normal;  
88 - }  
89 - }  
90 -}  
app/assets/stylesheets/selects.scss 0 → 100644
@@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
  1 +.ajax-users-select {
  2 + width: 400px;
  3 +}
  4 +
  5 +.user-result {
  6 + .user-image {
  7 + float: left;
  8 + }
  9 + .user-name {
  10 + }
  11 + .user-username {
  12 + color: #999;
  13 + }
  14 +}
  15 +
  16 +.select2-no-results {
  17 + padding: 7px;
  18 + color: #666;
  19 +}
  20 +
  21 +/** Branch/tag selector **/
  22 +.project-refs-form {
  23 + margin: 0;
  24 + span {
  25 + background:none !important;
  26 + position:static !important;
  27 + width:auto !important;
  28 + height:auto !important;
  29 + }
  30 +}
  31 +.project-refs-select {
  32 + width: 120px;
  33 +}
  34 +
  35 +.project-refs-form .chzn-container {
  36 + position: relative;
  37 + top: 0;
  38 + left: 0;
  39 + margin-right: 10px;
  40 +
  41 + .chzn-drop {
  42 + min-width: 400px;
  43 + .chzn-results {
  44 + max-height: 300px;
  45 + }
  46 + .chzn-search input {
  47 + min-width: 365px;
  48 + }
  49 + }
  50 +}
  51 +
  52 +/** Fix for Search Dropdown Border **/
  53 +.chzn-container {
  54 + .chzn-search {
  55 + input:focus {
  56 + @include box-shadow(none);
  57 + }
  58 + }
  59 +
  60 + .chzn-drop {
  61 + margin: 7px 0;
  62 + min-width: 200px;
  63 + border: 1px solid #bbb;
  64 + @include border-radius(0);
  65 +
  66 + .chzn-results {
  67 + margin-top: 5px;
  68 + max-height: 300px;
  69 +
  70 + .group-result {
  71 + color: $style_color;
  72 + border-bottom: 1px solid #EEE;
  73 + padding: 8px;
  74 + }
  75 + .active-result {
  76 + @include border-radius(0);
  77 +
  78 + &.highlighted {
  79 + background: $hover;
  80 + color: $style_color;
  81 + }
  82 + &.result-selected {
  83 + background: #EEE;
  84 + border-left: 4px solid #CCC;
  85 + }
  86 + }
  87 + }
  88 +
  89 + .chzn-search {
  90 + @include bg-gray-gradient;
  91 + input {
  92 + min-width: 165px;
  93 + border-color: #CCC;
  94 + }
  95 + }
  96 + }
  97 +
  98 + .chzn-single {
  99 + @include bg-light-gray-gradient;
  100 +
  101 + div {
  102 + background: transparent;
  103 + border-left: none;
  104 + }
  105 +
  106 + span {
  107 + font-weight: normal;
  108 + }
  109 + }
  110 +}
app/controllers/application_controller.rb
@@ -152,5 +152,8 @@ class ApplicationController &lt; ActionController::Base @@ -152,5 +152,8 @@ class ApplicationController &lt; ActionController::Base
152 152
153 def add_gon_variables 153 def add_gon_variables
154 gon.default_issues_tracker = Project.issues_tracker.default_value 154 gon.default_issues_tracker = Project.issues_tracker.default_value
  155 + gon.api_version = Gitlab::API.version
  156 + gon.api_token = current_user.private_token if current_user
  157 + gon.gravatar_url = request.ssl? ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
155 end 158 end
156 end 159 end
app/controllers/team_members_controller.rb
@@ -16,7 +16,7 @@ class TeamMembersController &lt; ProjectResourceController @@ -16,7 +16,7 @@ class TeamMembersController &lt; ProjectResourceController
16 end 16 end
17 17
18 def create 18 def create
19 - users = User.where(id: params[:user_ids]) 19 + users = User.where(id: params[:user_ids].split(','))
20 20
21 @project.team << [users, params[:project_access]] 21 @project.team << [users, params[:project_access]]
22 22
app/controllers/teams/members_controller.rb
@@ -13,7 +13,7 @@ class Teams::MembersController &lt; Teams::ApplicationController @@ -13,7 +13,7 @@ class Teams::MembersController &lt; Teams::ApplicationController
13 13
14 def create 14 def create
15 unless params[:user_ids].blank? 15 unless params[:user_ids].blank?
16 - user_ids = params[:user_ids] 16 + user_ids = params[:user_ids].split(',')
17 access = params[:default_project_access] 17 access = params[:default_project_access]
18 is_admin = params[:group_admin] 18 is_admin = params[:group_admin]
19 user_team.add_members(user_ids, access, is_admin) 19 user_team.add_members(user_ids, access, is_admin)
app/helpers/application_helper.rb
@@ -169,4 +169,10 @@ module ApplicationHelper @@ -169,4 +169,10 @@ module ApplicationHelper
169 end 169 end
170 170
171 alias_method :url_to_image, :image_url 171 alias_method :url_to_image, :image_url
  172 +
  173 + def users_select_tag(id, opts = {})
  174 + css_class = "ajax-users-select"
  175 + css_class << " multiselect" if opts[:multiple]
  176 + hidden_field_tag(id, '', class: css_class)
  177 + end
172 end 178 end
app/models/user_team.rb
@@ -69,6 +69,9 @@ class UserTeam &lt; ActiveRecord::Base @@ -69,6 +69,9 @@ class UserTeam &lt; ActiveRecord::Base
69 end 69 end
70 70
71 def add_members(users, access, group_admin) 71 def add_members(users, access, group_admin)
  72 + # reject existing users
  73 + users.reject! { |id| member_ids.include?(id.to_i) }
  74 +
72 users.each do |user| 75 users.each do |user|
73 add_member(user, access, group_admin) 76 add_member(user, access, group_admin)
74 end 77 end
app/views/team_members/_form.html.haml
@@ -11,7 +11,8 @@ @@ -11,7 +11,8 @@
11 %h6 1. Choose people you want in the team 11 %h6 1. Choose people you want in the team
12 .clearfix 12 .clearfix
13 = f.label :user_ids, "People" 13 = f.label :user_ids, "People"
14 - .input= select_tag(:user_ids, options_from_collection_for_select(User.active.not_in_project(@project).alphabetically, :id, :name_with_username), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true}) 14 + .input
  15 + = users_select_tag(:user_ids, multiple: true)
15 16
16 %h6 2. Set access level for them 17 %h6 2. Set access level for them
17 .clearfix 18 .clearfix
app/views/teams/members/new.html.haml
@@ -20,7 +20,8 @@ @@ -20,7 +20,8 @@
20 %td= @team.admin?(member) ? "Admin" : "Member" 20 %td= @team.admin?(member) ? "Admin" : "Member"
21 %td 21 %td
22 %tr 22 %tr
23 - %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5' 23 + %td
  24 + = users_select_tag(:user_ids, multiple: true)
24 %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" } 25 %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
25 %td 26 %td
26 %span= check_box_tag :group_admin 27 %span= check_box_tag :group_admin
features/project/team_management.feature
@@ -11,6 +11,7 @@ Feature: Project Team management @@ -11,6 +11,7 @@ Feature: Project Team management
11 Then I should be able to see myself in team 11 Then I should be able to see myself in team
12 And I should see "Sam" in team list 12 And I should see "Sam" in team list
13 13
  14 + @javascript
14 Scenario: Add user to project 15 Scenario: Add user to project
15 Given I click link "New Team Member" 16 Given I click link "New Team Member"
16 And I select "Mike" as "Reporter" 17 And I select "Mike" as "Reporter"
features/steps/project/project_team_management.rb
@@ -2,6 +2,7 @@ class ProjectTeamManagement &lt; Spinach::FeatureSteps @@ -2,6 +2,7 @@ class ProjectTeamManagement &lt; Spinach::FeatureSteps
2 include SharedAuthentication 2 include SharedAuthentication
3 include SharedProject 3 include SharedProject
4 include SharedPaths 4 include SharedPaths
  5 + include Select2Helper
5 6
6 Then 'I should be able to see myself in team' do 7 Then 'I should be able to see myself in team' do
7 page.should have_content(@user.name) 8 page.should have_content(@user.name)
@@ -20,8 +21,9 @@ class ProjectTeamManagement &lt; Spinach::FeatureSteps @@ -20,8 +21,9 @@ class ProjectTeamManagement &lt; Spinach::FeatureSteps
20 21
21 And 'I select "Mike" as "Reporter"' do 22 And 'I select "Mike" as "Reporter"' do
22 user = User.find_by_name("Mike") 23 user = User.find_by_name("Mike")
  24 +
  25 + select2(user.id, from: "#user_ids", multiple: true)
23 within "#new_team_member" do 26 within "#new_team_member" do
24 - select "#{user.name} (#{user.username})", :from => "user_ids"  
25 select "Reporter", :from => "project_access" 27 select "Reporter", :from => "project_access"
26 end 28 end
27 click_button "Add users" 29 click_button "Add users"
features/steps/userteams/userteams.rb
@@ -2,238 +2,239 @@ class Userteams &lt; Spinach::FeatureSteps @@ -2,238 +2,239 @@ class Userteams &lt; Spinach::FeatureSteps
2 include SharedAuthentication 2 include SharedAuthentication
3 include SharedPaths 3 include SharedPaths
4 include SharedProject 4 include SharedProject
  5 + include Select2Helper
5 6
6 - When 'I do not have teams with me' do  
7 - UserTeam.with_member(current_user).destroy_all  
8 - end 7 + When 'I do not have teams with me' do
  8 + UserTeam.with_member(current_user).destroy_all
  9 + end
9 10
10 - Then 'I should see dashboard page without teams info block' do  
11 - page.has_no_css?(".teams-box").must_equal true  
12 - end 11 + Then 'I should see dashboard page without teams info block' do
  12 + page.has_no_css?(".teams-box").must_equal true
  13 + end
13 14
14 - When 'I have teams with my membership' do  
15 - team = create :user_team, owner: current_user  
16 - team.add_member(current_user, UserTeam.access_roles["Master"], true)  
17 - end 15 + When 'I have teams with my membership' do
  16 + team = create :user_team, owner: current_user
  17 + team.add_member(current_user, UserTeam.access_roles["Master"], true)
  18 + end
18 19
19 - Then 'I should see dashboard page with teams information block' do  
20 - page.should have_css(".teams-box")  
21 - end 20 + Then 'I should see dashboard page with teams information block' do
  21 + page.should have_css(".teams-box")
  22 + end
22 23
23 - When 'exist user teams' do  
24 - team = create :user_team  
25 - team.add_member(current_user, UserTeam.access_roles["Master"], true)  
26 - end 24 + When 'exist user teams' do
  25 + team = create :user_team
  26 + team.add_member(current_user, UserTeam.access_roles["Master"], true)
  27 + end
27 28
28 - And 'I click on "All teams" link' do  
29 - click_link("All Teams")  
30 - end 29 + And 'I click on "All teams" link' do
  30 + click_link("All Teams")
  31 + end
31 32
32 - Then 'I should see "All teams" page' do  
33 - current_path.should == teams_path  
34 - end 33 + Then 'I should see "All teams" page' do
  34 + current_path.should == teams_path
  35 + end
35 36
36 - And 'I should see exist teams in teams list' do  
37 - team = UserTeam.last  
38 - find_in_list(".teams_list tr", team).must_equal true  
39 - end 37 + And 'I should see exist teams in teams list' do
  38 + team = UserTeam.last
  39 + find_in_list(".teams_list tr", team).must_equal true
  40 + end
40 41
41 - When 'I click to "New team" link' do  
42 - click_link("New Team")  
43 - end 42 + When 'I click to "New team" link' do
  43 + click_link("New Team")
  44 + end
44 45
45 - And 'I submit form with new team info' do  
46 - fill_in 'name', with: 'gitlab' 46 + And 'I submit form with new team info' do
  47 + fill_in 'name', with: 'gitlab'
47 48
48 - fill_in 'user_team_description', with: 'team description'  
49 - click_button 'Create team'  
50 - end 49 + fill_in 'user_team_description', with: 'team description'
  50 + click_button 'Create team'
  51 + end
51 52
52 - And 'I should see newly created team' do  
53 - page.should have_content "gitlab"  
54 - page.should have_content "team description"  
55 - end 53 + And 'I should see newly created team' do
  54 + page.should have_content "gitlab"
  55 + page.should have_content "team description"
  56 + end
56 57
57 - Then 'I should be redirected to new team page' do  
58 - team = UserTeam.last  
59 - current_path.should == team_path(team)  
60 - end 58 + Then 'I should be redirected to new team page' do
  59 + team = UserTeam.last
  60 + current_path.should == team_path(team)
  61 + end
61 62
62 - When 'I have teams with projects and members' do  
63 - team = create :user_team, owner: current_user  
64 - @project = create :project  
65 - team.add_member(current_user, UserTeam.access_roles["Master"], true)  
66 - team.assign_to_project(@project, UserTeam.access_roles["Master"])  
67 - @event = create(:closed_issue_event, project: @project)  
68 - end 63 + When 'I have teams with projects and members' do
  64 + team = create :user_team, owner: current_user
  65 + @project = create :project
  66 + team.add_member(current_user, UserTeam.access_roles["Master"], true)
  67 + team.assign_to_project(@project, UserTeam.access_roles["Master"])
  68 + @event = create(:closed_issue_event, project: @project)
  69 + end
69 70
70 - When 'I visit team page' do  
71 - visit team_path(UserTeam.last)  
72 - end 71 + When 'I visit team page' do
  72 + visit team_path(UserTeam.last)
  73 + end
73 74
74 - Then 'I should see projects list' do  
75 - page.should have_css(".projects_box")  
76 - projects_box = find(".projects_box")  
77 - projects_box.should have_content(@project.name)  
78 - end 75 + Then 'I should see projects list' do
  76 + page.should have_css(".projects_box")
  77 + projects_box = find(".projects_box")
  78 + projects_box.should have_content(@project.name)
  79 + end
79 80
80 - And 'project from team has issues assigned to me' do  
81 - team = UserTeam.last  
82 - team.projects.each do |project|  
83 - project.issues << create(:issue, assignee: current_user)  
84 - end 81 + And 'project from team has issues assigned to me' do
  82 + team = UserTeam.last
  83 + team.projects.each do |project|
  84 + project.issues << create(:issue, assignee: current_user)
85 end 85 end
  86 + end
86 87
87 - When 'I visit team issues page' do  
88 - team = UserTeam.last  
89 - visit issues_team_path(team)  
90 - end 88 + When 'I visit team issues page' do
  89 + team = UserTeam.last
  90 + visit issues_team_path(team)
  91 + end
91 92
92 - Then 'I should see issues from this team assigned to me' do  
93 - team = UserTeam.last  
94 - team.projects.each do |project|  
95 - project.issues.assigned(current_user).each do |issue|  
96 - page.should have_content issue.title  
97 - end 93 + Then 'I should see issues from this team assigned to me' do
  94 + team = UserTeam.last
  95 + team.projects.each do |project|
  96 + project.issues.assigned(current_user).each do |issue|
  97 + page.should have_content issue.title
98 end 98 end
99 end 99 end
  100 + end
100 101
101 - Given 'I have team with projects and members' do  
102 - team = create :user_team, owner: current_user  
103 - project = create :project  
104 - user = create :user  
105 - team.add_member(current_user, UserTeam.access_roles["Master"], true)  
106 - team.add_member(user, UserTeam.access_roles["Developer"], false)  
107 - team.assign_to_project(project, UserTeam.access_roles["Master"])  
108 - end 102 + Given 'I have team with projects and members' do
  103 + team = create :user_team, owner: current_user
  104 + project = create :project
  105 + user = create :user
  106 + team.add_member(current_user, UserTeam.access_roles["Master"], true)
  107 + team.add_member(user, UserTeam.access_roles["Developer"], false)
  108 + team.assign_to_project(project, UserTeam.access_roles["Master"])
  109 + end
109 110
110 - Given 'project from team has issues assigned to teams members' do  
111 - team = UserTeam.last  
112 - team.projects.each do |project|  
113 - team.members.each do |member|  
114 - project.issues << create(:issue, assignee: member)  
115 - end 111 + Given 'project from team has issues assigned to teams members' do
  112 + team = UserTeam.last
  113 + team.projects.each do |project|
  114 + team.members.each do |member|
  115 + project.issues << create(:issue, assignee: member)
116 end 116 end
117 end 117 end
  118 + end
118 119
119 - Then 'I should see issues from this team assigned to teams members' do  
120 - team = UserTeam.last  
121 - team.projects.each do |project|  
122 - team.members.each do |member|  
123 - project.issues.assigned(member).each do |issue|  
124 - page.should have_content issue.title  
125 - end 120 + Then 'I should see issues from this team assigned to teams members' do
  121 + team = UserTeam.last
  122 + team.projects.each do |project|
  123 + team.members.each do |member|
  124 + project.issues.assigned(member).each do |issue|
  125 + page.should have_content issue.title
126 end 126 end
127 end 127 end
128 end 128 end
  129 + end
129 130
130 - Given 'project from team has merge requests assigned to me' do  
131 - team = UserTeam.last  
132 - team.projects.each do |project|  
133 - team.members.each do |member|  
134 - 3.times { project.merge_requests << create(:merge_request, assignee: member) }  
135 - end 131 + Given 'project from team has merge requests assigned to me' do
  132 + team = UserTeam.last
  133 + team.projects.each do |project|
  134 + team.members.each do |member|
  135 + 3.times { project.merge_requests << create(:merge_request, assignee: member) }
136 end 136 end
137 end 137 end
  138 + end
138 139
139 - When 'I visit team merge requests page' do  
140 - team = UserTeam.last  
141 - visit merge_requests_team_path(team)  
142 - end 140 + When 'I visit team merge requests page' do
  141 + team = UserTeam.last
  142 + visit merge_requests_team_path(team)
  143 + end
143 144
144 - Then 'I should see merge requests from this team assigned to me' do  
145 - team = UserTeam.last  
146 - team.projects.each do |project|  
147 - team.members.each do |member|  
148 - project.issues.assigned(member).each do |merge_request|  
149 - page.should have_content merge_request.title  
150 - end 145 + Then 'I should see merge requests from this team assigned to me' do
  146 + team = UserTeam.last
  147 + team.projects.each do |project|
  148 + team.members.each do |member|
  149 + project.issues.assigned(member).each do |merge_request|
  150 + page.should have_content merge_request.title
151 end 151 end
152 end 152 end
153 end 153 end
  154 + end
154 155
155 - Given 'project from team has merge requests assigned to team members' do  
156 - team = UserTeam.last  
157 - team.projects.each do |project|  
158 - team.members.each do |member|  
159 - 3.times { project.merge_requests << create(:merge_request, assignee: member) }  
160 - end 156 + Given 'project from team has merge requests assigned to team members' do
  157 + team = UserTeam.last
  158 + team.projects.each do |project|
  159 + team.members.each do |member|
  160 + 3.times { project.merge_requests << create(:merge_request, assignee: member) }
161 end 161 end
162 end 162 end
  163 + end
163 164
164 - Then 'I should see merge requests from this team assigned to me' do  
165 - team = UserTeam.last  
166 - team.projects.each do |project|  
167 - team.members.each do |member|  
168 - project.issues.assigned(member).each do |merge_request|  
169 - page.should have_content merge_request.title  
170 - end 165 + Then 'I should see merge requests from this team assigned to me' do
  166 + team = UserTeam.last
  167 + team.projects.each do |project|
  168 + team.members.each do |member|
  169 + project.issues.assigned(member).each do |merge_request|
  170 + page.should have_content merge_request.title
171 end 171 end
172 end 172 end
173 end 173 end
  174 + end
174 175
175 - Given 'I have new user "John"' do  
176 - create :user, name: "John"  
177 - end 176 + Given 'I have new user "John"' do
  177 + create :user, name: "John"
  178 + end
178 179
179 - When 'I visit team people page' do  
180 - team = UserTeam.last  
181 - visit team_members_path(team)  
182 - end 180 + When 'I visit team people page' do
  181 + team = UserTeam.last
  182 + visit team_members_path(team)
  183 + end
183 184
184 - And 'I select user "John" from list with role "Reporter"' do  
185 - user = User.find_by_name("John")  
186 - within "#team_members" do  
187 - select "#{user.name} (#{user.username})", from: "user_ids"  
188 - select "Reporter", from: "default_project_access"  
189 - end  
190 - click_button "Add" 185 + And 'I select user "John" from list with role "Reporter"' do
  186 + user = User.find_by_name("John")
  187 + select2(user.id, from: "#user_ids", multiple: true)
  188 + within "#team_members" do
  189 + select "Reporter", from: "default_project_access"
191 end 190 end
  191 + click_button "Add"
  192 + end
192 193
193 - Then 'I should see user "John" in team list' do  
194 - user = User.find_by_name("John")  
195 - team_members_list = find(".team-table")  
196 - team_members_list.should have_content user.name  
197 - end 194 + Then 'I should see user "John" in team list' do
  195 + user = User.find_by_name("John")
  196 + team_members_list = find(".team-table")
  197 + team_members_list.should have_content user.name
  198 + end
198 199
199 - And 'I have my own project without teams' do  
200 - @project = create :project, namespace: current_user.namespace  
201 - end 200 + And 'I have my own project without teams' do
  201 + @project = create :project, namespace: current_user.namespace
  202 + end
202 203
203 - And 'I visit my team page' do  
204 - team = UserTeam.where(owner_id: current_user.id).last  
205 - visit team_path(team)  
206 - end 204 + And 'I visit my team page' do
  205 + team = UserTeam.where(owner_id: current_user.id).last
  206 + visit team_path(team)
  207 + end
207 208
208 - When 'I click on link "Projects"' do  
209 - click_link "Projects"  
210 - end 209 + When 'I click on link "Projects"' do
  210 + click_link "Projects"
  211 + end
211 212
212 - And 'I click link "Assign project to Team"' do  
213 - click_link "Assign project to Team"  
214 - end 213 + And 'I click link "Assign project to Team"' do
  214 + click_link "Assign project to Team"
  215 + end
215 216
216 - Then 'I should see form with my own project in avaliable projects list' do  
217 - projects_select = find("#project_ids")  
218 - projects_select.should have_content(@project.name)  
219 - end 217 + Then 'I should see form with my own project in avaliable projects list' do
  218 + projects_select = find("#project_ids")
  219 + projects_select.should have_content(@project.name)
  220 + end
220 221
221 - When 'I submit form with selected project and max access' do  
222 - within "#assign_projects" do  
223 - select @project.name_with_namespace, from: "project_ids"  
224 - select "Reporter", from: "greatest_project_access"  
225 - end  
226 - click_button "Add" 222 + When 'I submit form with selected project and max access' do
  223 + within "#assign_projects" do
  224 + select @project.name_with_namespace, from: "project_ids"
  225 + select "Reporter", from: "greatest_project_access"
227 end 226 end
  227 + click_button "Add"
  228 + end
228 229
229 - Then 'I should see my own project in team projects list' do  
230 - projects = find(".projects-table")  
231 - projects.should have_content(@project.name)  
232 - end 230 + Then 'I should see my own project in team projects list' do
  231 + projects = find(".projects-table")
  232 + projects.should have_content(@project.name)
  233 + end
233 234
234 - When 'I click link "New Team Member"' do  
235 - click_link "New Team Member"  
236 - end 235 + When 'I click link "New Team Member"' do
  236 + click_link "New Team Member"
  237 + end
237 238
238 protected 239 protected
239 240
@@ -257,5 +258,4 @@ class Userteams &lt; Spinach::FeatureSteps @@ -257,5 +258,4 @@ class Userteams &lt; Spinach::FeatureSteps
257 end 258 end
258 entered 259 entered
259 end 260 end
260 -  
261 end 261 end
features/support/env.rb
@@ -14,7 +14,7 @@ require &#39;spinach/capybara&#39; @@ -14,7 +14,7 @@ require &#39;spinach/capybara&#39;
14 require 'sidekiq/testing/inline' 14 require 'sidekiq/testing/inline'
15 15
16 16
17 -%w(stubbed_repository valid_commit).each do |f| 17 +%w(stubbed_repository valid_commit select2_helper).each do |f|
18 require Rails.root.join('spec', 'support', f) 18 require Rails.root.join('spec', 'support', f)
19 end 19 end
20 20
features/teams/team.feature
@@ -46,6 +46,7 @@ Feature: UserTeams @@ -46,6 +46,7 @@ Feature: UserTeams
46 When I visit team merge requests page 46 When I visit team merge requests page
47 Then I should see merge requests from this team assigned to me 47 Then I should see merge requests from this team assigned to me
48 48
  49 + @javascript
49 Scenario: I should add user to projects in Team 50 Scenario: I should add user to projects in Team
50 Given I have team with projects and members 51 Given I have team with projects and members
51 Given I have new user "John" 52 Given I have new user "John"
lib/api/users.rb
@@ -9,7 +9,8 @@ module Gitlab @@ -9,7 +9,8 @@ module Gitlab
9 # Example Request: 9 # Example Request:
10 # GET /users 10 # GET /users
11 get do 11 get do
12 - @users = paginate User 12 + @users = User.scoped
  13 + @users = @users.search(params[:search]) if params[:search].present?
13 present @users, with: Entities::User 14 present @users, with: Entities::User
14 end 15 end
15 16
spec/support/select2_helper.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +# Select2 ajax programatic helper
  2 +# It allows you to select value from select2
  3 +#
  4 +# Params
  5 +# value - real value of selected item
  6 +# opts - options containing css selector
  7 +#
  8 +# Usage:
  9 +#
  10 +# select2(2, from: '#user_ids')
  11 +#
  12 +
  13 +module Select2Helper
  14 + def select2(value, options={})
  15 + raise "Must pass a hash containing 'from'" if not options.is_a?(Hash) or not options.has_key?(:from)
  16 +
  17 + selector = options[:from]
  18 +
  19 + if options[:multiple]
  20 + page.execute_script("$('#{selector}').select2('val', ['#{value}']);")
  21 + else
  22 + page.execute_script("$('#{selector}').select2('val', '#{value}');")
  23 + end
  24 + end
  25 +end