mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Merge branch 'master' into pm-tags
This commit is contained in:
commit
2b509eaa91
7
.gitattributes
vendored
7
.gitattributes
vendored
@ -1,11 +1,14 @@
|
||||
# Set default behaviour, in case users don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files we want to always be normalized and converted
|
||||
# Treat email fixtures as binary files so CRLF are not converted to LF.
|
||||
*.eml binary
|
||||
|
||||
# Explicitly declare text files we want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.yml text
|
||||
|
||||
# Custom for Visual Studio, very unlikely, but lets keep it
|
||||
# Custom for Visual Studio, very unlikely, but lets keep it
|
||||
*.cs diff=csharp
|
||||
*.sln merge=union
|
||||
*.csproj merge=union
|
||||
|
8
Gemfile
8
Gemfile
@ -36,7 +36,7 @@ gem 'redis-namespace'
|
||||
|
||||
gem 'active_model_serializers', '~> 0.8.3'
|
||||
|
||||
gem 'onebox', '1.8.36'
|
||||
gem 'onebox', '1.8.40'
|
||||
|
||||
gem 'http_accept_language', '~>2.0.5', require: false
|
||||
|
||||
@ -67,9 +67,6 @@ gem 'multi_json'
|
||||
gem 'mustache'
|
||||
gem 'nokogiri'
|
||||
|
||||
# this may end up deprecating nokogiri
|
||||
gem 'oga', require: false
|
||||
|
||||
gem 'omniauth'
|
||||
gem 'omniauth-openid'
|
||||
gem 'openid-redis-store'
|
||||
@ -178,6 +175,9 @@ gem 'logster'
|
||||
|
||||
gem 'sassc', require: false
|
||||
|
||||
gem 'rotp'
|
||||
gem 'rqrcode'
|
||||
|
||||
if ENV["IMPORT"] == "1"
|
||||
gem 'mysql2'
|
||||
gem 'redcarpet'
|
||||
|
36
Gemfile.lock
36
Gemfile.lock
@ -41,7 +41,6 @@ GEM
|
||||
annotate (2.7.2)
|
||||
activerecord (>= 3.2, < 6.0)
|
||||
rake (>= 10.4, < 13.0)
|
||||
ansi (1.5.0)
|
||||
arel (8.0.0)
|
||||
ast (2.3.0)
|
||||
aws-partitions (1.24.0)
|
||||
@ -64,7 +63,7 @@ GEM
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.7.2)
|
||||
binding_of_caller (0.8.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bootsnap (1.0.0)
|
||||
msgpack (~> 1.0)
|
||||
@ -74,13 +73,14 @@ GEM
|
||||
uniform_notifier (~> 1.10.0)
|
||||
byebug (9.0.6)
|
||||
certified (1.0.0)
|
||||
chunky_png (1.3.8)
|
||||
coderay (1.1.2)
|
||||
concurrent-ruby (1.0.5)
|
||||
connection_pool (2.2.1)
|
||||
cppjieba_rb (0.3.0)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.2)
|
||||
crass (1.0.3)
|
||||
debug_inspector (0.0.3)
|
||||
diff-lcs (1.3)
|
||||
discourse-qunit-rails (0.0.11)
|
||||
@ -165,7 +165,7 @@ GEM
|
||||
lru_redux (1.1.0)
|
||||
mail (2.6.6)
|
||||
mime-types (>= 1.16, < 4)
|
||||
memory_profiler (0.9.8)
|
||||
memory_profiler (0.9.10)
|
||||
message_bus (2.1.2)
|
||||
rack (>= 1.1.3)
|
||||
metaclass (0.0.4)
|
||||
@ -185,13 +185,13 @@ GEM
|
||||
mock_redis (0.17.3)
|
||||
moneta (1.0.0)
|
||||
msgpack (1.1.0)
|
||||
multi_json (1.12.1)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
mustache (1.0.5)
|
||||
nokogiri (1.8.1)
|
||||
nokogiri (1.8.2)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nokogumbo (1.4.13)
|
||||
nokogumbo (1.5.0)
|
||||
nokogiri
|
||||
oauth (0.5.1)
|
||||
oauth2 (1.3.1)
|
||||
@ -200,10 +200,7 @@ GEM
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
oga (2.10)
|
||||
ast
|
||||
ruby-ll (~> 2.1)
|
||||
oj (3.1.0)
|
||||
oj (3.4.0)
|
||||
omniauth (1.6.1)
|
||||
hashie (>= 3.4.6, < 3.6.0)
|
||||
rack (>= 1.6.2, < 3)
|
||||
@ -232,7 +229,7 @@ GEM
|
||||
omniauth-twitter (1.3.0)
|
||||
omniauth-oauth (~> 1.1)
|
||||
rack
|
||||
onebox (1.8.36)
|
||||
onebox (1.8.40)
|
||||
fast_blank (>= 1.0.0)
|
||||
htmlentities (~> 4.3)
|
||||
moneta (~> 1.0)
|
||||
@ -302,6 +299,9 @@ GEM
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
request_store (1.3.2)
|
||||
rinku (2.0.2)
|
||||
rotp (3.3.0)
|
||||
rqrcode (0.10.1)
|
||||
chunky_png (~> 1.0)
|
||||
rspec (3.6.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
@ -334,9 +334,6 @@ GEM
|
||||
rainbow (>= 2.2.2, < 3.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
ruby-ll (2.1.2)
|
||||
ansi
|
||||
ast
|
||||
ruby-openid (2.7.0)
|
||||
ruby-prof (0.16.2)
|
||||
ruby-progressbar (1.9.0)
|
||||
@ -345,10 +342,10 @@ GEM
|
||||
nokogiri (>= 1.6.0)
|
||||
ruby_dep (1.5.0)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (4.5.0)
|
||||
sanitize (4.6.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (~> 1.4.1)
|
||||
nokogumbo (~> 1.4)
|
||||
sass (3.4.24)
|
||||
sassc (1.11.2)
|
||||
bundler
|
||||
@ -459,7 +456,6 @@ DEPENDENCIES
|
||||
multi_json
|
||||
mustache
|
||||
nokogiri
|
||||
oga
|
||||
oj
|
||||
omniauth
|
||||
omniauth-facebook
|
||||
@ -469,7 +465,7 @@ DEPENDENCIES
|
||||
omniauth-oauth2
|
||||
omniauth-openid
|
||||
omniauth-twitter
|
||||
onebox (= 1.8.36)
|
||||
onebox (= 1.8.40)
|
||||
openid-redis-store
|
||||
pg (~> 0.21.0)
|
||||
pry-nav
|
||||
@ -487,6 +483,8 @@ DEPENDENCIES
|
||||
redis
|
||||
redis-namespace
|
||||
rinku
|
||||
rotp
|
||||
rqrcode
|
||||
rspec
|
||||
rspec-html-matchers
|
||||
rspec-rails
|
||||
|
@ -57,7 +57,6 @@ Plus *lots* of Ruby Gems, a complete list of which is at [/master/Gemfile](https
|
||||
## Contributing
|
||||
|
||||
[](https://travis-ci.org/discourse/discourse)
|
||||
[](https://codeclimate.com/github/discourse/discourse)
|
||||
|
||||
Discourse is **100% free** and **open source**. We encourage and support an active, healthy community that
|
||||
accepts contributions from the public – including you!
|
||||
|
22
app/assets/javascripts/admin/components/staff-actions.js.es6
Normal file
22
app/assets/javascripts/admin/components/staff-actions.js.es6
Normal file
@ -0,0 +1,22 @@
|
||||
import DiscourseURL from 'discourse/lib/url';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['table', 'staff-actions'],
|
||||
|
||||
willDestroyElement() {
|
||||
this.$().off('click.discourse-staff-logs');
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
|
||||
this.$().on('click.discourse-staff-logs', '[data-link-post-id]', e => {
|
||||
let postId = $(e.target).attr('data-link-post-id');
|
||||
|
||||
this.store.find('post', postId).then(p => {
|
||||
DiscourseURL.routeTo(p.get('url'));
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
@ -19,6 +19,11 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
||||
|
||||
primaryGroupDirty: propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'),
|
||||
|
||||
canDisableSecondFactor: Ember.computed.and(
|
||||
'model.second_factor_enabled',
|
||||
'model.can_disable_second_factor'
|
||||
),
|
||||
|
||||
automaticGroups: function() {
|
||||
return this.get("model.automaticGroups").map((g) => g.name).join(", ");
|
||||
}.property("model.automaticGroups"),
|
||||
@ -63,6 +68,7 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
||||
deleteAllPosts() { return this.get("model").deleteAllPosts(); },
|
||||
anonymize() { return this.get('model').anonymize(); },
|
||||
destroy() { return this.get('model').destroy(); },
|
||||
disableSecondFactor() { return this.get('model').disableSecondFactor(); },
|
||||
|
||||
viewActionLogs() {
|
||||
this.get('adminTools').showActionLogs(this, {
|
||||
|
@ -168,6 +168,14 @@ const AdminUser = Discourse.User.extend({
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
disableSecondFactor() {
|
||||
return ajax(`/admin/users/${this.get('id')}/disable_second_factor`, {
|
||||
type: 'PUT'
|
||||
}).then(() => {
|
||||
this.set('second_factor_enabled', false);
|
||||
}).catch(popupAjaxError);
|
||||
},
|
||||
|
||||
refreshBrowsers() {
|
||||
return ajax("/admin/users/" + this.get('id') + "/refresh_browsers", {
|
||||
type: 'POST'
|
||||
|
@ -10,7 +10,7 @@ const StaffActionLog = Discourse.Model.extend({
|
||||
}.property('action_name'),
|
||||
|
||||
formattedDetails: function() {
|
||||
var formatted = "";
|
||||
let formatted = "";
|
||||
formatted += this.format('email', 'email');
|
||||
formatted += this.format('admin.logs.ip_address', 'ip_address');
|
||||
formatted += this.format('admin.logs.topic_id', 'topic_id');
|
||||
@ -26,9 +26,13 @@ const StaffActionLog = Discourse.Model.extend({
|
||||
return formatted;
|
||||
}.property('ip_address', 'email', 'topic_id', 'post_id', 'category_id'),
|
||||
|
||||
format: function(label, propertyName) {
|
||||
format(label, propertyName) {
|
||||
if (this.get(propertyName)) {
|
||||
return ('<b>' + I18n.t(label) + ':</b> ' + escapeExpression(this.get(propertyName)) + '<br/>');
|
||||
let value = escapeExpression(this.get(propertyName));
|
||||
if (propertyName === 'post_id') {
|
||||
value = `<a href data-link-post-id="${value}">${value}</a>`;
|
||||
}
|
||||
return `<b>${I18n.t(label)}:</b> ${value}<br/>`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
<div class='admin-backups'>
|
||||
<div class="admin-controls">
|
||||
<div class="span15">
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
{{nav-item route='admin.backups.index' label='admin.backups.menu.backups'}}
|
||||
{{nav-item route='admin.backups.logs' label='admin.backups.menu.logs'}}
|
||||
{{plugin-outlet name="downloader" tagName=""}}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="pull-right">
|
||||
{{#if model.canRollback}}
|
||||
{{d-button action="rollback"
|
||||
|
@ -1,4 +1,4 @@
|
||||
{{#d-section class="current-badge span13"}}
|
||||
{{#d-section class="current-badge content-body"}}
|
||||
<p>{{i18n 'admin.badges.none_selected'}}</p>
|
||||
|
||||
<div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{{#d-section class="current-badge span13"}}
|
||||
{{#d-section class="current-badge content-body"}}
|
||||
<form class="form-horizontal">
|
||||
<div>
|
||||
<label for="name">{{i18n 'admin.badges.name'}}</label>
|
||||
@ -144,7 +144,7 @@
|
||||
{{/d-section}}
|
||||
|
||||
{{#if grant_count}}
|
||||
<div class="span13 current-badge-actions">
|
||||
<div class="content-body current-badge-actions">
|
||||
<div>
|
||||
{{#link-to 'badges.show' this}}{{i18n 'badges.granted' count=grant_count}}{{/link-to}}
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="badges">
|
||||
|
||||
<div class='content-list span6'>
|
||||
<div class='content-list'>
|
||||
<h3>{{i18n 'admin.badges.title'}}</h3>
|
||||
<ul>
|
||||
{{#each model as |badge|}}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
{{yield}}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class='content-list span6 color-schemes'>
|
||||
<div class='content-list color-schemes'>
|
||||
<h3>{{i18n 'admin.customize.colors.long_title'}}</h3>
|
||||
<ul>
|
||||
{{#each model as |scheme|}}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class='row'>
|
||||
<div class='content-list span6'>
|
||||
<div class='content-list'>
|
||||
<ul>
|
||||
{{#each sortedTemplates as |et|}}
|
||||
<li>
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{#unless editingTheme}}
|
||||
<div class='content-list span6'>
|
||||
<div class='content-list'>
|
||||
<h3>{{i18n 'admin.customize.theme.long_title'}}</h3>
|
||||
<ul>
|
||||
{{#each sortedThemes as |theme|}}
|
||||
|
@ -19,7 +19,7 @@
|
||||
<div class='controls'>
|
||||
{{text-field value=testEmailAddress placeholderKey="admin.email.test_email_address"}}
|
||||
</div>
|
||||
<div class='span10 controls'>
|
||||
<div class='controls'>
|
||||
<button class='btn btn-primary' {{action "sendTestEmail"}} disabled={{sendTestEmailDisabled}}>{{i18n 'admin.email.send_test'}}</button>
|
||||
{{#if sentTestEmail}}<span class='result-message'>{{i18n 'admin.email.sent_test'}}</span>{{/if}}
|
||||
</div>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<p>{{emoji-uploader done="emojiUploaded"}}</p>
|
||||
|
||||
{{#if sortedEmojis}}
|
||||
<div class="span8">
|
||||
<div>
|
||||
<table id="custom_emoji">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class='row groups'>
|
||||
{{#if sortedGroups}}
|
||||
<div class='content-list span6'>
|
||||
<div class='content-list'>
|
||||
<h3>{{i18n 'admin.groups.edit'}}</h3>
|
||||
<ul>
|
||||
{{#each sortedGroups as |group|}}
|
||||
@ -25,7 +25,7 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="span13">
|
||||
<div class="content-body">
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -58,7 +58,7 @@
|
||||
{{#unless item.editing}}
|
||||
{{d-button action="destroy" actionParam=item icon="trash-o" class="btn-danger"}}
|
||||
{{d-button action="edit" actionParam=item icon="pencil"}}
|
||||
{{#if isBlocked}}
|
||||
{{#if item.isBlocked}}
|
||||
{{d-button action="allow" actionParam=item icon="check" label="admin.logs.screened_ips.actions.do_nothing"}}
|
||||
{{else}}
|
||||
{{d-button action="block" actionParam=item icon="ban" label="admin.logs.screened_ips.actions.block"}}
|
||||
|
@ -39,7 +39,7 @@
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class='table staff-actions'>
|
||||
{{#staff-actions}}
|
||||
<div class="heading-container">
|
||||
<div class="col heading first staff_user">{{i18n 'admin.logs.staff_actions.staff_user'}}</div>
|
||||
<div class="col heading action">{{i18n 'admin.logs.action'}}</div>
|
||||
@ -86,4 +86,4 @@
|
||||
{{i18n 'search.no_results'}}
|
||||
{{/each}}
|
||||
{{/conditional-loading-spinner}}
|
||||
</div>
|
||||
{{/staff-actions}}
|
||||
|
@ -1,9 +1,9 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<nav>
|
||||
<ul class='nav nav-pills'>
|
||||
<li>{{#link-to 'adminUser' user}}{{d-icon "caret-left"}} {{user.username}}{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{{#conditional-loading-spinner condition=loading}}
|
||||
|
@ -156,6 +156,22 @@
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class='display-row'>
|
||||
<div class='field'>{{i18n 'user.second_factor.title'}}</div>
|
||||
<div class='value'>
|
||||
{{#if model.second_factor_enabled}}
|
||||
{{i18n "yes_value"}}
|
||||
{{else}}
|
||||
{{i18n "no_value"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class='controls'>
|
||||
{{#if canDisableSecondFactor}}
|
||||
{{d-button action="disableSecondFactor" icon="unlock-alt" label="user.second_factor.disable"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{#if userFields}}
|
||||
|
@ -1,10 +1,10 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
<li>{{#link-to 'adminUser' model}}{{d-icon "caret-left"}} {{model.username}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminUsersList.show' 'member'}}{{i18n 'admin.user.trust_level_2_users'}}{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="admin-container tl3-requirements">
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class='admin-controls'>
|
||||
<div class='span15'>
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
{{nav-item route='adminUsersList.show' routeParam='active' label='admin.users.nav.active'}}
|
||||
{{nav-item route='adminUsersList.show' routeParam='new' label='admin.users.nav.new'}}
|
||||
@ -11,7 +11,7 @@
|
||||
{{nav-item route='adminUsersList.show' routeParam='silenced' label='admin.users.nav.silenced'}}
|
||||
{{nav-item route='adminUsersList.show' routeParam='suspect' label='admin.users.nav.suspect'}}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="pull-right">
|
||||
{{#unless siteSettings.enable_sso}}
|
||||
{{d-button action="sendInvites" title="admin.invite.button_title" icon="user-plus" label="admin.invite.button_text"}}
|
||||
|
@ -90,7 +90,7 @@ registerIconRenderer({
|
||||
if (params.label) { html += " aria-hidden='true'"; }
|
||||
html += `></${tagName}>`;
|
||||
if (params.label) {
|
||||
html += "<span class='sr-only'>" + I18n.t(params.label) + "</span>";
|
||||
html += `<span class='sr-only'>${params.label}</span>`;
|
||||
}
|
||||
return html;
|
||||
},
|
||||
|
@ -38,11 +38,11 @@ export default Ember.Component.extend({
|
||||
<a class="post-link" href="${postLink.href}">${postLink.anchor}</a>
|
||||
${userAvatar}
|
||||
<span class="username">${userLink.anchor}</span>
|
||||
${iconHTML("mail-forward", { class: "reply-to-glyph" })}
|
||||
`;
|
||||
|
||||
if (originalUser) {
|
||||
editTitle += `
|
||||
${iconHTML("mail-forward", { class: "reply-to-glyph" })}
|
||||
${originalUser.avatar}
|
||||
<span class="original-username">${originalUser.username}</span>
|
||||
`;
|
||||
|
@ -372,7 +372,13 @@ export default Ember.Component.extend({
|
||||
post.set('refreshedPost', true);
|
||||
}
|
||||
|
||||
$oneboxes.each((_, o) => load(o, refresh, ajax, this.currentUser.id));
|
||||
$oneboxes.each((_, o) => load({
|
||||
elem: o,
|
||||
refresh,
|
||||
ajax,
|
||||
categoryId: this.get('composer.category.id'),
|
||||
topicId: this.get('composer.topic.id')
|
||||
}));
|
||||
},
|
||||
|
||||
_warnMentionedGroups($preview) {
|
||||
|
@ -80,7 +80,14 @@ export default Ember.Component.extend({
|
||||
const link = document.createElement('a');
|
||||
link.href = this.get('composer.title');
|
||||
|
||||
let loadOnebox = load(link, false, ajax, this.currentUser.id, true);
|
||||
const loadOnebox = load({
|
||||
elem: link,
|
||||
refresh: false,
|
||||
ajax,
|
||||
synchronous: true,
|
||||
categoryId: this.get('composer.category.id'),
|
||||
topicId: this.get('composer.topic.id')
|
||||
});
|
||||
|
||||
if (loadOnebox && loadOnebox.then) {
|
||||
loadOnebox.then( () => {
|
||||
|
@ -0,0 +1,6 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
@computed('post.url')
|
||||
postUrl: Discourse.getURL
|
||||
});
|
@ -11,7 +11,7 @@ export default Ember.Component.extend({
|
||||
}
|
||||
|
||||
Ember.run.schedule('afterRender', () => {
|
||||
$('#login-account-password, #login-account-name').keydown(e => {
|
||||
$('#login-account-password, #login-account-name, #login-second-factor').keydown(e => {
|
||||
if (e.keyCode === 13) {
|
||||
this.sendAction();
|
||||
}
|
||||
|
@ -167,9 +167,9 @@ export default Ember.Controller.extend({
|
||||
return this.currentUser && this.currentUser.staff && hasResults;
|
||||
},
|
||||
|
||||
@computed('expanded', 'model.grouped_search_result.can_create_topic')
|
||||
canCreateTopic(expanded, userCanCreateTopic) {
|
||||
return this.currentUser && userCanCreateTopic && !expanded;
|
||||
@computed('model.grouped_search_result.can_create_topic')
|
||||
canCreateTopic(userCanCreateTopic) {
|
||||
return this.currentUser && userCanCreateTopic;
|
||||
},
|
||||
|
||||
@computed('expanded')
|
||||
|
@ -4,6 +4,7 @@ import showModal from 'discourse/lib/show-modal';
|
||||
import { setting } from 'discourse/lib/computed';
|
||||
import { findAll } from 'discourse/models/login-method';
|
||||
import { escape } from 'pretty-text/sanitizer';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
// This is happening outside of the app via popup
|
||||
const AuthErrors = [
|
||||
@ -31,6 +32,9 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
this.set('authenticate', null);
|
||||
this.set('loggingIn', false);
|
||||
this.set('loggedIn', false);
|
||||
this.set('secondFactorRequired', false);
|
||||
$("#credentials").show();
|
||||
$("#second-factor").hide();
|
||||
},
|
||||
|
||||
// Determines whether at least one login button is enabled
|
||||
@ -38,9 +42,10 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
return findAll(this.siteSettings).length > 0;
|
||||
}.property(),
|
||||
|
||||
loginButtonText: function() {
|
||||
return this.get('loggingIn') ? I18n.t('login.logging_in') : I18n.t('login.title');
|
||||
}.property('loggingIn'),
|
||||
@computed('loggingIn')
|
||||
loginButtonLabel(loggingIn) {
|
||||
return loggingIn ? 'login.logging_in' : 'login.title';
|
||||
},
|
||||
|
||||
loginDisabled: Em.computed.or('loggingIn', 'loggedIn'),
|
||||
|
||||
@ -67,13 +72,24 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
this.set('loggingIn', true);
|
||||
|
||||
ajax("/session", {
|
||||
data: { login: this.get('loginName'), password: this.get('loginPassword') },
|
||||
type: 'POST'
|
||||
type: 'POST',
|
||||
data: {
|
||||
login: this.get('loginName'),
|
||||
password: this.get('loginPassword'),
|
||||
second_factor_token: this.get('loginSecondFactor')
|
||||
},
|
||||
}).then(function (result) {
|
||||
// Successful login
|
||||
if (result && result.error) {
|
||||
self.set('loggingIn', false);
|
||||
if (result.reason === 'not_activated') {
|
||||
|
||||
if (result.reason === 'invalid_second_factor' && !self.get('secondFactorRequired')) {
|
||||
$('#modal-alert').hide();
|
||||
self.set('secondFactorRequired', true);
|
||||
$("#credentials").hide();
|
||||
$("#second-factor").show();
|
||||
return;
|
||||
} else if (result.reason === 'not_activated') {
|
||||
self.send('showNotActivated', {
|
||||
username: self.get('loginName'),
|
||||
sentTo: escape(result.sent_to_email),
|
||||
|
@ -8,6 +8,7 @@ import { userPath } from 'discourse/lib/url';
|
||||
export default Ember.Controller.extend(PasswordValidation, {
|
||||
isDeveloper: Ember.computed.alias('model.is_developer'),
|
||||
admin: Ember.computed.alias('model.admin'),
|
||||
secondFactorRequired: Ember.computed.alias('model.second_factor_required'),
|
||||
passwordRequired: true,
|
||||
errorMessage: null,
|
||||
successMessage: null,
|
||||
@ -32,7 +33,8 @@ export default Ember.Controller.extend(PasswordValidation, {
|
||||
url: userPath(`password-reset/${this.get('model.token')}.json`),
|
||||
type: 'PUT',
|
||||
data: {
|
||||
password: this.get('accountPassword')
|
||||
password: this.get('accountPassword'),
|
||||
second_factor_token: this.get('secondFactor')
|
||||
}
|
||||
}).then(result => {
|
||||
if (result.success) {
|
||||
@ -45,10 +47,22 @@ export default Ember.Controller.extend(PasswordValidation, {
|
||||
DiscourseURL.redirectTo(result.redirect_to || '/');
|
||||
}
|
||||
} else {
|
||||
if (result.errors && result.errors.password && result.errors.password.length > 0) {
|
||||
if (result.errors && result.errors.user_second_factor) {
|
||||
this.setProperties({
|
||||
secondFactorRequired: true,
|
||||
password: null,
|
||||
errorMessage: result.message
|
||||
});
|
||||
} else if (this.get('secondFactorRequired')) {
|
||||
this.setProperties({
|
||||
secondFactorRequired: false,
|
||||
errorMessage: null
|
||||
});
|
||||
} else if (result.errors && result.errors.password && result.errors.password.length > 0) {
|
||||
this.get('rejectedPasswords').pushObject(this.get('accountPassword'));
|
||||
this.get('rejectedPasswordsMessages').set(this.get('accountPassword'), result.errors.password[0]);
|
||||
}
|
||||
|
||||
if (result.message) {
|
||||
this.set('errorMessage', result.message);
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||
import { default as DiscourseURL, userPath } from 'discourse/lib/url';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
loading: false,
|
||||
password: null,
|
||||
secondFactorImage: null,
|
||||
secondFactorKey: null,
|
||||
showSecondFactorKey: false,
|
||||
errorMessage: null,
|
||||
newUsername: null,
|
||||
|
||||
loaded: Ember.computed.and('secondFactorImage', 'secondFactorKey'),
|
||||
|
||||
@computed('loading')
|
||||
submitButtonText(loading) {
|
||||
return loading ? 'loading' : 'submit';
|
||||
},
|
||||
|
||||
toggleSecondFactor(enable) {
|
||||
if (!this.get('second_factor_token')) return;
|
||||
this.set('loading', true);
|
||||
|
||||
this.get('content').toggleSecondFactor(this.get('second_factor_token'), enable)
|
||||
.then(response => {
|
||||
if (response.error) {
|
||||
this.set('errorMessage', response.error);
|
||||
return;
|
||||
}
|
||||
|
||||
this.set('errorMessage',null);
|
||||
DiscourseURL.redirectTo(userPath(`${this.get('content').username.toLowerCase()}/preferences`));
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => this.set('loading', false));
|
||||
},
|
||||
|
||||
actions: {
|
||||
confirmPassword() {
|
||||
if (!this.get('password')) return;
|
||||
this.set('loading', true);
|
||||
|
||||
this.get('content').loadSecondFactorCodes(this.get('password'))
|
||||
.then(response => {
|
||||
if(response.error) {
|
||||
this.set('errorMessage', response.error);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setProperties({
|
||||
errorMessage: null,
|
||||
secondFactorKey: response.key,
|
||||
secondFactorImage: response.qr,
|
||||
});
|
||||
})
|
||||
.catch(popupAjaxError)
|
||||
.finally(() => this.set('loading', false));
|
||||
},
|
||||
|
||||
showSecondFactorKey() {
|
||||
this.set('showSecondFactorKey', true);
|
||||
},
|
||||
|
||||
enableSecondFactor() {
|
||||
this.toggleSecondFactor(true);
|
||||
},
|
||||
|
||||
disableSecondFactor() {
|
||||
this.toggleSecondFactor(false);
|
||||
}
|
||||
}
|
||||
});
|
@ -30,7 +30,7 @@ export default htmlHelper((period, options) => {
|
||||
break;
|
||||
}
|
||||
|
||||
return `${title} <span class='top-date-string'>${dateString}</span>`;
|
||||
return `<span class="date-section">${title}</span><span class='top-date-string'>${dateString}</span>`;
|
||||
} else {
|
||||
return title;
|
||||
}
|
||||
|
@ -2,27 +2,27 @@ export default {
|
||||
name: 'register-service-worker',
|
||||
|
||||
initialize() {
|
||||
// only allow service worker on android for now
|
||||
if (!/(android)/i.test(navigator.userAgent)) {
|
||||
|
||||
// remove old service worker
|
||||
if ('serviceWorker' in navigator && navigator.serviceWorker.getRegistrations) {
|
||||
navigator.serviceWorker.getRegistrations().then((registrations) => {
|
||||
for(let registration of registrations) {
|
||||
registration.unregister();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const isSecure = (document.location.protocol === 'https:') ||
|
||||
window.addEventListener('load', () => {
|
||||
const isSecured = (document.location.protocol === 'https:') ||
|
||||
(location.hostname === "localhost");
|
||||
|
||||
const isSupported= isSecured && ('serviceWorker' in navigator);
|
||||
|
||||
if (isSecure && ('serviceWorker' in navigator)) {
|
||||
navigator.serviceWorker.register(`${Discourse.BaseUri}/service-worker.js`);
|
||||
if (isSupported) {
|
||||
if (Discourse.ServiceWorkerURL) {
|
||||
navigator.serviceWorker
|
||||
.register(`${Discourse.BaseUri}/${Discourse.ServiceWorkerURL}`)
|
||||
.catch(error => {
|
||||
Ember.Logger.info(`Failed to register Service Worker: ${error}`);
|
||||
});
|
||||
} else {
|
||||
navigator.serviceWorker.getRegistrations().then(registrations => {
|
||||
for(let registration of registrations) {
|
||||
registration.unregister();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -26,7 +26,7 @@ export default {
|
||||
}
|
||||
|
||||
// don't track links in quotes or in elided part
|
||||
let tracking = $link.parents('aside.quote,.elided').length === 0;
|
||||
let tracking = $link.parents('aside.quote, .elided').length === 0;
|
||||
|
||||
let href = $link.attr('href') || $link.data('href');
|
||||
|
||||
@ -113,8 +113,10 @@ export default {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isInternal = DiscourseURL.isInternal(href);
|
||||
|
||||
// If we're on the same site, use the router and track via AJAX
|
||||
if (tracking && DiscourseURL.isInternal(href) && !$link.hasClass('attachment')) {
|
||||
if (tracking && isInternal && !$link.hasClass('attachment')) {
|
||||
ajax("/clicks/track", {
|
||||
data: {
|
||||
url: href,
|
||||
@ -128,9 +130,11 @@ export default {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, use a custom URL with a redirect
|
||||
// consider CTRL+mouse-left-click / CMD+mouse-left-click or mouse-middle-click as well
|
||||
if (Discourse.User.currentProp('external_links_in_new_tab') || ((e.ctrlKey || e.metaKey) && (e.which === 1)) || (e.which === 2)) {
|
||||
const modifierLeftClicked = (e.ctrlKey || e.metaKey) && e.which === 1;
|
||||
const middleClicked = e.which === 2;
|
||||
const openExternalInNewTab = Discourse.User.currentProp('external_links_in_new_tab');
|
||||
|
||||
if (modifierLeftClicked || middleClicked || (!isInternal && openExternalInNewTab)) {
|
||||
window.open(destUrl, '_blank').focus();
|
||||
} else {
|
||||
DiscourseURL.redirectTo(destUrl);
|
||||
|
@ -22,6 +22,7 @@ const SERVER_SIDE_ONLY = [
|
||||
/^\/wizard/,
|
||||
/\.rss$/,
|
||||
/\.json$/,
|
||||
/^\/admin\/upgrade$/
|
||||
];
|
||||
|
||||
export function rewritePath(path) {
|
||||
|
@ -203,7 +203,7 @@ export function validateUploadedFile(file, opts) {
|
||||
|
||||
// check that the uploaded file is authorized
|
||||
if (opts.allowStaffToUploadAnyFileInPm && opts.isPrivateMessage) {
|
||||
if (Discourse.User.current("staff")) {
|
||||
if (Discourse.User.currentProp('staff')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -239,16 +239,28 @@ export function validateUploadedFile(file, opts) {
|
||||
|
||||
const IMAGES_EXTENSIONS_REGEX = /(png|jpe?g|gif|bmp|tiff?|svg|webp|ico)/i;
|
||||
|
||||
function extensionsToArray(exts) {
|
||||
return exts.toLowerCase()
|
||||
.replace(/[\s\.]+/g, "")
|
||||
.split("|")
|
||||
.filter(ext => ext.indexOf("*") === -1);
|
||||
}
|
||||
|
||||
function extensions() {
|
||||
return Discourse.SiteSettings.authorized_extensions
|
||||
.toLowerCase()
|
||||
.replace(/[\s\.]+/g, "")
|
||||
.split("|")
|
||||
.filter(ext => ext.indexOf("*") === -1);
|
||||
return extensionsToArray(Discourse.SiteSettings.authorized_extensions);
|
||||
}
|
||||
|
||||
function staffExtensions() {
|
||||
return extensionsToArray(Discourse.SiteSettings.authorized_extensions_for_staff);
|
||||
}
|
||||
|
||||
function imagesExtensions() {
|
||||
return extensions().filter(ext => IMAGES_EXTENSIONS_REGEX.test(ext));
|
||||
let exts = extensions().filter(ext => IMAGES_EXTENSIONS_REGEX.test(ext));
|
||||
if (Discourse.User.currentProp('staff')) {
|
||||
const staffExts = staffExtensions().filter(ext => IMAGES_EXTENSIONS_REGEX.test(ext));
|
||||
exts = _.union(exts, staffExts);
|
||||
}
|
||||
return exts;
|
||||
}
|
||||
|
||||
function extensionsRegex() {
|
||||
@ -259,7 +271,14 @@ function imagesExtensionsRegex() {
|
||||
return new RegExp("\\.(" + imagesExtensions().join("|") + ")$", "i");
|
||||
}
|
||||
|
||||
function staffExtensionsRegex() {
|
||||
return new RegExp("\\.(" + staffExtensions().join("|") + ")$", "i");
|
||||
}
|
||||
|
||||
function isAuthorizedFile(fileName) {
|
||||
if (Discourse.User.currentProp('staff') && staffExtensionsRegex().test(fileName)) {
|
||||
return true;
|
||||
}
|
||||
return extensionsRegex().test(fileName);
|
||||
}
|
||||
|
||||
@ -268,7 +287,8 @@ function isAuthorizedImage(fileName){
|
||||
}
|
||||
|
||||
export function authorizedExtensions() {
|
||||
return authorizesAllExtensions() ? "*" : extensions().join(", ");
|
||||
const exts = Discourse.User.currentProp('staff') ? [...extensions(), ...staffExtensions()] : extensions();
|
||||
return exts.filter(ext => ext.length > 0).join(", ");
|
||||
}
|
||||
|
||||
export function authorizedImagesExtensions() {
|
||||
@ -276,7 +296,9 @@ export function authorizedImagesExtensions() {
|
||||
}
|
||||
|
||||
export function authorizesAllExtensions() {
|
||||
return Discourse.SiteSettings.authorized_extensions.indexOf("*") >= 0;
|
||||
return Discourse.SiteSettings.authorized_extensions.indexOf("*") >= 0 || (
|
||||
Discourse.SiteSettings.authorized_extensions_for_staff.indexOf("*") >= 0 &&
|
||||
Discourse.User.currentProp('staff'));
|
||||
}
|
||||
|
||||
export function authorizesOneOrMoreExtensions() {
|
||||
@ -322,7 +344,7 @@ export function allowsImages() {
|
||||
}
|
||||
|
||||
export function allowsAttachments() {
|
||||
return authorizesAllExtensions() || extensions().length > imagesExtensions().length;
|
||||
return authorizesAllExtensions() || authorizedExtensions().split(", ").length > imagesExtensions().length;
|
||||
}
|
||||
|
||||
export function uploadLocation(url) {
|
||||
|
@ -304,6 +304,20 @@ const User = RestModel.extend({
|
||||
});
|
||||
},
|
||||
|
||||
loadSecondFactorCodes(password) {
|
||||
return ajax("/u/second_factors.json", {
|
||||
data: { password },
|
||||
type: 'POST'
|
||||
});
|
||||
},
|
||||
|
||||
toggleSecondFactor(token, enable) {
|
||||
return ajax("/u/second_factor.json", {
|
||||
data: { second_factor_token: token, enable },
|
||||
type: 'PUT'
|
||||
});
|
||||
},
|
||||
|
||||
loadUserAction(id) {
|
||||
const stream = this.get('stream');
|
||||
return ajax(`/user_actions/${id}.json`, { cache: 'false' }).then(result => {
|
||||
|
@ -111,6 +111,7 @@ export default function() {
|
||||
|
||||
this.route('username');
|
||||
this.route('email');
|
||||
this.route('second-factor');
|
||||
this.route('about', { path: '/about-me' });
|
||||
this.route('badgeTitle', { path: '/badge_title' });
|
||||
this.route('card-badge', { path: '/card-badge' });
|
||||
|
@ -0,0 +1,15 @@
|
||||
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||
|
||||
export default RestrictedUserRoute.extend({
|
||||
model() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
renderTemplate() {
|
||||
return this.render({ into: 'user' });
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.setProperties({ model, newUsername: model.get('username') });
|
||||
}
|
||||
});
|
@ -15,6 +15,10 @@ export default RestrictedUserRoute.extend({
|
||||
},
|
||||
|
||||
actions: {
|
||||
showTwoFactorModal() {
|
||||
showModal('second-factor-intro');
|
||||
},
|
||||
|
||||
showAvatarSelector() {
|
||||
showModal('avatar-selector');
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<a href={{category.url}}>
|
||||
<a class="category-title-link" href={{category.url}}>
|
||||
{{#if category.read_restricted}}
|
||||
{{d-icon 'lock'}}
|
||||
{{/if}}
|
||||
|
@ -3,7 +3,6 @@
|
||||
onChangeCallback='triggerResize'
|
||||
id="private-message-users"
|
||||
includeMessageableGroups='true'
|
||||
class="span8"
|
||||
placeholderKey="composer.users_placeholder"
|
||||
tabindex="1"
|
||||
usernames=usernames
|
||||
|
@ -7,7 +7,7 @@
|
||||
<div class='group-post-info'>
|
||||
<div class="group-post-topic">
|
||||
<div class='group-post-title'>
|
||||
<a href={{post.url}}>{{{post.topic.fancyTitle}}}</a>
|
||||
<a href={{postUrl}}>{{{post.topic.fancyTitle}}}</a>
|
||||
</div>
|
||||
<div class="group-post-category">{{category-link post.category}}</div>
|
||||
{{#if post.user.name}}
|
||||
|
@ -0,0 +1,16 @@
|
||||
<div id="second-factor" style="display: none;">
|
||||
<h3>{{i18n 'login.second_factor_title'}}</h3>
|
||||
<p>{{i18n 'login.second_factor_description'}}</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<label for='login-second-factor'>{{i18n 'login.second_factor_label'}} </label>
|
||||
</td>
|
||||
<td>
|
||||
{{yield}}
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
@ -1,9 +1,9 @@
|
||||
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword action="login"}}
|
||||
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword loginSecondFactor=loginSecondFactor action="login"}}
|
||||
{{#d-modal-body title="login.title" class="login-modal"}}
|
||||
{{login-buttons action="externalLogin"}}
|
||||
{{#if canLoginLocal}}
|
||||
<form id='login-form' method='post'>
|
||||
<div>
|
||||
<div id="credentials">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
@ -15,10 +15,10 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for='login-account-password'>{{i18n 'login.password'}} </label>
|
||||
<label for='login-account-password'>{{i18n 'login.password'}} </label>
|
||||
</td>
|
||||
<td>
|
||||
{{text-field value=loginPassword type="password" id="login-account-password" maxlength="200"}}
|
||||
{{text-field value=loginPassword type="password" id="login-account-password" maxlength="200"}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -29,8 +29,13 @@
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
{{#second-factor-form}}
|
||||
{{text-field value=loginSecondFactor
|
||||
id="login-second-factor"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
autofocus="autofocus"}}
|
||||
{{/second-factor-form}}
|
||||
</form>
|
||||
{{/if}}
|
||||
{{authMessage}}
|
||||
@ -43,11 +48,11 @@
|
||||
{{/if}}
|
||||
|
||||
{{#if canLoginLocal}}
|
||||
<button class='btn btn-large btn-primary'
|
||||
disabled={{loginDisabled}}
|
||||
{{action "login"}}>
|
||||
{{d-icon "unlock"}} {{loginButtonText}}
|
||||
</button>
|
||||
{{d-button action="login"
|
||||
icon="unlock"
|
||||
label=loginButtonLabel
|
||||
disabled=loginDisabled
|
||||
class='btn btn-large btn-primary'}}
|
||||
|
||||
{{#if showSignupLink}}
|
||||
<button class="btn btn-large" id="new-account-link" {{action "showCreateAccount"}}>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{{#d-modal-body id="keyboard-shortcuts-help"}}
|
||||
<div class="row">
|
||||
<div class="span6">
|
||||
<div>
|
||||
<h4>{{i18n 'keyboard_shortcuts_help.jump_to.title'}}</h4>
|
||||
<ul>
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.jump_to.home'}}}</li>
|
||||
@ -24,7 +24,7 @@
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.navigation.next_prev'}}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span6">
|
||||
<div>
|
||||
<h4>{{i18n 'keyboard_shortcuts_help.application.title'}}</h4>
|
||||
<ul>
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.application.hamburger_menu'}}}</li>
|
||||
@ -46,7 +46,7 @@
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.actions.quote_post'}}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span6">
|
||||
<div>
|
||||
<h4>{{i18n 'keyboard_shortcuts_help.actions.title'}}</h4>
|
||||
<ul>
|
||||
<li>{{{i18n 'keyboard_shortcuts_help.actions.bookmark_topic'}}}</li>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword action="login"}}
|
||||
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword loginSecondFactor=loginSecondFactor action="login"}}
|
||||
{{#d-modal-body title="login.title" class="login-modal"}}
|
||||
{{login-buttons action="externalLogin"}}
|
||||
{{#if canLoginLocal}}
|
||||
<form id='login-form' method='post'>
|
||||
<div>
|
||||
<div id="credentials">
|
||||
<table>
|
||||
<tr>
|
||||
<td><label for='login-account-name'>{{i18n 'login.username'}}</label></td>
|
||||
@ -22,6 +22,9 @@
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{{#second-factor-form}}
|
||||
{{text-field value=loginSecondFactor id="login-second-factor" autocorrect="off" autocapitalize="off" autofocus="autofocus"}}
|
||||
{{/second-factor-form}}
|
||||
</form>
|
||||
{{/if}}
|
||||
{{authMessage}}
|
||||
@ -30,9 +33,11 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
{{#if canLoginLocal}}
|
||||
<button form="login-form" type="submit" class="btn btn-large btn-primary" disabled={{loginDisabled}} {{action "login"}}>
|
||||
{{d-icon "unlock"}} {{loginButtonText}}
|
||||
</button>
|
||||
{{d-button action="login"
|
||||
icon="unlock"
|
||||
label=loginButtonLabel
|
||||
disabled=loginDisabled
|
||||
class='btn btn-large btn-primary'}}
|
||||
|
||||
{{#if showSignupLink}}
|
||||
<button class="btn btn-large" id="new-account-link" {{action "createAccount"}}>
|
||||
|
@ -0,0 +1,6 @@
|
||||
{{#d-modal-body title="user.second_factor.title"}}
|
||||
<div>{{{i18n 'user.second_factor.extended_description'}}}</div>
|
||||
{{/d-modal-body}}
|
||||
|
||||
<div class="modal-footer">
|
||||
</div>
|
@ -16,20 +16,28 @@
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<form>
|
||||
{{#if secondFactorRequired}}
|
||||
<h2>{{i18n 'login.second_factor_title'}}</h2>
|
||||
<p>{{i18n 'login.second_factor_description'}}</p>
|
||||
<div class="input">
|
||||
{{input value=secondFactor id="second-factor" autofocus="autofocus"}}
|
||||
</div>
|
||||
{{d-button action="submit" class='btn-primary' label='submit'}}
|
||||
{{else}}
|
||||
<h2>{{i18n 'user.change_password.choose'}}</h2>
|
||||
|
||||
<h2>{{i18n 'user.change_password.choose'}}</h2>
|
||||
<div class="input">
|
||||
{{password-field value=accountPassword type="password" id="new-account-password" capsLockOn=capsLockOn autofocus="autofocus"}}
|
||||
{{input-tip validation=passwordValidation}}
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
{{password-field value=accountPassword type="password" id="new-account-password" capsLockOn=capsLockOn autofocus="autofocus"}}
|
||||
{{input-tip validation=passwordValidation}}
|
||||
</div>
|
||||
<div class="instructions">
|
||||
<div class="caps-lock-warning {{unless capsLockOn 'invisible'}}">
|
||||
{{d-icon "exclamation-triangle"}} {{i18n 'login.caps_lock_warning'}}</div>
|
||||
</div>
|
||||
|
||||
<div class="instructions">
|
||||
<div class="caps-lock-warning {{unless capsLockOn 'invisible'}}">
|
||||
{{d-icon "exclamation-triangle"}} {{i18n 'login.caps_lock_warning'}}</div>
|
||||
</div>
|
||||
|
||||
<button class='btn btn-primary' {{action "submit"}}>{{i18n 'user.change_password.set_password'}}</button>
|
||||
{{d-button action="submit" class='btn-primary' label='user.change_password.set_password'}}
|
||||
{{/if}}
|
||||
|
||||
{{#if errorMessage}}
|
||||
<br/><br/>
|
||||
|
@ -0,0 +1,112 @@
|
||||
<section class='user-content user-preferences'>
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<h3>{{i18n 'user.second_factor.title'}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if errorMessage}}
|
||||
<div class="control-group">
|
||||
<div class="instructions">
|
||||
<div class='alert alert-error'>{{errorMessage}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if model.second_factor_enabled}}
|
||||
<label class='control-label'>{{i18n 'login.second_factor_label'}}</label>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{text-field value=second_factor_token
|
||||
id="second_factor_token"
|
||||
classNames="input-large"
|
||||
autofocus="autofocus"}}
|
||||
</div>
|
||||
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.second_factor.disable_description'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{d-button action="disableSecondFactor"
|
||||
class="btn btn-primary"
|
||||
disabled=loading
|
||||
label=submitButtonText}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if loaded}}
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{i18n 'user.second_factor.enable_description'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{{secondFactorImage}}}
|
||||
|
||||
<p>
|
||||
{{#if showSecondFactorKey}}
|
||||
{{secondFactorKey}}
|
||||
{{else}}
|
||||
<a {{action "showSecondFactorKey"}}>{{i18n 'user.second_factor.show_key_description'}}</a>
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label input-prepend">{{i18n 'login.second_factor_label'}}</label>
|
||||
|
||||
<div class="controls">
|
||||
{{text-field value=second_factor_token
|
||||
id="second-factor-token"
|
||||
classNames="input-xxlarge"
|
||||
autofocus="autofocus"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{d-button action="enableSecondFactor"
|
||||
class="btn btn-primary"
|
||||
disabled=loading
|
||||
label=submitButtonText}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="control-group">
|
||||
<label class='control-label'>{{i18n 'user.password.title'}}</label>
|
||||
|
||||
<div class="controls">
|
||||
{{text-field value=password
|
||||
id="password"
|
||||
type="password"
|
||||
classNames="input-xxlarge"
|
||||
autofocus="autofocus"}}
|
||||
</div>
|
||||
|
||||
<div class='instructions'>
|
||||
{{i18n 'user.second_factor.confirm_password_description'}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
{{d-button action="confirmPassword"
|
||||
class="btn btn-primary"
|
||||
disabled=loading
|
||||
label=submitButtonText}}
|
||||
|
||||
{{#if saved}}{{i18n 'saved'}}{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</form>
|
||||
</section>
|
@ -66,6 +66,25 @@
|
||||
{{passwordProgress}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group pref-second-factor">
|
||||
<label class="control-label">{{i18n 'user.second_factor.title'}}</label>
|
||||
|
||||
<div class="controls">
|
||||
{{#link-to "preferences.second-factor" class="btn"}}
|
||||
{{#if model.second_factor_enabled}}
|
||||
{{d-icon "unlock-alt"}}
|
||||
{{i18n 'user.second_factor.disable'}}
|
||||
{{else}}
|
||||
{{d-icon "lock"}}
|
||||
{{i18n 'user.second_factor.enable'}}
|
||||
{{/if}}
|
||||
{{/link-to}}
|
||||
</div>
|
||||
|
||||
<div class="instructions">
|
||||
<a href {{action "showTwoFactorModal"}}>{{i18n 'user.second_factor.info_prompt'}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="control-group pref-avatar">
|
||||
|
@ -5,13 +5,13 @@
|
||||
<h2>{{i18n 'user.invited.title'}}</h2>
|
||||
|
||||
{{#if model.can_see_invite_details}}
|
||||
<div class='user-invite-controls'>
|
||||
<div class='span15'>
|
||||
<div class='admin-controls'>
|
||||
<nav>
|
||||
<ul class="nav nav-pills">
|
||||
{{nav-item route='userInvited.show' routeParam='pending' i18nLabel=pendingLabel}}
|
||||
{{nav-item route='userInvited.show' routeParam='redeemed' i18nLabel=redeemedLabel}}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="pull-right">
|
||||
{{d-button icon="plus" action="showInvite" label="user.invited.create" class="btn"}}
|
||||
|
@ -39,7 +39,16 @@
|
||||
<div class='profile-image'></div>
|
||||
<div class='details'>
|
||||
<div class='primary'>
|
||||
{{bound-avatar model "huge"}}
|
||||
<div class='user-profile-avatar'>
|
||||
{{bound-avatar model "huge"}}
|
||||
{{#if model.primary_group_name}}
|
||||
{{avatar-flair
|
||||
flairURL=model.primary_group_flair_url
|
||||
flairBgColor=model.primary_group_flair_bg_color
|
||||
flairColor=model.primary_group_flair_color
|
||||
groupName=model.primary_group_name}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<section class='controls'>
|
||||
<ul>
|
||||
{{#if model.can_send_private_message_to_user}}
|
||||
|
@ -5,11 +5,17 @@ import { relativeAge } from 'discourse/lib/formatter';
|
||||
import { iconNode } from 'discourse-common/lib/icon-library';
|
||||
import RawHtml from 'discourse/widgets/raw-html';
|
||||
|
||||
const SCROLLAREA_HEIGHT = 300;
|
||||
const SCROLLER_HEIGHT = 50;
|
||||
const SCROLLAREA_REMAINING = SCROLLAREA_HEIGHT - SCROLLER_HEIGHT;
|
||||
const LAST_READ_HEIGHT = 20;
|
||||
|
||||
function scrollareaHeight() {
|
||||
return ($(window).height() < 425) ? 150 : 300;
|
||||
}
|
||||
|
||||
function scrollareaRemaining() {
|
||||
return scrollareaHeight() - SCROLLER_HEIGHT;
|
||||
}
|
||||
|
||||
function clamp(p, min=0.0, max=1.0) {
|
||||
return Math.max(Math.min(p, max), min);
|
||||
}
|
||||
@ -27,7 +33,7 @@ createWidget('timeline-last-read', {
|
||||
tagName: 'div.timeline-last-read',
|
||||
|
||||
buildAttributes(attrs) {
|
||||
const bottom = SCROLLAREA_HEIGHT - (LAST_READ_HEIGHT / 2);
|
||||
const bottom = scrollareaHeight() - (LAST_READ_HEIGHT / 2);
|
||||
const top = attrs.top > bottom ? bottom : attrs.top;
|
||||
return { style: `height: ${LAST_READ_HEIGHT}px; top: ${top}px` };
|
||||
},
|
||||
@ -115,7 +121,7 @@ createWidget('timeline-scrollarea', {
|
||||
buildKey: () => `timeline-scrollarea`,
|
||||
|
||||
buildAttributes() {
|
||||
return { style: `height: ${SCROLLAREA_HEIGHT}px` };
|
||||
return { style: `height: ${scrollareaHeight()}px` };
|
||||
},
|
||||
|
||||
defaultState(attrs) {
|
||||
@ -168,8 +174,8 @@ createWidget('timeline-scrollarea', {
|
||||
const percentage = state.percentage;
|
||||
if (percentage === null) { return; }
|
||||
|
||||
const before = SCROLLAREA_REMAINING * percentage;
|
||||
const after = (SCROLLAREA_HEIGHT - before) - SCROLLER_HEIGHT;
|
||||
const before = scrollareaRemaining() * percentage;
|
||||
const after = (scrollareaHeight() - before) - SCROLLER_HEIGHT;
|
||||
|
||||
let showButton = false;
|
||||
const hasBackPosition =
|
||||
@ -179,13 +185,13 @@ createWidget('timeline-scrollarea', {
|
||||
(position.lastRead && position.lastRead !== position.total);
|
||||
|
||||
if (hasBackPosition) {
|
||||
const lastReadTop = Math.round(position.lastReadPercentage * SCROLLAREA_HEIGHT);
|
||||
const lastReadTop = Math.round(position.lastReadPercentage * scrollareaHeight());
|
||||
showButton = ((before + SCROLLER_HEIGHT - 5) < lastReadTop) ||
|
||||
(before > (lastReadTop + 25));
|
||||
|
||||
|
||||
// Don't show if at the bottom of the timeline
|
||||
if (lastReadTop > (SCROLLAREA_HEIGHT - (LAST_READ_HEIGHT / 2))) {
|
||||
if (lastReadTop > (scrollareaHeight() - (LAST_READ_HEIGHT / 2))) {
|
||||
showButton = false;
|
||||
}
|
||||
}
|
||||
@ -200,7 +206,7 @@ createWidget('timeline-scrollarea', {
|
||||
];
|
||||
|
||||
if (hasBackPosition) {
|
||||
const lastReadTop = Math.round(position.lastReadPercentage * SCROLLAREA_HEIGHT);
|
||||
const lastReadTop = Math.round(position.lastReadPercentage * scrollareaHeight());
|
||||
result.push(this.attach('timeline-last-read', {
|
||||
top: lastReadTop,
|
||||
lastRead: position.lastRead,
|
||||
|
@ -105,7 +105,6 @@ const rule = {
|
||||
|
||||
let title = topicInfo.title;
|
||||
|
||||
|
||||
if (options.enableEmoji) {
|
||||
title = performEmojiUnescape(topicInfo.title, {
|
||||
getURL: options.getURL, emojiSet: options.emojiSet
|
||||
|
@ -43,16 +43,20 @@ function loadNext(ajax) {
|
||||
|
||||
let timeoutMs = 150;
|
||||
let removeLoading = true;
|
||||
const { url, refresh, $elem, userId } = loadingQueue.shift();
|
||||
const { url, refresh, $elem, categoryId, topicId } = loadingQueue.shift();
|
||||
|
||||
// Retrieve the onebox
|
||||
return ajax("/onebox", {
|
||||
dataType: 'html',
|
||||
data: { url, refresh },
|
||||
data: {
|
||||
url,
|
||||
refresh,
|
||||
category_id: categoryId,
|
||||
topic_id: topicId
|
||||
},
|
||||
cache: true
|
||||
}).then(html => {
|
||||
let $html = $(html);
|
||||
|
||||
localCache[normalize(url)] = $html;
|
||||
$elem.replaceWith($html);
|
||||
applySquareGenericOnebox($html, normalize(url));
|
||||
@ -60,7 +64,7 @@ function loadNext(ajax) {
|
||||
if (result && result.jqXHR && result.jqXHR.status === 429) {
|
||||
timeoutMs = 2000;
|
||||
removeLoading = false;
|
||||
loadingQueue.unshift({ url, refresh, $elem, userId });
|
||||
loadingQueue.unshift({ url, refresh, $elem, categoryId, topicId });
|
||||
} else {
|
||||
failedCache[normalize(url)] = true;
|
||||
}
|
||||
@ -75,14 +79,14 @@ function loadNext(ajax) {
|
||||
|
||||
// Perform a lookup of a onebox based an anchor $element.
|
||||
// It will insert a loading indicator and remove it when the loading is complete or fails.
|
||||
export function load(e, refresh, ajax, userId, synchronous) {
|
||||
const $elem = $(e);
|
||||
export function load({ elem , refresh = true, ajax, synchronous = false, categoryId, topicId }) {
|
||||
const $elem = $(elem);
|
||||
|
||||
// If the onebox has loaded or is loading, return
|
||||
if ($elem.data('onebox-loaded')) return;
|
||||
if ($elem.hasClass('loading-onebox')) return;
|
||||
|
||||
const url = e.href;
|
||||
const url = elem.href;
|
||||
|
||||
// Unless we're forcing a refresh...
|
||||
if (!refresh) {
|
||||
@ -99,7 +103,7 @@ export function load(e, refresh, ajax, userId, synchronous) {
|
||||
$elem.addClass('loading-onebox');
|
||||
|
||||
// Add to the loading queue
|
||||
loadingQueue.push({ url, refresh, $elem, userId });
|
||||
loadingQueue.push({ url, refresh, $elem, categoryId, topicId });
|
||||
|
||||
// Load next url in queue
|
||||
if (synchronous) {
|
||||
|
@ -137,6 +137,7 @@ const DEFAULT_LIST = [
|
||||
'div.quote-controls',
|
||||
'div.title',
|
||||
'div[align]',
|
||||
'div[data-theme-*]',
|
||||
'div[dir]',
|
||||
'dl',
|
||||
'dt',
|
||||
|
@ -3,6 +3,7 @@ import { on } from "ember-addons/ember-computed-decorators";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import PermissionType from "discourse/models/permission-type";
|
||||
import Category from "discourse/models/category";
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
const { get, isNone, isEmpty } = Ember;
|
||||
|
||||
export default ComboBoxComponent.extend({
|
||||
@ -57,6 +58,36 @@ export default ComboBoxComponent.extend({
|
||||
}
|
||||
},
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
|
||||
if (this.get("hasSelection")) {
|
||||
const category = Category.findById(content.value);
|
||||
const parentCategoryId = category.get("parent_category_id");
|
||||
const hasParentCategory = Ember.isPresent(parentCategoryId);
|
||||
|
||||
let badge = "";
|
||||
|
||||
if (hasParentCategory) {
|
||||
const parentCategory = Category.findById(parentCategoryId);
|
||||
badge += categoryBadgeHTML(parentCategory, {
|
||||
link: false,
|
||||
allowUncategorized: true
|
||||
}).htmlSafe();
|
||||
}
|
||||
|
||||
badge += categoryBadgeHTML(category, {
|
||||
link: false,
|
||||
hideParent: hasParentCategory ? true : false,
|
||||
allowUncategorized: true
|
||||
}).htmlSafe();
|
||||
|
||||
content.label = badge;
|
||||
}
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
@on("didRender")
|
||||
_bindComposerResizing() {
|
||||
this.appEvents.on("composer:resized", this, this.applyDirection);
|
||||
|
@ -32,18 +32,21 @@ export default SelectKitRowComponent.extend({
|
||||
}
|
||||
},
|
||||
|
||||
@computed("category")
|
||||
badgeForCategory(category) {
|
||||
@computed("category", "parentCategory")
|
||||
badgeForCategory(category, parentCategory) {
|
||||
return categoryBadgeHTML(category, {
|
||||
link: this.get("categoryLink"),
|
||||
allowUncategorized: this.get("allowUncategorized"),
|
||||
hideParent: this.get("hideParentCategory")
|
||||
hideParent: parentCategory ? true : false
|
||||
}).htmlSafe();
|
||||
},
|
||||
|
||||
@computed("parentCategory")
|
||||
badgeForParentCategory(parentCategory) {
|
||||
return categoryBadgeHTML(parentCategory, {link: false}).htmlSafe();
|
||||
return categoryBadgeHTML(parentCategory, {
|
||||
link: this.get("categoryLink"),
|
||||
allowUncategorized: this.get("allowUncategorized")
|
||||
}).htmlSafe();
|
||||
},
|
||||
|
||||
@computed("parentCategoryid")
|
||||
|
@ -180,8 +180,7 @@ export default DropdownSelectBoxComponent.extend({
|
||||
options.action = Composer.CREATE_TOPIC;
|
||||
options.categoryId = this.get("composerModel.topic.category.id");
|
||||
|
||||
this.get("composerController").close();
|
||||
this.get("composerController").open(options);
|
||||
this._replyFromExisting(options, _postSnapshot, _topicSnapshot);
|
||||
break;
|
||||
|
||||
case "reply_as_private_message":
|
||||
|
@ -3,7 +3,7 @@ import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||
import renderTag from "discourse/lib/render-tag";
|
||||
const { get, isEmpty, isPresent, run } = Ember;
|
||||
const { get, isEmpty, isPresent, run, makeArray } = Ember;
|
||||
|
||||
export default ComboBox.extend({
|
||||
allowContentReplacement: true,
|
||||
@ -14,6 +14,9 @@ export default ComboBox.extend({
|
||||
filterable: true,
|
||||
noTags: Ember.computed.empty("computedTags"),
|
||||
allowAny: true,
|
||||
maximumSelectionSize: Ember.computed.alias("siteSettings.max_tags_per_topic"),
|
||||
caretUpIcon: Ember.computed.alias("caretIcon"),
|
||||
caretDownIcon: Ember.computed.alias("caretIcon"),
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
@ -29,12 +32,41 @@ export default ComboBox.extend({
|
||||
});
|
||||
},
|
||||
|
||||
@computed("limitReached", "maximumSelectionSize")
|
||||
maxContentRow(limitReached, count) {
|
||||
if (limitReached) {
|
||||
return I18n.t("select_kit.max_content_reached", { count });
|
||||
}
|
||||
},
|
||||
|
||||
mutateAttributes() {
|
||||
this.set("value", null);
|
||||
},
|
||||
|
||||
@computed("limitReached")
|
||||
caretIcon(limitReached) {
|
||||
return limitReached ? null : "plus";
|
||||
},
|
||||
|
||||
@computed("computedTags.[]", "maximumSelectionSize")
|
||||
limitReached(computedTags, maximumSelectionSize) {
|
||||
if (computedTags.length >= maximumSelectionSize) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
@computed("tags")
|
||||
computedTags(tags) {
|
||||
return Ember.makeArray(tags);
|
||||
return makeArray(tags);
|
||||
},
|
||||
|
||||
validateCreate(term) {
|
||||
if (this.get("limitReached") || !this.site.get("can_create_tag")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const filterRegexp = new RegExp(this.site.tags_filter_regexp, "g");
|
||||
term = term.replace(filterRegexp, "").trim().toLowerCase();
|
||||
|
||||
@ -50,8 +82,11 @@ export default ComboBox.extend({
|
||||
},
|
||||
|
||||
validateSelect() {
|
||||
return this.get("computedTags").length < this.get("siteSettings.max_tags_per_topic") &&
|
||||
this.site.get("can_create_tag");
|
||||
return this.get("computedTags").length < this.get("siteSettings.max_tags_per_topic");
|
||||
},
|
||||
|
||||
filterComputedContent(computedContent) {
|
||||
return computedContent;
|
||||
},
|
||||
|
||||
didRender() {
|
||||
@ -111,7 +146,7 @@ export default ComboBox.extend({
|
||||
|
||||
@computed("tags.[]", "filter")
|
||||
collectionHeader(tags, filter) {
|
||||
if (!Ember.isEmpty(tags)) {
|
||||
if (!isEmpty(tags)) {
|
||||
let output = "";
|
||||
|
||||
if (tags.length >= 20) {
|
||||
@ -132,13 +167,16 @@ export default ComboBox.extend({
|
||||
|
||||
computeHeaderContent() {
|
||||
let content = this.baseHeaderComputedContent();
|
||||
const joinedTags = this.get("computedTags").join(", ");
|
||||
|
||||
if (isEmpty(this.get("computedTags"))) {
|
||||
content.label = I18n.t("tagging.choose_for_topic");
|
||||
} else {
|
||||
content.label = this.get("computedTags").join(",");
|
||||
content.label = joinedTags;
|
||||
}
|
||||
|
||||
content.title = content.name = content.value = joinedTags;
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
@ -148,43 +186,38 @@ export default ComboBox.extend({
|
||||
delete tags[tags.indexOf(tag)];
|
||||
this.set("tags", tags.filter(t => t));
|
||||
this.set("content", []);
|
||||
this.set("searchDebounce", run.debounce(this, this._searchTags, 200));
|
||||
this.set("searchDebounce", run.debounce(this, this._searchTags, this.get("filter"), 250));
|
||||
},
|
||||
|
||||
onExpand() {
|
||||
this.set("searchDebounce", run.debounce(this, this._searchTags, 200));
|
||||
if (isEmpty(this.get("content"))) {
|
||||
this.set("searchDebounce", run.debounce(this, this._searchTags, this.get("filter"), 250));
|
||||
}
|
||||
},
|
||||
|
||||
onFilter(filter) {
|
||||
filter = isEmpty(filter) ? null : filter;
|
||||
this.set("searchDebounce", run.debounce(this, this._searchTags, filter, 200));
|
||||
this.set("searchDebounce", run.debounce(this, this._searchTags, filter, 250));
|
||||
},
|
||||
|
||||
onSelect(tag) {
|
||||
if (isEmpty(this.get("computedTags"))) {
|
||||
this.set("tags", Ember.makeArray(tag));
|
||||
this.set("tags", makeArray(tag));
|
||||
} else {
|
||||
this.set("tags", this.get("computedTags").concat(tag));
|
||||
}
|
||||
|
||||
this.set("content", []);
|
||||
this.set("searchDebounce", run.debounce(this, this._searchTags, 200));
|
||||
this.set("searchDebounce", run.debounce(this, this._searchTags, this.get("filter"), 250));
|
||||
}
|
||||
},
|
||||
|
||||
muateAttributes() {
|
||||
this.set("value", null);
|
||||
},
|
||||
|
||||
_searchTags(query) {
|
||||
this.startLoading();
|
||||
|
||||
const selectedTags = Ember.makeArray(this.get("computedTags")).filter(t => t);
|
||||
|
||||
const self = this;
|
||||
|
||||
const selectedTags = makeArray(this.get("computedTags")).filter(t => t);
|
||||
const sortTags = this.siteSettings.tags_sort_alphabetically;
|
||||
|
||||
const data = {
|
||||
q: query,
|
||||
limit: this.siteSettings.max_tag_search_results,
|
||||
|
@ -1,9 +1,20 @@
|
||||
import CategoryRowComponent from "select-kit/components/category-row";
|
||||
import { categoryBadgeHTML } from "discourse/helpers/category-link";
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default CategoryRowComponent.extend({
|
||||
layoutName: "select-kit/templates/components/category-row",
|
||||
classNames: "none category-row",
|
||||
|
||||
@computed("category")
|
||||
badgeForCategory(category) {
|
||||
return categoryBadgeHTML(category, {
|
||||
link: this.get("categoryLink"),
|
||||
allowUncategorized: true,
|
||||
hideParent: true
|
||||
}).htmlSafe();
|
||||
},
|
||||
|
||||
click() {
|
||||
this.sendAction("clearSelection");
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
|
||||
tabindex: 0,
|
||||
none: null,
|
||||
highlightedValue: null,
|
||||
noContentLabel: "select_kit.no_content",
|
||||
valueAttribute: "id",
|
||||
nameProperty: "name",
|
||||
autoFilterable: false,
|
||||
@ -70,6 +69,8 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
|
||||
allowContentReplacement: false,
|
||||
collectionHeader: null,
|
||||
allowAutoSelectFirst: true,
|
||||
maximumSelectionSize: null,
|
||||
maxContentRow: null,
|
||||
|
||||
init() {
|
||||
this._super();
|
||||
@ -155,8 +156,10 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
|
||||
},
|
||||
|
||||
@computed("filter", "filteredComputedContent.[]")
|
||||
shouldDisplayNoContentRow(filter, filteredComputedContent) {
|
||||
return filter.length > 0 && filteredComputedContent.length === 0;
|
||||
noContentRow(filter, filteredComputedContent) {
|
||||
if (filter.length > 0 && filteredComputedContent.length === 0) {
|
||||
return I18n.t("select_kit.no_content");
|
||||
}
|
||||
},
|
||||
|
||||
@computed("filter", "filterable", "autoFilterable", "renderedFilterOnce")
|
||||
|
@ -40,11 +40,11 @@
|
||||
select=(action "select")
|
||||
highlight=(action "highlight")
|
||||
create=(action "create")
|
||||
noContentLabel=noContentLabel
|
||||
highlightedValue=highlightedValue
|
||||
computedValue=computedValue
|
||||
shouldDisplayNoContentRow=shouldDisplayNoContentRow
|
||||
rowComponentOptions=rowComponentOptions
|
||||
noContentRow=noContentRow
|
||||
maxContentRow=maxContentRow
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
@ -30,22 +30,26 @@
|
||||
}}
|
||||
{{/if}}
|
||||
|
||||
{{#each filteredComputedContent as |computedContent|}}
|
||||
{{component rowComponent
|
||||
computedContent=computedContent
|
||||
highlightedValue=highlightedValue
|
||||
computedValue=computedValue
|
||||
templateForRow=templateForRow
|
||||
select=select
|
||||
highlight=highlight
|
||||
options=rowComponentOptions
|
||||
}}
|
||||
{{/each}}
|
||||
|
||||
{{#if shouldDisplayNoContentRow}}
|
||||
{{#if noContentLabel}}
|
||||
{{#if maxContentRow}}
|
||||
<li class="select-kit-row max-content">
|
||||
{{maxContentRow}}
|
||||
</li>
|
||||
{{else}}
|
||||
{{#if noContentRow}}
|
||||
<li class="select-kit-row no-content">
|
||||
{{i18n noContentLabel}}
|
||||
{{noContentRow}}
|
||||
</li>
|
||||
{{else}}
|
||||
{{#each filteredComputedContent as |computedContent|}}
|
||||
{{component rowComponent
|
||||
computedContent=computedContent
|
||||
highlightedValue=highlightedValue
|
||||
computedValue=computedValue
|
||||
templateForRow=templateForRow
|
||||
select=select
|
||||
highlight=highlight
|
||||
options=rowComponentOptions
|
||||
}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
@ -130,14 +130,23 @@ $mobile-breakpoint: 700px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-list li a span.count {
|
||||
font-size: $font-down-1;
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
background-color: $primary-low;
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
color: $primary;
|
||||
.content-list {
|
||||
width: 27%;
|
||||
float: left;
|
||||
li a span.count {
|
||||
font-size: $font-down-1;
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
background-color: $primary-low;
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.content-body {
|
||||
float: left;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.admin-content {
|
||||
@ -196,7 +205,7 @@ $mobile-breakpoint: 700px;
|
||||
width: 460px;
|
||||
right: 0;
|
||||
z-index: z("dropdown");
|
||||
box-shadow: 0 2px 6px rgba(0,0,0, .8);
|
||||
box-shadow: shadow("card");
|
||||
margin-top: -2px;
|
||||
background-color: $secondary;
|
||||
padding: 12px 12px 5px;
|
||||
@ -259,6 +268,10 @@ $mobile-breakpoint: 700px;
|
||||
background-color: $primary-low;
|
||||
padding: 10px 10px 3px 0;
|
||||
@include clearfix;
|
||||
nav {
|
||||
float: left;
|
||||
margin-left: 12px;
|
||||
}
|
||||
.nav.nav-pills {
|
||||
li.active {
|
||||
a {
|
||||
@ -506,7 +519,6 @@ $mobile-breakpoint: 700px;
|
||||
background-color: $secondary;
|
||||
border: 1px solid $primary-low;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 1px 1px rgba(51, 51, 51, 0.3);
|
||||
transition: border linear 0.2s, box-shadow linear 0.2s;
|
||||
|
||||
li.select2-search-choice {
|
||||
|
@ -220,3 +220,7 @@
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#custom_emoji {
|
||||
width: 27%;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
z-index: z("composer","content");
|
||||
transition: height 250ms ease, background 250ms ease, transform 250ms ease, max-width 250ms ease;
|
||||
background-color: $secondary;
|
||||
box-shadow: 0 -1px 40px rgba(0,0,0, .12);
|
||||
box-shadow: shadow("composer");
|
||||
|
||||
.reply-area {
|
||||
display: flex;
|
||||
|
@ -6,7 +6,7 @@ body.crawler {
|
||||
z-index: z("max");
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
box-shadow: 0 2px 4px -1px rgba(0,0,0,0.25);
|
||||
box-shadow: shadow("header");
|
||||
}
|
||||
div.topic-list div[itemprop='itemListElement'] {
|
||||
padding: 10px 0;
|
||||
|
@ -165,7 +165,7 @@ textarea {
|
||||
|
||||
&:focus:required:invalid:focus {
|
||||
border-color: $danger;
|
||||
box-shadow: 0 0 6px $danger;
|
||||
box-shadow: shadow("focus-danger");
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ input {
|
||||
border-radius: 0;
|
||||
&:focus {
|
||||
border-color: $tertiary;
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
@ -208,7 +208,7 @@ textarea {
|
||||
border: 1px solid $primary-medium;
|
||||
&:focus {
|
||||
border-color: $tertiary;
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
@ -5,14 +5,12 @@ img.emoji {
|
||||
}
|
||||
|
||||
.emoji-picker {
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
|
||||
background-clip: padding-box;
|
||||
z-index: z("modal","content");
|
||||
position: fixed;
|
||||
display: none;
|
||||
flex-direction: row;
|
||||
height: 300px;
|
||||
border-radius: 3px;
|
||||
color: dark-light-choose(darken($primary, 40%), blend-primary-secondary(90%));
|
||||
background-color: $secondary;
|
||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
|
@ -4,7 +4,7 @@
|
||||
top: 0;
|
||||
z-index: z("header");
|
||||
background-color: $header_background;
|
||||
box-shadow: 0 2px 4px -1px rgba(0,0,0, .25);
|
||||
box-shadow: shadow("header");
|
||||
|
||||
.docked & {
|
||||
position: fixed;
|
||||
@ -33,11 +33,15 @@
|
||||
.panel {
|
||||
float: right;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-buttons {
|
||||
margin-top: .2em;
|
||||
}
|
||||
|
||||
.login-button, button.sign-up-button {
|
||||
float: left;
|
||||
margin-top: 7px;
|
||||
padding: 6px 10px;
|
||||
.fa { margin-right: 3px; }
|
||||
}
|
||||
@ -54,6 +58,7 @@
|
||||
.header-dropdown-toggle, .drop-down, .panel-body {
|
||||
.flagged-posts, .queued-posts {
|
||||
background: $danger;
|
||||
min-width: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,14 +67,17 @@
|
||||
margin: 0 0 0 5px;
|
||||
list-style: none;
|
||||
|
||||
|
||||
> li {
|
||||
float: left;
|
||||
}
|
||||
.icon {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.2857em;
|
||||
height: 2.2857em;
|
||||
padding: .2143em;
|
||||
color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
@ -77,11 +85,13 @@
|
||||
border-left: 1px solid transparent;
|
||||
border-right: 1px solid transparent;
|
||||
transition: all linear .15s;
|
||||
|
||||
|
||||
img.avatar {
|
||||
width: 2.2857em;
|
||||
height: 2.2857em;
|
||||
}
|
||||
&:hover {
|
||||
color: $primary;
|
||||
background-color: $primary-low;
|
||||
background-color: $primary-low;
|
||||
border-top: 1px solid transparent;
|
||||
border-left: 1px solid transparent;
|
||||
border-right: 1px solid transparent;
|
||||
@ -119,8 +129,7 @@
|
||||
}
|
||||
|
||||
.d-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
width: 100%;
|
||||
font-size: $font-up-4;
|
||||
line-height: $line-height-large;
|
||||
display: inline-block;
|
||||
|
@ -1,6 +1,7 @@
|
||||
.menu-panel.slide-in {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
box-shadow: shadow("header");
|
||||
|
||||
.panel-body {
|
||||
position: absolute;
|
||||
@ -19,7 +20,7 @@
|
||||
|
||||
.menu-panel {
|
||||
border: 1px solid $primary-low;
|
||||
box-shadow: 0 2px 2px rgba(0,0,0, .25);
|
||||
box-shadow: shadow("menu-panel");
|
||||
background-color: $secondary;
|
||||
z-index: z("header");
|
||||
padding: 0.5em;
|
||||
@ -44,7 +45,6 @@
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.menu-links.columned {
|
||||
@ -69,7 +69,6 @@
|
||||
margin-left: 0.5em;
|
||||
color: dark-light-choose($primary-medium, $secondary-medium);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
li.category-link {
|
||||
@ -77,7 +76,7 @@
|
||||
background-color: transparent;
|
||||
display: inline-flex;
|
||||
margin: 0.25em 0.5em;
|
||||
width: 44%;
|
||||
width: 43%;
|
||||
.badge-notification {
|
||||
color: dark-light-choose($primary-medium, $secondary-medium);
|
||||
background-color: transparent;
|
||||
@ -90,7 +89,6 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
max-width: 80%;
|
||||
&.bar, &.bullet {
|
||||
color: $primary;
|
||||
}
|
||||
@ -100,7 +98,7 @@
|
||||
padding-top: 2px;
|
||||
}
|
||||
span {
|
||||
z-index: z("base") * -1;
|
||||
z-index: z("base") * -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
overflow: auto;
|
||||
height: auto;
|
||||
background-color: $secondary;
|
||||
box-shadow: 0 2px 2px rgba(0,0,0, .25);
|
||||
box-shadow: shadow("card");
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
@ -45,8 +45,7 @@
|
||||
|
||||
.modal-backdrop,
|
||||
.modal-backdrop.fade.in {
|
||||
-webkit-animation: fade .3s;
|
||||
animation: fade .3s;
|
||||
animation: fade .3s;
|
||||
opacity: .9;
|
||||
filter: alpha(opacity=90);
|
||||
}
|
||||
@ -57,22 +56,12 @@
|
||||
to { opacity: .9 }
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade {
|
||||
from { opacity: 0 }
|
||||
to { opacity: .9 }
|
||||
}
|
||||
|
||||
// slide in
|
||||
@keyframes slidein {
|
||||
from { transform: translateY(-20%); }
|
||||
to { transform: translateY(0); }
|
||||
}
|
||||
|
||||
@-webkit-keyframes slidein {
|
||||
from { -webkit-transform: translateY(-20%); }
|
||||
to { -webkit-transform: translateY(0); }
|
||||
}
|
||||
|
||||
.modal-outer-container {
|
||||
display:table;
|
||||
table-layout: fixed;
|
||||
@ -85,6 +74,7 @@
|
||||
margin: 0 auto;
|
||||
background-color: $secondary;
|
||||
background-clip: padding-box;
|
||||
box-shadow: shadow("modal");
|
||||
|
||||
.select-kit {
|
||||
width: 220px;
|
||||
@ -93,8 +83,7 @@
|
||||
|
||||
.create-account.in .modal-inner-container,
|
||||
.login-modal.in .modal-inner-container {
|
||||
-webkit-animation: slidein .3s;
|
||||
animation: slidein .3s;
|
||||
animation: slidein .3s;
|
||||
}
|
||||
|
||||
|
||||
|
@ -463,10 +463,22 @@ aside.onebox.stackexchange .onebox-body {
|
||||
.label2 {
|
||||
float: right;
|
||||
}
|
||||
.site-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.onebox {
|
||||
&.whitelistedgeneric,
|
||||
&.gfycat {
|
||||
.site-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.onebox.gfycat p {
|
||||
span.label1 a {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
z-index: z("dropdown");
|
||||
box-shadow: 0 1px 5px rgba(0,0,0, .4);
|
||||
box-shadow: shadow("card");
|
||||
background-color: $secondary;
|
||||
padding: 6px 10px 10px 10px;
|
||||
width: 300px;
|
||||
|
@ -176,6 +176,7 @@ $tag-color: $primary-medium;
|
||||
|
||||
.mobile-view .topic-list-item .discourse-tags {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
font-size: $font-down-1;
|
||||
margin-top: 0;
|
||||
.discourse-tag {
|
||||
|
@ -12,8 +12,9 @@
|
||||
background-color: $secondary;
|
||||
width: 205px;
|
||||
padding: 10px;
|
||||
border: 1px solid $primary-low;
|
||||
border: 1px solid $primary-low;
|
||||
z-index: z("dropdown");
|
||||
box-shadow: shadow("card");
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
|
@ -263,7 +263,7 @@ aside.quote {
|
||||
|
||||
}
|
||||
|
||||
.topic-avatar, .avatar-flair-preview, .user-card-avatar, .topic-map .poster {
|
||||
.topic-avatar, .avatar-flair-preview, .user-card-avatar, .topic-map .poster, .user-profile-avatar {
|
||||
.avatar-flair {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -275,7 +275,7 @@ aside.quote {
|
||||
right: -6px;
|
||||
}
|
||||
}
|
||||
.topic-avatar .avatar-flair, .avatar-flair-preview .avatar-flair {
|
||||
.topic-avatar .avatar-flair, .avatar-flair-preview .avatar-flair, .collapsed-info .user-profile-avatar .avatar-flair {
|
||||
background-size: 20px 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
@ -288,7 +288,7 @@ aside.quote {
|
||||
right: -8px;
|
||||
}
|
||||
}
|
||||
.user-card-avatar .avatar-flair {
|
||||
.user-card-avatar .avatar-flair, .user-profile-avatar .avatar-flair {
|
||||
background-size: 40px 40px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
@ -398,7 +398,11 @@ kbd
|
||||
background-color: $secondary;
|
||||
border: 1px solid $primary-low;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 rgba(0,0,0, .8);
|
||||
box-shadow: shadow("kbd");
|
||||
background: dark-light-choose(#fafafa, #333);
|
||||
border: 1px solid dark-light-choose(#ccc, #555);
|
||||
border-bottom: medium none dark-light-choose(#fff, #000);
|
||||
|
||||
color: $primary;
|
||||
display: inline-block;
|
||||
font-size: $font-down-1;
|
||||
|
@ -124,7 +124,6 @@
|
||||
background-color: $primary-low;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
box-shadow: 1px 1px 3px rgba(0.0, 0.0, 0.0, 0.2);
|
||||
|
||||
.check-display {
|
||||
position: absolute;
|
||||
|
@ -120,6 +120,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-profile-avatar {
|
||||
position: relative;
|
||||
float: left;
|
||||
height: 100%;
|
||||
.avatar-flair {
|
||||
bottom: 8px;
|
||||
right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
@ -176,6 +186,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-profile-avatar {
|
||||
.avatar-flair {
|
||||
bottom: 8px;
|
||||
right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,11 +211,9 @@
|
||||
|
||||
.badge-group {
|
||||
@extend %badge;
|
||||
padding: 4px 5px 2px 5px;
|
||||
padding: 2px 5px;
|
||||
color: $primary;
|
||||
text-shadow: 0 1px 0 rgba($primary, 0.1);
|
||||
background-color: $primary-low;
|
||||
border-color: $primary-low;
|
||||
font-size: $font-down-1;
|
||||
box-shadow: inset 0 1px 0 rgba(0,0,0, 0.22);
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
#banner {
|
||||
padding: 10px;
|
||||
background: $tertiary-low;
|
||||
box-shadow: 0 2px 4px -1px rgba(0,0,0, .25);
|
||||
color: $primary;
|
||||
z-index: z("base") + 1;
|
||||
overflow: auto;
|
||||
|
@ -119,8 +119,6 @@
|
||||
|
||||
.btn-social {
|
||||
color: #fff;
|
||||
text-shadow: 0 1px 0 rgba($primary, 0.2);
|
||||
box-shadow: inset 0 1px 0 rgba(0,0,0, 0.1);
|
||||
&:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
@ -15,8 +15,12 @@
|
||||
}
|
||||
|
||||
#keyboard-shortcuts-help {
|
||||
.span6 {
|
||||
width:32%;
|
||||
div.row {
|
||||
width: 100%;
|
||||
div {
|
||||
float: left;
|
||||
width:32%;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
@ -29,7 +33,7 @@
|
||||
b {
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 0 rgba(0,0,0,0.2),0 0 0 1px dark-light-choose(#fff,#000) inset;
|
||||
box-shadow: shadow("kbd");
|
||||
background: dark-light-choose(#fafafa, #333);
|
||||
border: 1px solid dark-light-choose(#ccc, #555);
|
||||
border-bottom: medium none dark-light-choose(#fff, #000);
|
||||
|
@ -103,6 +103,26 @@ $z-layers: (
|
||||
@return map-deep-get($z-layers, $layers...);
|
||||
}
|
||||
|
||||
|
||||
// Box-shadow
|
||||
// --------------------------------------------------
|
||||
|
||||
$box-shadow: (
|
||||
"modal": 0 8px 60px rgba(0, 0, 0, 0.6),
|
||||
"composer": 0 -1px 40px rgba(0, 0, 0, 0.12),
|
||||
"menu-panel": 0 6px 14px rgba(0, 0, 0, 0.15),
|
||||
"card": 0 4px 14px rgba(0, 0, 0, 0.15),
|
||||
"dropdown": 0 2px 3px 0 rgba(0, 0, 0, 0.2),
|
||||
"header": 0 2px 4px -1px rgba(0, 0, 0, 0.25),
|
||||
"kbd": (0 2px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px dark-light-choose(#fff, #000) inset),
|
||||
"focus": 0 0 6px 0 $tertiary,
|
||||
"focus-danger": 0 0 6px 0 $danger
|
||||
);
|
||||
|
||||
@function shadow($key) {
|
||||
@return map-get($box-shadow, $key);
|
||||
}
|
||||
|
||||
// Color utilities
|
||||
// --------------------------------------------------
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
&.bad {
|
||||
background: $danger-medium;
|
||||
color: white;
|
||||
box-shadow: 1px 1px 5px rgba(0,0,0, .7);
|
||||
box-shadow: shadow("dropdown");
|
||||
}
|
||||
&.hide, &.good {
|
||||
display: none;
|
||||
|
@ -6,7 +6,6 @@
|
||||
.badge-wrapper {
|
||||
font-size: $font-0;
|
||||
font-weight: normal;
|
||||
line-height: $line-height-large;
|
||||
|
||||
&.box {
|
||||
margin: 0;
|
||||
@ -17,40 +16,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.bar.has-selection .category-drop-header {
|
||||
padding: 4.5px 10px;
|
||||
&.bar.has-selection .category-drop-header,
|
||||
&.box.has-selection .category-drop-header,
|
||||
&.none.has-selection .category-drop-header {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
&.bullet.has-selection .category-drop-header {
|
||||
padding: 6px 10px;
|
||||
span.badge-category {
|
||||
line-height: $line-height-medium;
|
||||
}
|
||||
.selected-name {
|
||||
.bullet {
|
||||
line-height: $line-height-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.box.has-selection .category-drop-header {
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
&.none.has-selection .category-drop-header {
|
||||
padding: 4.5px 10px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.category-drop-header {
|
||||
background: $primary-low;
|
||||
color: $primary;
|
||||
border: none;
|
||||
padding: 6px 10px;
|
||||
font-size: $font-up-1;
|
||||
line-height: $line-height-medium;
|
||||
border: 1px solid transparent;
|
||||
padding: 5px 10px;
|
||||
font-size: $font-0;
|
||||
transition: none;
|
||||
|
||||
|
||||
.badge-wrapper {
|
||||
margin-right: 0;
|
||||
}
|
||||
@ -64,6 +47,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.is-expanded .category-drop-header {
|
||||
border: 1px solid $tertiary;
|
||||
box-shadow: shadow("focus");
|
||||
}
|
||||
|
||||
.select-kit-collection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -93,8 +81,7 @@
|
||||
width: auto;
|
||||
min-width: 300px;
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.4);
|
||||
box-shadow: 0 2px 2px rgba(0,0,0,0.4);
|
||||
box-shadow: shadow("dropdown");
|
||||
}
|
||||
|
||||
.select-kit-row {
|
||||
|
@ -32,8 +32,7 @@
|
||||
|
||||
&.is-focused {
|
||||
border: 1px solid $tertiary;
|
||||
-webkit-box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,8 +46,7 @@
|
||||
&.is-highlighted {
|
||||
.select-kit-header {
|
||||
border: 1px solid $tertiary;
|
||||
-webkit-box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,14 +54,12 @@
|
||||
.select-kit-wrapper {
|
||||
display: block;
|
||||
border: 1px solid $tertiary;
|
||||
-webkit-box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
}
|
||||
|
||||
.select-kit-header {
|
||||
border-color: transparent;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.select-kit-body {
|
||||
|
@ -16,8 +16,7 @@
|
||||
.select-kit-body {
|
||||
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||
background-clip: padding-box;
|
||||
-webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.4);
|
||||
box-shadow: 0 2px 2px rgba(0,0,0,0.4);
|
||||
box-shadow: shadow("dropdown");
|
||||
max-width: 300px;
|
||||
width: 300px;
|
||||
}
|
||||
@ -120,6 +119,12 @@
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.d-icon {
|
||||
color: $primary-low;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-focused {
|
||||
outline-style: auto;
|
||||
outline-color: $tertiary;
|
||||
|
@ -79,7 +79,7 @@
|
||||
}
|
||||
|
||||
.select2-container-active {
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
}
|
||||
|
||||
.select2-results .select2-no-results, .select2-results .select2-searching, .select2-results .select2-selection-limit {
|
||||
|
@ -7,8 +7,7 @@
|
||||
&.is-expanded {
|
||||
.select-kit-header {
|
||||
border: 1px solid $tertiary;
|
||||
-webkit-box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,14 +20,15 @@
|
||||
.select-kit-body {
|
||||
max-width: 500px;
|
||||
width: 500px;
|
||||
border: 1px solid $primary-low;
|
||||
border: 1px solid $tertiary;
|
||||
box-shadow: shadow("focus")
|
||||
}
|
||||
|
||||
.select-kit-filter {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.select-kit-wrapper {
|
||||
&.is-expanded .select-kit-wrapper, .select-kit-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -59,8 +59,7 @@
|
||||
}
|
||||
|
||||
.selected-tag {
|
||||
background: $primary-low;
|
||||
border-radius: 2px;
|
||||
background: $primary-very-low;
|
||||
padding: 2px 4px;
|
||||
margin: 2px;
|
||||
border: 0;
|
||||
@ -69,10 +68,17 @@
|
||||
box-shadow: 0 0 2px $danger, 0 1px 0 rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
&:before {
|
||||
&:after {
|
||||
content: '\f00d';
|
||||
color: $primary-low-mid;
|
||||
font-family: 'FontAwesome';
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:after {
|
||||
color: $danger;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
border: 1px solid $primary-medium;
|
||||
|
||||
&.is-focused {
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
@ -40,7 +40,7 @@
|
||||
.multi-select-header {
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
.select-kit-wrapper {
|
||||
display: block;
|
||||
border: 1px solid $tertiary;
|
||||
box-shadow: $tertiary 0 0 6px 0px;
|
||||
box-shadow: shadow("focus");
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
@ -101,7 +101,6 @@
|
||||
margin: 0;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
height: 21px;
|
||||
|
@ -18,10 +18,15 @@
|
||||
|
||||
h2.selected-name {
|
||||
overflow: auto;
|
||||
color: black;
|
||||
color: $secondary;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
|
||||
.date-section {
|
||||
color: $primary;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.top-date-string {
|
||||
font-size: $font-down-1;
|
||||
color: dark-light-choose($primary-medium, $secondary-high);
|
||||
@ -31,7 +36,7 @@
|
||||
}
|
||||
|
||||
.d-icon {
|
||||
color: black;
|
||||
color: $primary;
|
||||
opacity: 1;
|
||||
margin: 5px 0 10px 5px;
|
||||
font-size: $font-up-3;
|
||||
@ -46,8 +51,7 @@
|
||||
|
||||
.period-chooser-row {
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
color: #222;
|
||||
padding: 5px;;
|
||||
font-size: $font-up-1;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@ -56,6 +60,10 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.date-section {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
.top-date-string {
|
||||
font-weight: normal;
|
||||
font-size: $font-down-1;
|
||||
|
@ -165,6 +165,11 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.max-content {
|
||||
white-space: nowrap;
|
||||
color: $danger;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
|
@ -6,10 +6,10 @@
|
||||
.tag-drop-header {
|
||||
background: $primary-low;
|
||||
color: $primary;
|
||||
border: none;
|
||||
padding: 4.5px 10px;
|
||||
font-size: $font-up-1;
|
||||
line-height: $line-height-large;
|
||||
border: 1px solid transparent;
|
||||
padding: 5px 10px;
|
||||
font-size: $font-0;
|
||||
transition: none;
|
||||
|
||||
.d-icon {
|
||||
opacity: 1;
|
||||
@ -17,6 +17,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.is-expanded .tag-drop-header {
|
||||
border: 1px solid $tertiary;
|
||||
box-shadow: shadow("focus");
|
||||
}
|
||||
|
||||
.select-kit-collection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -44,8 +49,7 @@
|
||||
width: auto;
|
||||
min-width: 150px;
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.4);
|
||||
box-shadow: 0 2px 2px rgba(0,0,0,0.4);
|
||||
box-shadow: shadow("dropdown");
|
||||
}
|
||||
|
||||
.select-kit-row {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user