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:
commit
2b509eaa91
357 changed files with 5863 additions and 2023 deletions
7
.gitattributes
vendored
7
.gitattributes
vendored
|
@ -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
|
||||||
|
|
8
Gemfile
8
Gemfile
|
@ -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'
|
||||||
|
|
36
Gemfile.lock
36
Gemfile.lock
|
@ -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
|
||||||
|
|
|
@ -57,7 +57,6 @@ Plus *lots* of Ruby Gems, a complete list of which is at [/master/Gemfile](https
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
[](https://travis-ci.org/discourse/discourse)
|
[](https://travis-ci.org/discourse/discourse)
|
||||||
[](https://codeclimate.com/github/discourse/discourse)
|
|
||||||
|
|
||||||
Discourse is **100% free** and **open source**. We encourage and support an active, healthy community that
|
Discourse is **100% free** and **open source**. We encourage and support an active, healthy community that
|
||||||
accepts contributions from the public – including you!
|
accepts contributions from the public – including you!
|
||||||
|
|
22
app/assets/javascripts/admin/components/staff-actions.js.es6
Normal file
22
app/assets/javascripts/admin/components/staff-actions.js.es6
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import DiscourseURL from 'discourse/lib/url';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: ['table', 'staff-actions'],
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
this.$().off('click.discourse-staff-logs');
|
||||||
|
},
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
this.$().on('click.discourse-staff-logs', '[data-link-post-id]', e => {
|
||||||
|
let postId = $(e.target).attr('data-link-post-id');
|
||||||
|
|
||||||
|
this.store.find('post', postId).then(p => {
|
||||||
|
DiscourseURL.routeTo(p.get('url'));
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -19,6 +19,11 @@ export default Ember.Controller.extend(CanCheckEmails, {
|
||||||
|
|
||||||
primaryGroupDirty: propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'),
|
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, {
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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 '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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|}}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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|}}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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|}}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"}}
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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"}} {{user.username}}{{/link-to}}</li>
|
<li>{{#link-to 'adminUser' user}}{{d-icon "caret-left"}} {{user.username}}{{/link-to}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#conditional-loading-spinner condition=loading}}
|
{{#conditional-loading-spinner condition=loading}}
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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"}} {{model.username}}{{/link-to}}</li>
|
<li>{{#link-to 'adminUser' model}}{{d-icon "caret-left"}} {{model.username}}{{/link-to}}</li>
|
||||||
<li>{{#link-to 'adminUsersList.show' 'member'}}{{i18n 'admin.user.trust_level_2_users'}}{{/link-to}}</li>
|
<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">
|
||||||
|
|
|
@ -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"}}
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
|
|
@ -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>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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( () => {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
@computed('post.url')
|
||||||
|
postUrl: Discourse.getURL
|
||||||
|
});
|
|
@ -11,7 +11,7 @@ export default Ember.Component.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
Ember.run.schedule('afterRender', () => {
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||||
|
import { default as DiscourseURL, userPath } from 'discourse/lib/url';
|
||||||
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
loading: false,
|
||||||
|
password: null,
|
||||||
|
secondFactorImage: null,
|
||||||
|
secondFactorKey: null,
|
||||||
|
showSecondFactorKey: false,
|
||||||
|
errorMessage: null,
|
||||||
|
newUsername: null,
|
||||||
|
|
||||||
|
loaded: Ember.computed.and('secondFactorImage', 'secondFactorKey'),
|
||||||
|
|
||||||
|
@computed('loading')
|
||||||
|
submitButtonText(loading) {
|
||||||
|
return loading ? 'loading' : 'submit';
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleSecondFactor(enable) {
|
||||||
|
if (!this.get('second_factor_token')) return;
|
||||||
|
this.set('loading', true);
|
||||||
|
|
||||||
|
this.get('content').toggleSecondFactor(this.get('second_factor_token'), enable)
|
||||||
|
.then(response => {
|
||||||
|
if (response.error) {
|
||||||
|
this.set('errorMessage', response.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('errorMessage',null);
|
||||||
|
DiscourseURL.redirectTo(userPath(`${this.get('content').username.toLowerCase()}/preferences`));
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError)
|
||||||
|
.finally(() => this.set('loading', false));
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
confirmPassword() {
|
||||||
|
if (!this.get('password')) return;
|
||||||
|
this.set('loading', true);
|
||||||
|
|
||||||
|
this.get('content').loadSecondFactorCodes(this.get('password'))
|
||||||
|
.then(response => {
|
||||||
|
if(response.error) {
|
||||||
|
this.set('errorMessage', response.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setProperties({
|
||||||
|
errorMessage: null,
|
||||||
|
secondFactorKey: response.key,
|
||||||
|
secondFactorImage: response.qr,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError)
|
||||||
|
.finally(() => this.set('loading', false));
|
||||||
|
},
|
||||||
|
|
||||||
|
showSecondFactorKey() {
|
||||||
|
this.set('showSecondFactorKey', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
enableSecondFactor() {
|
||||||
|
this.toggleSecondFactor(true);
|
||||||
|
},
|
||||||
|
|
||||||
|
disableSecondFactor() {
|
||||||
|
this.toggleSecondFactor(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -30,7 +30,7 @@ export default htmlHelper((period, options) => {
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -22,6 +22,7 @@ const SERVER_SIDE_ONLY = [
|
||||||
/^\/wizard/,
|
/^\/wizard/,
|
||||||
/\.rss$/,
|
/\.rss$/,
|
||||||
/\.json$/,
|
/\.json$/,
|
||||||
|
/^\/admin\/upgrade$/
|
||||||
];
|
];
|
||||||
|
|
||||||
export function rewritePath(path) {
|
export function rewritePath(path) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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' });
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import RestrictedUserRoute from "discourse/routes/restricted-user";
|
||||||
|
|
||||||
|
export default RestrictedUserRoute.extend({
|
||||||
|
model() {
|
||||||
|
return this.modelFor('user');
|
||||||
|
},
|
||||||
|
|
||||||
|
renderTemplate() {
|
||||||
|
return this.render({ into: 'user' });
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController(controller, model) {
|
||||||
|
controller.setProperties({ model, newUsername: model.get('username') });
|
||||||
|
}
|
||||||
|
});
|
|
@ -15,6 +15,10 @@ export default RestrictedUserRoute.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
showTwoFactorModal() {
|
||||||
|
showModal('second-factor-intro');
|
||||||
|
},
|
||||||
|
|
||||||
showAvatarSelector() {
|
showAvatarSelector() {
|
||||||
showModal('avatar-selector');
|
showModal('avatar-selector');
|
||||||
|
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<div id="second-factor" style="display: none;">
|
||||||
|
<h3>{{i18n 'login.second_factor_title'}}</h3>
|
||||||
|
<p>{{i18n 'login.second_factor_description'}}</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for='login-second-factor'>{{i18n 'login.second_factor_label'}} </label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{yield}}
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
|
@ -1,9 +1,9 @@
|
||||||
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword action="login"}}
|
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword loginSecondFactor=loginSecondFactor action="login"}}
|
||||||
{{#d-modal-body title="login.title" class="login-modal"}}
|
{{#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'}} </label>
|
<label for='login-account-password'>{{i18n 'login.password'}} </label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{text-field value=loginPassword type="password" id="login-account-password" maxlength="200"}}
|
{{text-field value=loginPassword type="password" id="login-account-password" maxlength="200"}}
|
||||||
</td>
|
</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"}} {{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"}}>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"}} {{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"}}>
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{{#d-modal-body title="user.second_factor.title"}}
|
||||||
|
<div>{{{i18n 'user.second_factor.extended_description'}}}</div>
|
||||||
|
{{/d-modal-body}}
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
</div>
|
|
@ -16,20 +16,28 @@
|
||||||
{{/if}}
|
{{/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"}}
|
||||||
|
{{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'}}">
|
||||||
{{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/>
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
<section class='user-content user-preferences'>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
<h3>{{i18n 'user.second_factor.title'}}</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if errorMessage}}
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="instructions">
|
||||||
|
<div class='alert alert-error'>{{errorMessage}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if model.second_factor_enabled}}
|
||||||
|
<label class='control-label'>{{i18n 'login.second_factor_label'}}</label>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
{{text-field value=second_factor_token
|
||||||
|
id="second_factor_token"
|
||||||
|
classNames="input-large"
|
||||||
|
autofocus="autofocus"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='instructions'>
|
||||||
|
{{i18n 'user.second_factor.disable_description'}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
{{d-button action="disableSecondFactor"
|
||||||
|
class="btn btn-primary"
|
||||||
|
disabled=loading
|
||||||
|
label=submitButtonText}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
{{#if loaded}}
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
{{i18n 'user.second_factor.enable_description'}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
{{{secondFactorImage}}}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{{#if showSecondFactorKey}}
|
||||||
|
{{secondFactorKey}}
|
||||||
|
{{else}}
|
||||||
|
<a {{action "showSecondFactorKey"}}>{{i18n 'user.second_factor.show_key_description'}}</a>
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label input-prepend">{{i18n 'login.second_factor_label'}}</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
{{text-field value=second_factor_token
|
||||||
|
id="second-factor-token"
|
||||||
|
classNames="input-xxlarge"
|
||||||
|
autofocus="autofocus"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
{{d-button action="enableSecondFactor"
|
||||||
|
class="btn btn-primary"
|
||||||
|
disabled=loading
|
||||||
|
label=submitButtonText}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="control-group">
|
||||||
|
<label class='control-label'>{{i18n 'user.password.title'}}</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
{{text-field value=password
|
||||||
|
id="password"
|
||||||
|
type="password"
|
||||||
|
classNames="input-xxlarge"
|
||||||
|
autofocus="autofocus"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='instructions'>
|
||||||
|
{{i18n 'user.second_factor.confirm_password_description'}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
{{d-button action="confirmPassword"
|
||||||
|
class="btn btn-primary"
|
||||||
|
disabled=loading
|
||||||
|
label=submitButtonText}}
|
||||||
|
|
||||||
|
{{#if saved}}{{i18n 'saved'}}{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</form>
|
||||||
|
</section>
|
|
@ -66,6 +66,25 @@
|
||||||
{{passwordProgress}}
|
{{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">
|
||||||
|
|
|
@ -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"}}
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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":
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -220,3 +220,7 @@
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#custom_emoji {
|
||||||
|
width: 27%;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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%);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue