mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-03-04 19:53:19 +08:00
Merge branch 'smart-annotations' of https://github.com/biosistemika/scinote-web into zd_SCI_834
This commit is contained in:
commit
fc76a4ae6d
10 changed files with 204 additions and 97 deletions
|
@ -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 += '<li class="atwho-li atwho-li-user">';
|
|
||||||
res += '<img src="' + map.img_url + '" height="20" width="20" />';
|
|
||||||
res += ' ';
|
|
||||||
res += '<span data-full-name>';
|
|
||||||
res += map.full_name;
|
|
||||||
res += '</span>';
|
|
||||||
res += ' ';
|
|
||||||
res += '<i class="fa fa-circle" aria-hidden="true"></i>';
|
|
||||||
res += ' ';
|
|
||||||
res += '<small data-email>';
|
|
||||||
res += map.email;
|
|
||||||
res += '</small>';
|
|
||||||
res += '</li>';
|
|
||||||
} 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, '<strong>' + query + '</strong>');
|
|
||||||
li2.find('[data-full-name]').html(newVal);
|
|
||||||
prevVal =
|
|
||||||
li2
|
|
||||||
.find('[data-email]')
|
|
||||||
.html();
|
|
||||||
newVal =
|
|
||||||
prevVal
|
|
||||||
.replace(query, '<strong>' + query + '</strong>');
|
|
||||||
li2.find('[data-email]').html(newVal);
|
|
||||||
return li2.html();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
insertTpl: '[${atwho-at}${full_name}~${id}]',
|
|
||||||
limit: 5,
|
|
||||||
startWithSpace: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})();
|
|
110
app/assets/javascripts/sitewide/atwho_users.js.erb
Normal file
110
app/assets/javascripts/sitewide/atwho_users.js.erb
Normal file
|
@ -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 += '<li class="atwho-li atwho-li-user" ';
|
||||||
|
res += 'data-id="' + map.id + '" ';
|
||||||
|
res += 'data-full-name="' + map.full_name + '">';
|
||||||
|
res += '<img src="' + map.img_url + '" class="avatar" />';
|
||||||
|
res += '<span data-val="full-name">';
|
||||||
|
res += map.full_name;
|
||||||
|
res += '</span>';
|
||||||
|
res += '<small>';
|
||||||
|
res += ' ';
|
||||||
|
res += '·';
|
||||||
|
res += ' ';
|
||||||
|
res += '<span data-val="email">';
|
||||||
|
res += map.email;
|
||||||
|
res += '</span>';
|
||||||
|
res += '</small>';
|
||||||
|
res += '</li>';
|
||||||
|
} 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, '<strong>$&</strong>');
|
||||||
|
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:
|
||||||
|
'<div class="atwho-header-user">' +
|
||||||
|
'<div class="title"><%= I18n.t("atwho.users.title") %></div>' +
|
||||||
|
'<div class="help">' +
|
||||||
|
'<div>' +
|
||||||
|
'<strong><%= I18n.t("atwho.users.navigate_1") %></strong> ' +
|
||||||
|
'<%= I18n.t("atwho.users.navigate_2") %>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div>' +
|
||||||
|
'<strong><%= I18n.t("atwho.users.confirm_1") %></strong> ' +
|
||||||
|
'<%= I18n.t("atwho.users.confirm_2") %>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div>' +
|
||||||
|
'<strong><%= I18n.t("atwho.users.dismiss_1") %></strong> ' +
|
||||||
|
'<%= I18n.t("atwho.users.dismiss_2") %>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="dismiss">' +
|
||||||
|
'<span class="glyphicon glyphicon-remove"></span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>',
|
||||||
|
limit: <%= Constants::ATWHO_SEARCH_LIMIT %>,
|
||||||
|
startsWithSpace: true,
|
||||||
|
acceptSpaceBar: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})();
|
|
@ -25,6 +25,7 @@ $color-emperor: #555;
|
||||||
$color-mine-shaft: #333;
|
$color-mine-shaft: #333;
|
||||||
$color-nero: #262626;
|
$color-nero: #262626;
|
||||||
$color-black: #000;
|
$color-black: #000;
|
||||||
|
$color-cloud: rgba(0, 0, 0, .1);
|
||||||
|
|
||||||
// Miscelaneous colors
|
// Miscelaneous colors
|
||||||
$color-mystic: #eaeff2;
|
$color-mystic: #eaeff2;
|
||||||
|
|
|
@ -1716,10 +1716,10 @@ th.custom-field .modal-tooltiptext {
|
||||||
margin-top: 18px;
|
margin-top: 18px;
|
||||||
background: $color-white;
|
background: $color-white;
|
||||||
color: $color-black;
|
color: $color-black;
|
||||||
border: 1px solid #DDD;
|
border: 1px solid $color-emperor;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-shadow: 0 0 5px rgba(0,0,0,0.1);
|
box-shadow: 0 0 5px $color-cloud;
|
||||||
min-width: 120px;
|
min-width: 520px;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
z-index: 11110 !important;
|
z-index: 11110 !important;
|
||||||
|
@ -1756,13 +1756,54 @@ th.custom-field .modal-tooltiptext {
|
||||||
li {
|
li {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-bottom: 1px solid #DDD;
|
border-bottom: 1px solid $color-emperor;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// <End of overrides>
|
// <End of overrides>
|
||||||
|
|
||||||
|
.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 {
|
.atwho-li-user {
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
|
|
|
@ -10,8 +10,12 @@ class AtWhoController < ApplicationController
|
||||||
.limit(Constants::ATWHO_SEARCH_LIMIT)
|
.limit(Constants::ATWHO_SEARCH_LIMIT)
|
||||||
.as_json
|
.as_json
|
||||||
|
|
||||||
# Add avatars, convert to JSON
|
# Add avatars, Base62, convert to JSON
|
||||||
res.each do |user_obj|
|
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)
|
user_obj['img_url'] = avatar_path(user_obj['id'], :icon_small)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -62,13 +62,13 @@ class Organization < ActiveRecord::Base
|
||||||
.strip
|
.strip
|
||||||
.gsub('_', '\\_')
|
.gsub('_', '\\_')
|
||||||
.gsub('%', '\\%')
|
.gsub('%', '\\%')
|
||||||
.split(/\s+/)
|
|
||||||
.map { |t| '%' + t + '%' }
|
|
||||||
else
|
else
|
||||||
a_query = query
|
a_query = query
|
||||||
end
|
end
|
||||||
|
|
||||||
users.where_attributes_like(attributes, a_query)
|
users
|
||||||
|
.where.not(confirmed_at: nil)
|
||||||
|
.where_attributes_like(attributes, a_query)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Writes to user log
|
# Writes to user log
|
||||||
|
|
|
@ -14,7 +14,12 @@
|
||||||
|
|
||||||
<%= csrf_meta_tags %>
|
<%= csrf_meta_tags %>
|
||||||
</head>
|
</head>
|
||||||
<body class="<%= yield :body_class %>">
|
<body
|
||||||
|
class="<%= yield :body_class %>"
|
||||||
|
<% if user_signed_in? && current_organization.present? %>
|
||||||
|
data-atwho-users-url="<%= atwho_users_organization_path(current_organization) %>"
|
||||||
|
<% end %>
|
||||||
|
>
|
||||||
|
|
||||||
<span style="display: none;" data-hook="body-js"></span>
|
<span style="display: none;" data-hook="body-js"></span>
|
||||||
<span style="display: none;" data-hook="application-body-html"></span>
|
<span style="display: none;" data-hook="application-body-html"></span>
|
||||||
|
|
|
@ -1451,6 +1451,16 @@ en:
|
||||||
assign_user_to_organization: "<i>%{assigned_user}</i> was added as %{role} to team <strong>%{organization}</strong> by <i>%{assigned_by_user}</i>."
|
assign_user_to_organization: "<i>%{assigned_user}</i> was added as %{role} to team <strong>%{organization}</strong> by <i>%{assigned_by_user}</i>."
|
||||||
unassign_user_from_organization: "<i>%{unassigned_user}</i> was removed from team <strong>%{organization}</strong> by <i>%{unassigned_by_user}</i>."
|
unassign_user_from_organization: "<i>%{unassigned_user}</i> was removed from team <strong>%{organization}</strong> by <i>%{unassigned_by_user}</i>."
|
||||||
|
|
||||||
|
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
|
# This section contains general words that can be used in any parts of
|
||||||
# application.
|
# application.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddIndexToUsersFullName < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_index :users, :full_name
|
||||||
|
end
|
||||||
|
end
|
35
db/schema.rb
35
db/schema.rb
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
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
|
add_index "user_projects", ["user_id"], name: "index_user_projects_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "users", force: :cascade do |t|
|
create_table "users", force: :cascade do |t|
|
||||||
t.string "full_name", null: false
|
t.string "full_name", null: false
|
||||||
t.string "initials", null: false
|
t.string "initials", null: false
|
||||||
t.string "email", default: "", null: false
|
t.string "email", default: "", null: false
|
||||||
t.string "encrypted_password", default: "", null: false
|
t.string "encrypted_password", default: "", null: false
|
||||||
t.string "reset_password_token"
|
t.string "reset_password_token"
|
||||||
t.datetime "reset_password_sent_at"
|
t.datetime "reset_password_sent_at"
|
||||||
t.datetime "remember_created_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 "current_sign_in_at"
|
||||||
t.datetime "last_sign_in_at"
|
t.datetime "last_sign_in_at"
|
||||||
t.string "current_sign_in_ip"
|
t.string "current_sign_in_ip"
|
||||||
t.string "last_sign_in_ip"
|
t.string "last_sign_in_ip"
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.string "avatar_file_name"
|
t.string "avatar_file_name"
|
||||||
t.string "avatar_content_type"
|
t.string "avatar_content_type"
|
||||||
t.integer "avatar_file_size"
|
t.integer "avatar_file_size"
|
||||||
|
@ -661,7 +661,7 @@ ActiveRecord::Schema.define(version: 20161125153600) do
|
||||||
t.datetime "confirmed_at"
|
t.datetime "confirmed_at"
|
||||||
t.datetime "confirmation_sent_at"
|
t.datetime "confirmation_sent_at"
|
||||||
t.string "unconfirmed_email"
|
t.string "unconfirmed_email"
|
||||||
t.string "time_zone", default: "UTC"
|
t.string "time_zone", default: "UTC"
|
||||||
t.string "invitation_token"
|
t.string "invitation_token"
|
||||||
t.datetime "invitation_created_at"
|
t.datetime "invitation_created_at"
|
||||||
t.datetime "invitation_sent_at"
|
t.datetime "invitation_sent_at"
|
||||||
|
@ -669,18 +669,21 @@ ActiveRecord::Schema.define(version: 20161125153600) do
|
||||||
t.integer "invitation_limit"
|
t.integer "invitation_limit"
|
||||||
t.integer "invited_by_id"
|
t.integer "invited_by_id"
|
||||||
t.string "invited_by_type"
|
t.string "invited_by_type"
|
||||||
t.integer "invitations_count", default: 0
|
t.integer "invitations_count", default: 0
|
||||||
t.integer "tutorial_status", default: 0, null: false
|
t.integer "tutorial_status", default: 0, null: false
|
||||||
t.boolean "assignments_notification", default: true
|
t.boolean "assignments_notification", default: true
|
||||||
t.boolean "recent_notification", default: true
|
t.boolean "recent_notification", default: true
|
||||||
t.boolean "assignments_notification_email", default: false
|
t.boolean "assignments_notification_email", default: false
|
||||||
t.boolean "recent_notification_email", default: false
|
t.boolean "recent_notification_email", default: false
|
||||||
t.integer "current_organization_id"
|
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
|
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", ["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", ["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", ["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", ["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
|
add_index "users", ["invited_by_id"], name: "index_users_on_invited_by_id", using: :btree
|
||||||
|
|
Loading…
Reference in a new issue