From a78bb796184690a8af1d259ca06b157904b50662 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Thu, 5 Jan 2017 11:00:00 +0100 Subject: [PATCH 1/6] Temp commit --- .../javascripts/sitewide/atwho_users.js | 65 ++++++++++++++----- app/controllers/at_who_controller.rb | 4 +- app/models/organization.rb | 2 - 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/sitewide/atwho_users.js b/app/assets/javascripts/sitewide/atwho_users.js index 29249b3b1..941a65023 100644 --- a/app/assets/javascripts/sitewide/atwho_users.js +++ b/app/assets/javascripts/sitewide/atwho_users.js @@ -10,6 +10,23 @@ .atwho({ at: '@', callbacks: { + matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) { + var _a, _y, match, regexp, space; + flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + if (should_startWithSpace) { + flag = '(?:^|\\s)' + flag; + } + _a = decodeURI("%C3%80"); + _y = decodeURI("%C3%BF"); + space = ' \xa0'; // Use space... + regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi'); + match = regexp.exec(subtext); + if (match) { + return match[2] || match[1] + } else { + return null; + } + }, remoteFilter: function(query, callback) { $.getJSON( '/organizations/1/atwho_users.json', @@ -19,21 +36,28 @@ } ); }, + 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 += ''; + res += '
  • '; + res += ''; + res += ''; res += map.full_name; res += ''; + res += ''; res += ' '; - res += ''; + res += '·'; res += ' '; - res += ''; + res += ''; res += map.email; + res += ''; res += ''; res += '
  • '; } catch (_error) { @@ -42,30 +66,39 @@ return res; }, highlighter: function(li, query) { + if (!query) { + return li; + } + var li2 = $(li); - li2.addClass('highlighted'); + var re = new RegExp(query, 'gi'); var prevVal = li2 - .find('[data-full-name]') + .find('[data-val=full-name]') .html(); var newVal = prevVal - .replace(query, '' + query + ''); - li2.find('[data-full-name]').html(newVal); + .replace(re, '$&'); + li2.find('[data-val=full-name]').html(newVal); prevVal = li2 - .find('[data-email]') + .find('[data-val=email]') .html(); newVal = prevVal - .replace(query, '' + query + ''); - li2.find('[data-email]').html(newVal); - return li2.html(); + .replace(re, '$&'); + li2.find('[data-val=email]').html(newVal); + return li2[0].outerHTML; + }, + beforeInsert: function(value, li) { + var res = ''; + res += '[@' + li.attr('data-full-name'); + res += '~' + li.attr('data-id') + ']'; + return res; } }, - insertTpl: '[${atwho-at}${full_name}~${id}]', limit: 5, - startWithSpace: true + startsWithSpace: true }); } }); diff --git a/app/controllers/at_who_controller.rb b/app/controllers/at_who_controller.rb index 9602f749f..a93541890 100644 --- a/app/controllers/at_who_controller.rb +++ b/app/controllers/at_who_controller.rb @@ -10,8 +10,10 @@ 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..cdda44e70 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -62,8 +62,6 @@ class Organization < ActiveRecord::Base .strip .gsub('_', '\\_') .gsub('%', '\\%') - .split(/\s+/) - .map { |t| '%' + t + '%' } else a_query = query end From b614ce714b93c789e4e3282c972f53b779fb3980 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Thu, 5 Jan 2017 14:06:48 +0100 Subject: [PATCH 2/6] Restyle atwho dropdown --- .../{atwho_users.js => atwho_users.js.erb} | 42 ++++++++++--------- app/assets/stylesheets/constants.scss | 1 + app/assets/stylesheets/themes/scinote.scss | 40 ++++++++++++++++-- config/locales/en.yml | 10 +++++ 4 files changed, 69 insertions(+), 24 deletions(-) rename app/assets/javascripts/sitewide/{atwho_users.js => atwho_users.js.erb} (73%) diff --git a/app/assets/javascripts/sitewide/atwho_users.js b/app/assets/javascripts/sitewide/atwho_users.js.erb similarity index 73% rename from app/assets/javascripts/sitewide/atwho_users.js rename to app/assets/javascripts/sitewide/atwho_users.js.erb index 941a65023..9c3227a59 100644 --- a/app/assets/javascripts/sitewide/atwho_users.js +++ b/app/assets/javascripts/sitewide/atwho_users.js.erb @@ -10,26 +10,9 @@ .atwho({ at: '@', callbacks: { - matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) { - var _a, _y, match, regexp, space; - flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - if (should_startWithSpace) { - flag = '(?:^|\\s)' + flag; - } - _a = decodeURI("%C3%80"); - _y = decodeURI("%C3%BF"); - space = ' \xa0'; // Use space... - regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi'); - match = regexp.exec(subtext); - if (match) { - return match[2] || match[1] - } else { - return null; - } - }, remoteFilter: function(query, callback) { $.getJSON( - '/organizations/1/atwho_users.json', + '/organizations/1/atwho_users.json', // TODO {query: query}, function(data) { callback(data.users); @@ -97,8 +80,27 @@ return res; } }, - limit: 5, - startsWithSpace: true + headerTpl: + '
    ' + + '
    <%= I18n.t("atwho.users.title") %>
    ' + + '
    ' + + '
    ' + + '<%= I18n.t("atwho.users.navigate_1") %> ' + + '<%= I18n.t("atwho.users.navigate_2") %>' + + '
    ' + + '
    ' + + '<%= I18n.t("atwho.users.confirm_1") %> ' + + '<%= I18n.t("atwho.users.confirm_2") %>' + + '
    ' + + '
    ' + + '<%= I18n.t("atwho.users.dismiss_1") %> ' + + '<%= I18n.t("atwho.users.dismiss_2") %>' + + '
    ' + + '
    ' + + '
    ', + 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..4761fb8e8 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,45 @@ 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; + + strong { + color: $color-black; + } + } + } +} + .atwho-li-user { .avatar { diff --git a/config/locales/en.yml b/config/locales/en.yml index 4f19afc78..3e4e28de0 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. From 488613afcc02f9e0ada848de83660f8c7cec2b47 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Thu, 5 Jan 2017 16:26:49 +0100 Subject: [PATCH 3/6] Add index to users' full name attribute --- app/models/organization.rb | 4 ++- ...0105162500_add_index_to_users_full_name.rb | 5 +++ db/schema.rb | 35 ++++++++++--------- 3 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 db/migrate/20170105162500_add_index_to_users_full_name.rb diff --git a/app/models/organization.rb b/app/models/organization.rb index cdda44e70..63c83cbd5 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -66,7 +66,9 @@ class Organization < ActiveRecord::Base 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/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 From 7835753d3c48978ce4d9147eeb64d511e4f62cbc Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Fri, 6 Jan 2017 12:48:37 +0100 Subject: [PATCH 4/6] Add atwho-users URL to --- app/assets/javascripts/sitewide/atwho_users.js.erb | 12 +++++++++--- app/views/layouts/application.html.erb | 7 ++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/sitewide/atwho_users.js.erb b/app/assets/javascripts/sitewide/atwho_users.js.erb index 9c3227a59..2e406dca7 100644 --- a/app/assets/javascripts/sitewide/atwho_users.js.erb +++ b/app/assets/javascripts/sitewide/atwho_users.js.erb @@ -5,14 +5,19 @@ 'focus', '[data-atwho-users-edit]', function() { - if (_.isUndefined($(this).data('atwho'))) { + 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( - '/organizations/1/atwho_users.json', // TODO + dataUrl, {query: query}, function(data) { callback(data.users); @@ -103,5 +108,6 @@ acceptSpaceBar: true }); } - }); + } + ); })(); 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 %> + > From 0ee5b98977557105bf8bc7ceae8c69db9084e385 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Sat, 7 Jan 2017 09:59:52 +0100 Subject: [PATCH 5/6] Refactor code a bit, add dismiss button to header --- .../javascripts/sitewide/atwho_users.js.erb | 33 +++++++++---------- app/assets/stylesheets/themes/scinote.scss | 15 +++++++-- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/sitewide/atwho_users.js.erb b/app/assets/javascripts/sitewide/atwho_users.js.erb index 2e406dca7..91adb1838 100644 --- a/app/assets/javascripts/sitewide/atwho_users.js.erb +++ b/app/assets/javascripts/sitewide/atwho_users.js.erb @@ -5,6 +5,8 @@ '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')) @@ -54,29 +56,21 @@ 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 li2 = $(li); + var $li = $(li); var re = new RegExp(query, 'gi'); - var prevVal = - li2 - .find('[data-val=full-name]') - .html(); - var newVal = - prevVal - .replace(re, '$&'); - li2.find('[data-val=full-name]').html(newVal); - prevVal = - li2 - .find('[data-val=email]') - .html(); - newVal = - prevVal - .replace(re, '$&'); - li2.find('[data-val=email]').html(newVal); - return li2[0].outerHTML; + highlight($li, '[data-val=full-name]', re); + highlight($li, '[data-val=email]', re); + return $li[0].outerHTML; }, beforeInsert: function(value, li) { var res = ''; @@ -101,6 +95,9 @@ '<%= I18n.t("atwho.users.dismiss_1") %> ' + '<%= I18n.t("atwho.users.dismiss_2") %>' + '' + + '
    ' + + '' + + '
    ' + '' + '', limit: <%= Constants::ATWHO_SEARCH_LIMIT %>, diff --git a/app/assets/stylesheets/themes/scinote.scss b/app/assets/stylesheets/themes/scinote.scss index 4761fb8e8..9e4e8ae6f 100644 --- a/app/assets/stylesheets/themes/scinote.scss +++ b/app/assets/stylesheets/themes/scinote.scss @@ -1787,10 +1787,19 @@ th.custom-field .modal-tooltiptext { display: inline; margin-right: 15px; font-size: smaller; + } - strong { - color: $color-black; - } + div strong { + color: $color-black; + } + + .dismiss { + color: $color-emperor; + } + + .dismiss:hover { + color: $color-black; + cursor: pointer; } } } From 5873eea99428e5b149fd323b5c2133b4bec6af46 Mon Sep 17 00:00:00 2001 From: Luka Murn Date: Tue, 10 Jan 2017 10:42:52 +0100 Subject: [PATCH 6/6] We serve to please the Hound --- app/controllers/at_who_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/at_who_controller.rb b/app/controllers/at_who_controller.rb index a93541890..960f6198c 100644 --- a/app/controllers/at_who_controller.rb +++ b/app/controllers/at_who_controller.rb @@ -12,7 +12,9 @@ class AtWhoController < ApplicationController # 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['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