diff --git a/app/assets/javascripts/sitewide/atwho_users.js b/app/assets/javascripts/sitewide/atwho_users.js
deleted file mode 100644
index 29249b3b1..000000000
--- a/app/assets/javascripts/sitewide/atwho_users.js
+++ /dev/null
@@ -1,72 +0,0 @@
-(function() {
- 'use strict';
-
- $(document).on(
- 'focus',
- '[data-atwho-users-edit]',
- function() {
- if (_.isUndefined($(this).data('atwho'))) {
- $(this)
- .atwho({
- at: '@',
- callbacks: {
- remoteFilter: function(query, callback) {
- $.getJSON(
- '/organizations/1/atwho_users.json',
- {query: query},
- function(data) {
- callback(data.users);
- }
- );
- },
- tplEval: function(_tpl, map) {
- var res;
- try {
- res = '';
- res += '
';
- res += '
';
- res += ' ';
- res += '';
- res += map.full_name;
- res += '';
- res += ' ';
- res += '';
- res += ' ';
- res += '';
- res += map.email;
- res += '';
- res += '';
- } catch (_error) {
- res = '';
- }
- return res;
- },
- highlighter: function(li, query) {
- var li2 = $(li);
- li2.addClass('highlighted');
- var prevVal =
- li2
- .find('[data-full-name]')
- .html();
- var newVal =
- prevVal
- .replace(query, '' + query + '');
- li2.find('[data-full-name]').html(newVal);
- prevVal =
- li2
- .find('[data-email]')
- .html();
- newVal =
- prevVal
- .replace(query, '' + query + '');
- li2.find('[data-email]').html(newVal);
- return li2.html();
- }
- },
- insertTpl: '[${atwho-at}${full_name}~${id}]',
- limit: 5,
- startWithSpace: true
- });
- }
- });
-})();
diff --git a/app/assets/javascripts/sitewide/atwho_users.js.erb b/app/assets/javascripts/sitewide/atwho_users.js.erb
new file mode 100644
index 000000000..91adb1838
--- /dev/null
+++ b/app/assets/javascripts/sitewide/atwho_users.js.erb
@@ -0,0 +1,110 @@
+(function() {
+ 'use strict';
+
+ $(document).on(
+ 'focus',
+ '[data-atwho-users-edit]',
+ function() {
+ // Only initialize if URL is present and
+ // atwho is not initialized yet
+ if (
+ $(document.body).is('[data-atwho-users-url]') &&
+ _.isUndefined($(this).data('atwho'))
+ ) {
+ var dataUrl = $(document.body).attr('data-atwho-users-url');
+
+ $(this)
+ .atwho({
+ at: '@',
+ callbacks: {
+ remoteFilter: function(query, callback) {
+ $.getJSON(
+ dataUrl,
+ {query: query},
+ function(data) {
+ callback(data.users);
+ }
+ );
+ },
+ sorter: function(query, items, _searchKey) {
+ // Sorting is already done on server-side
+ return items;
+ },
+ tplEval: function(_tpl, map) {
+ var res;
+ try {
+ res = '';
+ res += '';
+ res += '
';
+ res += '';
+ res += map.full_name;
+ res += '';
+ res += '';
+ res += ' ';
+ res += '·';
+ res += ' ';
+ res += '';
+ res += map.email;
+ res += '';
+ res += '';
+ res += '';
+ } catch (_error) {
+ res = '';
+ }
+ return res;
+ },
+ highlighter: function(li, query) {
+ function highlight(el, sel, re) {
+ var prevVal = el.find(sel).html();
+ var newVal = prevVal.replace(re, '$&');
+ el.find(sel).html(newVal);
+ }
+
+ if (!query) {
+ return li;
+ }
+
+ var $li = $(li);
+ var re = new RegExp(query, 'gi');
+ highlight($li, '[data-val=full-name]', re);
+ highlight($li, '[data-val=email]', re);
+ return $li[0].outerHTML;
+ },
+ beforeInsert: function(value, li) {
+ var res = '';
+ res += '[@' + li.attr('data-full-name');
+ res += '~' + li.attr('data-id') + ']';
+ return res;
+ }
+ },
+ headerTpl:
+ '',
+ limit: <%= Constants::ATWHO_SEARCH_LIMIT %>,
+ startsWithSpace: true,
+ acceptSpaceBar: true
+ });
+ }
+ }
+ );
+})();
diff --git a/app/assets/stylesheets/constants.scss b/app/assets/stylesheets/constants.scss
index b887e7c14..136383817 100644
--- a/app/assets/stylesheets/constants.scss
+++ b/app/assets/stylesheets/constants.scss
@@ -25,6 +25,7 @@ $color-emperor: #555;
$color-mine-shaft: #333;
$color-nero: #262626;
$color-black: #000;
+$color-cloud: rgba(0, 0, 0, .1);
// Miscelaneous colors
$color-mystic: #eaeff2;
diff --git a/app/assets/stylesheets/themes/scinote.scss b/app/assets/stylesheets/themes/scinote.scss
index 34ffed6e9..9e4e8ae6f 100644
--- a/app/assets/stylesheets/themes/scinote.scss
+++ b/app/assets/stylesheets/themes/scinote.scss
@@ -1716,10 +1716,10 @@ th.custom-field .modal-tooltiptext {
margin-top: 18px;
background: $color-white;
color: $color-black;
- border: 1px solid #DDD;
+ border: 1px solid $color-emperor;
border-radius: 3px;
- box-shadow: 0 0 5px rgba(0,0,0,0.1);
- min-width: 120px;
+ box-shadow: 0 0 5px $color-cloud;
+ min-width: 520px;
max-height: 200px;
overflow: auto;
z-index: 11110 !important;
@@ -1756,13 +1756,54 @@ th.custom-field .modal-tooltiptext {
li {
display: block;
padding: 5px 10px;
- border-bottom: 1px solid #DDD;
+ border-bottom: 1px solid $color-emperor;
cursor: pointer;
}
}
}
//
+.atwho-header-user {
+ padding-top: 7px;
+ padding-bottom: 7px;
+ height: 34px;
+ background-color: $color-gallery;
+ border-bottom: 1px solid $color-emperor;
+ clear: both;
+
+ > div {
+ display: inline;
+ }
+
+ .title {
+ float: left;
+ margin-left: 15px;
+ }
+
+ .help {
+ float: right;
+
+ div {
+ display: inline;
+ margin-right: 15px;
+ font-size: smaller;
+ }
+
+ div strong {
+ color: $color-black;
+ }
+
+ .dismiss {
+ color: $color-emperor;
+ }
+
+ .dismiss:hover {
+ color: $color-black;
+ cursor: pointer;
+ }
+ }
+}
+
.atwho-li-user {
.avatar {
diff --git a/app/controllers/at_who_controller.rb b/app/controllers/at_who_controller.rb
index 6db7ba23f..2b4db9afa 100644
--- a/app/controllers/at_who_controller.rb
+++ b/app/controllers/at_who_controller.rb
@@ -10,8 +10,12 @@ class AtWhoController < ApplicationController
.limit(Constants::ATWHO_SEARCH_LIMIT)
.as_json
- # Add avatars, convert to JSON
+ # Add avatars, Base62, convert to JSON
res.each do |user_obj|
+ user_obj['full_name'] =
+ user_obj['full_name']
+ .truncate(Constants::NAME_TRUNCATION_LENGTH_DROPDOWN)
+ user_obj['id'] = user_obj['id'].base62_encode
user_obj['img_url'] = avatar_path(user_obj['id'], :icon_small)
end
diff --git a/app/models/organization.rb b/app/models/organization.rb
index 7efdf67a4..63c83cbd5 100644
--- a/app/models/organization.rb
+++ b/app/models/organization.rb
@@ -62,13 +62,13 @@ class Organization < ActiveRecord::Base
.strip
.gsub('_', '\\_')
.gsub('%', '\\%')
- .split(/\s+/)
- .map { |t| '%' + t + '%' }
else
a_query = query
end
- users.where_attributes_like(attributes, a_query)
+ users
+ .where.not(confirmed_at: nil)
+ .where_attributes_like(attributes, a_query)
end
# Writes to user log
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index cf6b352c0..61fbf0c6d 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -14,7 +14,12 @@
<%= csrf_meta_tags %>
-
+
+ data-atwho-users-url="<%= atwho_users_organization_path(current_organization) %>"
+ <% end %>
+ >
diff --git a/config/locales/en.yml b/config/locales/en.yml
index a4cfb68cf..f26b0c85d 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1451,6 +1451,16 @@ en:
assign_user_to_organization: "%{assigned_user} was added as %{role} to team %{organization} by %{assigned_by_user}."
unassign_user_from_organization: "%{unassigned_user} was removed from team %{organization} by %{unassigned_by_user}."
+ atwho:
+ users:
+ title: "People"
+ navigate_1: "up/down"
+ navigate_2: "to navigate"
+ confirm_1: "enter/tab"
+ confirm_2: "to confirm"
+ dismiss_1: "esc"
+ dismiss_2: "to dismiss"
+
# This section contains general words that can be used in any parts of
# application.
diff --git a/db/migrate/20170105162500_add_index_to_users_full_name.rb b/db/migrate/20170105162500_add_index_to_users_full_name.rb
new file mode 100644
index 000000000..539634924
--- /dev/null
+++ b/db/migrate/20170105162500_add_index_to_users_full_name.rb
@@ -0,0 +1,5 @@
+class AddIndexToUsersFullName < ActiveRecord::Migration
+ def change
+ add_index :users, :full_name
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1504e1b50..fb3e1e700 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20161125153600) do
+ActiveRecord::Schema.define(version: 20170105162500) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -639,20 +639,20 @@ ActiveRecord::Schema.define(version: 20161125153600) do
add_index "user_projects", ["user_id"], name: "index_user_projects_on_user_id", using: :btree
create_table "users", force: :cascade do |t|
- t.string "full_name", null: false
- t.string "initials", null: false
- t.string "email", default: "", null: false
- t.string "encrypted_password", default: "", null: false
+ t.string "full_name", null: false
+ t.string "initials", null: false
+ t.string "email", default: "", null: false
+ t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
- t.integer "sign_in_count", default: 0, null: false
+ t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
@@ -661,7 +661,7 @@ ActiveRecord::Schema.define(version: 20161125153600) do
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
- t.string "time_zone", default: "UTC"
+ t.string "time_zone", default: "UTC"
t.string "invitation_token"
t.datetime "invitation_created_at"
t.datetime "invitation_sent_at"
@@ -669,18 +669,21 @@ ActiveRecord::Schema.define(version: 20161125153600) do
t.integer "invitation_limit"
t.integer "invited_by_id"
t.string "invited_by_type"
- t.integer "invitations_count", default: 0
- t.integer "tutorial_status", default: 0, null: false
- t.boolean "assignments_notification", default: true
- t.boolean "recent_notification", default: true
- t.boolean "assignments_notification_email", default: false
- t.boolean "recent_notification_email", default: false
+ t.integer "invitations_count", default: 0
+ t.integer "tutorial_status", default: 0, null: false
+ t.boolean "assignments_notification", default: true
+ t.boolean "recent_notification", default: true
+ t.boolean "assignments_notification_email", default: false
+ t.boolean "recent_notification_email", default: false
t.integer "current_organization_id"
- t.boolean "system_message_notification_email", default: false
+ t.boolean "system_message_notification_email", default: false
+ t.string "authentication_token", limit: 30
end
+ add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
+ add_index "users", ["full_name"], name: "index_users_on_full_name", using: :btree
add_index "users", ["invitation_token"], name: "index_users_on_invitation_token", unique: true, using: :btree
add_index "users", ["invitations_count"], name: "index_users_on_invitations_count", using: :btree
add_index "users", ["invited_by_id"], name: "index_users_on_invited_by_id", using: :btree