Merge branch 'master' into pm-tags

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

7
.gitattributes vendored
View File

@ -1,11 +1,14 @@
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto
# Explicitly declare text files we want to always be normalized and converted
# Treat email fixtures as binary files so CRLF are not converted to LF.
*.eml binary
# Explicitly declare text files we want to always be normalized and converted
# to native line endings on checkout.
*.yml text
# Custom for Visual Studio, very unlikely, but lets keep it
# Custom for Visual Studio, very unlikely, but lets keep it
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union

View File

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

View File

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

View File

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

View File

@ -0,0 +1,22 @@
import DiscourseURL from 'discourse/lib/url';
export default Ember.Component.extend({
classNames: ['table', 'staff-actions'],
willDestroyElement() {
this.$().off('click.discourse-staff-logs');
},
didInsertElement() {
this._super();
this.$().on('click.discourse-staff-logs', '[data-link-post-id]', e => {
let postId = $(e.target).attr('data-link-post-id');
this.store.find('post', postId).then(p => {
DiscourseURL.routeTo(p.get('url'));
});
return false;
});
}
});

View File

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

View File

@ -168,6 +168,14 @@ const AdminUser = Discourse.User.extend({
}).catch(popupAjaxError);
},
disableSecondFactor() {
return ajax(`/admin/users/${this.get('id')}/disable_second_factor`, {
type: 'PUT'
}).then(() => {
this.set('second_factor_enabled', false);
}).catch(popupAjaxError);
},
refreshBrowsers() {
return ajax("/admin/users/" + this.get('id') + "/refresh_browsers", {
type: 'POST'

View File

@ -10,7 +10,7 @@ const StaffActionLog = Discourse.Model.extend({
}.property('action_name'),
formattedDetails: function() {
var formatted = "";
let formatted = "";
formatted += this.format('email', 'email');
formatted += this.format('admin.logs.ip_address', 'ip_address');
formatted += this.format('admin.logs.topic_id', 'topic_id');
@ -26,9 +26,13 @@ const StaffActionLog = Discourse.Model.extend({
return formatted;
}.property('ip_address', 'email', 'topic_id', 'post_id', 'category_id'),
format: function(label, propertyName) {
format(label, propertyName) {
if (this.get(propertyName)) {
return ('<b>' + I18n.t(label) + ':</b> ' + escapeExpression(this.get(propertyName)) + '<br/>');
let value = escapeExpression(this.get(propertyName));
if (propertyName === 'post_id') {
value = `<a href data-link-post-id="${value}">${value}</a>`;
}
return `<b>${I18n.t(label)}:</b> ${value}<br/>`;
} else {
return '';
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
<div class='content-list span6 color-schemes'>
<div class='content-list color-schemes'>
<h3>{{i18n 'admin.customize.colors.long_title'}}</h3>
<ul>
{{#each model as |scheme|}}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -156,6 +156,22 @@
</div>
</div>
{{/if}}
<div class='display-row'>
<div class='field'>{{i18n 'user.second_factor.title'}}</div>
<div class='value'>
{{#if model.second_factor_enabled}}
{{i18n "yes_value"}}
{{else}}
{{i18n "no_value"}}
{{/if}}
</div>
<div class='controls'>
{{#if canDisableSecondFactor}}
{{d-button action="disableSecondFactor" icon="unlock-alt" label="user.second_factor.disable"}}
{{/if}}
</div>
</div>
</section>
{{#if userFields}}

View File

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

View File

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

View File

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

View File

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

View File

@ -372,7 +372,13 @@ export default Ember.Component.extend({
post.set('refreshedPost', true);
}
$oneboxes.each((_, o) => load(o, refresh, ajax, this.currentUser.id));
$oneboxes.each((_, o) => load({
elem: o,
refresh,
ajax,
categoryId: this.get('composer.category.id'),
topicId: this.get('composer.topic.id')
}));
},
_warnMentionedGroups($preview) {

View File

@ -80,7 +80,14 @@ export default Ember.Component.extend({
const link = document.createElement('a');
link.href = this.get('composer.title');
let loadOnebox = load(link, false, ajax, this.currentUser.id, true);
const loadOnebox = load({
elem: link,
refresh: false,
ajax,
synchronous: true,
categoryId: this.get('composer.category.id'),
topicId: this.get('composer.topic.id')
});
if (loadOnebox && loadOnebox.then) {
loadOnebox.then( () => {

View File

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

View File

@ -11,7 +11,7 @@ export default Ember.Component.extend({
}
Ember.run.schedule('afterRender', () => {
$('#login-account-password, #login-account-name').keydown(e => {
$('#login-account-password, #login-account-name, #login-second-factor').keydown(e => {
if (e.keyCode === 13) {
this.sendAction();
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,73 @@
import { default as computed } from 'ember-addons/ember-computed-decorators';
import { default as DiscourseURL, userPath } from 'discourse/lib/url';
import { popupAjaxError } from 'discourse/lib/ajax-error';
export default Ember.Controller.extend({
loading: false,
password: null,
secondFactorImage: null,
secondFactorKey: null,
showSecondFactorKey: false,
errorMessage: null,
newUsername: null,
loaded: Ember.computed.and('secondFactorImage', 'secondFactorKey'),
@computed('loading')
submitButtonText(loading) {
return loading ? 'loading' : 'submit';
},
toggleSecondFactor(enable) {
if (!this.get('second_factor_token')) return;
this.set('loading', true);
this.get('content').toggleSecondFactor(this.get('second_factor_token'), enable)
.then(response => {
if (response.error) {
this.set('errorMessage', response.error);
return;
}
this.set('errorMessage',null);
DiscourseURL.redirectTo(userPath(`${this.get('content').username.toLowerCase()}/preferences`));
})
.catch(popupAjaxError)
.finally(() => this.set('loading', false));
},
actions: {
confirmPassword() {
if (!this.get('password')) return;
this.set('loading', true);
this.get('content').loadSecondFactorCodes(this.get('password'))
.then(response => {
if(response.error) {
this.set('errorMessage', response.error);
return;
}
this.setProperties({
errorMessage: null,
secondFactorKey: response.key,
secondFactorImage: response.qr,
});
})
.catch(popupAjaxError)
.finally(() => this.set('loading', false));
},
showSecondFactorKey() {
this.set('showSecondFactorKey', true);
},
enableSecondFactor() {
this.toggleSecondFactor(true);
},
disableSecondFactor() {
this.toggleSecondFactor(false);
}
}
});

View File

@ -30,7 +30,7 @@ export default htmlHelper((period, options) => {
break;
}
return `${title} <span class='top-date-string'>${dateString}</span>`;
return `<span class="date-section">${title}</span><span class='top-date-string'>${dateString}</span>`;
} else {
return title;
}

View File

@ -2,27 +2,27 @@ export default {
name: 'register-service-worker',
initialize() {
// only allow service worker on android for now
if (!/(android)/i.test(navigator.userAgent)) {
// remove old service worker
if ('serviceWorker' in navigator && navigator.serviceWorker.getRegistrations) {
navigator.serviceWorker.getRegistrations().then((registrations) => {
for(let registration of registrations) {
registration.unregister();
};
});
}
} else {
const isSecure = (document.location.protocol === 'https:') ||
window.addEventListener('load', () => {
const isSecured = (document.location.protocol === 'https:') ||
(location.hostname === "localhost");
const isSupported= isSecured && ('serviceWorker' in navigator);
if (isSecure && ('serviceWorker' in navigator)) {
navigator.serviceWorker.register(`${Discourse.BaseUri}/service-worker.js`);
if (isSupported) {
if (Discourse.ServiceWorkerURL) {
navigator.serviceWorker
.register(`${Discourse.BaseUri}/${Discourse.ServiceWorkerURL}`)
.catch(error => {
Ember.Logger.info(`Failed to register Service Worker: ${error}`);
});
} else {
navigator.serviceWorker.getRegistrations().then(registrations => {
for(let registration of registrations) {
registration.unregister();
};
});
}
}
}
});
}
};

View File

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

View File

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

View File

@ -203,7 +203,7 @@ export function validateUploadedFile(file, opts) {
// check that the uploaded file is authorized
if (opts.allowStaffToUploadAnyFileInPm && opts.isPrivateMessage) {
if (Discourse.User.current("staff")) {
if (Discourse.User.currentProp('staff')) {
return true;
}
}
@ -239,16 +239,28 @@ export function validateUploadedFile(file, opts) {
const IMAGES_EXTENSIONS_REGEX = /(png|jpe?g|gif|bmp|tiff?|svg|webp|ico)/i;
function extensionsToArray(exts) {
return exts.toLowerCase()
.replace(/[\s\.]+/g, "")
.split("|")
.filter(ext => ext.indexOf("*") === -1);
}
function extensions() {
return Discourse.SiteSettings.authorized_extensions
.toLowerCase()
.replace(/[\s\.]+/g, "")
.split("|")
.filter(ext => ext.indexOf("*") === -1);
return extensionsToArray(Discourse.SiteSettings.authorized_extensions);
}
function staffExtensions() {
return extensionsToArray(Discourse.SiteSettings.authorized_extensions_for_staff);
}
function imagesExtensions() {
return extensions().filter(ext => IMAGES_EXTENSIONS_REGEX.test(ext));
let exts = extensions().filter(ext => IMAGES_EXTENSIONS_REGEX.test(ext));
if (Discourse.User.currentProp('staff')) {
const staffExts = staffExtensions().filter(ext => IMAGES_EXTENSIONS_REGEX.test(ext));
exts = _.union(exts, staffExts);
}
return exts;
}
function extensionsRegex() {
@ -259,7 +271,14 @@ function imagesExtensionsRegex() {
return new RegExp("\\.(" + imagesExtensions().join("|") + ")$", "i");
}
function staffExtensionsRegex() {
return new RegExp("\\.(" + staffExtensions().join("|") + ")$", "i");
}
function isAuthorizedFile(fileName) {
if (Discourse.User.currentProp('staff') && staffExtensionsRegex().test(fileName)) {
return true;
}
return extensionsRegex().test(fileName);
}
@ -268,7 +287,8 @@ function isAuthorizedImage(fileName){
}
export function authorizedExtensions() {
return authorizesAllExtensions() ? "*" : extensions().join(", ");
const exts = Discourse.User.currentProp('staff') ? [...extensions(), ...staffExtensions()] : extensions();
return exts.filter(ext => ext.length > 0).join(", ");
}
export function authorizedImagesExtensions() {
@ -276,7 +296,9 @@ export function authorizedImagesExtensions() {
}
export function authorizesAllExtensions() {
return Discourse.SiteSettings.authorized_extensions.indexOf("*") >= 0;
return Discourse.SiteSettings.authorized_extensions.indexOf("*") >= 0 || (
Discourse.SiteSettings.authorized_extensions_for_staff.indexOf("*") >= 0 &&
Discourse.User.currentProp('staff'));
}
export function authorizesOneOrMoreExtensions() {
@ -322,7 +344,7 @@ export function allowsImages() {
}
export function allowsAttachments() {
return authorizesAllExtensions() || extensions().length > imagesExtensions().length;
return authorizesAllExtensions() || authorizedExtensions().split(", ").length > imagesExtensions().length;
}
export function uploadLocation(url) {

View File

@ -304,6 +304,20 @@ const User = RestModel.extend({
});
},
loadSecondFactorCodes(password) {
return ajax("/u/second_factors.json", {
data: { password },
type: 'POST'
});
},
toggleSecondFactor(token, enable) {
return ajax("/u/second_factor.json", {
data: { second_factor_token: token, enable },
type: 'PUT'
});
},
loadUserAction(id) {
const stream = this.get('stream');
return ajax(`/user_actions/${id}.json`, { cache: 'false' }).then(result => {

View File

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

View File

@ -0,0 +1,15 @@
import RestrictedUserRoute from "discourse/routes/restricted-user";
export default RestrictedUserRoute.extend({
model() {
return this.modelFor('user');
},
renderTemplate() {
return this.render({ into: 'user' });
},
setupController(controller, model) {
controller.setProperties({ model, newUsername: model.get('username') });
}
});

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,16 @@
<div id="second-factor" style="display: none;">
<h3>{{i18n 'login.second_factor_title'}}</h3>
<p>{{i18n 'login.second_factor_description'}}</p>
<table>
<tr>
<td>
<label for='login-second-factor'>{{i18n 'login.second_factor_label'}}&nbsp;</label>
</td>
<td>
{{yield}}
</td>
<td></td>
</tr>
</table>
</div>

View File

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

View File

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

View File

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

View File

@ -0,0 +1,6 @@
{{#d-modal-body title="user.second_factor.title"}}
<div>{{{i18n 'user.second_factor.extended_description'}}}</div>
{{/d-modal-body}}
<div class="modal-footer">
</div>

View File

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

View File

@ -0,0 +1,112 @@
<section class='user-content user-preferences'>
<form class="form-horizontal">
<div class="control-group">
<div class="controls">
<h3>{{i18n 'user.second_factor.title'}}</h3>
</div>
</div>
{{#if errorMessage}}
<div class="control-group">
<div class="instructions">
<div class='alert alert-error'>{{errorMessage}}</div>
</div>
</div>
{{/if}}
{{#if model.second_factor_enabled}}
<label class='control-label'>{{i18n 'login.second_factor_label'}}</label>
<div class="control-group">
<div class="controls">
{{text-field value=second_factor_token
id="second_factor_token"
classNames="input-large"
autofocus="autofocus"}}
</div>
<div class='instructions'>
{{i18n 'user.second_factor.disable_description'}}
</div>
</div>
<div class="control-group">
<div class="controls">
{{d-button action="disableSecondFactor"
class="btn btn-primary"
disabled=loading
label=submitButtonText}}
</div>
</div>
{{else}}
{{#if loaded}}
<div class="control-group">
<div class="controls">
{{i18n 'user.second_factor.enable_description'}}
</div>
</div>
<div class="control-group">
<div class="controls">
{{{secondFactorImage}}}
<p>
{{#if showSecondFactorKey}}
{{secondFactorKey}}
{{else}}
<a {{action "showSecondFactorKey"}}>{{i18n 'user.second_factor.show_key_description'}}</a>
{{/if}}
</p>
</div>
</div>
<div class="control-group">
<label class="control-label input-prepend">{{i18n 'login.second_factor_label'}}</label>
<div class="controls">
{{text-field value=second_factor_token
id="second-factor-token"
classNames="input-xxlarge"
autofocus="autofocus"}}
</div>
</div>
<div class="control-group">
<div class="controls">
{{d-button action="enableSecondFactor"
class="btn btn-primary"
disabled=loading
label=submitButtonText}}
</div>
</div>
{{else}}
<div class="control-group">
<label class='control-label'>{{i18n 'user.password.title'}}</label>
<div class="controls">
{{text-field value=password
id="password"
type="password"
classNames="input-xxlarge"
autofocus="autofocus"}}
</div>
<div class='instructions'>
{{i18n 'user.second_factor.confirm_password_description'}}
</div>
</div>
<div class="control-group">
<div class="controls">
{{d-button action="confirmPassword"
class="btn btn-primary"
disabled=loading
label=submitButtonText}}
{{#if saved}}{{i18n 'saved'}}{{/if}}
</div>
</div>
{{/if}}
{{/if}}
</form>
</section>

View File

@ -66,6 +66,25 @@
{{passwordProgress}}
</div>
</div>
<div class="control-group pref-second-factor">
<label class="control-label">{{i18n 'user.second_factor.title'}}</label>
<div class="controls">
{{#link-to "preferences.second-factor" class="btn"}}
{{#if model.second_factor_enabled}}
{{d-icon "unlock-alt"}}
{{i18n 'user.second_factor.disable'}}
{{else}}
{{d-icon "lock"}}
{{i18n 'user.second_factor.enable'}}
{{/if}}
{{/link-to}}
</div>
<div class="instructions">
<a href {{action "showTwoFactorModal"}}>{{i18n 'user.second_factor.info_prompt'}}</a>
</div>
</div>
{{/if}}
<div class="control-group pref-avatar">

View File

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

View File

@ -39,7 +39,16 @@
<div class='profile-image'></div>
<div class='details'>
<div class='primary'>
{{bound-avatar model "huge"}}
<div class='user-profile-avatar'>
{{bound-avatar model "huge"}}
{{#if model.primary_group_name}}
{{avatar-flair
flairURL=model.primary_group_flair_url
flairBgColor=model.primary_group_flair_bg_color
flairColor=model.primary_group_flair_color
groupName=model.primary_group_name}}
{{/if}}
</div>
<section class='controls'>
<ul>
{{#if model.can_send_private_message_to_user}}

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@ import { on } from "ember-addons/ember-computed-decorators";
import computed from "ember-addons/ember-computed-decorators";
import PermissionType from "discourse/models/permission-type";
import Category from "discourse/models/category";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
const { get, isNone, isEmpty } = Ember;
export default ComboBoxComponent.extend({
@ -57,6 +58,36 @@ export default ComboBoxComponent.extend({
}
},
computeHeaderContent() {
let content = this.baseHeaderComputedContent();
if (this.get("hasSelection")) {
const category = Category.findById(content.value);
const parentCategoryId = category.get("parent_category_id");
const hasParentCategory = Ember.isPresent(parentCategoryId);
let badge = "";
if (hasParentCategory) {
const parentCategory = Category.findById(parentCategoryId);
badge += categoryBadgeHTML(parentCategory, {
link: false,
allowUncategorized: true
}).htmlSafe();
}
badge += categoryBadgeHTML(category, {
link: false,
hideParent: hasParentCategory ? true : false,
allowUncategorized: true
}).htmlSafe();
content.label = badge;
}
return content;
},
@on("didRender")
_bindComposerResizing() {
this.appEvents.on("composer:resized", this, this.applyDirection);

View File

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

View File

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

View File

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

View File

@ -1,9 +1,20 @@
import CategoryRowComponent from "select-kit/components/category-row";
import { categoryBadgeHTML } from "discourse/helpers/category-link";
import computed from "ember-addons/ember-computed-decorators";
export default CategoryRowComponent.extend({
layoutName: "select-kit/templates/components/category-row",
classNames: "none category-row",
@computed("category")
badgeForCategory(category) {
return categoryBadgeHTML(category, {
link: this.get("categoryLink"),
allowUncategorized: true,
hideParent: true
}).htmlSafe();
},
click() {
this.sendAction("clearSelection");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -263,7 +263,7 @@ aside.quote {
}
.topic-avatar, .avatar-flair-preview, .user-card-avatar, .topic-map .poster {
.topic-avatar, .avatar-flair-preview, .user-card-avatar, .topic-map .poster, .user-profile-avatar {
.avatar-flair {
display: flex;
align-items: center;
@ -275,7 +275,7 @@ aside.quote {
right: -6px;
}
}
.topic-avatar .avatar-flair, .avatar-flair-preview .avatar-flair {
.topic-avatar .avatar-flair, .avatar-flair-preview .avatar-flair, .collapsed-info .user-profile-avatar .avatar-flair {
background-size: 20px 20px;
width: 20px;
height: 20px;
@ -288,7 +288,7 @@ aside.quote {
right: -8px;
}
}
.user-card-avatar .avatar-flair {
.user-card-avatar .avatar-flair, .user-profile-avatar .avatar-flair {
background-size: 40px 40px;
width: 40px;
height: 40px;
@ -398,7 +398,11 @@ kbd
background-color: $secondary;
border: 1px solid $primary-low;
border-radius: 3px;
box-shadow: 0 1px 0 rgba(0,0,0, .8);
box-shadow: shadow("kbd");
background: dark-light-choose(#fafafa, #333);
border: 1px solid dark-light-choose(#ccc, #555);
border-bottom: medium none dark-light-choose(#fff, #000);
color: $primary;
display: inline-block;
font-size: $font-down-1;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -103,6 +103,26 @@ $z-layers: (
@return map-deep-get($z-layers, $layers...);
}
// Box-shadow
// --------------------------------------------------
$box-shadow: (
"modal": 0 8px 60px rgba(0, 0, 0, 0.6),
"composer": 0 -1px 40px rgba(0, 0, 0, 0.12),
"menu-panel": 0 6px 14px rgba(0, 0, 0, 0.15),
"card": 0 4px 14px rgba(0, 0, 0, 0.15),
"dropdown": 0 2px 3px 0 rgba(0, 0, 0, 0.2),
"header": 0 2px 4px -1px rgba(0, 0, 0, 0.25),
"kbd": (0 2px 0 rgba(0, 0, 0, 0.2), 0 0 0 1px dark-light-choose(#fff, #000) inset),
"focus": 0 0 6px 0 $tertiary,
"focus-danger": 0 0 6px 0 $danger
);
@function shadow($key) {
@return map-get($box-shadow, $key);
}
// Color utilities
// --------------------------------------------------

View File

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

View File

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

View File

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

View File

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

View File

@ -79,7 +79,7 @@
}
.select2-container-active {
box-shadow: $tertiary 0 0 6px 0px;
box-shadow: shadow("focus");
}
.select2-results .select2-no-results, .select2-results .select2-searching, .select2-results .select2-selection-limit {

View File

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

View File

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

View File

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

View File

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

View File

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

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