2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2025-09-04 08:47:37 +08:00

Merge branch 'master' into pm-tags

This commit is contained in:
Vinoth Kannan 2018-02-21 23:55:59 +05:30 committed by GitHub
commit 2b509eaa91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
357 changed files with 5863 additions and 2023 deletions

7
.gitattributes vendored
View file

@ -1,11 +1,14 @@
# Set default behaviour, in case users don't have core.autocrlf set. # Set default behaviour, in case users don't have core.autocrlf set.
* text=auto * 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. # to native line endings on checkout.
*.yml text *.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 *.cs diff=csharp
*.sln merge=union *.sln merge=union
*.csproj merge=union *.csproj merge=union

View file

@ -36,7 +36,7 @@ gem 'redis-namespace'
gem 'active_model_serializers', '~> 0.8.3' 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 gem 'http_accept_language', '~>2.0.5', require: false
@ -67,9 +67,6 @@ gem 'multi_json'
gem 'mustache' gem 'mustache'
gem 'nokogiri' gem 'nokogiri'
# this may end up deprecating nokogiri
gem 'oga', require: false
gem 'omniauth' gem 'omniauth'
gem 'omniauth-openid' gem 'omniauth-openid'
gem 'openid-redis-store' gem 'openid-redis-store'
@ -178,6 +175,9 @@ gem 'logster'
gem 'sassc', require: false gem 'sassc', require: false
gem 'rotp'
gem 'rqrcode'
if ENV["IMPORT"] == "1" if ENV["IMPORT"] == "1"
gem 'mysql2' gem 'mysql2'
gem 'redcarpet' gem 'redcarpet'

View file

@ -41,7 +41,6 @@ GEM
annotate (2.7.2) annotate (2.7.2)
activerecord (>= 3.2, < 6.0) activerecord (>= 3.2, < 6.0)
rake (>= 10.4, < 13.0) rake (>= 10.4, < 13.0)
ansi (1.5.0)
arel (8.0.0) arel (8.0.0)
ast (2.3.0) ast (2.3.0)
aws-partitions (1.24.0) aws-partitions (1.24.0)
@ -64,7 +63,7 @@ GEM
coderay (>= 1.0.0) coderay (>= 1.0.0)
erubis (>= 2.6.6) erubis (>= 2.6.6)
rack (>= 0.9.0) rack (>= 0.9.0)
binding_of_caller (0.7.2) binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootsnap (1.0.0) bootsnap (1.0.0)
msgpack (~> 1.0) msgpack (~> 1.0)
@ -74,13 +73,14 @@ GEM
uniform_notifier (~> 1.10.0) uniform_notifier (~> 1.10.0)
byebug (9.0.6) byebug (9.0.6)
certified (1.0.0) certified (1.0.0)
chunky_png (1.3.8)
coderay (1.1.2) coderay (1.1.2)
concurrent-ruby (1.0.5) concurrent-ruby (1.0.5)
connection_pool (2.2.1) connection_pool (2.2.1)
cppjieba_rb (0.3.0) cppjieba_rb (0.3.0)
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
crass (1.0.2) crass (1.0.3)
debug_inspector (0.0.3) debug_inspector (0.0.3)
diff-lcs (1.3) diff-lcs (1.3)
discourse-qunit-rails (0.0.11) discourse-qunit-rails (0.0.11)
@ -165,7 +165,7 @@ GEM
lru_redux (1.1.0) lru_redux (1.1.0)
mail (2.6.6) mail (2.6.6)
mime-types (>= 1.16, < 4) mime-types (>= 1.16, < 4)
memory_profiler (0.9.8) memory_profiler (0.9.10)
message_bus (2.1.2) message_bus (2.1.2)
rack (>= 1.1.3) rack (>= 1.1.3)
metaclass (0.0.4) metaclass (0.0.4)
@ -185,13 +185,13 @@ GEM
mock_redis (0.17.3) mock_redis (0.17.3)
moneta (1.0.0) moneta (1.0.0)
msgpack (1.1.0) msgpack (1.1.0)
multi_json (1.12.1) multi_json (1.13.1)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
mustache (1.0.5) mustache (1.0.5)
nokogiri (1.8.1) nokogiri (1.8.2)
mini_portile2 (~> 2.3.0) mini_portile2 (~> 2.3.0)
nokogumbo (1.4.13) nokogumbo (1.5.0)
nokogiri nokogiri
oauth (0.5.1) oauth (0.5.1)
oauth2 (1.3.1) oauth2 (1.3.1)
@ -200,10 +200,7 @@ GEM
multi_json (~> 1.3) multi_json (~> 1.3)
multi_xml (~> 0.5) multi_xml (~> 0.5)
rack (>= 1.2, < 3) rack (>= 1.2, < 3)
oga (2.10) oj (3.4.0)
ast
ruby-ll (~> 2.1)
oj (3.1.0)
omniauth (1.6.1) omniauth (1.6.1)
hashie (>= 3.4.6, < 3.6.0) hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.6.2, < 3) rack (>= 1.6.2, < 3)
@ -232,7 +229,7 @@ GEM
omniauth-twitter (1.3.0) omniauth-twitter (1.3.0)
omniauth-oauth (~> 1.1) omniauth-oauth (~> 1.1)
rack rack
onebox (1.8.36) onebox (1.8.40)
fast_blank (>= 1.0.0) fast_blank (>= 1.0.0)
htmlentities (~> 4.3) htmlentities (~> 4.3)
moneta (~> 1.0) moneta (~> 1.0)
@ -302,6 +299,9 @@ GEM
redis (~> 3.0, >= 3.0.4) redis (~> 3.0, >= 3.0.4)
request_store (1.3.2) request_store (1.3.2)
rinku (2.0.2) rinku (2.0.2)
rotp (3.3.0)
rqrcode (0.10.1)
chunky_png (~> 1.0)
rspec (3.6.0) rspec (3.6.0)
rspec-core (~> 3.6.0) rspec-core (~> 3.6.0)
rspec-expectations (~> 3.6.0) rspec-expectations (~> 3.6.0)
@ -334,9 +334,6 @@ GEM
rainbow (>= 2.2.2, < 3.0) rainbow (>= 2.2.2, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1) unicode-display_width (~> 1.0, >= 1.0.1)
ruby-ll (2.1.2)
ansi
ast
ruby-openid (2.7.0) ruby-openid (2.7.0)
ruby-prof (0.16.2) ruby-prof (0.16.2)
ruby-progressbar (1.9.0) ruby-progressbar (1.9.0)
@ -345,10 +342,10 @@ GEM
nokogiri (>= 1.6.0) nokogiri (>= 1.6.0)
ruby_dep (1.5.0) ruby_dep (1.5.0)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (4.5.0) sanitize (4.6.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
nokogumbo (~> 1.4.1) nokogumbo (~> 1.4)
sass (3.4.24) sass (3.4.24)
sassc (1.11.2) sassc (1.11.2)
bundler bundler
@ -459,7 +456,6 @@ DEPENDENCIES
multi_json multi_json
mustache mustache
nokogiri nokogiri
oga
oj oj
omniauth omniauth
omniauth-facebook omniauth-facebook
@ -469,7 +465,7 @@ DEPENDENCIES
omniauth-oauth2 omniauth-oauth2
omniauth-openid omniauth-openid
omniauth-twitter omniauth-twitter
onebox (= 1.8.36) onebox (= 1.8.40)
openid-redis-store openid-redis-store
pg (~> 0.21.0) pg (~> 0.21.0)
pry-nav pry-nav
@ -487,6 +483,8 @@ DEPENDENCIES
redis redis
redis-namespace redis-namespace
rinku rinku
rotp
rqrcode
rspec rspec
rspec-html-matchers rspec-html-matchers
rspec-rails rspec-rails

View file

@ -57,7 +57,6 @@ Plus *lots* of Ruby Gems, a complete list of which is at [/master/Gemfile](https
## Contributing ## Contributing
[![Build Status](https://api.travis-ci.org/discourse/discourse.svg?branch=master)](https://travis-ci.org/discourse/discourse) [![Build Status](https://api.travis-ci.org/discourse/discourse.svg?branch=master)](https://travis-ci.org/discourse/discourse)
[![Code Climate](https://codeclimate.com/github/discourse/discourse.svg)](https://codeclimate.com/github/discourse/discourse)
Discourse is **100% free** and **open source**. We encourage and support an active, healthy community that Discourse is **100% free** and **open source**. We encourage and support an active, healthy community that
accepts contributions from the public &ndash; including you! accepts contributions from the public &ndash; including you!

View 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;
});
}
});

View file

@ -19,6 +19,11 @@ export default Ember.Controller.extend(CanCheckEmails, {
primaryGroupDirty: propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'), primaryGroupDirty: propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'),
canDisableSecondFactor: Ember.computed.and(
'model.second_factor_enabled',
'model.can_disable_second_factor'
),
automaticGroups: function() { automaticGroups: function() {
return this.get("model.automaticGroups").map((g) => g.name).join(", "); return this.get("model.automaticGroups").map((g) => g.name).join(", ");
}.property("model.automaticGroups"), }.property("model.automaticGroups"),
@ -63,6 +68,7 @@ export default Ember.Controller.extend(CanCheckEmails, {
deleteAllPosts() { return this.get("model").deleteAllPosts(); }, deleteAllPosts() { return this.get("model").deleteAllPosts(); },
anonymize() { return this.get('model').anonymize(); }, anonymize() { return this.get('model').anonymize(); },
destroy() { return this.get('model').destroy(); }, destroy() { return this.get('model').destroy(); },
disableSecondFactor() { return this.get('model').disableSecondFactor(); },
viewActionLogs() { viewActionLogs() {
this.get('adminTools').showActionLogs(this, { this.get('adminTools').showActionLogs(this, {

View file

@ -168,6 +168,14 @@ const AdminUser = Discourse.User.extend({
}).catch(popupAjaxError); }).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() { refreshBrowsers() {
return ajax("/admin/users/" + this.get('id') + "/refresh_browsers", { return ajax("/admin/users/" + this.get('id') + "/refresh_browsers", {
type: 'POST' type: 'POST'

View file

@ -10,7 +10,7 @@ const StaffActionLog = Discourse.Model.extend({
}.property('action_name'), }.property('action_name'),
formattedDetails: function() { formattedDetails: function() {
var formatted = ""; let formatted = "";
formatted += this.format('email', 'email'); formatted += this.format('email', 'email');
formatted += this.format('admin.logs.ip_address', 'ip_address'); formatted += this.format('admin.logs.ip_address', 'ip_address');
formatted += this.format('admin.logs.topic_id', 'topic_id'); formatted += this.format('admin.logs.topic_id', 'topic_id');
@ -26,9 +26,13 @@ const StaffActionLog = Discourse.Model.extend({
return formatted; return formatted;
}.property('ip_address', 'email', 'topic_id', 'post_id', 'category_id'), }.property('ip_address', 'email', 'topic_id', 'post_id', 'category_id'),
format: function(label, propertyName) { format(label, propertyName) {
if (this.get(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 { } else {
return ''; return '';
} }

View file

@ -1,12 +1,12 @@
<div class='admin-backups'> <div class='admin-backups'>
<div class="admin-controls"> <div class="admin-controls">
<div class="span15"> <nav>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
{{nav-item route='admin.backups.index' label='admin.backups.menu.backups'}} {{nav-item route='admin.backups.index' label='admin.backups.menu.backups'}}
{{nav-item route='admin.backups.logs' label='admin.backups.menu.logs'}} {{nav-item route='admin.backups.logs' label='admin.backups.menu.logs'}}
{{plugin-outlet name="downloader" tagName=""}} {{plugin-outlet name="downloader" tagName=""}}
</ul> </ul>
</div> </nav>
<div class="pull-right"> <div class="pull-right">
{{#if model.canRollback}} {{#if model.canRollback}}
{{d-button action="rollback" {{d-button action="rollback"

View file

@ -1,4 +1,4 @@
{{#d-section class="current-badge span13"}} {{#d-section class="current-badge content-body"}}
<p>{{i18n 'admin.badges.none_selected'}}</p> <p>{{i18n 'admin.badges.none_selected'}}</p>
<div> <div>

View file

@ -1,4 +1,4 @@
{{#d-section class="current-badge span13"}} {{#d-section class="current-badge content-body"}}
<form class="form-horizontal"> <form class="form-horizontal">
<div> <div>
<label for="name">{{i18n 'admin.badges.name'}}</label> <label for="name">{{i18n 'admin.badges.name'}}</label>
@ -144,7 +144,7 @@
{{/d-section}} {{/d-section}}
{{#if grant_count}} {{#if grant_count}}
<div class="span13 current-badge-actions"> <div class="content-body current-badge-actions">
<div> <div>
{{#link-to 'badges.show' this}}{{i18n 'badges.granted' count=grant_count}}{{/link-to}} {{#link-to 'badges.show' this}}{{i18n 'badges.granted' count=grant_count}}{{/link-to}}
</div> </div>

View file

@ -1,6 +1,6 @@
<div class="badges"> <div class="badges">
<div class='content-list span6'> <div class='content-list'>
<h3>{{i18n 'admin.badges.title'}}</h3> <h3>{{i18n 'admin.badges.title'}}</h3>
<ul> <ul>
{{#each model as |badge|}} {{#each model as |badge|}}

View file

@ -1,7 +1,7 @@
<div class='admin-controls'> <div class='admin-controls'>
<div class='span15'> <nav>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
{{yield}} {{yield}}
</ul> </ul>
</div> </nav>
</div> </div>

View file

@ -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> <h3>{{i18n 'admin.customize.colors.long_title'}}</h3>
<ul> <ul>
{{#each model as |scheme|}} {{#each model as |scheme|}}

View file

@ -1,5 +1,5 @@
<div class='row'> <div class='row'>
<div class='content-list span6'> <div class='content-list'>
<ul> <ul>
{{#each sortedTemplates as |et|}} {{#each sortedTemplates as |et|}}
<li> <li>

View file

@ -1,5 +1,5 @@
{{#unless editingTheme}} {{#unless editingTheme}}
<div class='content-list span6'> <div class='content-list'>
<h3>{{i18n 'admin.customize.theme.long_title'}}</h3> <h3>{{i18n 'admin.customize.theme.long_title'}}</h3>
<ul> <ul>
{{#each sortedThemes as |theme|}} {{#each sortedThemes as |theme|}}

View file

@ -19,7 +19,7 @@
<div class='controls'> <div class='controls'>
{{text-field value=testEmailAddress placeholderKey="admin.email.test_email_address"}} {{text-field value=testEmailAddress placeholderKey="admin.email.test_email_address"}}
</div> </div>
<div class='span10 controls'> <div class='controls'>
<button class='btn btn-primary' {{action "sendTestEmail"}} disabled={{sendTestEmailDisabled}}>{{i18n 'admin.email.send_test'}}</button> <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}} {{#if sentTestEmail}}<span class='result-message'>{{i18n 'admin.email.sent_test'}}</span>{{/if}}
</div> </div>

View file

@ -6,7 +6,7 @@
<p>{{emoji-uploader done="emojiUploaded"}}</p> <p>{{emoji-uploader done="emojiUploaded"}}</p>
{{#if sortedEmojis}} {{#if sortedEmojis}}
<div class="span8"> <div>
<table id="custom_emoji"> <table id="custom_emoji">
<thead> <thead>
<tr> <tr>

View file

@ -1,6 +1,6 @@
<div class='row groups'> <div class='row groups'>
{{#if sortedGroups}} {{#if sortedGroups}}
<div class='content-list span6'> <div class='content-list'>
<h3>{{i18n 'admin.groups.edit'}}</h3> <h3>{{i18n 'admin.groups.edit'}}</h3>
<ul> <ul>
{{#each sortedGroups as |group|}} {{#each sortedGroups as |group|}}
@ -25,7 +25,7 @@
</div> </div>
{{/if}} {{/if}}
<div class="span13"> <div class="content-body">
{{outlet}} {{outlet}}
</div> </div>
</div> </div>

View file

@ -58,7 +58,7 @@
{{#unless item.editing}} {{#unless item.editing}}
{{d-button action="destroy" actionParam=item icon="trash-o" class="btn-danger"}} {{d-button action="destroy" actionParam=item icon="trash-o" class="btn-danger"}}
{{d-button action="edit" actionParam=item icon="pencil"}} {{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"}} {{d-button action="allow" actionParam=item icon="check" label="admin.logs.screened_ips.actions.do_nothing"}}
{{else}} {{else}}
{{d-button action="block" actionParam=item icon="ban" label="admin.logs.screened_ips.actions.block"}} {{d-button action="block" actionParam=item icon="ban" label="admin.logs.screened_ips.actions.block"}}

View file

@ -39,7 +39,7 @@
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
<div class='table staff-actions'> {{#staff-actions}}
<div class="heading-container"> <div class="heading-container">
<div class="col heading first staff_user">{{i18n 'admin.logs.staff_actions.staff_user'}}</div> <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> <div class="col heading action">{{i18n 'admin.logs.action'}}</div>
@ -86,4 +86,4 @@
{{i18n 'search.no_results'}} {{i18n 'search.no_results'}}
{{/each}} {{/each}}
{{/conditional-loading-spinner}} {{/conditional-loading-spinner}}
</div> {{/staff-actions}}

View file

@ -1,9 +1,9 @@
<div class='admin-controls'> <div class='admin-controls'>
<div class='span15'> <nav>
<ul class='nav nav-pills'> <ul class='nav nav-pills'>
<li>{{#link-to 'adminUser' user}}{{d-icon "caret-left"}} &nbsp;{{user.username}}{{/link-to}}</li> <li>{{#link-to 'adminUser' user}}{{d-icon "caret-left"}} &nbsp;{{user.username}}{{/link-to}}</li>
</ul> </ul>
</div> </nav>
</div> </div>
{{#conditional-loading-spinner condition=loading}} {{#conditional-loading-spinner condition=loading}}

View file

@ -156,6 +156,22 @@
</div> </div>
</div> </div>
{{/if}} {{/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> </section>
{{#if userFields}} {{#if userFields}}

View file

@ -1,10 +1,10 @@
<div class='admin-controls'> <div class='admin-controls'>
<div class='span15'> <nav>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
<li>{{#link-to 'adminUser' model}}{{d-icon "caret-left"}} &nbsp;{{model.username}}{{/link-to}}</li> <li>{{#link-to 'adminUser' model}}{{d-icon "caret-left"}} &nbsp;{{model.username}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'member'}}{{i18n 'admin.user.trust_level_2_users'}}{{/link-to}}</li> <li>{{#link-to 'adminUsersList.show' 'member'}}{{i18n 'admin.user.trust_level_2_users'}}{{/link-to}}</li>
</ul> </ul>
</div> </nav>
</div> </div>
<div class="admin-container tl3-requirements"> <div class="admin-container tl3-requirements">

View file

@ -1,5 +1,5 @@
<div class='admin-controls'> <div class='admin-controls'>
<div class='span15'> <nav>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
{{nav-item route='adminUsersList.show' routeParam='active' label='admin.users.nav.active'}} {{nav-item route='adminUsersList.show' routeParam='active' label='admin.users.nav.active'}}
{{nav-item route='adminUsersList.show' routeParam='new' label='admin.users.nav.new'}} {{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='silenced' label='admin.users.nav.silenced'}}
{{nav-item route='adminUsersList.show' routeParam='suspect' label='admin.users.nav.suspect'}} {{nav-item route='adminUsersList.show' routeParam='suspect' label='admin.users.nav.suspect'}}
</ul> </ul>
</div> </nav>
<div class="pull-right"> <div class="pull-right">
{{#unless siteSettings.enable_sso}} {{#unless siteSettings.enable_sso}}
{{d-button action="sendInvites" title="admin.invite.button_title" icon="user-plus" label="admin.invite.button_text"}} {{d-button action="sendInvites" title="admin.invite.button_title" icon="user-plus" label="admin.invite.button_text"}}

View file

@ -90,7 +90,7 @@ registerIconRenderer({
if (params.label) { html += " aria-hidden='true'"; } if (params.label) { html += " aria-hidden='true'"; }
html += `></${tagName}>`; html += `></${tagName}>`;
if (params.label) { if (params.label) {
html += "<span class='sr-only'>" + I18n.t(params.label) + "</span>"; html += `<span class='sr-only'>${params.label}</span>`;
} }
return html; return html;
}, },

View file

@ -38,11 +38,11 @@ export default Ember.Component.extend({
<a class="post-link" href="${postLink.href}">${postLink.anchor}</a> <a class="post-link" href="${postLink.href}">${postLink.anchor}</a>
${userAvatar} ${userAvatar}
<span class="username">${userLink.anchor}</span> <span class="username">${userLink.anchor}</span>
${iconHTML("mail-forward", { class: "reply-to-glyph" })}
`; `;
if (originalUser) { if (originalUser) {
editTitle += ` editTitle += `
${iconHTML("mail-forward", { class: "reply-to-glyph" })}
${originalUser.avatar} ${originalUser.avatar}
<span class="original-username">${originalUser.username}</span> <span class="original-username">${originalUser.username}</span>
`; `;

View file

@ -372,7 +372,13 @@ export default Ember.Component.extend({
post.set('refreshedPost', true); 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) { _warnMentionedGroups($preview) {

View file

@ -80,7 +80,14 @@ export default Ember.Component.extend({
const link = document.createElement('a'); const link = document.createElement('a');
link.href = this.get('composer.title'); 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) { if (loadOnebox && loadOnebox.then) {
loadOnebox.then( () => { loadOnebox.then( () => {

View file

@ -0,0 +1,6 @@
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
@computed('post.url')
postUrl: Discourse.getURL
});

View file

@ -11,7 +11,7 @@ export default Ember.Component.extend({
} }
Ember.run.schedule('afterRender', () => { 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) { if (e.keyCode === 13) {
this.sendAction(); this.sendAction();
} }

View file

@ -167,9 +167,9 @@ export default Ember.Controller.extend({
return this.currentUser && this.currentUser.staff && hasResults; return this.currentUser && this.currentUser.staff && hasResults;
}, },
@computed('expanded', 'model.grouped_search_result.can_create_topic') @computed('model.grouped_search_result.can_create_topic')
canCreateTopic(expanded, userCanCreateTopic) { canCreateTopic(userCanCreateTopic) {
return this.currentUser && userCanCreateTopic && !expanded; return this.currentUser && userCanCreateTopic;
}, },
@computed('expanded') @computed('expanded')

View file

@ -4,6 +4,7 @@ import showModal from 'discourse/lib/show-modal';
import { setting } from 'discourse/lib/computed'; import { setting } from 'discourse/lib/computed';
import { findAll } from 'discourse/models/login-method'; import { findAll } from 'discourse/models/login-method';
import { escape } from 'pretty-text/sanitizer'; import { escape } from 'pretty-text/sanitizer';
import computed from 'ember-addons/ember-computed-decorators';
// This is happening outside of the app via popup // This is happening outside of the app via popup
const AuthErrors = [ const AuthErrors = [
@ -31,6 +32,9 @@ export default Ember.Controller.extend(ModalFunctionality, {
this.set('authenticate', null); this.set('authenticate', null);
this.set('loggingIn', false); this.set('loggingIn', false);
this.set('loggedIn', false); this.set('loggedIn', false);
this.set('secondFactorRequired', false);
$("#credentials").show();
$("#second-factor").hide();
}, },
// Determines whether at least one login button is enabled // 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; return findAll(this.siteSettings).length > 0;
}.property(), }.property(),
loginButtonText: function() { @computed('loggingIn')
return this.get('loggingIn') ? I18n.t('login.logging_in') : I18n.t('login.title'); loginButtonLabel(loggingIn) {
}.property('loggingIn'), return loggingIn ? 'login.logging_in' : 'login.title';
},
loginDisabled: Em.computed.or('loggingIn', 'loggedIn'), loginDisabled: Em.computed.or('loggingIn', 'loggedIn'),
@ -67,13 +72,24 @@ export default Ember.Controller.extend(ModalFunctionality, {
this.set('loggingIn', true); this.set('loggingIn', true);
ajax("/session", { 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) { }).then(function (result) {
// Successful login // Successful login
if (result && result.error) { if (result && result.error) {
self.set('loggingIn', false); 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', { self.send('showNotActivated', {
username: self.get('loginName'), username: self.get('loginName'),
sentTo: escape(result.sent_to_email), sentTo: escape(result.sent_to_email),

View file

@ -8,6 +8,7 @@ import { userPath } from 'discourse/lib/url';
export default Ember.Controller.extend(PasswordValidation, { export default Ember.Controller.extend(PasswordValidation, {
isDeveloper: Ember.computed.alias('model.is_developer'), isDeveloper: Ember.computed.alias('model.is_developer'),
admin: Ember.computed.alias('model.admin'), admin: Ember.computed.alias('model.admin'),
secondFactorRequired: Ember.computed.alias('model.second_factor_required'),
passwordRequired: true, passwordRequired: true,
errorMessage: null, errorMessage: null,
successMessage: null, successMessage: null,
@ -32,7 +33,8 @@ export default Ember.Controller.extend(PasswordValidation, {
url: userPath(`password-reset/${this.get('model.token')}.json`), url: userPath(`password-reset/${this.get('model.token')}.json`),
type: 'PUT', type: 'PUT',
data: { data: {
password: this.get('accountPassword') password: this.get('accountPassword'),
second_factor_token: this.get('secondFactor')
} }
}).then(result => { }).then(result => {
if (result.success) { if (result.success) {
@ -45,10 +47,22 @@ export default Ember.Controller.extend(PasswordValidation, {
DiscourseURL.redirectTo(result.redirect_to || '/'); DiscourseURL.redirectTo(result.redirect_to || '/');
} }
} else { } 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('rejectedPasswords').pushObject(this.get('accountPassword'));
this.get('rejectedPasswordsMessages').set(this.get('accountPassword'), result.errors.password[0]); this.get('rejectedPasswordsMessages').set(this.get('accountPassword'), result.errors.password[0]);
} }
if (result.message) { if (result.message) {
this.set('errorMessage', result.message); this.set('errorMessage', result.message);
} }

View file

@ -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);
}
}
});

View file

@ -30,7 +30,7 @@ export default htmlHelper((period, options) => {
break; 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 { } else {
return title; return title;
} }

View file

@ -2,27 +2,27 @@ export default {
name: 'register-service-worker', name: 'register-service-worker',
initialize() { initialize() {
// only allow service worker on android for now window.addEventListener('load', () => {
if (!/(android)/i.test(navigator.userAgent)) { const isSecured = (document.location.protocol === 'https:') ||
// 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:') ||
(location.hostname === "localhost"); (location.hostname === "localhost");
const isSupported= isSecured && ('serviceWorker' in navigator);
if (isSecure && ('serviceWorker' in navigator)) { if (isSupported) {
navigator.serviceWorker.register(`${Discourse.BaseUri}/service-worker.js`); 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();
};
});
}
} }
} });
} }
}; };

View file

@ -26,7 +26,7 @@ export default {
} }
// don't track links in quotes or in elided part // 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'); let href = $link.attr('href') || $link.data('href');
@ -113,8 +113,10 @@ export default {
return false; return false;
} }
const isInternal = DiscourseURL.isInternal(href);
// If we're on the same site, use the router and track via AJAX // 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", { ajax("/clicks/track", {
data: { data: {
url: href, url: href,
@ -128,9 +130,11 @@ export default {
return false; return false;
} }
// Otherwise, use a custom URL with a redirect const modifierLeftClicked = (e.ctrlKey || e.metaKey) && e.which === 1;
// consider CTRL+mouse-left-click / CMD+mouse-left-click or mouse-middle-click as well const middleClicked = e.which === 2;
if (Discourse.User.currentProp('external_links_in_new_tab') || ((e.ctrlKey || e.metaKey) && (e.which === 1)) || (e.which === 2)) { const openExternalInNewTab = Discourse.User.currentProp('external_links_in_new_tab');
if (modifierLeftClicked || middleClicked || (!isInternal && openExternalInNewTab)) {
window.open(destUrl, '_blank').focus(); window.open(destUrl, '_blank').focus();
} else { } else {
DiscourseURL.redirectTo(destUrl); DiscourseURL.redirectTo(destUrl);

View file

@ -22,6 +22,7 @@ const SERVER_SIDE_ONLY = [
/^\/wizard/, /^\/wizard/,
/\.rss$/, /\.rss$/,
/\.json$/, /\.json$/,
/^\/admin\/upgrade$/
]; ];
export function rewritePath(path) { export function rewritePath(path) {

View file

@ -203,7 +203,7 @@ export function validateUploadedFile(file, opts) {
// check that the uploaded file is authorized // check that the uploaded file is authorized
if (opts.allowStaffToUploadAnyFileInPm && opts.isPrivateMessage) { if (opts.allowStaffToUploadAnyFileInPm && opts.isPrivateMessage) {
if (Discourse.User.current("staff")) { if (Discourse.User.currentProp('staff')) {
return true; 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; 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() { function extensions() {
return Discourse.SiteSettings.authorized_extensions return extensionsToArray(Discourse.SiteSettings.authorized_extensions);
.toLowerCase() }
.replace(/[\s\.]+/g, "")
.split("|") function staffExtensions() {
.filter(ext => ext.indexOf("*") === -1); return extensionsToArray(Discourse.SiteSettings.authorized_extensions_for_staff);
} }
function imagesExtensions() { 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() { function extensionsRegex() {
@ -259,7 +271,14 @@ function imagesExtensionsRegex() {
return new RegExp("\\.(" + imagesExtensions().join("|") + ")$", "i"); return new RegExp("\\.(" + imagesExtensions().join("|") + ")$", "i");
} }
function staffExtensionsRegex() {
return new RegExp("\\.(" + staffExtensions().join("|") + ")$", "i");
}
function isAuthorizedFile(fileName) { function isAuthorizedFile(fileName) {
if (Discourse.User.currentProp('staff') && staffExtensionsRegex().test(fileName)) {
return true;
}
return extensionsRegex().test(fileName); return extensionsRegex().test(fileName);
} }
@ -268,7 +287,8 @@ function isAuthorizedImage(fileName){
} }
export function authorizedExtensions() { 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() { export function authorizedImagesExtensions() {
@ -276,7 +296,9 @@ export function authorizedImagesExtensions() {
} }
export function authorizesAllExtensions() { 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() { export function authorizesOneOrMoreExtensions() {
@ -322,7 +344,7 @@ export function allowsImages() {
} }
export function allowsAttachments() { export function allowsAttachments() {
return authorizesAllExtensions() || extensions().length > imagesExtensions().length; return authorizesAllExtensions() || authorizedExtensions().split(", ").length > imagesExtensions().length;
} }
export function uploadLocation(url) { export function uploadLocation(url) {

View file

@ -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) { loadUserAction(id) {
const stream = this.get('stream'); const stream = this.get('stream');
return ajax(`/user_actions/${id}.json`, { cache: 'false' }).then(result => { return ajax(`/user_actions/${id}.json`, { cache: 'false' }).then(result => {

View file

@ -111,6 +111,7 @@ export default function() {
this.route('username'); this.route('username');
this.route('email'); this.route('email');
this.route('second-factor');
this.route('about', { path: '/about-me' }); this.route('about', { path: '/about-me' });
this.route('badgeTitle', { path: '/badge_title' }); this.route('badgeTitle', { path: '/badge_title' });
this.route('card-badge', { path: '/card-badge' }); this.route('card-badge', { path: '/card-badge' });

View file

@ -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') });
}
});

View file

@ -15,6 +15,10 @@ export default RestrictedUserRoute.extend({
}, },
actions: { actions: {
showTwoFactorModal() {
showModal('second-factor-intro');
},
showAvatarSelector() { showAvatarSelector() {
showModal('avatar-selector'); showModal('avatar-selector');

View file

@ -1,4 +1,4 @@
<a href={{category.url}}> <a class="category-title-link" href={{category.url}}>
{{#if category.read_restricted}} {{#if category.read_restricted}}
{{d-icon 'lock'}} {{d-icon 'lock'}}
{{/if}} {{/if}}

View file

@ -3,7 +3,6 @@
onChangeCallback='triggerResize' onChangeCallback='triggerResize'
id="private-message-users" id="private-message-users"
includeMessageableGroups='true' includeMessageableGroups='true'
class="span8"
placeholderKey="composer.users_placeholder" placeholderKey="composer.users_placeholder"
tabindex="1" tabindex="1"
usernames=usernames usernames=usernames

View file

@ -7,7 +7,7 @@
<div class='group-post-info'> <div class='group-post-info'>
<div class="group-post-topic"> <div class="group-post-topic">
<div class='group-post-title'> <div class='group-post-title'>
<a href={{post.url}}>{{{post.topic.fancyTitle}}}</a> <a href={{postUrl}}>{{{post.topic.fancyTitle}}}</a>
</div> </div>
<div class="group-post-category">{{category-link post.category}}</div> <div class="group-post-category">{{category-link post.category}}</div>
{{#if post.user.name}} {{#if post.user.name}}

View file

@ -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'}}&nbsp;</label>
</td>
<td>
{{yield}}
</td>
<td></td>
</tr>
</table>
</div>

View file

@ -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"}} {{#d-modal-body title="login.title" class="login-modal"}}
{{login-buttons action="externalLogin"}} {{login-buttons action="externalLogin"}}
{{#if canLoginLocal}} {{#if canLoginLocal}}
<form id='login-form' method='post'> <form id='login-form' method='post'>
<div> <div id="credentials">
<table> <table>
<tr> <tr>
<td> <td>
@ -15,10 +15,10 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<label for='login-account-password'>{{i18n 'login.password'}}&nbsp;</label> <label for='login-account-password'>{{i18n 'login.password'}}&nbsp;</label>
</td> </td>
<td> <td>
{{text-field value=loginPassword type="password" id="login-account-password" maxlength="200"}} &nbsp; {{text-field value=loginPassword type="password" id="login-account-password" maxlength="200"}} &nbsp;
</td> </td>
</tr> </tr>
<tr> <tr>
@ -29,8 +29,13 @@
</tr> </tr>
</table> </table>
</div> </div>
{{#second-factor-form}}
{{text-field value=loginSecondFactor
id="login-second-factor"
autocorrect="off"
autocapitalize="off"
autofocus="autofocus"}}
{{/second-factor-form}}
</form> </form>
{{/if}} {{/if}}
{{authMessage}} {{authMessage}}
@ -43,11 +48,11 @@
{{/if}} {{/if}}
{{#if canLoginLocal}} {{#if canLoginLocal}}
<button class='btn btn-large btn-primary' {{d-button action="login"
disabled={{loginDisabled}} icon="unlock"
{{action "login"}}> label=loginButtonLabel
{{d-icon "unlock"}}&nbsp;{{loginButtonText}} disabled=loginDisabled
</button> class='btn btn-large btn-primary'}}
{{#if showSignupLink}} {{#if showSignupLink}}
<button class="btn btn-large" id="new-account-link" {{action "showCreateAccount"}}> <button class="btn btn-large" id="new-account-link" {{action "showCreateAccount"}}>

View file

@ -1,6 +1,6 @@
{{#d-modal-body id="keyboard-shortcuts-help"}} {{#d-modal-body id="keyboard-shortcuts-help"}}
<div class="row"> <div class="row">
<div class="span6"> <div>
<h4>{{i18n 'keyboard_shortcuts_help.jump_to.title'}}</h4> <h4>{{i18n 'keyboard_shortcuts_help.jump_to.title'}}</h4>
<ul> <ul>
<li>{{{i18n 'keyboard_shortcuts_help.jump_to.home'}}}</li> <li>{{{i18n 'keyboard_shortcuts_help.jump_to.home'}}}</li>
@ -24,7 +24,7 @@
<li>{{{i18n 'keyboard_shortcuts_help.navigation.next_prev'}}}</li> <li>{{{i18n 'keyboard_shortcuts_help.navigation.next_prev'}}}</li>
</ul> </ul>
</div> </div>
<div class="span6"> <div>
<h4>{{i18n 'keyboard_shortcuts_help.application.title'}}</h4> <h4>{{i18n 'keyboard_shortcuts_help.application.title'}}</h4>
<ul> <ul>
<li>{{{i18n 'keyboard_shortcuts_help.application.hamburger_menu'}}}</li> <li>{{{i18n 'keyboard_shortcuts_help.application.hamburger_menu'}}}</li>
@ -46,7 +46,7 @@
<li>{{{i18n 'keyboard_shortcuts_help.actions.quote_post'}}}</li> <li>{{{i18n 'keyboard_shortcuts_help.actions.quote_post'}}}</li>
</ul> </ul>
</div> </div>
<div class="span6"> <div>
<h4>{{i18n 'keyboard_shortcuts_help.actions.title'}}</h4> <h4>{{i18n 'keyboard_shortcuts_help.actions.title'}}</h4>
<ul> <ul>
<li>{{{i18n 'keyboard_shortcuts_help.actions.bookmark_topic'}}}</li> <li>{{{i18n 'keyboard_shortcuts_help.actions.bookmark_topic'}}}</li>

View file

@ -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"}} {{#d-modal-body title="login.title" class="login-modal"}}
{{login-buttons action="externalLogin"}} {{login-buttons action="externalLogin"}}
{{#if canLoginLocal}} {{#if canLoginLocal}}
<form id='login-form' method='post'> <form id='login-form' method='post'>
<div> <div id="credentials">
<table> <table>
<tr> <tr>
<td><label for='login-account-name'>{{i18n 'login.username'}}</label></td> <td><label for='login-account-name'>{{i18n 'login.username'}}</label></td>
@ -22,6 +22,9 @@
</tr> </tr>
</table> </table>
</div> </div>
{{#second-factor-form}}
{{text-field value=loginSecondFactor id="login-second-factor" autocorrect="off" autocapitalize="off" autofocus="autofocus"}}
{{/second-factor-form}}
</form> </form>
{{/if}} {{/if}}
{{authMessage}} {{authMessage}}
@ -30,9 +33,11 @@
<div class="modal-footer"> <div class="modal-footer">
{{#if canLoginLocal}} {{#if canLoginLocal}}
<button form="login-form" type="submit" class="btn btn-large btn-primary" disabled={{loginDisabled}} {{action "login"}}> {{d-button action="login"
{{d-icon "unlock"}}&nbsp;{{loginButtonText}} icon="unlock"
</button> label=loginButtonLabel
disabled=loginDisabled
class='btn btn-large btn-primary'}}
{{#if showSignupLink}} {{#if showSignupLink}}
<button class="btn btn-large" id="new-account-link" {{action "createAccount"}}> <button class="btn btn-large" id="new-account-link" {{action "createAccount"}}>

View file

@ -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>

View file

@ -16,20 +16,28 @@
{{/if}} {{/if}}
{{else}} {{else}}
<form> <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"}}
&nbsp;{{input-tip validation=passwordValidation}}
</div>
<div class="input"> <div class="instructions">
{{password-field value=accountPassword type="password" id="new-account-password" capsLockOn=capsLockOn autofocus="autofocus"}} <div class="caps-lock-warning {{unless capsLockOn 'invisible'}}">
&nbsp;{{input-tip validation=passwordValidation}} {{d-icon "exclamation-triangle"}} {{i18n 'login.caps_lock_warning'}}</div>
</div> </div>
<div class="instructions"> {{d-button action="submit" class='btn-primary' label='user.change_password.set_password'}}
<div class="caps-lock-warning {{unless capsLockOn 'invisible'}}"> {{/if}}
{{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>
{{#if errorMessage}} {{#if errorMessage}}
<br/><br/> <br/><br/>

View file

@ -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>

View file

@ -66,6 +66,25 @@
{{passwordProgress}} {{passwordProgress}}
</div> </div>
</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}} {{/if}}
<div class="control-group pref-avatar"> <div class="control-group pref-avatar">

View file

@ -5,13 +5,13 @@
<h2>{{i18n 'user.invited.title'}}</h2> <h2>{{i18n 'user.invited.title'}}</h2>
{{#if model.can_see_invite_details}} {{#if model.can_see_invite_details}}
<div class='user-invite-controls'> <div class='admin-controls'>
<div class='span15'> <nav>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
{{nav-item route='userInvited.show' routeParam='pending' i18nLabel=pendingLabel}} {{nav-item route='userInvited.show' routeParam='pending' i18nLabel=pendingLabel}}
{{nav-item route='userInvited.show' routeParam='redeemed' i18nLabel=redeemedLabel}} {{nav-item route='userInvited.show' routeParam='redeemed' i18nLabel=redeemedLabel}}
</ul> </ul>
</div> </nav>
<div class="pull-right"> <div class="pull-right">
{{d-button icon="plus" action="showInvite" label="user.invited.create" class="btn"}} {{d-button icon="plus" action="showInvite" label="user.invited.create" class="btn"}}

View file

@ -39,7 +39,16 @@
<div class='profile-image'></div> <div class='profile-image'></div>
<div class='details'> <div class='details'>
<div class='primary'> <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'> <section class='controls'>
<ul> <ul>
{{#if model.can_send_private_message_to_user}} {{#if model.can_send_private_message_to_user}}

View file

@ -5,11 +5,17 @@ import { relativeAge } from 'discourse/lib/formatter';
import { iconNode } from 'discourse-common/lib/icon-library'; import { iconNode } from 'discourse-common/lib/icon-library';
import RawHtml from 'discourse/widgets/raw-html'; import RawHtml from 'discourse/widgets/raw-html';
const SCROLLAREA_HEIGHT = 300;
const SCROLLER_HEIGHT = 50; const SCROLLER_HEIGHT = 50;
const SCROLLAREA_REMAINING = SCROLLAREA_HEIGHT - SCROLLER_HEIGHT;
const LAST_READ_HEIGHT = 20; 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) { function clamp(p, min=0.0, max=1.0) {
return Math.max(Math.min(p, max), min); return Math.max(Math.min(p, max), min);
} }
@ -27,7 +33,7 @@ createWidget('timeline-last-read', {
tagName: 'div.timeline-last-read', tagName: 'div.timeline-last-read',
buildAttributes(attrs) { 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; const top = attrs.top > bottom ? bottom : attrs.top;
return { style: `height: ${LAST_READ_HEIGHT}px; top: ${top}px` }; return { style: `height: ${LAST_READ_HEIGHT}px; top: ${top}px` };
}, },
@ -115,7 +121,7 @@ createWidget('timeline-scrollarea', {
buildKey: () => `timeline-scrollarea`, buildKey: () => `timeline-scrollarea`,
buildAttributes() { buildAttributes() {
return { style: `height: ${SCROLLAREA_HEIGHT}px` }; return { style: `height: ${scrollareaHeight()}px` };
}, },
defaultState(attrs) { defaultState(attrs) {
@ -168,8 +174,8 @@ createWidget('timeline-scrollarea', {
const percentage = state.percentage; const percentage = state.percentage;
if (percentage === null) { return; } if (percentage === null) { return; }
const before = SCROLLAREA_REMAINING * percentage; const before = scrollareaRemaining() * percentage;
const after = (SCROLLAREA_HEIGHT - before) - SCROLLER_HEIGHT; const after = (scrollareaHeight() - before) - SCROLLER_HEIGHT;
let showButton = false; let showButton = false;
const hasBackPosition = const hasBackPosition =
@ -179,13 +185,13 @@ createWidget('timeline-scrollarea', {
(position.lastRead && position.lastRead !== position.total); (position.lastRead && position.lastRead !== position.total);
if (hasBackPosition) { if (hasBackPosition) {
const lastReadTop = Math.round(position.lastReadPercentage * SCROLLAREA_HEIGHT); const lastReadTop = Math.round(position.lastReadPercentage * scrollareaHeight());
showButton = ((before + SCROLLER_HEIGHT - 5) < lastReadTop) || showButton = ((before + SCROLLER_HEIGHT - 5) < lastReadTop) ||
(before > (lastReadTop + 25)); (before > (lastReadTop + 25));
// Don't show if at the bottom of the timeline // 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; showButton = false;
} }
} }
@ -200,7 +206,7 @@ createWidget('timeline-scrollarea', {
]; ];
if (hasBackPosition) { if (hasBackPosition) {
const lastReadTop = Math.round(position.lastReadPercentage * SCROLLAREA_HEIGHT); const lastReadTop = Math.round(position.lastReadPercentage * scrollareaHeight());
result.push(this.attach('timeline-last-read', { result.push(this.attach('timeline-last-read', {
top: lastReadTop, top: lastReadTop,
lastRead: position.lastRead, lastRead: position.lastRead,

View file

@ -105,7 +105,6 @@ const rule = {
let title = topicInfo.title; let title = topicInfo.title;
if (options.enableEmoji) { if (options.enableEmoji) {
title = performEmojiUnescape(topicInfo.title, { title = performEmojiUnescape(topicInfo.title, {
getURL: options.getURL, emojiSet: options.emojiSet getURL: options.getURL, emojiSet: options.emojiSet

View file

@ -43,16 +43,20 @@ function loadNext(ajax) {
let timeoutMs = 150; let timeoutMs = 150;
let removeLoading = true; let removeLoading = true;
const { url, refresh, $elem, userId } = loadingQueue.shift(); const { url, refresh, $elem, categoryId, topicId } = loadingQueue.shift();
// Retrieve the onebox // Retrieve the onebox
return ajax("/onebox", { return ajax("/onebox", {
dataType: 'html', dataType: 'html',
data: { url, refresh }, data: {
url,
refresh,
category_id: categoryId,
topic_id: topicId
},
cache: true cache: true
}).then(html => { }).then(html => {
let $html = $(html); let $html = $(html);
localCache[normalize(url)] = $html; localCache[normalize(url)] = $html;
$elem.replaceWith($html); $elem.replaceWith($html);
applySquareGenericOnebox($html, normalize(url)); applySquareGenericOnebox($html, normalize(url));
@ -60,7 +64,7 @@ function loadNext(ajax) {
if (result && result.jqXHR && result.jqXHR.status === 429) { if (result && result.jqXHR && result.jqXHR.status === 429) {
timeoutMs = 2000; timeoutMs = 2000;
removeLoading = false; removeLoading = false;
loadingQueue.unshift({ url, refresh, $elem, userId }); loadingQueue.unshift({ url, refresh, $elem, categoryId, topicId });
} else { } else {
failedCache[normalize(url)] = true; failedCache[normalize(url)] = true;
} }
@ -75,14 +79,14 @@ function loadNext(ajax) {
// Perform a lookup of a onebox based an anchor $element. // 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. // It will insert a loading indicator and remove it when the loading is complete or fails.
export function load(e, refresh, ajax, userId, synchronous) { export function load({ elem , refresh = true, ajax, synchronous = false, categoryId, topicId }) {
const $elem = $(e); const $elem = $(elem);
// If the onebox has loaded or is loading, return // If the onebox has loaded or is loading, return
if ($elem.data('onebox-loaded')) return; if ($elem.data('onebox-loaded')) return;
if ($elem.hasClass('loading-onebox')) return; if ($elem.hasClass('loading-onebox')) return;
const url = e.href; const url = elem.href;
// Unless we're forcing a refresh... // Unless we're forcing a refresh...
if (!refresh) { if (!refresh) {
@ -99,7 +103,7 @@ export function load(e, refresh, ajax, userId, synchronous) {
$elem.addClass('loading-onebox'); $elem.addClass('loading-onebox');
// Add to the loading queue // Add to the loading queue
loadingQueue.push({ url, refresh, $elem, userId }); loadingQueue.push({ url, refresh, $elem, categoryId, topicId });
// Load next url in queue // Load next url in queue
if (synchronous) { if (synchronous) {

View file

@ -137,6 +137,7 @@ const DEFAULT_LIST = [
'div.quote-controls', 'div.quote-controls',
'div.title', 'div.title',
'div[align]', 'div[align]',
'div[data-theme-*]',
'div[dir]', 'div[dir]',
'dl', 'dl',
'dt', 'dt',

View file

@ -3,6 +3,7 @@ import { on } from "ember-addons/ember-computed-decorators";
import computed from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators";
import PermissionType from "discourse/models/permission-type"; import PermissionType from "discourse/models/permission-type";
import Category from "discourse/models/category"; import Category from "discourse/models/category";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
const { get, isNone, isEmpty } = Ember; const { get, isNone, isEmpty } = Ember;
export default ComboBoxComponent.extend({ 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") @on("didRender")
_bindComposerResizing() { _bindComposerResizing() {
this.appEvents.on("composer:resized", this, this.applyDirection); this.appEvents.on("composer:resized", this, this.applyDirection);

View file

@ -32,18 +32,21 @@ export default SelectKitRowComponent.extend({
} }
}, },
@computed("category") @computed("category", "parentCategory")
badgeForCategory(category) { badgeForCategory(category, parentCategory) {
return categoryBadgeHTML(category, { return categoryBadgeHTML(category, {
link: this.get("categoryLink"), link: this.get("categoryLink"),
allowUncategorized: this.get("allowUncategorized"), allowUncategorized: this.get("allowUncategorized"),
hideParent: this.get("hideParentCategory") hideParent: parentCategory ? true : false
}).htmlSafe(); }).htmlSafe();
}, },
@computed("parentCategory") @computed("parentCategory")
badgeForParentCategory(parentCategory) { badgeForParentCategory(parentCategory) {
return categoryBadgeHTML(parentCategory, {link: false}).htmlSafe(); return categoryBadgeHTML(parentCategory, {
link: this.get("categoryLink"),
allowUncategorized: this.get("allowUncategorized")
}).htmlSafe();
}, },
@computed("parentCategoryid") @computed("parentCategoryid")

View file

@ -180,8 +180,7 @@ export default DropdownSelectBoxComponent.extend({
options.action = Composer.CREATE_TOPIC; options.action = Composer.CREATE_TOPIC;
options.categoryId = this.get("composerModel.topic.category.id"); options.categoryId = this.get("composerModel.topic.category.id");
this.get("composerController").close(); this._replyFromExisting(options, _postSnapshot, _topicSnapshot);
this.get("composerController").open(options);
break; break;
case "reply_as_private_message": case "reply_as_private_message":

View file

@ -3,7 +3,7 @@ import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from 'discourse/lib/ajax-error'; import { popupAjaxError } from 'discourse/lib/ajax-error';
import { default as computed } from "ember-addons/ember-computed-decorators"; import { default as computed } from "ember-addons/ember-computed-decorators";
import renderTag from "discourse/lib/render-tag"; import renderTag from "discourse/lib/render-tag";
const { get, isEmpty, isPresent, run } = Ember; const { get, isEmpty, isPresent, run, makeArray } = Ember;
export default ComboBox.extend({ export default ComboBox.extend({
allowContentReplacement: true, allowContentReplacement: true,
@ -14,6 +14,9 @@ export default ComboBox.extend({
filterable: true, filterable: true,
noTags: Ember.computed.empty("computedTags"), noTags: Ember.computed.empty("computedTags"),
allowAny: true, allowAny: true,
maximumSelectionSize: Ember.computed.alias("siteSettings.max_tags_per_topic"),
caretUpIcon: Ember.computed.alias("caretIcon"),
caretDownIcon: Ember.computed.alias("caretIcon"),
init() { init() {
this._super(); 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") @computed("tags")
computedTags(tags) { computedTags(tags) {
return Ember.makeArray(tags); return makeArray(tags);
}, },
validateCreate(term) { validateCreate(term) {
if (this.get("limitReached") || !this.site.get("can_create_tag")) {
return false;
}
const filterRegexp = new RegExp(this.site.tags_filter_regexp, "g"); const filterRegexp = new RegExp(this.site.tags_filter_regexp, "g");
term = term.replace(filterRegexp, "").trim().toLowerCase(); term = term.replace(filterRegexp, "").trim().toLowerCase();
@ -50,8 +82,11 @@ export default ComboBox.extend({
}, },
validateSelect() { validateSelect() {
return this.get("computedTags").length < this.get("siteSettings.max_tags_per_topic") && return this.get("computedTags").length < this.get("siteSettings.max_tags_per_topic");
this.site.get("can_create_tag"); },
filterComputedContent(computedContent) {
return computedContent;
}, },
didRender() { didRender() {
@ -111,7 +146,7 @@ export default ComboBox.extend({
@computed("tags.[]", "filter") @computed("tags.[]", "filter")
collectionHeader(tags, filter) { collectionHeader(tags, filter) {
if (!Ember.isEmpty(tags)) { if (!isEmpty(tags)) {
let output = ""; let output = "";
if (tags.length >= 20) { if (tags.length >= 20) {
@ -132,13 +167,16 @@ export default ComboBox.extend({
computeHeaderContent() { computeHeaderContent() {
let content = this.baseHeaderComputedContent(); let content = this.baseHeaderComputedContent();
const joinedTags = this.get("computedTags").join(", ");
if (isEmpty(this.get("computedTags"))) { if (isEmpty(this.get("computedTags"))) {
content.label = I18n.t("tagging.choose_for_topic"); content.label = I18n.t("tagging.choose_for_topic");
} else { } else {
content.label = this.get("computedTags").join(","); content.label = joinedTags;
} }
content.title = content.name = content.value = joinedTags;
return content; return content;
}, },
@ -148,43 +186,38 @@ export default ComboBox.extend({
delete tags[tags.indexOf(tag)]; delete tags[tags.indexOf(tag)];
this.set("tags", tags.filter(t => t)); this.set("tags", tags.filter(t => t));
this.set("content", []); 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() { 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) { onFilter(filter) {
filter = isEmpty(filter) ? null : 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) { onSelect(tag) {
if (isEmpty(this.get("computedTags"))) { if (isEmpty(this.get("computedTags"))) {
this.set("tags", Ember.makeArray(tag)); this.set("tags", makeArray(tag));
} else { } else {
this.set("tags", this.get("computedTags").concat(tag)); this.set("tags", this.get("computedTags").concat(tag));
} }
this.set("content", []); 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) { _searchTags(query) {
this.startLoading(); this.startLoading();
const selectedTags = Ember.makeArray(this.get("computedTags")).filter(t => t);
const self = this; const self = this;
const selectedTags = makeArray(this.get("computedTags")).filter(t => t);
const sortTags = this.siteSettings.tags_sort_alphabetically; const sortTags = this.siteSettings.tags_sort_alphabetically;
const data = { const data = {
q: query, q: query,
limit: this.siteSettings.max_tag_search_results, limit: this.siteSettings.max_tag_search_results,

View file

@ -1,9 +1,20 @@
import CategoryRowComponent from "select-kit/components/category-row"; 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({ export default CategoryRowComponent.extend({
layoutName: "select-kit/templates/components/category-row", layoutName: "select-kit/templates/components/category-row",
classNames: "none category-row", classNames: "none category-row",
@computed("category")
badgeForCategory(category) {
return categoryBadgeHTML(category, {
link: this.get("categoryLink"),
allowUncategorized: true,
hideParent: true
}).htmlSafe();
},
click() { click() {
this.sendAction("clearSelection"); this.sendAction("clearSelection");
} }

View file

@ -37,7 +37,6 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
tabindex: 0, tabindex: 0,
none: null, none: null,
highlightedValue: null, highlightedValue: null,
noContentLabel: "select_kit.no_content",
valueAttribute: "id", valueAttribute: "id",
nameProperty: "name", nameProperty: "name",
autoFilterable: false, autoFilterable: false,
@ -70,6 +69,8 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
allowContentReplacement: false, allowContentReplacement: false,
collectionHeader: null, collectionHeader: null,
allowAutoSelectFirst: true, allowAutoSelectFirst: true,
maximumSelectionSize: null,
maxContentRow: null,
init() { init() {
this._super(); this._super();
@ -155,8 +156,10 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
}, },
@computed("filter", "filteredComputedContent.[]") @computed("filter", "filteredComputedContent.[]")
shouldDisplayNoContentRow(filter, filteredComputedContent) { noContentRow(filter, filteredComputedContent) {
return filter.length > 0 && filteredComputedContent.length === 0; if (filter.length > 0 && filteredComputedContent.length === 0) {
return I18n.t("select_kit.no_content");
}
}, },
@computed("filter", "filterable", "autoFilterable", "renderedFilterOnce") @computed("filter", "filterable", "autoFilterable", "renderedFilterOnce")

View file

@ -40,11 +40,11 @@
select=(action "select") select=(action "select")
highlight=(action "highlight") highlight=(action "highlight")
create=(action "create") create=(action "create")
noContentLabel=noContentLabel
highlightedValue=highlightedValue highlightedValue=highlightedValue
computedValue=computedValue computedValue=computedValue
shouldDisplayNoContentRow=shouldDisplayNoContentRow
rowComponentOptions=rowComponentOptions rowComponentOptions=rowComponentOptions
noContentRow=noContentRow
maxContentRow=maxContentRow
}} }}
{{/if}} {{/if}}
</div> </div>

View file

@ -30,22 +30,26 @@
}} }}
{{/if}} {{/if}}
{{#each filteredComputedContent as |computedContent|}} {{#if maxContentRow}}
{{component rowComponent <li class="select-kit-row max-content">
computedContent=computedContent {{maxContentRow}}
highlightedValue=highlightedValue </li>
computedValue=computedValue {{else}}
templateForRow=templateForRow {{#if noContentRow}}
select=select
highlight=highlight
options=rowComponentOptions
}}
{{/each}}
{{#if shouldDisplayNoContentRow}}
{{#if noContentLabel}}
<li class="select-kit-row no-content"> <li class="select-kit-row no-content">
{{i18n noContentLabel}} {{noContentRow}}
</li> </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}}
{{/if}} {{/if}}

View file

@ -130,14 +130,23 @@ $mobile-breakpoint: 700px;
} }
} }
.content-list li a span.count { .content-list {
font-size: $font-down-1; width: 27%;
float: right; float: left;
margin-right: 10px; li a span.count {
background-color: $primary-low; font-size: $font-down-1;
padding: 2px 5px; float: right;
border-radius: 5px; margin-right: 10px;
color: $primary; background-color: $primary-low;
padding: 2px 5px;
border-radius: 5px;
color: $primary;
}
}
.content-body {
float: left;
width: 60%;
} }
.admin-content { .admin-content {
@ -196,7 +205,7 @@ $mobile-breakpoint: 700px;
width: 460px; width: 460px;
right: 0; right: 0;
z-index: z("dropdown"); z-index: z("dropdown");
box-shadow: 0 2px 6px rgba(0,0,0, .8); box-shadow: shadow("card");
margin-top: -2px; margin-top: -2px;
background-color: $secondary; background-color: $secondary;
padding: 12px 12px 5px; padding: 12px 12px 5px;
@ -259,6 +268,10 @@ $mobile-breakpoint: 700px;
background-color: $primary-low; background-color: $primary-low;
padding: 10px 10px 3px 0; padding: 10px 10px 3px 0;
@include clearfix; @include clearfix;
nav {
float: left;
margin-left: 12px;
}
.nav.nav-pills { .nav.nav-pills {
li.active { li.active {
a { a {
@ -506,7 +519,6 @@ $mobile-breakpoint: 700px;
background-color: $secondary; background-color: $secondary;
border: 1px solid $primary-low; border: 1px solid $primary-low;
border-radius: 3px; 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; transition: border linear 0.2s, box-shadow linear 0.2s;
li.select2-search-choice { li.select2-search-choice {

View file

@ -220,3 +220,7 @@
margin-top: 20px; margin-top: 20px;
} }
} }
#custom_emoji {
width: 27%;
}

View file

@ -19,7 +19,7 @@
z-index: z("composer","content"); z-index: z("composer","content");
transition: height 250ms ease, background 250ms ease, transform 250ms ease, max-width 250ms ease; transition: height 250ms ease, background 250ms ease, transform 250ms ease, max-width 250ms ease;
background-color: $secondary; background-color: $secondary;
box-shadow: 0 -1px 40px rgba(0,0,0, .12); box-shadow: shadow("composer");
.reply-area { .reply-area {
display: flex; display: flex;

View file

@ -6,7 +6,7 @@ body.crawler {
z-index: z("max"); z-index: z("max");
background-color: #fff; background-color: #fff;
padding: 10px; padding: 10px;
box-shadow: 0 2px 4px -1px rgba(0,0,0,0.25); box-shadow: shadow("header");
} }
div.topic-list div[itemprop='itemListElement'] { div.topic-list div[itemprop='itemListElement'] {
padding: 10px 0; padding: 10px 0;

View file

@ -165,7 +165,7 @@ textarea {
&:focus:required:invalid:focus { &:focus:required:invalid:focus {
border-color: $danger; border-color: $danger;
box-shadow: 0 0 6px $danger; box-shadow: shadow("focus-danger");
} }
} }
@ -196,7 +196,7 @@ input {
border-radius: 0; border-radius: 0;
&:focus { &:focus {
border-color: $tertiary; border-color: $tertiary;
box-shadow: $tertiary 0 0 6px 0px; box-shadow: shadow("focus");
outline: 0; outline: 0;
} }
} }
@ -208,7 +208,7 @@ textarea {
border: 1px solid $primary-medium; border: 1px solid $primary-medium;
&:focus { &:focus {
border-color: $tertiary; border-color: $tertiary;
box-shadow: $tertiary 0 0 6px 0px; box-shadow: shadow("focus");
outline: 0; outline: 0;
} }
} }

View file

@ -5,14 +5,12 @@ img.emoji {
} }
.emoji-picker { .emoji-picker {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background-clip: padding-box; background-clip: padding-box;
z-index: z("modal","content"); z-index: z("modal","content");
position: fixed; position: fixed;
display: none; display: none;
flex-direction: row; flex-direction: row;
height: 300px; height: 300px;
border-radius: 3px;
color: dark-light-choose(darken($primary, 40%), blend-primary-secondary(90%)); color: dark-light-choose(darken($primary, 40%), blend-primary-secondary(90%));
background-color: $secondary; background-color: $secondary;
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);

View file

@ -4,7 +4,7 @@
top: 0; top: 0;
z-index: z("header"); z-index: z("header");
background-color: $header_background; background-color: $header_background;
box-shadow: 0 2px 4px -1px rgba(0,0,0, .25); box-shadow: shadow("header");
.docked & { .docked & {
position: fixed; position: fixed;
@ -33,11 +33,15 @@
.panel { .panel {
float: right; float: right;
position: relative; position: relative;
display: flex;
align-items: center;
}
.header-buttons {
margin-top: .2em;
} }
.login-button, button.sign-up-button { .login-button, button.sign-up-button {
float: left;
margin-top: 7px;
padding: 6px 10px; padding: 6px 10px;
.fa { margin-right: 3px; } .fa { margin-right: 3px; }
} }
@ -54,6 +58,7 @@
.header-dropdown-toggle, .drop-down, .panel-body { .header-dropdown-toggle, .drop-down, .panel-body {
.flagged-posts, .queued-posts { .flagged-posts, .queued-posts {
background: $danger; background: $danger;
min-width: 6px;
} }
} }
@ -62,14 +67,17 @@
margin: 0 0 0 5px; margin: 0 0 0 5px;
list-style: none; list-style: none;
> li { > li {
float: left; float: left;
} }
.icon { .icon {
position: relative; position: relative;
display: block; display: flex;
padding: 3px; 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); color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary);
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
@ -77,11 +85,13 @@
border-left: 1px solid transparent; border-left: 1px solid transparent;
border-right: 1px solid transparent; border-right: 1px solid transparent;
transition: all linear .15s; transition: all linear .15s;
img.avatar {
width: 2.2857em;
height: 2.2857em;
}
&:hover { &:hover {
color: $primary; color: $primary;
background-color: $primary-low; background-color: $primary-low;
border-top: 1px solid transparent; border-top: 1px solid transparent;
border-left: 1px solid transparent; border-left: 1px solid transparent;
border-right: 1px solid transparent; border-right: 1px solid transparent;
@ -119,8 +129,7 @@
} }
.d-icon { .d-icon {
width: 32px; width: 100%;
height: 32px;
font-size: $font-up-4; font-size: $font-up-4;
line-height: $line-height-large; line-height: $line-height-large;
display: inline-block; display: inline-block;

View file

@ -1,6 +1,7 @@
.menu-panel.slide-in { .menu-panel.slide-in {
position: fixed; position: fixed;
right: 0; right: 0;
box-shadow: shadow("header");
.panel-body { .panel-body {
position: absolute; position: absolute;
@ -19,7 +20,7 @@
.menu-panel { .menu-panel {
border: 1px solid $primary-low; border: 1px solid $primary-low;
box-shadow: 0 2px 2px rgba(0,0,0, .25); box-shadow: shadow("menu-panel");
background-color: $secondary; background-color: $secondary;
z-index: z("header"); z-index: z("header");
padding: 0.5em; padding: 0.5em;
@ -44,7 +45,6 @@
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
} }
.menu-links.columned { .menu-links.columned {
@ -69,7 +69,6 @@
margin-left: 0.5em; margin-left: 0.5em;
color: dark-light-choose($primary-medium, $secondary-medium); color: dark-light-choose($primary-medium, $secondary-medium);
} }
} }
li.category-link { li.category-link {
@ -77,7 +76,7 @@
background-color: transparent; background-color: transparent;
display: inline-flex; display: inline-flex;
margin: 0.25em 0.5em; margin: 0.25em 0.5em;
width: 44%; width: 43%;
.badge-notification { .badge-notification {
color: dark-light-choose($primary-medium, $secondary-medium); color: dark-light-choose($primary-medium, $secondary-medium);
background-color: transparent; background-color: transparent;
@ -90,7 +89,6 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: inline-block; display: inline-block;
max-width: 80%;
&.bar, &.bullet { &.bar, &.bullet {
color: $primary; color: $primary;
} }
@ -100,7 +98,7 @@
padding-top: 2px; padding-top: 2px;
} }
span { span {
z-index: z("base") * -1; z-index: z("base") * -1;
} }
} }
} }

View file

@ -17,7 +17,7 @@
overflow: auto; overflow: auto;
height: auto; height: auto;
background-color: $secondary; background-color: $secondary;
box-shadow: 0 2px 2px rgba(0,0,0, .25); box-shadow: shadow("card");
background-clip: padding-box; background-clip: padding-box;
} }
@ -45,8 +45,7 @@
.modal-backdrop, .modal-backdrop,
.modal-backdrop.fade.in { .modal-backdrop.fade.in {
-webkit-animation: fade .3s; animation: fade .3s;
animation: fade .3s;
opacity: .9; opacity: .9;
filter: alpha(opacity=90); filter: alpha(opacity=90);
} }
@ -57,22 +56,12 @@
to { opacity: .9 } to { opacity: .9 }
} }
@-webkit-keyframes fade {
from { opacity: 0 }
to { opacity: .9 }
}
// slide in // slide in
@keyframes slidein { @keyframes slidein {
from { transform: translateY(-20%); } from { transform: translateY(-20%); }
to { transform: translateY(0); } to { transform: translateY(0); }
} }
@-webkit-keyframes slidein {
from { -webkit-transform: translateY(-20%); }
to { -webkit-transform: translateY(0); }
}
.modal-outer-container { .modal-outer-container {
display:table; display:table;
table-layout: fixed; table-layout: fixed;
@ -85,6 +74,7 @@
margin: 0 auto; margin: 0 auto;
background-color: $secondary; background-color: $secondary;
background-clip: padding-box; background-clip: padding-box;
box-shadow: shadow("modal");
.select-kit { .select-kit {
width: 220px; width: 220px;
@ -93,8 +83,7 @@
.create-account.in .modal-inner-container, .create-account.in .modal-inner-container,
.login-modal.in .modal-inner-container { .login-modal.in .modal-inner-container {
-webkit-animation: slidein .3s; animation: slidein .3s;
animation: slidein .3s;
} }

View file

@ -463,10 +463,22 @@ aside.onebox.stackexchange .onebox-body {
.label2 { .label2 {
float: right; float: right;
} }
.site-icon { }
width: 16px;
height: 16px; .onebox {
margin-right: 3px; &.whitelistedgeneric,
&.gfycat {
.site-icon {
width: 16px;
height: 16px;
margin-right: 3px;
}
}
}
.onebox.gfycat p {
span.label1 a {
white-space: nowrap;
} }
} }

View file

@ -4,7 +4,7 @@
position: absolute; position: absolute;
left: 20px; left: 20px;
z-index: z("dropdown"); z-index: z("dropdown");
box-shadow: 0 1px 5px rgba(0,0,0, .4); box-shadow: shadow("card");
background-color: $secondary; background-color: $secondary;
padding: 6px 10px 10px 10px; padding: 6px 10px 10px 10px;
width: 300px; width: 300px;

View file

@ -176,6 +176,7 @@ $tag-color: $primary-medium;
.mobile-view .topic-list-item .discourse-tags { .mobile-view .topic-list-item .discourse-tags {
display: inline-flex; display: inline-flex;
flex-wrap: wrap;
font-size: $font-down-1; font-size: $font-down-1;
margin-top: 0; margin-top: 0;
.discourse-tag { .discourse-tag {

View file

@ -12,8 +12,9 @@
background-color: $secondary; background-color: $secondary;
width: 205px; width: 205px;
padding: 10px; padding: 10px;
border: 1px solid $primary-low; border: 1px solid $primary-low;
z-index: z("dropdown"); z-index: z("dropdown");
box-shadow: shadow("card");
ul { ul {
list-style: none; list-style: none;

View file

@ -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 { .avatar-flair {
display: flex; display: flex;
align-items: center; align-items: center;
@ -275,7 +275,7 @@ aside.quote {
right: -6px; 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; background-size: 20px 20px;
width: 20px; width: 20px;
height: 20px; height: 20px;
@ -288,7 +288,7 @@ aside.quote {
right: -8px; right: -8px;
} }
} }
.user-card-avatar .avatar-flair { .user-card-avatar .avatar-flair, .user-profile-avatar .avatar-flair {
background-size: 40px 40px; background-size: 40px 40px;
width: 40px; width: 40px;
height: 40px; height: 40px;
@ -398,7 +398,11 @@ kbd
background-color: $secondary; background-color: $secondary;
border: 1px solid $primary-low; border: 1px solid $primary-low;
border-radius: 3px; 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; color: $primary;
display: inline-block; display: inline-block;
font-size: $font-down-1; font-size: $font-down-1;

View file

@ -124,7 +124,6 @@
background-color: $primary-low; background-color: $primary-low;
margin-right: 5px; margin-right: 5px;
margin-bottom: 10px; margin-bottom: 10px;
box-shadow: 1px 1px 3px rgba(0.0, 0.0, 0.0, 0.2);
.check-display { .check-display {
position: absolute; position: absolute;

View file

@ -120,6 +120,16 @@
} }
} }
} }
.user-profile-avatar {
position: relative;
float: left;
height: 100%;
.avatar-flair {
bottom: 8px;
right: 16px;
}
}
} }
.controls { .controls {
@ -176,6 +186,13 @@
} }
} }
} }
.user-profile-avatar {
.avatar-flair {
bottom: 8px;
right: 2px;
}
}
} }
} }

View file

@ -211,11 +211,9 @@
.badge-group { .badge-group {
@extend %badge; @extend %badge;
padding: 4px 5px 2px 5px; padding: 2px 5px;
color: $primary; color: $primary;
text-shadow: 0 1px 0 rgba($primary, 0.1);
background-color: $primary-low; background-color: $primary-low;
border-color: $primary-low; border-color: $primary-low;
font-size: $font-down-1; font-size: $font-down-1;
box-shadow: inset 0 1px 0 rgba(0,0,0, 0.22);
} }

View file

@ -5,7 +5,6 @@
#banner { #banner {
padding: 10px; padding: 10px;
background: $tertiary-low; background: $tertiary-low;
box-shadow: 0 2px 4px -1px rgba(0,0,0, .25);
color: $primary; color: $primary;
z-index: z("base") + 1; z-index: z("base") + 1;
overflow: auto; overflow: auto;

View file

@ -119,8 +119,6 @@
.btn-social { .btn-social {
color: #fff; color: #fff;
text-shadow: 0 1px 0 rgba($primary, 0.2);
box-shadow: inset 0 1px 0 rgba(0,0,0, 0.1);
&:hover { &:hover {
color: #fff; color: #fff;
} }

View file

@ -15,8 +15,12 @@
} }
#keyboard-shortcuts-help { #keyboard-shortcuts-help {
.span6 { div.row {
width:32%; width: 100%;
div {
float: left;
width:32%;
}
} }
ul { ul {
list-style: none; list-style: none;
@ -29,7 +33,7 @@
b { b {
padding: 2px 6px; padding: 2px 6px;
border-radius: 4px; 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); background: dark-light-choose(#fafafa, #333);
border: 1px solid dark-light-choose(#ccc, #555); border: 1px solid dark-light-choose(#ccc, #555);
border-bottom: medium none dark-light-choose(#fff, #000); border-bottom: medium none dark-light-choose(#fff, #000);

View file

@ -103,6 +103,26 @@ $z-layers: (
@return map-deep-get($z-layers, $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 // Color utilities
// -------------------------------------------------- // --------------------------------------------------

View file

@ -9,7 +9,7 @@
&.bad { &.bad {
background: $danger-medium; background: $danger-medium;
color: white; color: white;
box-shadow: 1px 1px 5px rgba(0,0,0, .7); box-shadow: shadow("dropdown");
} }
&.hide, &.good { &.hide, &.good {
display: none; display: none;

View file

@ -6,7 +6,6 @@
.badge-wrapper { .badge-wrapper {
font-size: $font-0; font-size: $font-0;
font-weight: normal; font-weight: normal;
line-height: $line-height-large;
&.box { &.box {
margin: 0; margin: 0;
@ -17,40 +16,24 @@
} }
} }
&.bar.has-selection .category-drop-header { &.bar.has-selection .category-drop-header,
padding: 4.5px 10px; &.box.has-selection .category-drop-header,
&.none.has-selection .category-drop-header {
padding: 5px 10px;
} }
&.bullet.has-selection .category-drop-header { &.bullet.has-selection .category-drop-header {
padding: 6px 10px; padding: 5px 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;
} }
.category-drop-header { .category-drop-header {
background: $primary-low; background: $primary-low;
color: $primary; color: $primary;
border: none; border: 1px solid transparent;
padding: 6px 10px; padding: 5px 10px;
font-size: $font-up-1; font-size: $font-0;
line-height: $line-height-medium;
transition: none; transition: none;
.badge-wrapper { .badge-wrapper {
margin-right: 0; margin-right: 0;
} }
@ -64,6 +47,11 @@
} }
} }
&.is-expanded .category-drop-header {
border: 1px solid $tertiary;
box-shadow: shadow("focus");
}
.select-kit-collection { .select-kit-collection {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -93,8 +81,7 @@
width: auto; width: auto;
min-width: 300px; min-width: 300px;
border-radius: 0; border-radius: 0;
-webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.4); box-shadow: shadow("dropdown");
box-shadow: 0 2px 2px rgba(0,0,0,0.4);
} }
.select-kit-row { .select-kit-row {

View file

@ -32,8 +32,7 @@
&.is-focused { &.is-focused {
border: 1px solid $tertiary; border: 1px solid $tertiary;
-webkit-box-shadow: $tertiary 0 0 6px 0px; box-shadow: shadow("focus");
box-shadow: $tertiary 0 0 6px 0px;
} }
} }
@ -47,8 +46,7 @@
&.is-highlighted { &.is-highlighted {
.select-kit-header { .select-kit-header {
border: 1px solid $tertiary; border: 1px solid $tertiary;
-webkit-box-shadow: $tertiary 0 0 6px 0px; box-shadow: shadow("focus");
box-shadow: $tertiary 0 0 6px 0px;
} }
} }
@ -56,14 +54,12 @@
.select-kit-wrapper { .select-kit-wrapper {
display: block; display: block;
border: 1px solid $tertiary; border: 1px solid $tertiary;
-webkit-box-shadow: $tertiary 0 0 6px 0px; box-shadow: shadow("focus");
box-shadow: $tertiary 0 0 6px 0px;
} }
.select-kit-header { .select-kit-header {
border-color: transparent; border-color: transparent;
-webkit-box-shadow: none; box-shadow: none;
box-shadow: none;
} }
.select-kit-body { .select-kit-body {

View file

@ -16,8 +16,7 @@
.select-kit-body { .select-kit-body {
border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); border: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
background-clip: padding-box; background-clip: padding-box;
-webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.4); box-shadow: shadow("dropdown");
box-shadow: 0 2px 2px rgba(0,0,0,0.4);
max-width: 300px; max-width: 300px;
width: 300px; width: 300px;
} }
@ -120,6 +119,12 @@
margin-left: 5px; margin-left: 5px;
} }
&:hover {
.d-icon {
color: $primary-low;
}
}
&.is-focused { &.is-focused {
outline-style: auto; outline-style: auto;
outline-color: $tertiary; outline-color: $tertiary;

View file

@ -79,7 +79,7 @@
} }
.select2-container-active { .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 { .select2-results .select2-no-results, .select2-results .select2-searching, .select2-results .select2-selection-limit {

View file

@ -7,8 +7,7 @@
&.is-expanded { &.is-expanded {
.select-kit-header { .select-kit-header {
border: 1px solid $tertiary; border: 1px solid $tertiary;
-webkit-box-shadow: $tertiary 0 0 6px 0px; box-shadow: shadow("focus");
box-shadow: $tertiary 0 0 6px 0px;
} }
} }
@ -21,14 +20,15 @@
.select-kit-body { .select-kit-body {
max-width: 500px; max-width: 500px;
width: 500px; width: 500px;
border: 1px solid $primary-low; border: 1px solid $tertiary;
box-shadow: shadow("focus")
} }
.select-kit-filter { .select-kit-filter {
border-top: 0; border-top: 0;
} }
.select-kit-wrapper { &.is-expanded .select-kit-wrapper, .select-kit-wrapper {
display: none; display: none;
} }
@ -59,8 +59,7 @@
} }
.selected-tag { .selected-tag {
background: $primary-low; background: $primary-very-low;
border-radius: 2px;
padding: 2px 4px; padding: 2px 4px;
margin: 2px; margin: 2px;
border: 0; border: 0;
@ -69,10 +68,17 @@
box-shadow: 0 0 2px $danger, 0 1px 0 rgba(0,0,0,0.05); box-shadow: 0 0 2px $danger, 0 1px 0 rgba(0,0,0,0.05);
} }
&:before { &:after {
content: '\f00d'; content: '\f00d';
color: $primary-low-mid;
font-family: 'FontAwesome'; font-family: 'FontAwesome';
} }
&:hover {
&:after {
color: $danger;
}
}
} }
} }
} }

View file

@ -24,7 +24,7 @@
border: 1px solid $primary-medium; border: 1px solid $primary-medium;
&.is-focused { &.is-focused {
box-shadow: $tertiary 0 0 6px 0px; box-shadow: shadow("focus");
border-radius: 0; border-radius: 0;
} }
} }
@ -40,7 +40,7 @@
.multi-select-header { .multi-select-header {
border-radius: 0; border-radius: 0;
border-bottom: 1px solid transparent; border-bottom: 1px solid transparent;
box-shadow: $tertiary 0 0 6px 0px; box-shadow: shadow("focus");
} }
} }
@ -48,7 +48,7 @@
.select-kit-wrapper { .select-kit-wrapper {
display: block; display: block;
border: 1px solid $tertiary; border: 1px solid $tertiary;
box-shadow: $tertiary 0 0 6px 0px; box-shadow: shadow("focus");
border-radius: 0; border-radius: 0;
} }
@ -101,7 +101,6 @@
margin: 0; margin: 0;
outline: 0; outline: 0;
border: 0; border: 0;
-webkit-box-shadow: none;
box-shadow: none; box-shadow: none;
border-radius: 0; border-radius: 0;
height: 21px; height: 21px;

View file

@ -18,10 +18,15 @@
h2.selected-name { h2.selected-name {
overflow: auto; overflow: auto;
color: black; color: $secondary;
display: inline-block; display: inline-block;
box-sizing: border-box; box-sizing: border-box;
.date-section {
color: $primary;
margin-right: 5px;
}
.top-date-string { .top-date-string {
font-size: $font-down-1; font-size: $font-down-1;
color: dark-light-choose($primary-medium, $secondary-high); color: dark-light-choose($primary-medium, $secondary-high);
@ -31,7 +36,7 @@
} }
.d-icon { .d-icon {
color: black; color: $primary;
opacity: 1; opacity: 1;
margin: 5px 0 10px 5px; margin: 5px 0 10px 5px;
font-size: $font-up-3; font-size: $font-up-3;
@ -46,8 +51,7 @@
.period-chooser-row { .period-chooser-row {
font-weight: bold; font-weight: bold;
padding: 5px; padding: 5px;;
color: #222;
font-size: $font-up-1; font-size: $font-up-1;
align-items: center; align-items: center;
display: flex; display: flex;
@ -56,6 +60,10 @@
flex: 1; flex: 1;
} }
.date-section {
color: $primary;
}
.top-date-string { .top-date-string {
font-weight: normal; font-weight: normal;
font-size: $font-down-1; font-size: $font-down-1;

View file

@ -165,6 +165,11 @@
white-space: nowrap; white-space: nowrap;
} }
&.max-content {
white-space: nowrap;
color: $danger;
}
.name { .name {
margin: 0; margin: 0;
overflow: hidden; overflow: hidden;

View file

@ -6,10 +6,10 @@
.tag-drop-header { .tag-drop-header {
background: $primary-low; background: $primary-low;
color: $primary; color: $primary;
border: none; border: 1px solid transparent;
padding: 4.5px 10px; padding: 5px 10px;
font-size: $font-up-1; font-size: $font-0;
line-height: $line-height-large; transition: none;
.d-icon { .d-icon {
opacity: 1; opacity: 1;
@ -17,6 +17,11 @@
} }
} }
&.is-expanded .tag-drop-header {
border: 1px solid $tertiary;
box-shadow: shadow("focus");
}
.select-kit-collection { .select-kit-collection {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -44,8 +49,7 @@
width: auto; width: auto;
min-width: 150px; min-width: 150px;
border-radius: 0; border-radius: 0;
-webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.4); box-shadow: shadow("dropdown");
box-shadow: 0 2px 2px rgba(0,0,0,0.4);
} }
.select-kit-row { .select-kit-row {

Some files were not shown because too many files have changed in this diff Show more