Merge branch 'master' into fix_limited_search_results

This commit is contained in:
Neil Lalonde 2017-07-31 15:55:31 -04:00 committed by GitHub
commit 7c1d7fb423
2937 changed files with 30379 additions and 11967 deletions

View File

@ -1,12 +0,0 @@
skip_missing_workers: true
allow_lossy: false
# PNG
advpng: false
optipng:
level: 2
pngcrush: false
pngout: false
pngquant: false
# JPG
jpegrecompress: false
timeout: 15

View File

@ -1,14 +1,104 @@
AllCops: AllCops:
TargetRubyVersion: 2.3 TargetRubyVersion: 2.4
DisabledByDefault: true
Metrics/LineLength: # Prefer &&/|| over and/or.
Max: 120 Style/AndOr:
Enabled: true
Metrics/MethodLength: # Do not use braces for hash literals when they are the last argument of a
# method call.
Style/BracesAroundHashParameters:
Enabled: true
# Align `when` with `case`.
Layout/CaseIndentation:
Enabled: true
# Align comments with method definitions.
Layout/CommentIndentation:
Enabled: true
# No extra empty lines.
Layout/EmptyLines:
Enabled: true
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
Style/HashSyntax:
Enabled: true
# Two spaces, no tabs (for indentation).
Layout/IndentationWidth:
Enabled: true
Layout/SpaceAfterColon:
Enabled: true
Layout/SpaceAfterComma:
Enabled: true
Layout/SpaceAroundEqualsInParameterDefault:
Enabled: true
Layout/SpaceAroundKeyword:
Enabled: true
Layout/SpaceAroundOperators:
Enabled: true
Layout/SpaceBeforeFirstArg:
Enabled: true
# Defining a method with parameters needs parentheses.
Style/MethodDefParentheses:
Enabled: true
# Use `foo {}` not `foo{}`.
Layout/SpaceBeforeBlockBraces:
Enabled: true
# Use `foo { bar }` not `foo {bar}`.
Layout/SpaceInsideBlockBraces:
Enabled: true
# Use `{ a: 1 }` not `{a:1}`.
Layout/SpaceInsideHashLiteralBraces:
Enabled: true
Layout/SpaceInsideParens:
Enabled: true
# Detect hard tabs, no hard tabs.
Layout/Tab:
Enabled: true
# Blank lines should not have any spaces.
Layout/TrailingBlankLines:
Enabled: true
# No trailing whitespace.
Layout/TrailingWhitespace:
Enabled: true
Lint/BlockAlignment:
Enabled: true
# Align `end` with the matching keyword or starting expression except for
# assignments, where it should be aligned with the LHS.
Lint/EndAlignment:
Enabled: true
EnforcedStyleAlignWith: variable
# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
Lint/RequireParentheses:
Enabled: true
Layout/MultilineMethodCallIndentation:
Enabled: true
EnforcedStyle: indented
Layout/AlignHash:
Enabled: true
Bundler/OrderedGems:
Enabled: false Enabled: false
Style/Documentation:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: False

View File

@ -6,9 +6,7 @@ env:
- RUBY_GC_MALLOC_LIMIT=50000000 - RUBY_GC_MALLOC_LIMIT=50000000
matrix: matrix:
- "RAILS_MASTER=0 QUNIT_RUN=0" - "RAILS_MASTER=0 QUNIT_RUN=0"
- "RAILS_MASTER=1 QUNIT_RUN=0"
- "RAILS_MASTER=0 QUNIT_RUN=1" - "RAILS_MASTER=0 QUNIT_RUN=1"
- "RAILS_MASTER=1 QUNIT_RUN=1"
addons: addons:
postgresql: 9.5 postgresql: 9.5
@ -20,9 +18,6 @@ addons:
- jhead - jhead
matrix: matrix:
allow_failures:
- env: "RAILS_MASTER=1 QUNIT_RUN=0"
- env: "RAILS_MASTER=1 QUNIT_RUN=1"
fast_finish: true fast_finish: true
rvm: rvm:
@ -41,7 +36,7 @@ cache:
- vendor/bundle - vendor/bundle
before_install: before_install:
- gem install bundler - gem install bundler rubocop
- git clone --depth=1 https://github.com/discourse/discourse-backup-uploads-to-s3.git plugins/discourse-backup-uploads-to-s3 - git clone --depth=1 https://github.com/discourse/discourse-backup-uploads-to-s3.git plugins/discourse-backup-uploads-to-s3
- git clone --depth=1 https://github.com/discourse/discourse-spoiler-alert.git plugins/discourse-spoiler-alert - git clone --depth=1 https://github.com/discourse/discourse-spoiler-alert.git plugins/discourse-spoiler-alert
- git clone --depth=1 https://github.com/discourse/discourse-cakeday.git plugins/discourse-cakeday - git clone --depth=1 https://github.com/discourse/discourse-cakeday.git plugins/discourse-cakeday
@ -53,6 +48,7 @@ before_install:
- eslint --ext .es6 test/javascripts - eslint --ext .es6 test/javascripts
- eslint --ext .es6 plugins/**/assets/javascripts - eslint --ext .es6 plugins/**/assets/javascripts
- eslint test/javascripts - eslint test/javascripts
- rubocop --parallel
before_script: before_script:
- bundle exec rake db:create db:migrate - bundle exec rake db:create db:migrate
@ -62,4 +58,4 @@ install:
- bash -c "if [ '$RAILS_MASTER' == '0' ]; then bundle install --without development --deployment --retry=3 --jobs=3; fi" - bash -c "if [ '$RAILS_MASTER' == '0' ]; then bundle install --without development --deployment --retry=3 --jobs=3; fi"
script: script:
- bash -c "if [ '$QUNIT_RUN' == '0' ]; then bundle exec rspec && bundle exec rake plugin:spec; else LOAD_PLUGINS=1 bundle exec rake qunit:test['200000']; fi" - bash -c "if [ '$QUNIT_RUN' == '0' ]; then bundle exec rspec && bundle exec rake plugin:spec; else LOAD_PLUGINS=1 bundle exec rake qunit:test['300000']; fi"

View File

@ -66,7 +66,7 @@ gem 'aws-sdk', require: false
gem 'excon', require: false gem 'excon', require: false
gem 'unf', require: false gem 'unf', require: false
gem 'email_reply_trimmer', '0.1.6' gem 'email_reply_trimmer', '0.1.7'
# TODO Use official image_optim gem once https://github.com/toy/image_optim/pull/149 # TODO Use official image_optim gem once https://github.com/toy/image_optim/pull/149
# is merged. # is merged.
@ -122,7 +122,6 @@ group :test do
gem 'webmock', require: false gem 'webmock', require: false
gem 'fakeweb', '~> 1.3.0', require: false gem 'fakeweb', '~> 1.3.0', require: false
gem 'minitest', require: false gem 'minitest', require: false
gem 'timecop'
# TODO: Remove once we upgrade to Rails 5. # TODO: Remove once we upgrade to Rails 5.
gem 'test_after_commit' gem 'test_after_commit'
end end
@ -190,7 +189,6 @@ gem 'logster'
gem 'sassc', require: false gem 'sassc', require: false
if ENV["IMPORT"] == "1" if ENV["IMPORT"] == "1"
gem 'mysql2' gem 'mysql2'
gem 'redcarpet' gem 'redcarpet'

View File

@ -1,38 +1,38 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (4.2.8) actionmailer (4.2.9)
actionpack (= 4.2.8) actionpack (= 4.2.9)
actionview (= 4.2.8) actionview (= 4.2.9)
activejob (= 4.2.8) activejob (= 4.2.9)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.8) actionpack (4.2.9)
actionview (= 4.2.8) actionview (= 4.2.9)
activesupport (= 4.2.8) activesupport (= 4.2.9)
rack (~> 1.6) rack (~> 1.6)
rack-test (~> 0.6.2) rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.8) actionview (4.2.9)
activesupport (= 4.2.8) activesupport (= 4.2.9)
builder (~> 3.1) builder (~> 3.1)
erubis (~> 2.7.0) erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5) rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.3) rails-html-sanitizer (~> 1.0, >= 1.0.3)
active_model_serializers (0.8.3) active_model_serializers (0.8.3)
activemodel (>= 3.0) activemodel (>= 3.0)
activejob (4.2.8) activejob (4.2.9)
activesupport (= 4.2.8) activesupport (= 4.2.9)
globalid (>= 0.3.0) globalid (>= 0.3.0)
activemodel (4.2.8) activemodel (4.2.9)
activesupport (= 4.2.8) activesupport (= 4.2.9)
builder (~> 3.1) builder (~> 3.1)
activerecord (4.2.8) activerecord (4.2.9)
activemodel (= 4.2.8) activemodel (= 4.2.9)
activesupport (= 4.2.8) activesupport (= 4.2.9)
arel (~> 6.0) arel (~> 6.0)
activesupport (4.2.8) activesupport (4.2.9)
i18n (~> 0.7) i18n (~> 0.7)
minitest (~> 5.1) minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4) thread_safe (~> 0.3, >= 0.3.4)
@ -84,7 +84,7 @@ GEM
image_size (~> 1.5) image_size (~> 1.5)
in_threads (~> 1.3) in_threads (~> 1.3)
progress (~> 3.0, >= 3.0.1) progress (~> 3.0, >= 3.0.1)
email_reply_trimmer (0.1.6) email_reply_trimmer (0.1.7)
ember-data-source (2.2.1) ember-data-source (2.2.1)
ember-source (>= 1.8, < 3.0) ember-source (>= 1.8, < 3.0)
ember-handlebars-template (0.7.5) ember-handlebars-template (0.7.5)
@ -127,7 +127,7 @@ GEM
hiredis (0.6.1) hiredis (0.6.1)
htmlentities (4.3.4) htmlentities (4.3.4)
http_accept_language (2.0.5) http_accept_language (2.0.5)
i18n (0.8.4) i18n (0.8.6)
image_size (1.5.0) image_size (1.5.0)
in_threads (1.4.0) in_threads (1.4.0)
jmespath (1.3.1) jmespath (1.3.1)
@ -137,7 +137,7 @@ GEM
thor (>= 0.14, < 2.0) thor (>= 0.14, < 2.0)
jwt (1.5.6) jwt (1.5.6)
kgio (2.11.0) kgio (2.11.0)
libv8 (5.3.332.38.5) libv8 (5.9.211.38.1)
listen (3.1.5) listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4) rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7) rb-inotify (~> 0.9, >= 0.9.7)
@ -153,12 +153,14 @@ GEM
rack (>= 1.1.3) rack (>= 1.1.3)
metaclass (0.0.4) metaclass (0.0.4)
method_source (0.8.2) method_source (0.8.2)
mime-types (2.99.3) mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_mime (0.1.3) mini_mime (0.1.3)
mini_portile2 (2.2.0) mini_portile2 (2.2.0)
mini_racer (0.1.10) mini_racer (0.1.11)
libv8 (~> 5.3) libv8 (~> 5.7)
minitest (5.10.2) minitest (5.10.3)
mocha (1.2.1) mocha (1.2.1)
metaclass (~> 0.0.1) metaclass (~> 0.0.1)
mock_redis (0.17.3) mock_redis (0.17.3)
@ -211,7 +213,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.15) onebox (1.8.16)
fast_blank (>= 1.0.0) fast_blank (>= 1.0.0)
htmlentities (~> 4.3) htmlentities (~> 4.3)
moneta (~> 1.0) moneta (~> 1.0)
@ -245,16 +247,16 @@ GEM
rack rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rails (4.2.8) rails (4.2.9)
actionmailer (= 4.2.8) actionmailer (= 4.2.9)
actionpack (= 4.2.8) actionpack (= 4.2.9)
actionview (= 4.2.8) actionview (= 4.2.9)
activejob (= 4.2.8) activejob (= 4.2.9)
activemodel (= 4.2.8) activemodel (= 4.2.9)
activerecord (= 4.2.8) activerecord (= 4.2.9)
activesupport (= 4.2.8) activesupport (= 4.2.9)
bundler (>= 1.3.0, < 2.0) bundler (>= 1.3.0, < 2.0)
railties (= 4.2.8) railties (= 4.2.9)
sprockets-rails sprockets-rails
rails-deprecated_sanitizer (1.0.3) rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha) activesupport (>= 4.2.0.alpha)
@ -266,9 +268,9 @@ GEM
loofah (~> 2.0) loofah (~> 2.0)
rails_multisite (1.0.6) rails_multisite (1.0.6)
rails (> 4.2, < 5) rails (> 4.2, < 5)
railties (4.2.8) railties (4.2.9)
actionpack (= 4.2.8) actionpack (= 4.2.9)
activesupport (= 4.2.8) activesupport (= 4.2.9)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
raindrops (0.18.0) raindrops (0.18.0)
@ -363,7 +365,6 @@ GEM
thor (0.19.4) thor (0.19.4)
thread_safe (0.3.6) thread_safe (0.3.6)
tilt (2.0.7) tilt (2.0.7)
timecop (0.8.1)
trollop (2.1.2) trollop (2.1.2)
tzinfo (1.2.3) tzinfo (1.2.3)
thread_safe (~> 0.1) thread_safe (~> 0.1)
@ -397,7 +398,7 @@ DEPENDENCIES
certified certified
discourse-qunit-rails discourse-qunit-rails
discourse_image_optim discourse_image_optim
email_reply_trimmer (= 0.1.6) email_reply_trimmer (= 0.1.7)
ember-handlebars-template (= 0.7.5) ember-handlebars-template (= 0.7.5)
ember-rails (= 0.18.5) ember-rails (= 0.18.5)
ember-source ember-source
@ -476,11 +477,10 @@ DEPENDENCIES
test_after_commit test_after_commit
thor thor
tilt tilt
timecop
uglifier uglifier
unf unf
unicorn unicorn
webmock webmock
BUNDLED WITH BUNDLED WITH
1.15.1 1.15.3

View File

@ -9,4 +9,3 @@ Discourse::Application.load_tasks
# this prevents crashes when migrating a database in production in certain # this prevents crashes when migrating a database in production in certain
# PostgreSQL configuations when trying to create structure.sql # PostgreSQL configuations when trying to create structure.sql
Rake::Task["db:structure:dump"].clear if Rails.env.production? Rake::Task["db:structure:dump"].clear if Rails.env.production?

View File

@ -1,4 +1,4 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(bufferedRender({ export default Ember.Component.extend(bufferedRender({

View File

@ -0,0 +1,19 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(bufferedRender({
classNames: ['watched-word'],
buildBuffer(buffer) {
buffer.push(iconHTML('times'));
buffer.push(' ' + this.get('word.word'));
},
click() {
this.get('word').destroy().then(() => {
this.sendAction('action', this.get('word'));
}).catch(e => {
bootbox.alert(I18n.t("generic_error_with_reason", {error: `http: ${e.status} - ${e.body}`}));
});;
}
}));

View File

@ -1,5 +1,5 @@
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(bufferedRender({ export default Ember.Component.extend(bufferedRender({

View File

@ -1,3 +1,4 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
/*global Resumable:true */ /*global Resumable:true */
@ -40,7 +41,7 @@ export default Ember.Component.extend(bufferedRender({
buildBuffer(buffer) { buildBuffer(buffer) {
const icon = this.get("isUploading") ? "times" : "upload"; const icon = this.get("isUploading") ? "times" : "upload";
buffer.push(`<i class="fa fa-${icon}"></i>`); buffer.push(iconHTML(icon));
buffer.push("<span class='ru-label'>" + this.get("text") + "</span>"); buffer.push("<span class='ru-label'>" + this.get("text") + "</span>");
buffer.push("<span class='ru-progress' style='width:" + this.get("progress") + "%'></span>"); buffer.push("<span class='ru-progress' style='width:" + this.get("progress") + "%'></span>");
}, },

View File

@ -0,0 +1,49 @@
import WatchedWord from 'admin/models/watched-word';
import { on, observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
classNames: ['watched-word-form'],
formSubmitted: false,
actionKey: null,
showSuccessMessage: false,
@observes('word')
removeSuccessMessage() {
if (this.get('showSuccessMessage') && !Ember.isEmpty(this.get('word'))) {
this.set('showSuccessMessage', false);
}
},
actions: {
submit() {
if (!this.get('formSubmitted')) {
this.set('formSubmitted', true);
const watchedWord = WatchedWord.create({ word: this.get('word'), action: this.get('actionKey') });
watchedWord.save().then(result => {
this.setProperties({ word: '', formSubmitted: false, showSuccessMessage: true });
this.sendAction('action', WatchedWord.create(result));
Ember.run.schedule('afterRender', () => this.$('.watched-word-input').focus());
}).catch(e => {
this.set('formSubmitted', false);
const msg = (e.responseJSON && e.responseJSON.errors) ?
I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')}) :
I18n.t("generic_error");
bootbox.alert(msg, () => this.$('.watched-word-input').focus());
});
}
}
},
@on("didInsertElement")
_init() {
Ember.run.schedule('afterRender', () => {
this.$('.watched-word-input').keydown(e => {
if (e.keyCode === 13) {
this.send('submit');
}
});
});
}
});

View File

@ -0,0 +1,25 @@
import computed from "ember-addons/ember-computed-decorators";
import UploadMixin from "discourse/mixins/upload";
export default Em.Component.extend(UploadMixin, {
type: 'csv',
classNames: 'watched-words-uploader',
uploadUrl: '/admin/watched_words/upload',
addDisabled: Em.computed.alias("uploading"),
validateUploadedFilesOptions() {
return { csvOnly: true };
},
@computed('actionKey')
data(actionKey) {
return { action_key: actionKey };
},
uploadDone() {
if (this) {
bootbox.alert(I18n.t("admin.watched_words.form.upload_successful"));
this.sendAction("done");
}
}
});

View File

@ -31,10 +31,10 @@ export default Ember.Controller.extend({
]; ];
}.property(), }.property(),
@computed('model.visibility_level', 'model.public') @computed('model.visibility_level', 'model.public_admission')
disableMembershipRequestSetting(visibility_level, publicGroup) { disableMembershipRequestSetting(visibility_level, publicAdmission) {
visibility_level = parseInt(visibility_level); visibility_level = parseInt(visibility_level);
return (visibility_level !== 0) || publicGroup; return (visibility_level !== 0) || publicAdmission;
}, },
@computed('model.visibility_level', 'model.allow_membership_requests') @computed('model.visibility_level', 'model.allow_membership_requests')

View File

@ -0,0 +1,65 @@
import computed from 'ember-addons/ember-computed-decorators';
import WatchedWord from 'admin/models/watched-word';
export default Ember.Controller.extend({
actionNameKey: null,
adminWatchedWords: Ember.inject.controller(),
showWordsList: Ember.computed.or('adminWatchedWords.filtered', 'adminWatchedWords.showWords'),
findAction(actionName) {
return (this.get('adminWatchedWords.model') || []).findBy('nameKey', actionName);
},
@computed('adminWatchedWords.model', 'actionNameKey')
filteredContent() {
if (!this.get('actionNameKey')) { return []; }
const a = this.findAction(this.get('actionNameKey'));
return a ? a.words : [];
},
@computed('actionNameKey')
actionDescription(actionNameKey) {
return I18n.t('admin.watched_words.action_descriptions.' + actionNameKey);
},
actions: {
recordAdded(arg) {
const a = this.findAction(this.get('actionNameKey'));
if (a) {
a.words.unshiftObject(arg);
a.incrementProperty('count');
Em.run.schedule('afterRender', () => {
// remove from other actions lists
let match = null;
this.get('adminWatchedWords.model').forEach(action => {
if (match) return;
if (action.nameKey !== this.get('actionNameKey')) {
match = action.words.findBy('id', arg.id);
if (match) {
action.words.removeObject(match);
action.decrementProperty('count');
}
}
});
});
}
},
recordRemoved(arg) {
const a = this.findAction(this.get('actionNameKey'));
if (a) {
a.words.removeObject(arg);
a.decrementProperty('count');
}
},
uploadComplete() {
WatchedWord.findAll().then(data => {
this.set('adminWatchedWords.model', data);
});
}
}
});

View File

@ -0,0 +1,51 @@
import debounce from 'discourse/lib/debounce';
export default Ember.Controller.extend({
filter: null,
filtered: false,
showWords: false,
disableShowWords: Ember.computed.alias('filtered'),
filterContentNow() {
if (!!Ember.isEmpty(this.get('allWatchedWords'))) return;
let filter;
if (this.get('filter')) {
filter = this.get('filter').toLowerCase();
}
if (filter === undefined || filter.length < 1) {
this.set('model', this.get('allWatchedWords'));
return;
}
const matchesByAction = [];
this.get('allWatchedWords').forEach(wordsForAction => {
const wordRecords = wordsForAction.words.filter(wordRecord => {
return (wordRecord.word.indexOf(filter) > -1);
});
matchesByAction.pushObject( Ember.Object.create({
nameKey: wordsForAction.nameKey,
name: wordsForAction.name,
words: wordRecords,
count: wordRecords.length
}) );
});
this.set('model', matchesByAction);
},
filterContent: debounce(function() {
this.filterContentNow();
this.set('filtered', !Ember.isEmpty(this.get('filter')));
}, 250).observes('filter'),
actions: {
clearFilter() {
this.setProperties({ filter: '' });
}
}
});

View File

@ -0,0 +1,7 @@
import { registerUnbound } from 'discourse-common/lib/helpers';
import { renderIcon } from 'discourse-common/lib/icon-library';
registerUnbound('check-icon', function(value) {
let icon = value ? "check" : "times";
return new Handlebars.SafeString(renderIcon('string', icon));
});

View File

@ -1,3 +1,4 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
import { propertyNotEqual } from 'discourse/lib/computed'; import { propertyNotEqual } from 'discourse/lib/computed';
@ -108,7 +109,7 @@ const AdminUser = Discourse.User.extend({
"class": "cancel-inline", "class": "cancel-inline",
"link": true "link": true
}, { }, {
"label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("admin.user.delete_all_posts"), "label": `${iconHTML('exclamation-triangle')} ` + I18n.t("admin.user.delete_all_posts"),
"class": "btn btn-danger", "class": "btn btn-danger",
"callback": function() { "callback": function() {
ajax("/admin/users/" + user.get('id') + "/delete_all_posts", { ajax("/admin/users/" + user.get('id') + "/delete_all_posts", {
@ -337,7 +338,7 @@ const AdminUser = Discourse.User.extend({
"class": "cancel", "class": "cancel",
"link": true "link": true
}, { }, {
"label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.block_accept'), "label": `${iconHTML('exclamation-triangle')} ` + I18n.t('admin.user.block_accept'),
"class": "btn btn-danger", "class": "btn btn-danger",
"callback": function() { performBlock(); } "callback": function() { performBlock(); }
}]; }];
@ -386,7 +387,7 @@ const AdminUser = Discourse.User.extend({
"class": "cancel", "class": "cancel",
"link": true "link": true
}, { }, {
"label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.anonymize_yes'), "label": `${iconHTML('exclamation-triangle')} ` + I18n.t('admin.user.anonymize_yes'),
"class": "btn btn-danger", "class": "btn btn-danger",
"callback": function() { performAnonymize(); } "callback": function() { performAnonymize(); }
}]; }];
@ -450,7 +451,7 @@ const AdminUser = Discourse.User.extend({
"class": "btn", "class": "btn",
"link": true "link": true
}, { }, {
"label": '<i class="fa fa-exclamation-triangle"></i>' + I18n.t('admin.user.delete_and_block'), "label": `${iconHTML('exclamation-triangle')} ` + I18n.t('admin.user.delete_and_block'),
"class": "btn btn-danger", "class": "btn btn-danger",
"callback": function(){ performDestroy(true); } "callback": function(){ performDestroy(true); }
}, { }, {
@ -479,7 +480,7 @@ const AdminUser = Discourse.User.extend({
"class": "cancel-inline", "class": "cancel-inline",
"link": true "link": true
}, { }, {
"label": '<i class="fa fa-exclamation-triangle"></i> ' + I18n.t("flagging.yes_delete_spammer"), "label": `${iconHTML('exclamation-triangle')} ` + I18n.t("flagging.yes_delete_spammer"),
"class": "btn btn-danger", "class": "btn btn-danger",
"callback": function() { "callback": function() {
return ajax("/admin/users/" + user.get('id') + '.json', { return ajax("/admin/users/" + user.get('id') + '.json', {

View File

@ -2,7 +2,7 @@ import { ajax } from 'discourse/lib/ajax';
import AdminUser from 'admin/models/admin-user'; import AdminUser from 'admin/models/admin-user';
import Topic from 'discourse/models/topic'; import Topic from 'discourse/models/topic';
import Post from 'discourse/models/post'; import Post from 'discourse/models/post';
import { iconHTML } from 'discourse-common/lib/icon-library';
const FlaggedPost = Post.extend({ const FlaggedPost = Post.extend({
@ -35,13 +35,14 @@ const FlaggedPost = Post.extend({
dispositionIcon: function (disposition) { dispositionIcon: function (disposition) {
if (!disposition) { return null; } if (!disposition) { return null; }
var icon, title = I18n.t('admin.flags.dispositions.' + disposition); let icon;
let title = 'admin.flags.dispositions.' + disposition;
switch (disposition) { switch (disposition) {
case "deferred": { icon = "fa-external-link"; break; } case "deferred": { icon = "external-link"; break; }
case "agreed": { icon = "fa-thumbs-o-up"; break; } case "agreed": { icon = "thumbs-o-up"; break; }
case "disagreed": { icon = "fa-thumbs-o-down"; break; } case "disagreed": { icon = "thumbs-o-down"; break; }
} }
return "<i class='fa " + icon + "' title='" + title + "'></i>"; return iconHTML(icon, { title });
}, },
wasEdited: function () { wasEdited: function () {

View File

@ -0,0 +1,37 @@
import { ajax } from 'discourse/lib/ajax';
const WatchedWord = Discourse.Model.extend({
save() {
return ajax("/admin/watched_words" + (this.id ? '/' + this.id : '') + ".json", {
type: this.id ? 'PUT' : 'POST',
data: {word: this.get('word'), action_key: this.get('action')},
dataType: 'json'
});
},
destroy() {
return ajax("/admin/watched_words/" + this.get('id') + ".json", {type: 'DELETE'});
}
});
WatchedWord.reopenClass({
findAll() {
return ajax("/admin/watched_words").then(function (list) {
const actions = {};
list.words.forEach(s => {
if (!actions[s.action]) { actions[s.action] = []; }
actions[s.action].pushObject(WatchedWord.create(s));
});
list.actions.forEach(a => {
if (!actions[a]) { actions[a] = []; }
});
return Object.keys(actions).map(function(n) {
return Ember.Object.create({nameKey: n, name: I18n.t('admin.watched_words.actions.' + n), words: actions[n], count: actions[n].length});
});
});
}
});
export default WatchedWord;

View File

@ -37,7 +37,7 @@ export default RestModel.extend({
}, },
groupFinder(term) { groupFinder(term) {
return Group.findAll({search: term, ignore_automatic: false}); return Group.findAll({ term: term, ignore_automatic: false });
}, },
@computed('wildcard_web_hook', 'web_hook_event_types.[]') @computed('wildcard_web_hook', 'web_hook_event_types.[]')
@ -82,4 +82,3 @@ export default RestModel.extend({
return this.createProperties(); return this.createProperties();
} }
}); });

View File

@ -90,5 +90,10 @@ export default function() {
this.route('adminPlugins', { path: '/plugins', resetNamespace: true }, function() { this.route('adminPlugins', { path: '/plugins', resetNamespace: true }, function() {
this.route('index', { path: '/' }); this.route('index', { path: '/' });
}); });
this.route('adminWatchedWords', { path: '/watched_words', resetNamespace: true}, function() {
this.route('index', { path: '/' } );
this.route('action', { path: '/action/:action_id' } );
});
}); });
}; };

View File

@ -0,0 +1,11 @@
export default Discourse.Route.extend({
model(params) {
this.controllerFor('adminWatchedWordsAction').set('actionNameKey', params.action_id);
let filteredContent = this.controllerFor('adminWatchedWordsAction').get('filteredContent');
return Ember.Object.create({
nameKey: params.action_id,
name: I18n.t('admin.watched_words.actions.' + params.action_id),
words: filteredContent
});
}
});

View File

@ -0,0 +1,5 @@
export default Discourse.Route.extend({
beforeModel() {
this.replaceWith('adminWatchedWords.action', this.modelFor('adminWatchedWords')[0].nameKey);
}
});

View File

@ -0,0 +1,15 @@
import WatchedWord from 'admin/models/watched-word';
export default Discourse.Route.extend({
queryParams: {
filter: { replace: true }
},
model() {
return WatchedWord.findAll();
},
afterModel(watchedWordsList) {
this.controllerFor('adminWatchedWords').set('allWatchedWords', watchedWordsList);
}
});

View File

@ -22,6 +22,7 @@
{{nav-item route='adminApi' label='admin.api.title'}} {{nav-item route='adminApi' label='admin.api.title'}}
{{nav-item route='admin.backups' label='admin.backups.title'}} {{nav-item route='admin.backups' label='admin.backups.title'}}
{{/if}} {{/if}}
{{nav-item route='adminWatchedWords' label='admin.watched_words.title'}}
{{nav-item route='adminPlugins' label='admin.plugins.title'}} {{nav-item route='adminPlugins' label='admin.plugins.title'}}
{{plugin-outlet name="admin-menu" connectorTagName="li"}} {{plugin-outlet name="admin-menu" connectorTagName="li"}}
</ul> </ul>

View File

@ -29,5 +29,5 @@
{{/if}} {{/if}}
{{#unless hasMasterKey}} {{#unless hasMasterKey}}
<button class='btn' {{action "generateMasterKey"}}><i class="fa fa-key"></i>{{i18n 'admin.api.generate_master'}}</button> <button class='btn' {{action "generateMasterKey"}}>{{d-icon "key"}}</button>
{{/unless}} {{/unless}}

View File

@ -3,7 +3,7 @@
<div> <div>
{{#link-to 'adminBadges.show' 'new' class="btn"}} {{#link-to 'adminBadges.show' 'new' class="btn"}}
{{fa-icon "plus"}} {{i18n 'admin.badges.new'}} {{d-icon "plus"}} {{i18n 'admin.badges.new'}}
{{/link-to}} {{/link-to}}
</div> </div>
{{/d-section}} {{/d-section}}

View File

@ -38,7 +38,7 @@
content=badgeGroupings content=badgeGroupings
optionValuePath="content.id" optionValuePath="content.id"
optionLabelPath="content.displayName"}} optionLabelPath="content.displayName"}}
&nbsp;<button {{action "editGroupings"}} class='btn'>{{fa-icon 'pencil'}}</button> &nbsp;<button {{action "editGroupings"}} class='btn'>{{d-icon 'pencil'}}</button>
</div> </div>

View File

@ -15,7 +15,7 @@
{{/each}} {{/each}}
</ul> </ul>
{{#link-to 'adminBadges.show' 'new' class="btn"}} {{#link-to 'adminBadges.show' 'new' class="btn"}}
{{fa-icon "plus"}} {{i18n 'admin.badges.new'}} {{d-icon "plus"}} {{i18n 'admin.badges.new'}}
{{/link-to}} {{/link-to}}
<br> <br>
<br> <br>

View File

@ -1,6 +1,6 @@
<td class="title"> <td class="title">
{{#if report.icon}} {{#if report.icon}}
{{fa-icon report.icon}} {{d-icon report.icon}}
{{/if}} {{/if}}
<a href="{{report.reportUrl}}">{{report.title}}</a> <a href="{{report.reportUrl}}">{{report.title}}</a>
</td> </td>
@ -8,15 +8,15 @@
<td class="value">{{number report.todayCount}}</td> <td class="value">{{number report.todayCount}}</td>
<td class="value {{report.yesterdayTrend}}" title={{report.yesterdayCountTitle}}> <td class="value {{report.yesterdayTrend}}" title={{report.yesterdayCountTitle}}>
{{number report.yesterdayCount}} {{fa-icon "caret-up" class="up"}} {{fa-icon "caret-down" class="down"}} {{number report.yesterdayCount}} {{d-icon "caret-up" class="up"}} {{d-icon "caret-down" class="down"}}
</td> </td>
<td class="value {{report.sevenDayTrend}}" title={{report.sevenDayCountTitle}}> <td class="value {{report.sevenDayTrend}}" title={{report.sevenDayCountTitle}}>
{{number report.lastSevenDaysCount}} {{fa-icon "caret-up" class="up"}} {{fa-icon "caret-down" class="down"}} {{number report.lastSevenDaysCount}} {{d-icon "caret-up" class="up"}} {{d-icon "caret-down" class="down"}}
</td> </td>
<td class="value {{report.thirtyDayTrend}}" title={{report.thirtyDayCountTitle}}> <td class="value {{report.thirtyDayTrend}}" title={{report.thirtyDayCountTitle}}>
{{number report.lastThirtyDaysCount}} {{fa-icon "caret-up" class="up"}} {{fa-icon "caret-down" class="down"}} {{number report.lastThirtyDaysCount}} {{d-icon "caret-up" class="up"}} {{d-icon "caret-down" class="down"}}
</td> </td>
<td class="value">{{number report.total}}</td> <td class="value">{{number report.total}}</td>

View File

@ -1,4 +1,4 @@
<div class="validation-error {{unless message 'hidden'}}"> <div class="validation-error {{unless message 'hidden'}}">
{{fa-icon "times"}} {{d-icon "times"}}
{{message}} {{message}}
</div> </div>

View File

@ -0,0 +1,7 @@
<b>{{i18n 'admin.watched_words.form.label'}}</b>
{{text-field value=word disabled=formSubmitted class="watched-word-input" autocorrect="off" autocapitalize="off"}}
{{d-button action="submit" disabled=formSubmitted label="admin.watched_words.form.add"}}
{{#if showSuccessMessage}}
<span class="success-message">{{i18n 'admin.watched_words.form.success'}}</span>
{{/if}}

View File

@ -0,0 +1,7 @@
<label class="btn {{if addDisabled 'disabled'}}">
{{d-icon "upload"}}
{{i18n 'admin.watched_words.form.upload'}}
<input disabled={{addDisabled}} type="file" accept="text/plain,text/csv" style="visibility: hidden; position: absolute;" />
</label>
<br/>
<span class="instructions">One word per line</span>

View File

@ -5,13 +5,13 @@
{{#unless model.theme_id}} {{#unless model.theme_id}}
<button {{action "save"}} disabled={{model.disableSave}} class='btn'>{{i18n 'admin.customize.save'}}</button> <button {{action "save"}} disabled={{model.disableSave}} class='btn'>{{i18n 'admin.customize.save'}}</button>
{{/unless}} {{/unless}}
<button {{action "copy" model}} class='btn'><i class="fa fa-copy"></i> {{i18n 'admin.customize.copy'}}</button> <button {{action "copy" model}} class='btn'>{{d-icon "copy"}} {{i18n 'admin.customize.copy'}}</button>
<button {{action "copyToClipboard" model}} class='btn'><i class="fa fa-clipboard"></i> {{i18n 'admin.customize.copy_to_clipboard'}}</button> <button {{action "copyToClipboard" model}} class='btn'>{{d-icon "clipboard"}} {{i18n 'admin.customize.copy_to_clipboard'}}</button>
{{#if model.theme_id}} {{#if model.theme_id}}
{{i18n "admin.customize.theme_owner"}} {{i18n "admin.customize.theme_owner"}}
{{#link-to "adminCustomizeThemes.show" model.theme_id}}{{model.theme_name}}{{/link-to}} {{#link-to "adminCustomizeThemes.show" model.theme_id}}{{model.theme_name}}{{/link-to}}
{{else}} {{else}}
<button {{action "destroy"}} class='btn btn-danger'><i class="fa fa-trash-o"></i> {{i18n 'admin.customize.delete'}}</button> <button {{action "destroy"}} class='btn btn-danger'>{{d-icon "trash-o"}} {{i18n 'admin.customize.delete'}}</button>
{{/if}} {{/if}}
<span class="saving {{unless model.savingStatus 'hidden'}}">{{model.savingStatus}}</span> <span class="saving {{unless model.savingStatus 'hidden'}}">{{model.savingStatus}}</span>
</div> </div>

View File

@ -4,12 +4,12 @@
{{#each model as |scheme|}} {{#each model as |scheme|}}
{{#unless scheme.is_base}} {{#unless scheme.is_base}}
<li> <li>
{{#link-to 'adminCustomize.colors.show' scheme replace=true}}{{fa-icon 'paint-brush'}}{{scheme.description}}{{/link-to}} {{#link-to 'adminCustomize.colors.show' scheme replace=true}}{{d-icon 'paint-brush'}}{{scheme.description}}{{/link-to}}
</li> </li>
{{/unless}} {{/unless}}
{{/each}} {{/each}}
</ul> </ul>
<button {{action "newColorScheme"}} class='btn'>{{fa-icon 'plus'}}{{i18n 'admin.customize.new'}}</button> <button {{action "newColorScheme"}} class='btn'>{{d-icon 'plus'}}{{i18n 'admin.customize.new'}}</button>
</div> </div>
{{outlet}} {{outlet}}

View File

@ -19,7 +19,7 @@
<li> <li>
{{#link-to 'adminCustomizeThemes.edit' model.id 'desktop' fieldName replace=true title=field.title}} {{#link-to 'adminCustomizeThemes.edit' model.id 'desktop' fieldName replace=true title=field.title}}
{{i18n 'admin.customize.theme.desktop'}} {{i18n 'admin.customize.theme.desktop'}}
{{fa-icon 'desktop'}} {{d-icon 'desktop'}}
{{/link-to}} {{/link-to}}
</li> </li>
{{/if}} {{/if}}
@ -27,7 +27,7 @@
<li class='mobile'> <li class='mobile'>
{{#link-to 'adminCustomizeThemes.edit' model.id 'mobile' fieldName replace=true title=field.title}} {{#link-to 'adminCustomizeThemes.edit' model.id 'mobile' fieldName replace=true title=field.title}}
{{i18n 'admin.customize.theme.mobile'}} {{i18n 'admin.customize.theme.mobile'}}
{{fa-icon 'mobile'}} {{d-icon 'mobile'}}
{{/link-to}} {{/link-to}}
</li> </li>
{{/if}} {{/if}}
@ -46,14 +46,14 @@
{{#each fields as |field|}} {{#each fields as |field|}}
<li> <li>
{{#link-to 'adminCustomizeThemes.edit' model.id currentTargetName field.name replace=true title=field.title}} {{#link-to 'adminCustomizeThemes.edit' model.id currentTargetName field.name replace=true title=field.title}}
{{#if field.icon}}{{fa-icon field.icon}} {{/if}} {{#if field.icon}}{{d-icon field.icon}} {{/if}}
{{i18n field.key}} {{i18n field.key}}
{{/link-to}} {{/link-to}}
</li> </li>
{{/each}} {{/each}}
<li class='toggle-maximize'> <li class='toggle-maximize'>
<a {{action "toggleMaximize"}}> <a {{action "toggleMaximize"}}>
<i class="fa fa-{{maximizeIcon}}"></i> {{d-icon maximizeIcon}}
</a> </a>
</li> </li>
</ul> </ul>

View File

@ -5,7 +5,7 @@
{{d-button action="finishedEditingName" class="btn-primary btn-small submit-edit" icon="check"}} {{d-button action="finishedEditingName" class="btn-primary btn-small submit-edit" icon="check"}}
{{d-button action="cancelEditingName" class="btn-small cancel-edit" icon="times"}} {{d-button action="cancelEditingName" class="btn-small cancel-edit" icon="times"}}
{{else}} {{else}}
{{model.name}} <a {{action "startEditingName"}}>{{fa-icon "pencil"}}</a> {{model.name}} <a {{action "startEditingName"}}>{{d-icon "pencil"}}</a>
{{/if}} {{/if}}
</h2> </h2>
@ -15,7 +15,7 @@
</p> </p>
{{#if model.remote_theme.license_url}} {{#if model.remote_theme.license_url}}
<p> <p>
<a href="{{model.remote_theme.license_url}}">{{i18n "admin.customize.theme.license"}} {{fa-icon "copyright"}}</a> <a href="{{model.remote_theme.license_url}}">{{i18n "admin.customize.theme.license"}} {{d-icon "copyright"}}</a>
</p> </p>
{{/if}} {{/if}}
{{/if}} {{/if}}
@ -133,8 +133,8 @@
{{/if}} {{/if}}
{{/if}} {{/if}}
<a href='{{previewUrl}}' title="{{i18n 'admin.customize.explain_preview'}}" target='_blank' class='btn'>{{fa-icon 'desktop'}}{{i18n 'admin.customize.theme.preview'}}</a> <a href='{{previewUrl}}' title="{{i18n 'admin.customize.explain_preview'}}" target='_blank' class='btn'>{{d-icon 'desktop'}}{{i18n 'admin.customize.theme.preview'}}</a>
<a class="btn export" target="_blank" href={{downloadUrl}}>{{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}}</a> <a class="btn export" target="_blank" href={{downloadUrl}}>{{d-icon "download"}} {{i18n 'admin.export_json.button_text'}}</a>
{{d-button action="destroy" label="admin.customize.delete" icon="trash" class="btn-danger"}} {{d-button action="destroy" label="admin.customize.delete" icon="trash" class="btn-danger"}}
</div> </div>

View File

@ -7,10 +7,10 @@
{{#link-to 'adminCustomizeThemes.show' theme replace=true}} {{#link-to 'adminCustomizeThemes.show' theme replace=true}}
{{theme.name}} {{theme.name}}
{{#if theme.user_selectable}} {{#if theme.user_selectable}}
{{fa-icon "user"}} {{d-icon "user"}}
{{/if}} {{/if}}
{{#if theme.default}} {{#if theme.default}}
{{fa-icon "asterisk"}} {{d-icon "asterisk"}}
{{/if}} {{/if}}
{{/link-to}} {{/link-to}}
</li> </li>

View File

@ -29,15 +29,15 @@
<div class="dashboard-stats totals"> <div class="dashboard-stats totals">
<table> <table>
<tr> <tr>
<td class="title">{{fa-icon "shield"}} {{i18n 'admin.dashboard.admins'}}</td> <td class="title">{{d-icon "shield"}} {{i18n 'admin.dashboard.admins'}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'admins'}}{{admins}}{{/link-to}}</td> <td class="value">{{#link-to 'adminUsersList.show' 'admins'}}{{admins}}{{/link-to}}</td>
<td class="title">{{fa-icon "ban"}} {{i18n 'admin.dashboard.suspended'}}</td> <td class="title">{{d-icon "ban"}} {{i18n 'admin.dashboard.suspended'}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'suspended'}}{{suspended}}{{/link-to}}</td> <td class="value">{{#link-to 'adminUsersList.show' 'suspended'}}{{suspended}}{{/link-to}}</td>
</tr> </tr>
<tr> <tr>
<td class="title">{{fa-icon "shield"}} {{i18n 'admin.dashboard.moderators'}}</td> <td class="title">{{d-icon "shield"}} {{i18n 'admin.dashboard.moderators'}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'moderators'}}{{moderators}}{{/link-to}}</td> <td class="value">{{#link-to 'adminUsersList.show' 'moderators'}}{{moderators}}{{/link-to}}</td>
<td class="title">{{fa-icon "ban"}} {{i18n 'admin.dashboard.blocked'}}</td> <td class="title">{{d-icon "ban"}} {{i18n 'admin.dashboard.blocked'}}</td>
<td class="value">{{#link-to 'adminUsersList.show' 'blocked'}}{{blocked}}{{/link-to}}</td> <td class="value">{{#link-to 'adminUsersList.show' 'blocked'}}{{blocked}}{{/link-to}}</td>
</tr> </tr>
</table> </table>
@ -87,7 +87,7 @@
<table class="table table-condensed table-hover"> <table class="table table-condensed table-hover">
<thead> <thead>
<tr> <tr>
<th class="title" title="{{i18n 'admin.dashboard.private_messages_title'}}">{{fa-icon "envelope"}} {{i18n 'admin.dashboard.private_messages_short'}}</th> <th class="title" title="{{i18n 'admin.dashboard.private_messages_title'}}">{{d-icon "envelope"}} {{i18n 'admin.dashboard.private_messages_short'}}</th>
<th>{{i18n 'admin.dashboard.reports.today'}}</th> <th>{{i18n 'admin.dashboard.reports.today'}}</th>
<th>{{i18n 'admin.dashboard.reports.yesterday'}}</th> <th>{{i18n 'admin.dashboard.reports.yesterday'}}</th>
<th>{{i18n 'admin.dashboard.reports.last_7_days'}}</th> <th>{{i18n 'admin.dashboard.reports.last_7_days'}}</th>
@ -176,7 +176,7 @@
<div class="dashboard-right"> <div class="dashboard-right">
{{#if foundProblems}} {{#if foundProblems}}
<div class="dashboard-stats detected-problems"> <div class="dashboard-stats detected-problems">
<div class="look-here">{{fa-icon "exclamation-triangle"}}</div> <div class="look-here">{{d-icon "exclamation-triangle"}}</div>
<div class="problem-messages"> <div class="problem-messages">
{{#conditional-loading-spinner condition=loadingProblems}} {{#conditional-loading-spinner condition=loadingProblems}}
<p> <p>

View File

@ -30,7 +30,7 @@
{{/if}} {{/if}}
</td> </td>
<td> <td>
{{#if l.bounced}}{{fa-icon "repeat" title="admin.email.bounced"}}{{/if}} {{#if l.bounced}}{{d-icon "repeat" title="admin.email.bounced"}}{{/if}}
<a href='mailto:{{unbound l.to_address}}'>{{l.to_address}}</a> <a href='mailto:{{unbound l.to_address}}'>{{l.to_address}}</a>
</td> </td>
<td>{{l.email_type}}</td> <td>{{l.email_type}}</td>

View File

@ -20,7 +20,7 @@
<tr> <tr>
<th><img class="emoji emoji-custom" src="{{unbound e.url}}" title="{{unbound e.name}}"></th> <th><img class="emoji emoji-custom" src="{{unbound e.url}}" title="{{unbound e.name}}"></th>
<th>:{{e.name}}:</th> <th>:{{e.name}}:</th>
<th><button {{action "destroy" e}} class='btn btn-danger no-text pull-right'>{{fa-icon 'trash-o'}} </button></th> <th><button {{action "destroy" e}} class='btn btn-danger no-text pull-right'>{{d-icon 'trash-o'}} </button></th>
</tr> </tr>
{{/each}} {{/each}}
</tbody> </tbody>

View File

@ -19,7 +19,9 @@
{{#if flaggedPost.postAuthorFlagged}} {{#if flaggedPost.postAuthorFlagged}}
{{#if flaggedPost.user}} {{#if flaggedPost.user}}
{{#link-to 'adminUser' flaggedPost.user}}{{avatar flaggedPost.user imageSize="large"}}{{/link-to}} {{#link-to 'adminUser' flaggedPost.user}}{{avatar flaggedPost.user imageSize="large"}}{{/link-to}}
{{#if flaggedPost.wasEdited}}<i class="fa fa-pencil" title="{{i18n 'admin.flags.was_edited'}}"></i>{{/if}} {{#if flaggedPost.wasEdited}}
{{d-icon "pencil" title="admin.flags.was_edited"}}
{{/if}}
{{/if}} {{/if}}
{{/if}} {{/if}}
{{#if adminActiveFlagsView}} {{#if adminActiveFlagsView}}
@ -31,7 +33,7 @@
<div class="topic-excerpt"> <div class="topic-excerpt">
<h3> <h3>
{{#if flaggedPost.topic.isPrivateMessage}} {{#if flaggedPost.topic.isPrivateMessage}}
<span class="private-message-glyph">{{fa-icon "envelope"}}</span> <span class="private-message-glyph">{{d-icon "envelope"}}</span>
{{/if}} {{/if}}
{{topic-status topic=flaggedPost.topic}} {{topic-status topic=flaggedPost.topic}}
<a href='{{unbound flaggedPost.url}}'>{{{unbound flaggedPost.topic.fancyTitle}}}</a> <a href='{{unbound flaggedPost.url}}'>{{{unbound flaggedPost.topic.fancyTitle}}}</a>
@ -90,7 +92,7 @@
{{format-age flagger.disposedAt}} {{format-age flagger.disposedAt}}
{{{flagger.dispositionIcon}}} {{{flagger.dispositionIcon}}}
{{#if flagger.tookAction}} {{#if flagger.tookAction}}
<i class='fa fa-gavel' title='{{i18n 'admin.flags.took_action'}}'></i> {{d-icon "gavel" title="admin.flags.took_action"}}
{{/if}} {{/if}}
</td> </td>
</tr> </tr>
@ -129,7 +131,8 @@
</p> </p>
{{/if}} {{/if}}
<a href="{{unbound c.permalink}}"> <a href="{{unbound c.permalink}}">
<button class='btn btn-reply'><i class="fa fa-reply"></i>&nbsp;{{i18n 'admin.flags.reply_message'}}</button> <button class='btn btn-reply'>{{d-icon "reply"}}&nbsp;{{i18n 'admin.flags.reply_message'}}</button>
</a> </a>
{{/if}} {{/if}}
</div> </div>
@ -141,14 +144,14 @@
<tr> <tr>
<td colspan="3" class="action"> <td colspan="3" class="action">
{{#if adminActiveFlagsView}} {{#if adminActiveFlagsView}}
<button title='{{i18n 'admin.flags.agree_title'}}' class='btn' {{action "showAgreeFlagModal" flaggedPost}}><i class="fa fa-thumbs-o-up"></i>{{i18n 'admin.flags.agree'}}&hellip;</button> <button title='{{i18n 'admin.flags.agree_title'}}' class='btn' {{action "showAgreeFlagModal" flaggedPost}}>{{d-icon "thumbs-o-up"}}{{i18n 'admin.flags.agree'}}&hellip;</button>
{{#if flaggedPost.postHidden}} {{#if flaggedPost.postHidden}}
<button title='{{i18n 'admin.flags.disagree_flag_unhide_post_title'}}' class='btn' {{action "disagreeFlags" flaggedPost}}><i class="fa fa-thumbs-o-down"></i>{{i18n 'admin.flags.disagree_flag_unhide_post'}}</button> <button title='{{i18n 'admin.flags.disagree_flag_unhide_post_title'}}' class='btn' {{action "disagreeFlags" flaggedPost}}>{{d-icon "thumbs-o-down"}}{{i18n 'admin.flags.disagree_flag_unhide_post'}}</button>
{{else}} {{else}}
<button title='{{i18n 'admin.flags.disagree_flag_title'}}' class='btn' {{action "disagreeFlags" flaggedPost}}><i class="fa fa-thumbs-o-down"></i>{{i18n 'admin.flags.disagree_flag'}}</button> <button title='{{i18n 'admin.flags.disagree_flag_title'}}' class='btn' {{action "disagreeFlags" flaggedPost}}>{{d-icon "thumbs-o-down"}}{{i18n 'admin.flags.disagree_flag'}}</button>
{{/if}} {{/if}}
<button title='{{i18n 'admin.flags.defer_flag_title'}}' class='btn' {{action "deferFlags" flaggedPost}}><i class="fa fa-external-link"></i>{{i18n 'admin.flags.defer_flag'}}</button> <button title='{{i18n 'admin.flags.defer_flag_title'}}' class='btn' {{action "deferFlags" flaggedPost}}>{{d-icon "external-link"}}{{i18n 'admin.flags.defer_flag'}}</button>
<button title='{{i18n 'admin.flags.delete_title'}}' class='btn btn-danger' {{action "showDeleteFlagModal" flaggedPost}}><i class="fa fa-trash-o"></i>{{i18n 'admin.flags.delete'}}&hellip;</button> <button title='{{i18n 'admin.flags.delete_title'}}' class='btn btn-danger' {{action "showDeleteFlagModal" flaggedPost}}>{{d-icon "trash-o"}}{{i18n 'admin.flags.delete'}}&hellip;</button>
{{/if}} {{/if}}
</td> </td>
</tr> </tr>

View File

@ -9,19 +9,18 @@
{{/if}} {{/if}}
</div> </div>
{{#if model.id}} {{#unless model.automatic}}
{{#unless model.automatic}} <div>
<div> <label for='full_name'>{{i18n 'groups.edit.full_name'}}</label>
<label for='full_name'>{{i18n 'groups.edit.full_name'}}</label> {{input type='text' name='full_name' value=model.full_name class='group-edit-full-name'}}
{{input type='text' name='full_name' value=model.full_name class='group-edit-full-name'}} </div>
</div>
<div> <div>
<label for="bio">{{i18n 'groups.bio'}}</label> <label for="bio">{{i18n 'groups.bio'}}</label>
{{d-editor value=model.bio_raw}} {{d-editor value=model.bio_raw}}
</div> </div>
{{#if model.hasOwners}} {{#if model.hasOwners}}
<div> <div>
<label for='owner-list'>{{i18n 'admin.groups.group_owners'}}</label> <label for='owner-list'>{{i18n 'admin.groups.group_owners'}}</label>
<div class="ac-wrap clearfix" id='owner-list'> <div class="ac-wrap clearfix" id='owner-list'>
@ -30,31 +29,55 @@
{{/each}} {{/each}}
</div> </div>
</div> </div>
{{/if}} {{/if}}
<div>
<label for="owner-selector">{{i18n 'admin.groups.add_owners'}}</label>
{{user-selector usernames=model.ownerUsernames placeholderKey="admin.groups.selector_placeholder" id="owner-selector"}}
{{d-button action="addOwners" class="add" icon="plus" label="admin.groups.add"}}
</div>
{{/unless}}
<div> <div>
{{group-members-input model=model}} <label for="owner-selector">{{i18n 'admin.groups.add_owners'}}</label>
{{user-selector usernames=model.ownerUsernames
placeholderKey="admin.groups.selector_placeholder"
id="owner-selector"}}
{{#if model.id}}
{{d-button
action="addOwners"
class="add"
icon="plus"
label="admin.groups.add"}}
{{/if}}
</div> </div>
{{/if}} {{/unless}}
<div>
{{group-members-input model=model addButton=model.id}}
</div>
<div> <div>
<label for="visiblity">{{i18n 'groups.visibility_levels.title'}}</label> <label for="visiblity">{{i18n 'groups.visibility_levels.title'}}</label>
{{combo-box name="alias" valueAttribute="value" value=model.visibility_level content=visibilityLevelOptions}} {{combo-box name="alias"
valueAttribute="value"
value=model.visibility_level
content=visibilityLevelOptions
castInteger=true}}
</div> </div>
{{#unless model.automatic}} {{#unless model.automatic}}
<div> <div>
<label> <label>
{{input type="checkbox" {{input type="checkbox"
checked=model.public checked=model.public_admission
disabled=disablePublicSetting}} disabled=disablePublicSetting}}
{{i18n 'groups.public'}} {{i18n 'groups.public_admission'}}
</label>
</div>
<div>
<label>
{{input type='checkbox'
checked=model.public_exit}}
{{i18n 'groups.public_exit'}}
</label> </label>
</div> </div>
@ -123,7 +146,7 @@
<div class='buttons'> <div class='buttons'>
<button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.customize.save'}}</button> <button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.customize.save'}}</button>
{{#unless model.automatic}} {{#unless model.automatic}}
<button {{action "destroy"}} class='btn btn-danger'>{{fa-icon "trash-o"}}{{i18n 'admin.customize.delete'}}</button> <button {{action "destroy"}} class='btn btn-danger'>{{d-icon "trash-o"}}{{i18n 'admin.customize.delete'}}</button>
{{/unless}} {{/unless}}
<span class="saving {{unless savingStatus 'hidden'}}">{{savingStatus}}</span> <span class="saving {{unless savingStatus 'hidden'}}">{{savingStatus}}</span>

View File

@ -3,7 +3,7 @@
<div> <div>
{{#link-to 'adminGroup' 'new' class="btn"}} {{#link-to 'adminGroup' 'new' class="btn"}}
{{fa-icon "plus"}} {{i18n 'admin.groups.new'}} {{d-icon "plus"}} {{i18n 'admin.groups.new'}}
{{/link-to}} {{/link-to}}
</div> </div>
</div> </div>

View File

@ -18,7 +18,7 @@
{{d-button action="refreshAutoGroups" icon="refresh" label="admin.groups.refresh" disabled=refreshingAutoGroups}} {{d-button action="refreshAutoGroups" icon="refresh" label="admin.groups.refresh" disabled=refreshingAutoGroups}}
{{else}} {{else}}
{{#link-to 'adminGroup' 'new' class="btn"}} {{#link-to 'adminGroup' 'new' class="btn"}}
{{fa-icon "plus"}} {{i18n 'admin.groups.new'}} {{d-icon "plus"}} {{i18n 'admin.groups.new'}}
{{/link-to}} {{/link-to}}
{{/if}} {{/if}}
</div> </div>

View File

@ -1,6 +1,6 @@
<p> <p>
{{i18n 'admin.logs.screened_emails.description'}} {{i18n 'admin.logs.screened_emails.description'}}
<button class="btn pull-right" {{action "exportScreenedEmailList"}} title="{{i18n 'admin.export_csv.button_title.screened_email'}}">{{fa-icon "download"}}{{i18n 'admin.export_csv.button_text'}}</button> <button class="btn pull-right" {{action "exportScreenedEmailList"}} title="{{i18n 'admin.export_csv.button_title.screened_email'}}">{{d-icon "download"}}{{i18n 'admin.export_csv.button_text'}}</button>
</p> </p>
<br> <br>

View File

@ -41,9 +41,9 @@
</div> </div>
<div class="col action"> <div class="col action">
{{#if item.isBlocked}} {{#if item.isBlocked}}
{{fa-icon "ban"}} {{d-icon "ban"}}
{{else}} {{else}}
{{fa-icon "check"}} {{d-icon "check"}}
{{/if}} {{/if}}
{{item.actionName}} {{item.actionName}}
</div> </div>

View File

@ -1,6 +1,6 @@
<p> <p>
{{i18n 'admin.logs.screened_urls.description'}} {{i18n 'admin.logs.screened_urls.description'}}
<button class="btn pull-right" {{action "exportScreenedUrlList"}} title="{{i18n 'admin.export_csv.button_title.screened_url'}}">{{fa-icon "download"}}{{i18n 'admin.export_csv.button_text'}}</button> <button class="btn pull-right" {{action "exportScreenedUrlList"}} title="{{i18n 'admin.export_csv.button_title.screened_url'}}">{{d-icon "download"}}{{i18n 'admin.export_csv.button_text'}}</button>
</p> </p>
<br> <br>

View File

@ -7,25 +7,25 @@
{{#if actionFilter}} {{#if actionFilter}}
<a {{action "clearFilter" "actionFilter"}} class="filter"> <a {{action "clearFilter" "actionFilter"}} class="filter">
<span class="label">{{i18n 'admin.logs.action'}}</span>: {{actionFilter}} <span class="label">{{i18n 'admin.logs.action'}}</span>: {{actionFilter}}
{{fa-icon "times-circle"}} {{d-icon "times-circle"}}
</a> </a>
{{/if}} {{/if}}
{{#if filters.acting_user}} {{#if filters.acting_user}}
<a {{action "clearFilter" "acting_user"}} class="filter"> <a {{action "clearFilter" "acting_user"}} class="filter">
<span class="label">{{i18n 'admin.logs.staff_actions.staff_user'}}</span>: {{filters.acting_user}} <span class="label">{{i18n 'admin.logs.staff_actions.staff_user'}}</span>: {{filters.acting_user}}
{{fa-icon "times-circle"}} {{d-icon "times-circle"}}
</a> </a>
{{/if}} {{/if}}
{{#if filters.target_user}} {{#if filters.target_user}}
<a {{action "clearFilter" "target_user"}} class="filter"> <a {{action "clearFilter" "target_user"}} class="filter">
<span class="label">{{i18n 'admin.logs.staff_actions.target_user'}}</span>: {{filters.target_user}} <span class="label">{{i18n 'admin.logs.staff_actions.target_user'}}</span>: {{filters.target_user}}
{{fa-icon "times-circle"}} {{d-icon "times-circle"}}
</a> </a>
{{/if}} {{/if}}
{{#if filters.subject}} {{#if filters.subject}}
<a {{action "clearFilter" "subject"}} class="filter"> <a {{action "clearFilter" "subject"}} class="filter">
<span class="label">{{i18n 'admin.logs.staff_actions.subject'}}</span>: {{filters.subject}} <span class="label">{{i18n 'admin.logs.staff_actions.subject'}}</span>: {{filters.subject}}
{{fa-icon "times-circle"}} {{d-icon "times-circle"}}
</a> </a>
{{/if}} {{/if}}
</div> </div>

View File

@ -1,13 +1,13 @@
{{#d-modal-body title="admin.flags.agree_flag_modal_title"}} {{#d-modal-body title="admin.flags.agree_flag_modal_title"}}
{{#if model.user_deleted}} {{#if model.user_deleted}}
<button title="{{i18n 'admin.flags.agree_flag_restore_post_title'}}" {{action "agreeFlagRestorePost"}} class="btn"><i class="fa fa-thumbs-o-up"></i><i class="fa fa-eye"></i>{{i18n 'admin.flags.agree_flag_restore_post'}}</button> <button title={{i18n 'admin.flags.agree_flag_restore_post_title'}} {{action "agreeFlagRestorePost"}} class="btn">{{d-icon "thumbs-o-up"}}{{d-icon "eye"}}{{i18n 'admin.flags.agree_flag_restore_post'}}</button>
{{else}} {{else}}
{{#unless model.postHidden}} {{#unless model.postHidden}}
<button title="{{i18n 'admin.flags.agree_flag_hide_post_title'}}" {{action "agreeFlagHidePost"}} class="btn"><i class="fa fa-thumbs-o-up"></i><i class="fa fa-eye-slash"></i>{{i18n 'admin.flags.agree_flag_hide_post'}}</button> <button title="{{i18n 'admin.flags.agree_flag_hide_post_title'}}" {{action "agreeFlagHidePost"}} class="btn">{{d-icon "thumbs-o-up"}}{{d-icon "eye-slash"}}{{i18n 'admin.flags.agree_flag_hide_post'}}</button>
{{/unless}} {{/unless}}
{{/if}} {{/if}}
<button title="{{i18n 'admin.flags.agree_flag_title'}}" {{action "agreeFlagKeepPost"}} class="btn"><i class="fa fa-thumbs-o-up"></i>{{i18n 'admin.flags.agree_flag'}}</button> <button title="{{i18n 'admin.flags.agree_flag_title'}}" {{action "agreeFlagKeepPost"}} class="btn">{{d-icon "thumbs-o-up"}}{{i18n 'admin.flags.agree_flag'}}</button>
{{#if model.canDeleteAsSpammer}} {{#if model.canDeleteAsSpammer}}
<button title="{{i18n 'admin.flags.delete_spammer_title'}}" {{action "deleteSpammer" model.user}} class="btn btn-danger"><i class="fa fa-exclamation-triangle"></i>{{i18n 'admin.flags.delete_spammer'}}</button> <button title="{{i18n 'admin.flags.delete_spammer_title'}}" {{action "deleteSpammer" model.user}} class="btn btn-danger">{{d-icon "exclamation-triangle"}}{{i18n 'admin.flags.delete_spammer'}}</button>
{{/if}} {{/if}}
{{/d-modal-body}} {{/d-modal-body}}

View File

@ -24,7 +24,7 @@
{{#if count_warning}} {{#if count_warning}}
<div class="count-warning"> <div class="count-warning">
<p class="heading"><i class="fa fa-warning"></i> {{i18n 'admin.badges.preview.bad_count_warning.header'}}</p> <p class="heading">{{d-icon "warning"}} {{i18n 'admin.badges.preview.bad_count_warning.header'}}</p>
<p class="body">{{i18n 'admin.badges.preview.bad_count_warning.text'}}</p> <p class="body">{{i18n 'admin.badges.preview.bad_count_warning.text'}}</p>
</div> </div>
{{/if}} {{/if}}

View File

@ -7,6 +7,6 @@
valueAttribute="base_scheme_id"}} valueAttribute="base_scheme_id"}}
{{/d-modal-body}} {{/d-modal-body}}
<div class="modal-footer"> <div class="modal-footer">
<button class='btn btn-primary' {{action "selectBase"}}>{{fa-icon 'plus'}}{{i18n 'admin.customize.new'}}</button> <button class='btn btn-primary' {{action "selectBase"}}>{{d-icon 'plus'}}{{i18n 'admin.customize.new'}}</button>
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
{{#d-modal-body title="admin.flags.delete_flag_modal_title"}} {{#d-modal-body title="admin.flags.delete_flag_modal_title"}}
<button title="{{i18n 'admin.flags.delete_post_defer_flag_title'}}" {{action "deletePostDeferFlag"}} class="btn"><i class="fa fa-trash-o"></i><i class="fa fa-external-link"></i>{{i18n 'admin.flags.delete_post_defer_flag'}}</button> <button title="{{i18n 'admin.flags.delete_post_defer_flag_title'}}" {{action "deletePostDeferFlag"}} class="btn">{{d-icon "trash-o"}}{{d-icon "external-link"}}{{i18n 'admin.flags.delete_post_defer_flag'}}</button>
<button title="{{i18n 'admin.flags.delete_post_agree_flag_title'}}" {{action "deletePostAgreeFlag"}} class="btn"><i class="fa fa-trash-o"></i><i class="fa fa-thumbs-o-up"></i>{{i18n 'admin.flags.delete_post_agree_flag'}}</button> <button title="{{i18n 'admin.flags.delete_post_agree_flag_title'}}" {{action "deletePostAgreeFlag"}} class="btn">{{d-icon "trash-o"}}{{d-icon "thumbs-o-up"}}{{i18n 'admin.flags.delete_post_agree_flag'}}</button>
{{#if model.canDeleteAsSpammer}} {{#if model.canDeleteAsSpammer}}
<button title="{{i18n 'admin.flags.delete_spammer_title'}}" {{action "deleteSpammer" model.user}} class="btn btn-danger"><i class="fa fa-exclamation-triangle"></i>{{i18n 'admin.flags.delete_spammer'}}</button> <button title="{{i18n 'admin.flags.delete_spammer_title'}}" {{action "deleteSpammer" model.user}} class="btn btn-danger">{{d-icon "exclamation-triangle"}}{{i18n 'admin.flags.delete_spammer'}}</button>
{{/if}} {{/if}}
{{/d-modal-body}} {{/d-modal-body}}

View File

@ -5,15 +5,15 @@
<li> <li>
{{#if wc.editing}} {{#if wc.editing}}
{{input value=wc.name}} {{input value=wc.name}}
<button {{action "save" wc}} class="btn no-text">{{fa-icon 'check'}}</button> <button {{action "save" wc}} class="btn no-text">{{d-icon 'check'}}</button>
{{else}} {{else}}
{{wc.displayName}} {{wc.displayName}}
{{/if}} {{/if}}
<div class='actions'> <div class='actions'>
<button {{action "edit" wc}} class="btn no-text" disabled={{wc.system}}>{{fa-icon 'pencil'}}</button> <button {{action "edit" wc}} class="btn no-text" disabled={{wc.system}}>{{d-icon 'pencil'}}</button>
<button {{action "up" wc}} class="btn no-text">{{fa-icon 'toggle-up'}}</button> <button {{action "up" wc}} class="btn no-text">{{d-icon 'toggle-up'}}</button>
<button {{action "down" wc}} class="btn no-text">{{fa-icon 'toggle-down'}}</button> <button {{action "down" wc}} class="btn no-text">{{d-icon 'toggle-down'}}</button>
<button {{action "delete" wc}} class="btn no-text btn-danger" disabled={{wc.system}}>{{fa-icon 'times'}}</button> <button {{action "delete" wc}} class="btn no-text btn-danger" disabled={{wc.system}}>{{d-icon 'times'}}</button>
</div> </div>
</li> </li>
{{/each}} {{/each}}

View File

@ -11,6 +11,6 @@
{{/d-modal-body}} {{/d-modal-body}}
<div class="modal-footer"> <div class="modal-footer">
<button class='btn btn-danger' {{action "suspend"}} disabled={{submitDisabled}}><i class='fa fa-ban'></i>{{i18n 'admin.user.suspend'}}</button> <button class='btn btn-danger' {{action "suspend"}} disabled={{submitDisabled}}>{{d-icon "ban"}}{{i18n 'admin.user.suspend'}}</button>
<a {{action "closeModal"}}>{{i18n 'cancel'}}</a> <a {{action "closeModal"}}>{{i18n 'cancel'}}</a>
</div> </div>

View File

@ -13,7 +13,7 @@
{{/save-controls}} {{/save-controls}}
{{#link-to 'adminSiteText.index' class="go-back"}} {{#link-to 'adminSiteText.index' class="go-back"}}
{{fa-icon 'arrow-left'}} {{d-icon 'arrow-left'}}
{{i18n 'admin.site_text.go_back'}} {{i18n 'admin.site_text.go_back'}}
{{/link-to}} {{/link-to}}

View File

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

View File

@ -3,7 +3,7 @@
<div class='user-controls'> <div class='user-controls'>
{{#if model.canViewProfile}} {{#if model.canViewProfile}}
{{#link-to 'user' model class="btn"}} {{#link-to 'user' model class="btn"}}
{{fa-icon "user"}} {{d-icon "user"}}
{{i18n 'admin.user.show_public_profile'}} {{i18n 'admin.user.show_public_profile'}}
{{/link-to}} {{/link-to}}
{{/if}} {{/if}}
@ -151,7 +151,7 @@
{{i18n 'badges.badge_count' count=model.badge_count}} {{i18n 'badges.badge_count' count=model.badge_count}}
</div> </div>
<div class='controls'> <div class='controls'>
{{#link-to 'adminUser.badges' model class="btn"}}{{fa-icon "certificate"}}{{i18n 'admin.badges.edit_badges'}}{{/link-to}} {{#link-to 'adminUser.badges' model class="btn"}}{{d-icon "certificate"}}{{i18n 'admin.badges.edit_badges'}}{{/link-to}}
</div> </div>
</div> </div>
{{/if}} {{/if}}
@ -281,9 +281,11 @@
<div class="controls"> <div class="controls">
{{#if model.canLockTrustLevel}} {{#if model.canLockTrustLevel}}
{{#if model.trust_level_locked}} {{#if model.trust_level_locked}}
<i title='{{i18n 'admin.user.trust_level_locked_tip'}}' class='fa fa-lock'></i> {{d-button action="lockTrustLevel" actionParam=false label="admin.user.unlock_trust_level"}} {{d-icon "lock" title="admin.user.trust_level_locked_tip"}}
{{d-button action="lockTrustLevel" actionParam=false label="admin.user.unlock_trust_level"}}
{{else}} {{else}}
<i title='{{i18n 'admin.user.trust_level_unlocked_tip'}}' class='fa fa-unlock'></i> {{d-button action="lockTrustLevel" actionParam=true label="admin.user.lock_trust_level"}} {{d-icon "unlock" title="admin.user.trust_level_unlocked_tip"}}
{{d-button action="lockTrustLevel" actionParam=true label="admin.user.lock_trust_level"}}
{{/if}} {{/if}}
{{/if}} {{/if}}
{{#if model.tl3Requirements}} {{#if model.tl3Requirements}}
@ -487,7 +489,7 @@
<div class="clearfix"></div> <div class="clearfix"></div>
<br/> <br/>
<div class="pull-right"> <div class="pull-right">
{{fa-icon "exclamation-triangle"}} {{model.deleteExplanation}} {{d-icon "exclamation-triangle"}} {{model.deleteExplanation}}
</div> </div>
{{/if}} {{/if}}
</section> </section>

View File

@ -1,7 +1,7 @@
<div class='admin-controls'> <div class='admin-controls'>
<div class='span15'> <div class='span15'>
<ul class="nav nav-pills"> <ul class="nav nav-pills">
<li>{{#link-to 'adminUser' model}}<i class="fa fa-caret-left"></i> &nbsp;{{model.username}}{{/link-to}}</li> <li>{{#link-to 'adminUser' model}}{{d-icon "caret-left"}} &nbsp;{{model.username}}{{/link-to}}</li>
<li>{{#link-to 'adminUsersList.show' 'member'}}{{i18n 'admin.user.trust_level_2_users'}}{{/link-to}}</li> <li>{{#link-to 'adminUsersList.show' 'member'}}{{i18n 'admin.user.trust_level_2_users'}}{{/link-to}}</li>
</ul> </ul>
</div> </div>
@ -24,7 +24,7 @@
<tbody> <tbody>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.visits'}}</th> <th>{{i18n 'admin.user.tl3_requirements.visits'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.days_visited 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.days_visited}}</td>
<td> <td>
{{model.tl3Requirements.days_visited_percent}}% ({{model.tl3Requirements.days_visited}} / {{model.tl3Requirements.time_period}} {{i18n 'admin.user.tl3_requirements.days'}}) {{model.tl3Requirements.days_visited_percent}}% ({{model.tl3Requirements.days_visited}} / {{model.tl3Requirements.time_period}} {{i18n 'admin.user.tl3_requirements.days'}})
</td> </td>
@ -32,67 +32,67 @@
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.topics_replied_to'}}</th> <th>{{i18n 'admin.user.tl3_requirements.topics_replied_to'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.topics_replied_to 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.topics_replied_to}}</td>
<td>{{model.tl3Requirements.num_topics_replied_to}}</td> <td>{{model.tl3Requirements.num_topics_replied_to}}</td>
<td>{{model.tl3Requirements.min_topics_replied_to}}</td> <td>{{model.tl3Requirements.min_topics_replied_to}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.topics_viewed'}}</th> <th>{{i18n 'admin.user.tl3_requirements.topics_viewed'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.topics_viewed 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.topics_viewed}}</td>
<td>{{model.tl3Requirements.topics_viewed}}</td> <td>{{model.tl3Requirements.topics_viewed}}</td>
<td>{{model.tl3Requirements.min_topics_viewed}}</td> <td>{{model.tl3Requirements.min_topics_viewed}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.topics_viewed_all_time'}}</th> <th>{{i18n 'admin.user.tl3_requirements.topics_viewed_all_time'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.topics_viewed_all_time 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.topics_viewed_all_time}}</td>
<td>{{model.tl3Requirements.topics_viewed_all_time}}</td> <td>{{model.tl3Requirements.topics_viewed_all_time}}</td>
<td>{{model.tl3Requirements.min_topics_viewed_all_time}}</td> <td>{{model.tl3Requirements.min_topics_viewed_all_time}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.posts_read'}}</th> <th>{{i18n 'admin.user.tl3_requirements.posts_read'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.posts_read 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.posts_read}}</td>
<td>{{model.tl3Requirements.posts_read}}</td> <td>{{model.tl3Requirements.posts_read}}</td>
<td>{{model.tl3Requirements.min_posts_read}}</td> <td>{{model.tl3Requirements.min_posts_read}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.posts_read_all_time'}}</th> <th>{{i18n 'admin.user.tl3_requirements.posts_read_all_time'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.posts_read_all_time 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.posts_read_all_time}}</td>
<td>{{model.tl3Requirements.posts_read_all_time}}</td> <td>{{model.tl3Requirements.posts_read_all_time}}</td>
<td>{{model.tl3Requirements.min_posts_read_all_time}}</td> <td>{{model.tl3Requirements.min_posts_read_all_time}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.flagged_posts'}}</th> <th>{{i18n 'admin.user.tl3_requirements.flagged_posts'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.flagged_posts 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.flagged_posts}}</td>
<td>{{model.tl3Requirements.num_flagged_posts}}</td> <td>{{model.tl3Requirements.num_flagged_posts}}</td>
<td>{{i18n 'max_of_count' count=model.tl3Requirements.max_flagged_posts}}</td> <td>{{i18n 'max_of_count' count=model.tl3Requirements.max_flagged_posts}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.flagged_by_users'}}</th> <th>{{i18n 'admin.user.tl3_requirements.flagged_by_users'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.flagged_by_users 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.flagged_by_users}}</td>
<td>{{model.tl3Requirements.num_flagged_by_users}}</td> <td>{{model.tl3Requirements.num_flagged_by_users}}</td>
<td>{{i18n 'max_of_count' count=model.tl3Requirements.max_flagged_by_users}}</td> <td>{{i18n 'max_of_count' count=model.tl3Requirements.max_flagged_by_users}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.likes_given'}}</th> <th>{{i18n 'admin.user.tl3_requirements.likes_given'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.likes_given 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.likes_given}}</td>
<td>{{model.tl3Requirements.num_likes_given}}</td> <td>{{model.tl3Requirements.num_likes_given}}</td>
<td>{{model.tl3Requirements.min_likes_given}}</td> <td>{{model.tl3Requirements.min_likes_given}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.likes_received'}}</th> <th>{{i18n 'admin.user.tl3_requirements.likes_received'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.likes_received 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.likes_received}}</td>
<td>{{model.tl3Requirements.num_likes_received}}</td> <td>{{model.tl3Requirements.num_likes_received}}</td>
<td>{{model.tl3Requirements.min_likes_received}}</td> <td>{{model.tl3Requirements.min_likes_received}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.likes_received_days'}}</th> <th>{{i18n 'admin.user.tl3_requirements.likes_received_days'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.likes_received_days 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.likes_received_days}}</td>
<td>{{model.tl3Requirements.num_likes_received_days}}</td> <td>{{model.tl3Requirements.num_likes_received_days}}</td>
<td>{{model.tl3Requirements.min_likes_received_days}}</td> <td>{{model.tl3Requirements.min_likes_received_days}}</td>
</tr> </tr>
<tr> <tr>
<th>{{i18n 'admin.user.tl3_requirements.likes_received_users'}}</th> <th>{{i18n 'admin.user.tl3_requirements.likes_received_users'}}</th>
<td><i class="fa {{if model.tl3Requirements.met.likes_received_users 'fa-check' 'fa-times'}}"></i></td> <td>{{check-icon model.tl3Requirements.met.likes_received_users}}</td>
<td>{{model.tl3Requirements.num_likes_received_users}}</td> <td>{{model.tl3Requirements.num_likes_received_users}}</td>
<td>{{model.tl3Requirements.min_likes_received_users}}</td> <td>{{model.tl3Requirements.min_likes_received_users}}</td>
</tr> </tr>
@ -105,16 +105,16 @@
{{#if model.tl3Requirements.requirements_lost}} {{#if model.tl3Requirements.requirements_lost}}
{{! tl implicitly not locked }} {{! tl implicitly not locked }}
{{#if model.tl3Requirements.on_grace_period}} {{#if model.tl3Requirements.on_grace_period}}
<i class="fa fa-times"></i> {{i18n 'admin.user.tl3_requirements.on_grace_period'}} {{d-icon "times"}} {{i18n 'admin.user.tl3_requirements.on_grace_period'}}
{{else}} {{! not on grace period }} {{else}} {{! not on grace period }}
<i class="fa fa-times"></i> {{i18n 'admin.user.tl3_requirements.does_not_qualify'}} {{d-icon "times"}} {{i18n 'admin.user.tl3_requirements.does_not_qualify'}}
{{i18n 'admin.user.tl3_requirements.will_be_demoted'}} {{i18n 'admin.user.tl3_requirements.will_be_demoted'}}
{{/if}} {{/if}}
{{else}} {{! requirements not lost - remains tl3 }} {{else}} {{! requirements not lost - remains tl3 }}
{{#if model.tl3Requirements.trust_level_locked}} {{#if model.tl3Requirements.trust_level_locked}}
<i class="fa fa-lock"></i> {{i18n 'admin.user.tl3_requirements.locked_will_not_be_demoted'}} {{d-icon "lock"}} {{i18n 'admin.user.tl3_requirements.locked_will_not_be_demoted'}}
{{else}} {{! tl not locked }} {{else}} {{! tl not locked }}
<i class="fa fa-check"></i> {{i18n 'admin.user.tl3_requirements.qualifies'}} {{d-icon "check"}} {{i18n 'admin.user.tl3_requirements.qualifies'}}
{{#if model.tl3Requirements.on_grace_period}} {{#if model.tl3Requirements.on_grace_period}}
{{i18n 'admin.user.tl3_requirements.on_grace_period'}} {{i18n 'admin.user.tl3_requirements.on_grace_period'}}
{{/if}} {{/if}}
@ -123,13 +123,13 @@
{{else}} {{! is not tl3 }} {{else}} {{! is not tl3 }}
{{#if model.tl3Requirements.requirements_met}} {{#if model.tl3Requirements.requirements_met}}
{{! met & not tl3 - will be promoted}} {{! met & not tl3 - will be promoted}}
<i class="fa fa-check"></i> {{i18n 'admin.user.tl3_requirements.qualifies'}} {{d-icon "check"}} {{i18n 'admin.user.tl3_requirements.qualifies'}}
{{i18n 'admin.user.tl3_requirements.will_be_promoted'}} {{i18n 'admin.user.tl3_requirements.will_be_promoted'}}
{{else}} {{! requirements not met - remains regular }} {{else}} {{! requirements not met - remains regular }}
{{#if model.tl3Requirements.trust_level_locked}} {{#if model.tl3Requirements.trust_level_locked}}
<i class="fa fa-lock"></i> {{i18n 'admin.user.tl3_requirements.locked_will_not_be_promoted'}} {{d-icon "lock"}} {{i18n 'admin.user.tl3_requirements.locked_will_not_be_promoted'}}
{{else}} {{else}}
<i class="fa fa-times"></i> {{i18n 'admin.user.tl3_requirements.does_not_qualify'}} {{d-icon "times"}} {{i18n 'admin.user.tl3_requirements.does_not_qualify'}}
{{/if}} {{/if}}
{{/if}} {{/if}}
{{/if}} {{/if}}

View File

@ -92,10 +92,10 @@
{{/if}} {{/if}}
<td> <td>
{{#if user.admin}} {{#if user.admin}}
{{fa-icon "shield" title="admin.title" }} {{d-icon "shield" title="admin.title" }}
{{/if}} {{/if}}
{{#if user.moderator}} {{#if user.moderator}}
{{fa-icon "shield" title="admin.moderator" }} {{d-icon "shield" title="admin.moderator" }}
{{/if}} {{/if}}
</td> </td>
</tr> </tr>

View File

@ -18,7 +18,7 @@
{{#if versionCheck.noCheckPerformed}} {{#if versionCheck.noCheckPerformed}}
<td class="version-number">&mdash;</td> <td class="version-number">&mdash;</td>
<td class="face"> <td class="face">
<span class="icon critical-updates-available">{{fa-icon "frown-o"}}</span> <span class="icon critical-updates-available">{{d-icon "frown-o"}}</span>
</td> </td>
<td class="version-notes"> <td class="version-notes">
<span class="normal-note">{{i18n 'admin.dashboard.no_check_performed'}}</span> <span class="normal-note">{{i18n 'admin.dashboard.no_check_performed'}}</span>
@ -28,9 +28,9 @@
<td class="version-number">{{#if versionCheck.version_check_pending}}{{dash-if-empty versionCheck.installed_version}}{{/if}}</td> <td class="version-number">{{#if versionCheck.version_check_pending}}{{dash-if-empty versionCheck.installed_version}}{{/if}}</td>
<td class="face"> <td class="face">
{{#if versionCheck.version_check_pending}} {{#if versionCheck.version_check_pending}}
<span class='icon up-to-date'>{{fa-icon "smile-o"}}</span> <span class='icon up-to-date'>{{d-icon "smile-o"}}</span>
{{else}} {{else}}
<span class="icon critical-updates-available">{{fa-icon "frown-o"}}</span> <span class="icon critical-updates-available">{{d-icon "frown-o"}}</span>
{{/if}} {{/if}}
</td> </td>
<td class="version-notes"> <td class="version-notes">
@ -46,13 +46,13 @@
<td class="version-number">{{dash-if-empty versionCheck.latest_version}}</td> <td class="version-number">{{dash-if-empty versionCheck.latest_version}}</td>
<td class="face"> <td class="face">
{{#if versionCheck.upToDate }} {{#if versionCheck.upToDate }}
<span class='icon up-to-date'>{{fa-icon "smile-o"}}</span> <span class='icon up-to-date'>{{d-icon "smile-o"}}</span>
{{else}} {{else}}
<span class="icon {{if versionCheck.critical_updates 'critical-updates-available' 'updates-available'}}"> <span class="icon {{if versionCheck.critical_updates 'critical-updates-available' 'updates-available'}}">
{{#if versionCheck.behindByOneVersion}} {{#if versionCheck.behindByOneVersion}}
{{fa-icon "meh-o"}} {{d-icon "meh-o"}}
{{else}} {{else}}
{{fa-icon "frown-o"}} {{d-icon "frown-o"}}
{{/if}} {{/if}}
</span> </span>
{{/if}} {{/if}}

View File

@ -0,0 +1,18 @@
<h2>{{model.name}}</h2>
<p class="about">{{actionDescription}}</p>
{{watched-word-form actionKey=actionNameKey action="recordAdded"}}
{{watched-word-uploader uploading=uploading actionKey=actionNameKey done="uploadComplete"}}
<div class='clearfix'></div>
<div class="watched-words-list">
{{#if showWordsList}}
{{#each filteredContent as |word| }}
<div class="watched-word-box">{{admin-watched-word word=word action="recordRemoved"}}</div>
{{/each}}
{{else}}
{{i18n 'admin.watched_words.word_count' count=model.words.length}}
{{/if}}
</div>

View File

@ -0,0 +1,31 @@
<div class='admin-controls'>
<div class='search controls'>
<label class="show-words-checkbox">
{{input type="checkbox" checked=showWords disabled=disableShowWords}}
{{i18n 'admin.watched_words.show_words'}}
</label>
</div>
<div class='controls'>
{{text-field value=filter placeholderKey="admin.watched_words.search" class="no-blur"}}
{{d-button action="clearFilter" label="admin.watched_words.clear_filter"}}
</div>
</div>
<div class="admin-nav pull-left">
<ul class="nav nav-stacked">
{{#each model as |action|}}
{{#link-to 'adminWatchedWords.action' action.nameKey tagName='li' class=action.nameKey}}
{{#link-to 'adminWatchedWords.action' action.nameKey}}
{{action.name}}
{{#if action.count}}<span class="count">({{action.count}})</span>{{/if}}
{{/link-to}}
{{/link-to}}
{{/each}}
</ul>
</div>
<div class="admin-detail pull-left mobile-closed watched-words-detail">
{{outlet}}
</div>
<div class="clearfix"></div>

View File

@ -1,10 +1,10 @@
<div class="web-hook-direction"> <div class="web-hook-direction">
{{#link-to 'adminWebHooks' tagName='button' classNames='btn'}} {{#link-to 'adminWebHooks' tagName='button' classNames='btn'}}
{{fa-icon 'list'}} {{i18n 'admin.web_hooks.events.go_list'}} {{d-icon 'list'}} {{i18n 'admin.web_hooks.events.go_list'}}
{{/link-to}} {{/link-to}}
{{d-button icon="send" label="admin.web_hooks.events.ping" action="ping" disabled=pingDisabled}} {{d-button icon="send" label="admin.web_hooks.events.ping" action="ping" disabled=pingDisabled}}
{{#link-to 'adminWebHooks.show' model.extras.web_hook_id tagName='button' classNames='btn'}} {{#link-to 'adminWebHooks.show' model.extras.web_hook_id tagName='button' classNames='btn'}}
{{fa-icon 'edit'}} {{i18n 'admin.web_hooks.events.go_details'}} {{d-icon 'edit'}} {{i18n 'admin.web_hooks.events.go_details'}}
{{/link-to}} {{/link-to}}
</div> </div>

View File

@ -1,5 +1,5 @@
{{#link-to 'adminWebHooks' class="go-back"}} {{#link-to 'adminWebHooks' class="go-back"}}
{{fa-icon 'arrow-left'}} {{d-icon 'arrow-left'}}
{{i18n 'admin.web_hooks.go_back'}} {{i18n 'admin.web_hooks.go_back'}}
{{/link-to}} {{/link-to}}
@ -49,12 +49,12 @@
<div class='filters'> <div class='filters'>
<div> <div>
<label>{{fa-icon 'circle' class='tracking'}}{{i18n 'admin.web_hooks.categories_filter'}}</label> <label>{{d-icon 'circle' class='tracking'}}{{i18n 'admin.web_hooks.categories_filter'}}</label>
{{category-selector categories=model.categories blacklist=model.categories}} {{category-selector categories=model.categories blacklist=model.categories}}
<div class="instructions">{{i18n 'admin.web_hooks.categories_filter_instructions'}}</div> <div class="instructions">{{i18n 'admin.web_hooks.categories_filter_instructions'}}</div>
</div> </div>
<div> <div>
<label>{{fa-icon 'circle' class='tracking'}}{{i18n 'admin.web_hooks.groups_filter'}}</label> <label>{{d-icon 'circle' class='tracking'}}{{i18n 'admin.web_hooks.groups_filter'}}</label>
{{group-selector groupNames=model.groupsFilterInName groupFinder=model.groupFinder}} {{group-selector groupNames=model.groupsFilterInName groupFinder=model.groupFinder}}
<div class="instructions">{{i18n 'admin.web_hooks.groups_filter_instructions'}}</div> <div class="instructions">{{i18n 'admin.web_hooks.groups_filter_instructions'}}</div>
</div> </div>

View File

@ -1,6 +1,6 @@
<div class='pull-right'> <div class='pull-right'>
{{#link-to 'adminWebHooks.show' 'new' tagName='button' classNames='btn'}} {{#link-to 'adminWebHooks.show' 'new' tagName='button' classNames='btn'}}
{{fa-icon 'plus'}} {{i18n 'admin.web_hooks.new'}} {{d-icon 'plus'}} {{i18n 'admin.web_hooks.new'}}
{{/link-to}} {{/link-to}}
</div> </div>
<div class='clearfix'></div> <div class='clearfix'></div>
@ -24,7 +24,7 @@
<td>{{#link-to 'adminWebHooks.show' webHook}}{{webHook.payload_url}}{{/link-to}}</td> <td>{{#link-to 'adminWebHooks.show' webHook}}{{webHook.payload_url}}{{/link-to}}</td>
<td class='description'>{{webHook.description}}</td> <td class='description'>{{webHook.description}}</td>
<td class='controls'> <td class='controls'>
{{#link-to 'adminWebHooks.show' webHook tagName='button' classNames='btn btn-default no-text'}}{{fa-icon 'edit'}}{{/link-to}} {{#link-to 'adminWebHooks.show' webHook tagName='button' classNames='btn btn-default no-text'}}{{d-icon 'edit'}}{{/link-to}}
{{d-button class="destroy btn-danger" action='destroy' actionParam=webHook icon="remove"}} {{d-button class="destroy btn-danger" action='destroy' actionParam=webHook icon="remove"}}
</td> </td>
</tr> </tr>

View File

@ -69,8 +69,6 @@
//= require ./discourse/components/notifications-button //= require ./discourse/components/notifications-button
//= require ./discourse/lib/link-mentions //= require ./discourse/lib/link-mentions
//= require ./discourse/components/site-header //= require ./discourse/components/site-header
//= require ./discourse/lib/emoji/groups
//= require ./discourse/lib/emoji/toolbar
//= require ./discourse/components/d-editor //= require ./discourse/components/d-editor
//= require ./discourse/lib/screen-track //= require ./discourse/lib/screen-track
//= require ./discourse/routes/discourse //= require ./discourse/routes/discourse

View File

@ -1,5 +1,6 @@
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { on, observes } from 'ember-addons/ember-computed-decorators'; import { on, observes } from 'ember-addons/ember-computed-decorators';
import { iconHTML } from 'discourse-common/lib/icon-library';
export default Ember.Component.extend(bufferedRender({ export default Ember.Component.extend(bufferedRender({
tagName: 'select', tagName: 'select',
@ -97,7 +98,7 @@ export default Ember.Component.extend(bufferedRender({
this.selectionTemplate = (item) => { this.selectionTemplate = (item) => {
let name = Em.get(item, 'text'); let name = Em.get(item, 'text');
name = Handlebars.escapeExpression(name); name = Handlebars.escapeExpression(name);
return `<i class='fa fa-${this.get("selectionIcon")}'></i>${name}`; return iconHTML(this.get('selectionIcon')) + name;
}; };
} }

View File

@ -0,0 +1,6 @@
import { registerUnbound } from 'discourse-common/lib/helpers';
import { renderIcon } from 'discourse-common/lib/icon-library';
registerUnbound('d-icon', function(id, params) {
return new Handlebars.SafeString(renderIcon('string', id, params));
});

View File

@ -1,25 +1,12 @@
import { registerUnbound } from 'discourse-common/lib/helpers'; import { registerUnbound } from 'discourse-common/lib/helpers';
import { renderIcon } from 'discourse-common/lib/icon-library';
import deprecated from 'discourse-common/lib/deprecated';
export function iconClasses(icon, params) { export function iconHTML(id, params) {
let classes = "fa fa-" + icon; return renderIcon('string', id, params);
if (params.modifier) { classes += " fa-" + params.modifier; }
if (params['class']) { classes += ' ' + params['class']; }
return classes;
}
export function iconHTML(icon, params) {
params = params || {};
var html = "<i class='" + iconClasses(icon, params) + "'";
if (params.title) { html += ` title='${I18n.t(params.title)}'`; }
if (params.label) { html += " aria-hidden='true'"; }
html += "></i>";
if (params.label) {
html += "<span class='sr-only'>" + I18n.t(params.label) + "</span>";
}
return html;
} }
registerUnbound('fa-icon', function(icon, params) { registerUnbound('fa-icon', function(icon, params) {
deprecated("Use `{{d-icon}}` instead of `{{fa-icon}}");
return new Handlebars.SafeString(iconHTML(icon, params)); return new Handlebars.SafeString(iconHTML(icon, params));
}); });

View File

@ -0,0 +1,71 @@
import { h } from 'virtual-dom';
let _renderers = [];
export function renderIcon(renderType, id, params) {
for (let i=0; i<_renderers.length; i++) {
let renderer = _renderers[i];
let rendererForType = renderer[renderType];
if (rendererForType) {
let result = rendererForType(id, params || {});
if (result) {
return result;
}
}
}
}
export function iconHTML(id, params) {
return renderIcon('string', id, params);
}
export function iconNode(id, params) {
return renderIcon('node', id, params);
}
export function registerIconRenderer(renderer) {
_renderers.unshift(renderer);
}
// Support for font awesome icons
function faClasses(id, params) {
let classNames = `fa fa-${id} d-icon d-icon-${id}`;
if (params) {
if (params.modifier) { classNames += " fa-" + params.modifier; }
if (params['class']) { classNames += ' ' + params['class']; }
}
return classNames;
}
// default resolver is font awesome
registerIconRenderer({
name: 'font-awesome',
string(id, params) {
let tagName = params.tagName || 'i';
let html = `<${tagName} class='${faClasses(id, params)}'`;
if (params.title) { html += ` title='${I18n.t(params.title)}'`; }
if (params.label) { html += " aria-hidden='true'"; }
html += `></${tagName}>`;
if (params.label) {
html += "<span class='sr-only'>" + I18n.t(params.label) + "</span>";
}
return html;
},
node(id, params) {
let tagName = params.tagName || 'i';
const properties = {
className: faClasses(id, params),
attributes: { "aria-hidden": true }
};
if (params.title) { properties.attributes.title = params.title; }
if (params.label) {
return h(tagName, properties, h('span.sr-only', I18n.t(params.label)));
} else {
return h(tagName, properties);
}
}
});

View File

@ -1,3 +1,4 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import { default as computed, observes } from "ember-addons/ember-computed-decorators"; import { default as computed, observes } from "ember-addons/ember-computed-decorators";
import Combobox from 'discourse-common/components/combo-box'; import Combobox from 'discourse-common/components/combo-box';
import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer'; import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer';
@ -111,9 +112,7 @@ export default Combobox.extend({
let icons; let icons;
if (icon) { if (icon) {
icons = icon.split(',').map(i => { icons = icon.split(',').map(i => iconHTML(i)).join(" ");
return `<i class='fa fa-${i}'/>`;
}).join(" ");
} }
if (time) { if (time) {

View File

@ -1,4 +1,4 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import DropdownButton from 'discourse/components/dropdown-button'; import DropdownButton from 'discourse/components/dropdown-button';
import computed from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators";
@ -15,14 +15,14 @@ export default DropdownButton.extend({
{ id: 'create', { id: 'create',
title: I18n.t('category.create'), title: I18n.t('category.create'),
description: I18n.t('category.create_long'), description: I18n.t('category.create_long'),
styleClasses: 'fa fa-plus' } icon: 'plus' }
]; ];
if (includeReorder) { if (includeReorder) {
items.push({ items.push({
id: 'reorder', id: 'reorder',
title: I18n.t('categories.reorder.title'), title: I18n.t('categories.reorder.title'),
description: I18n.t('categories.reorder.title_long'), description: I18n.t('categories.reorder.title_long'),
styleClasses: 'fa fa-random' icon: 'random'
}); });
} }
return items; return items;

View File

@ -1,4 +1,6 @@
import { setting } from 'discourse/lib/computed'; import { setting } from 'discourse/lib/computed';
import computed from 'ember-addons/ember-computed-decorators';
var get = Ember.get; var get = Ember.get;
export default Ember.Component.extend({ export default Ember.Component.extend({
@ -8,10 +10,10 @@ export default Ember.Component.extend({
tagName: 'li', tagName: 'li',
iconClass: function() { @computed('expanded')
if (this.get('expanded')) { return "fa fa-caret-down"; } expandIcon(expanded) {
return "fa fa-caret-right"; return expanded ? 'caret-down' : 'caret-right';
}.property('expanded'), },
allCategoriesUrl: function() { allCategoriesUrl: function() {
if (this.get('subCategory')) { if (this.get('subCategory')) {
@ -33,7 +35,7 @@ export default Ember.Component.extend({
}.property('category'), }.property('category'),
dropdownButtonClass: function() { dropdownButtonClass: function() {
var result = 'badge-category category-dropdown-button'; let result = 'dropdown-header category-dropdown-button';
if (Em.isNone(this.get('category'))) { if (Em.isNone(this.get('category'))) {
result += ' home'; result += ' home';
} }
@ -57,21 +59,35 @@ export default Ember.Component.extend({
}.property('category'), }.property('category'),
badgeStyle: function() { badgeStyle: function() {
var category = this.get('category'); let category = this.get('category');
const categoryStyle = this.siteSettings.category_style;
if (categoryStyle === 'bullet') {
return;
}
if (category) { if (category) {
var color = get(category, 'color'), let color = get(category, 'color');
textColor = get(category, 'text_color'); let textColor = get(category, 'text_color');
if (color || textColor) { if (color || textColor) {
var style = ""; let style = "";
if (color) { style += "background-color: #" + color + "; border-color: #" + color + ";"; } if (color) {
if (textColor) { style += "color: #" + textColor + "; "; } if (categoryStyle === "bar") {
style += `border-color: #${color};`;
} else if (categoryStyle === "box") {
style += `background-color: #${color};`;
if (textColor) { style += "color: #" + textColor + "; "; }
}
}
return style.htmlSafe(); return style.htmlSafe();
} }
} }
return "background-color: #eee; color: #333".htmlSafe(); if (categoryStyle === 'box') {
return "background-color: #eee; color: #333".htmlSafe();
}
}.property('category'), }.property('category'),
clickEventName: function() { clickEventName: function() {

View File

@ -5,6 +5,7 @@ import { linkSeenCategoryHashtags, fetchUnseenCategoryHashtags } from 'discourse
import { linkSeenTagHashtags, fetchUnseenTagHashtags } from 'discourse/lib/link-tag-hashtag'; import { linkSeenTagHashtags, fetchUnseenTagHashtags } from 'discourse/lib/link-tag-hashtag';
import Composer from 'discourse/models/composer'; import Composer from 'discourse/models/composer';
import { load } from 'pretty-text/oneboxer'; import { load } from 'pretty-text/oneboxer';
import { applyInlineOneboxes } from 'pretty-text/inline-oneboxer';
import { ajax } from 'discourse/lib/ajax'; import { ajax } from 'discourse/lib/ajax';
import InputValidation from 'discourse/models/input-validation'; import InputValidation from 'discourse/models/input-validation';
import { findRawTemplate } from 'discourse/lib/raw-templates'; import { findRawTemplate } from 'discourse/lib/raw-templates';
@ -58,6 +59,8 @@ export default Ember.Component.extend({
@computed @computed
markdownOptions() { markdownOptions() {
return { return {
previewing: true,
lookupAvatarByPostNumber: (postNumber, topicId) => { lookupAvatarByPostNumber: (postNumber, topicId) => {
const topic = this.get('topic'); const topic = this.get('topic');
if (!topic) { return; } if (!topic) { return; }
@ -171,6 +174,10 @@ export default Ember.Component.extend({
}); });
}, },
_loadInlineOneboxes(inline) {
applyInlineOneboxes(inline, ajax);
},
_loadOneboxes($oneboxes) { _loadOneboxes($oneboxes) {
const post = this.get('composer.post'); const post = this.get('composer.post');
let refresh = false; let refresh = false;
@ -572,6 +579,18 @@ export default Ember.Component.extend({
Ember.run.debounce(this, this._loadOneboxes, $oneboxes, 450); Ember.run.debounce(this, this._loadOneboxes, $oneboxes, 450);
} }
let inline = {};
$('a.inline-onebox-loading', $preview).each(function(index, link) {
let $link = $(link);
$link.removeClass('inline-onebox-loading');
let text = $link.text();
inline[text] = inline[text] || [];
inline[text].push($link);
});
if (Object.keys(inline).length > 0) {
Ember.run.debounce(this, this._loadInlineOneboxes, inline, 450);
}
this.trigger('previewRefreshed', $preview); this.trigger('previewRefreshed', $preview);
this.sendAction('afterRefresh', $preview); this.sendAction('afterRefresh', $preview);
}, },

View File

@ -0,0 +1,14 @@
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
tagName: '',
@computed('composeState')
toggleIcon(composeState) {
if (composeState === "draft" || composeState === "saving") {
return "times";
}
return "chevron-down";
}
});

View File

@ -1,6 +1,5 @@
/*global Mousetrap:true */ /*global Mousetrap:true */
import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators'; import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
import { showSelector } from "discourse/lib/emoji/toolbar";
import Category from 'discourse/models/category'; import Category from 'discourse/models/category';
import { categoryHashtagTriggerRule } from 'discourse/lib/category-hashtags'; import { categoryHashtagTriggerRule } from 'discourse/lib/category-hashtags';
import { TAG_HASHTAG_POSTFIX } from 'discourse/lib/tag-hashtags'; import { TAG_HASHTAG_POSTFIX } from 'discourse/lib/tag-hashtags';
@ -78,7 +77,11 @@ class Toolbar {
group: 'insertions', group: 'insertions',
icon: 'quote-right', icon: 'quote-right',
shortcut: 'Shift+9', shortcut: 'Shift+9',
perform: e => e.applyList('> ', 'blockquote_text') perform: e => e.applyList(
'> ',
'blockquote_text',
{ applyEmptyLines: true, multiline: true }
)
}); });
this.addButton({id: 'code', group: 'insertions', shortcut: 'Shift+C', action: 'formatCode'}); this.addButton({id: 'code', group: 'insertions', shortcut: 'Shift+C', action: 'formatCode'});
@ -198,6 +201,7 @@ export default Ember.Component.extend({
linkText: '', linkText: '',
lastSel: null, lastSel: null,
_mouseTrap: null, _mouseTrap: null,
emojiPickerIsActive: false,
@computed('placeholder') @computed('placeholder')
placeholderTranslated(placeholder) { placeholderTranslated(placeholder) {
@ -328,7 +332,6 @@ export default Ember.Component.extend({
_applyEmojiAutocomplete($editorInput) { _applyEmojiAutocomplete($editorInput) {
if (!this.siteSettings.enable_emoji) { return; } if (!this.siteSettings.enable_emoji) { return; }
const register = this.register;
const self = this; const self = this;
$editorInput.autocomplete({ $editorInput.autocomplete({
@ -346,20 +349,8 @@ export default Ember.Component.extend({
if (v.code) { if (v.code) {
return `${v.code}:`; return `${v.code}:`;
} else { } else {
showSelector({ $editorInput.autocomplete({cancel: true});
appendTo: self.$(), self.set('emojiPickerIsActive', true);
register,
onSelect: title => {
// Remove the previously type characters when a new emoji is selected from the selector.
let selected = self._getSelected();
let newPre = selected.pre.replace(/:[^:]+$/, ":");
let numOfRemovedChars = selected.pre.length - newPre.length;
selected.pre = newPre;
selected.start -= numOfRemovedChars;
selected.end -= numOfRemovedChars;
self._addText(selected, `${title}:`);
}
});
return ""; return "";
} }
}, },
@ -453,11 +444,15 @@ export default Ember.Component.extend({
}, },
// perform the same operation over many lines of text // perform the same operation over many lines of text
_getMultilineContents(lines, head, hval, hlen, tail, tlen) { _getMultilineContents(lines, head, hval, hlen, tail, tlen, opts) {
let operation = OP.NONE; let operation = OP.NONE;
const applyEmptyLines = opts && opts.applyEmptyLines;
return lines.map(l => { return lines.map(l => {
if (l.length === 0) { return l; } if (!applyEmptyLines && l.length === 0) {
return l;
}
if (operation !== OP.ADDED && if (operation !== OP.ADDED &&
(l.slice(0, hlen) === hval && tlen === 0 || l.slice(-tlen) === tail)) { (l.slice(0, hlen) === hval && tlen === 0 || l.slice(-tlen) === tail)) {
@ -513,8 +508,15 @@ export default Ember.Component.extend({
this.set('value', `${pre.slice(0, -hlen)}${sel.value}${post.slice(tlen)}`); this.set('value', `${pre.slice(0, -hlen)}${sel.value}${post.slice(tlen)}`);
this._selectText(sel.start - hlen, sel.value.length); this._selectText(sel.start - hlen, sel.value.length);
} else { } else {
const contents = this._getMultilineContents(lines, head, hval, hlen, tail, tlen); const contents = this._getMultilineContents(
lines,
head,
hval,
hlen,
tail,
tlen,
opts
);
this.set('value', `${pre}${contents}${post}`); this.set('value', `${pre}${contents}${post}`);
if (lines.length === 1 && tlen > 0) { if (lines.length === 1 && tlen > 0) {
this._selectText(sel.start + hlen, sel.value.length); this._selectText(sel.start + hlen, sel.value.length);
@ -525,9 +527,9 @@ export default Ember.Component.extend({
} }
}, },
_applyList(sel, head, exampleKey) { _applyList(sel, head, exampleKey, opts) {
if (sel.value.indexOf("\n") !== -1) { if (sel.value.indexOf("\n") !== -1) {
this._applySurround(sel, head, '', exampleKey); this._applySurround(sel, head, '', exampleKey, opts);
} else { } else {
const [hval, hlen] = getHead(head); const [hval, hlen] = getHead(head);
@ -587,9 +589,10 @@ export default Ember.Component.extend({
if (post.length > 0) { if (post.length > 0) {
post = post.replace(/^\n*/, "\n\n"); post = post.replace(/^\n*/, "\n\n");
} else {
post = "\n";
} }
const value = pre + text + post; const value = pre + text + post;
const $textarea = this.$('textarea.d-editor-input'); const $textarea = this.$('textarea.d-editor-input');
@ -614,13 +617,28 @@ export default Ember.Component.extend({
}, },
actions: { actions: {
emojiSelected(code) {
let selected = this._getSelected();
const captures = selected.pre.match(/\B:(\w*)$/);
if(_.isEmpty(captures)) {
this._addText(selected, `:${code}:`);
} else {
let numOfRemovedChars = selected.pre.length - captures[1].length;
selected.pre = selected.pre.slice(0, selected.pre.length - captures[1].length);
selected.start -= numOfRemovedChars;
selected.end -= numOfRemovedChars;
this._addText(selected, `${code}:`);
}
},
toolbarButton(button) { toolbarButton(button) {
const selected = this._getSelected(button.trimLeading); const selected = this._getSelected(button.trimLeading);
const toolbarEvent = { const toolbarEvent = {
selected, selected,
selectText: (from, length) => this._selectText(from, length), selectText: (from, length) => this._selectText(from, length),
applySurround: (head, tail, exampleKey, opts) => this._applySurround(selected, head, tail, exampleKey, opts), applySurround: (head, tail, exampleKey, opts) => this._applySurround(selected, head, tail, exampleKey, opts),
applyList: (head, exampleKey) => this._applyList(selected, head, exampleKey), applyList: (head, exampleKey, opts) => this._applyList(selected, head, exampleKey, opts),
addText: text => this._addText(selected, text), addText: text => this._addText(selected, text),
replaceText: text => this._addText({pre: '', post: ''}, text), replaceText: text => this._addText({pre: '', post: ''}, text),
getText: () => this.get('value'), getText: () => this.get('value'),
@ -692,11 +710,7 @@ export default Ember.Component.extend({
}, },
emoji() { emoji() {
showSelector({ this.set('emojiPickerIsActive', !this.get('emojiPickerIsActive'));
appendTo: this.$(),
register: this.register,
onSelect: title => this._addText(this._getSelected(), `:${title}:`)
});
} }
} }
}); });

View File

@ -2,7 +2,7 @@ import { on } from "ember-addons/ember-computed-decorators";
export default Ember.Component.extend({ export default Ember.Component.extend({
elementId: 'discourse-modal', elementId: 'discourse-modal',
classNameBindings: [':modal', ':hidden', 'modalClass'], classNameBindings: [':modal', 'modalClass'],
attributeBindings: ['data-keyboard'], attributeBindings: ['data-keyboard'],
// We handle ESC ourselves // We handle ESC ourselves
@ -17,6 +17,7 @@ export default Ember.Component.extend({
}); });
this.appEvents.on('modal:body-shown', data => { this.appEvents.on('modal:body-shown', data => {
this.$().removeClass('hidden');
if (data.title) { if (data.title) {
this.set('title', I18n.t(data.title)); this.set('title', I18n.t(data.title));
} else if (data.rawTitle) { } else if (data.rawTitle) {

View File

@ -1,4 +1,4 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(bufferedRender({ export default Ember.Component.extend(bufferedRender({

View File

@ -1,3 +1,4 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(bufferedRender({ export default Ember.Component.extend(bufferedRender({
@ -29,7 +30,7 @@ export default Ember.Component.extend(bufferedRender({
buffer.push("<h4 class='title'>" + title + "</h4>"); buffer.push("<h4 class='title'>" + title + "</h4>");
} }
buffer.push(`<button class='btn standard dropdown-toggle ${this.get('buttonExtraClasses')}' data-toggle='dropdown'>${this.get('text')}</button>`); buffer.push(`<button class='btn standard dropdown-toggle ${this.get('buttonExtraClasses') || ''}' data-toggle='dropdown'>${this.get('text')}</button>`);
buffer.push("<ul class='dropdown-menu'>"); buffer.push("<ul class='dropdown-menu'>");
const contents = this.get('dropDownContent'); const contents = this.get('dropDownContent');
@ -40,7 +41,15 @@ export default Ember.Component.extend(bufferedRender({
className = (self.get('activeItem') === id ? 'disabled': ''); className = (self.get('activeItem') === id ? 'disabled': '');
buffer.push("<li data-id=\"" + id + "\" class=\"" + className + "\"><a href>"); buffer.push("<li data-id=\"" + id + "\" class=\"" + className + "\"><a href>");
buffer.push("<span class='icon " + row.styleClasses + "'></span>");
if (row.icon) {
let iconClass = 'icon';
if (row.iconClass) {
iconClass += ` ${row.iconClass}`;
}
buffer.push(iconHTML(row.icon, { tagName: 'span', class: iconClass }));
}
buffer.push("<div><span class='title'>" + row.title + "</span>"); buffer.push("<div><span class='title'>" + row.title + "</span>");
buffer.push("<span>" + row.description + "</span></div>"); buffer.push("<span>" + row.description + "</span></div>");
buffer.push("</a></li>"); buffer.push("</a></li>");

View File

@ -0,0 +1,543 @@
import { observes } from "ember-addons/ember-computed-decorators";
import { findRawTemplate } from "discourse/lib/raw-templates";
import { emojiUrlFor } from "discourse/lib/text";
import KeyValueStore from "discourse/lib/key-value-store";
import { emojis } from "pretty-text/emoji/data";
import { extendedEmojiList, isSkinTonableEmoji } from "pretty-text/emoji";
const keyValueStore = new KeyValueStore("discourse_emojis_");
const EMOJI_USAGE = "emojiUsage";
const EMOJI_SELECTED_DIVERSITY = "emojiSelectedDiversity";
const PER_ROW = 11;
const customEmojis = _.map(_.keys(extendedEmojiList()), code => {
return { code, src: emojiUrlFor(code) };
});
export function resetCache() {
keyValueStore.setObject({ key: EMOJI_USAGE, value: [] });
keyValueStore.setObject({ key: EMOJI_SELECTED_DIVERSITY, value: 1 });
}
let $picker, $filter, $results, $list, scrollPosition, $visibleSections, _checkTimeout;
export default Ember.Component.extend({
willDestroyElement() {
this._super();
this._unbindEvents();
this.appEvents.off("emoji-picker:close");
},
didInsertElement() {
this._super();
this.appEvents.on("emoji-picker:close", () => this.set("active", false));
$picker = this.$(".emoji-picker");
if (!keyValueStore.getObject(EMOJI_USAGE)) {
keyValueStore.setObject({ key: EMOJI_USAGE, value: [] });
} else if(_.isPlainObject(keyValueStore.getObject(EMOJI_USAGE))) {
// handle legacy format
keyValueStore.setObject({ key: EMOJI_USAGE, value: _.keys(keyValueStore.getObject(EMOJI_USAGE)) });
}
scrollPosition = 0;
},
didUpdateAttrs() {
this._super();
if (this.get("active")) {
this.show();
} else {
this.close();
}
},
@observes("filter")
filterChanged() {
$filter.find(".clear-filter").toggle(!_.isEmpty(this.get("filter")));
Ember.run.debounce(this, this._filterEmojisList, 250);
},
@observes("selectedDiversity")
selectedDiversityChanged() {
keyValueStore.setObject({key: EMOJI_SELECTED_DIVERSITY, value: this.get("selectedDiversity")});
$.each($list.find(".emoji[data-loaded='1'].diversity"), (_, button) => {
$(button).css("background-image", "").removeAttr("data-loaded");
});
if(this.get("filter") !== "") {
$.each($results.find(".emoji.diversity"), (_, button) => this._setButtonBackground(button, true) );
}
this._updateSelectedDiversity();
},
@observes("recentEmojis")
recentEmojisChanged() {
const previousScrollTop = scrollPosition;
const $recentSection = $list.find(".section[data-section='recent']");
const $recentSectionGroup = $recentSection.find(".section-group");
const $recentCategory = $picker.find(".category-icon button[data-section='recent']").parent();
let persistScrollPosition = !$recentCategory.is(":visible") ? true : false;
// we set height to 0 to avoid it being taken into account for scroll position
if(_.isEmpty(this.get("recentEmojis"))) {
$recentCategory.hide();
$recentSection.css("height", 0).hide();
} else {
$recentCategory.show();
$recentSection.css("height", "auto").show();
}
const recentEmojis = _.map(this.get("recentEmojis"), code => {
return { code, src: emojiUrlFor(code) };
});
const template = findRawTemplate("emoji-picker-recent")({recentEmojis});
$recentSectionGroup.html(template);
if(persistScrollPosition) {
$list.scrollTop(previousScrollTop + $recentSection.outerHeight());
}
this._bindHover($recentSectionGroup);
},
close() {
$picker
.css({width: "", left: "", bottom: "", display: "none"})
.empty();
this.$().find(".emoji-picker-modal").remove();
this._unbindEvents();
clearTimeout(_checkTimeout);
},
show() {
const template = findRawTemplate("emoji-picker")({customEmojis});
$picker.html(template);
this.$().append("<div class='emoji-picker-modal'></div>");
$filter = $picker.find(".filter");
$results = $picker.find(".results");
$list = $picker.find(".list");
this.set("selectedDiversity", keyValueStore.getObject(EMOJI_SELECTED_DIVERSITY) || 1);
this.set("recentEmojis", keyValueStore.getObject(EMOJI_USAGE) || []);
this._bindEvents();
Ember.run.scheduleOnce("afterRender", this, function() {
this._sectionLoadingCheck();
this._loadCategoriesEmojis();
this._positionPicker();
this._scrollTo();
this._updateSelectedDiversity();
});
},
_updateSelectedDiversity() {
const $diversityPicker = $picker.find(".diversity-picker");
$diversityPicker.find(".diversity-scale").removeClass("selected");
$diversityPicker
.find(`.diversity-scale[data-level="${this.get("selectedDiversity")}"]`)
.addClass("selected");
},
_sectionLoadingCheck() {
_checkTimeout = setTimeout(() => { this._sectionLoadingCheck(); }, 500);
Ember.run.throttle(this, this._checkVisibleSection, 100);
},
_loadCategoriesEmojis() {
$.each($picker.find(".categories-column button.emoji"), (_, button) => {
const $button = $(button);
const code = this._codeWithDiversity($button.data("tabicon"), false);
$button.css("background-image", `url("${emojiUrlFor(code)}")`);
});
},
_bindEvents() {
this._bindDiversityClick();
this._bindSectionsScroll();
this._bindEmojiClick($list.find(".section-group"));
this._bindClearRecentEmojisGroup();
this._bindResizing();
this._bindCategoryClick();
this._bindModalClick();
this._bindFilterInput();
if(!this.site.isMobileDevice) {
this._bindHover();
}
},
_bindModalClick() {
this.$(".emoji-picker-modal")
.on("click", () => this.set("active", false));
this.$(document).on("click.emoji-picker", (event) => {
const onPicker = $(event.target).parents(".emoji-picker").length === 1;
const onGrippie = event.target.className.indexOf("grippie") > -1;
if(!onPicker && !onGrippie) {
this.set("active", false);
return false;
}
});
},
_unbindEvents() {
this.$(window).off("resize");
this.$(".emoji-picker-modal").off("click");
Ember.$("#reply-control").off("div-resizing");
this.$(document).off("click.emoji-picker");
},
_filterEmojisList() {
if (this.get("filter") === "") {
$filter.find("input[name='filter']").val("");
$results.empty().hide();
$list.show();
} else {
const lowerCaseFilter = this.get("filter").toLowerCase();
const filterableEmojis = emojis.concat(_.keys(extendedEmojiList()));
const filteredCodes = _.filter(filterableEmojis, code => {
return code.indexOf(lowerCaseFilter) > -1;
}).slice(0, 30);
$results.empty().html(
_.map(filteredCodes, (code) => {
const hasDiversity = isSkinTonableEmoji(code);
const diversity = hasDiversity ? "diversity" : "";
const scaledCode = this._codeWithDiversity(code, hasDiversity);
return `<button style="background-image: url('${emojiUrlFor(scaledCode)}')" type="button" class="emoji ${diversity}" tabindex="-1" title="${code}"></button>`;
})
).show();
this._bindHover($results);
this._bindEmojiClick($results);
$list.hide();
}
},
_bindFilterInput() {
const $input = $filter.find("input");
$input.on("input", (event) => {
this.set("filter", event.currentTarget.value);
});
$filter.find(".clear-filter").on("click", () => {
$input.val("").focus();
this.set("filter", "");
return false;
});
},
_bindCategoryClick() {
$picker.find(".category-icon").on("click", "button.emoji", (event) => {
this.set("filter", "");
$results.empty();
$list.show();
const section = $(event.currentTarget).data("section");
const $section = $list.find(`.section[data-section="${section}"]`);
const scrollTop = $list.scrollTop() + ($section.offset().top - $list.offset().top);
this._scrollTo(scrollTop);
return false;
});
},
_bindHover($hoverables) {
const replaceInfoContent = (html) => $picker.find(".footer .info").html(html || "");
($hoverables || $list.find(".section-group")).on({
mouseover: (event) => {
const code = this._codeForEmojiButton($(event.currentTarget));
const html = `<img src="${emojiUrlFor(code)}" class="emoji"> <span>:${code}:<span>`;
replaceInfoContent(html);
},
mouseleave: () => replaceInfoContent()
}, "button.emoji");
},
_bindResizing() {
this.$(window).on("resize", () => {
Ember.run.throttle(this, this._positionPicker, 16);
});
Ember.$("#reply-control").on("div-resizing", () => {
Ember.run.throttle(this, this._positionPicker, 16);
});
},
_bindClearRecentEmojisGroup() {
const $recent = $picker.find(".section[data-section='recent'] .clear-recent");
$recent.on("click", () => {
keyValueStore.setObject({ key: EMOJI_USAGE, value: [] });
this.set("recentEmojis", []);
this._scrollTo(0);
return false;
});
},
_bindEmojiClick($emojisContainer) {
const handler = (event) => {
const code = this._codeForEmojiButton($(event.currentTarget));
if($(event.currentTarget).parents(".section[data-section='recent']").length === 0) {
this._trackEmojiUsage(code);
}
this.sendAction("emojiSelected", code);
if(this.$(".emoji-picker-modal").hasClass("fadeIn")) {
this.set("active", false);
}
return false;
};
if(this.site.isMobileDevice) {
const self = this;
$emojisContainer
.off("touchstart")
.on("touchstart", "button.emoji", (touchStartEvent) => {
const $this = $(touchStartEvent.currentTarget);
$this.on("touchend", (touchEndEvent) => {
handler.bind(self)(touchEndEvent);
$this.off("touchend");
});
$this.on("touchmove", () => $this.off("touchend") );
});
} else {
$emojisContainer.off("click").on("click", "button.emoji", e => handler.bind(this)(e) );
}
},
_bindSectionsScroll() {
$list.on("scroll", () => {
scrollPosition = $list.scrollTop();
Ember.run.throttle(this, this._checkVisibleSection, 150);
});
},
_checkVisibleSection() {
// make sure we stop loading if picker has been removed
if(!$picker) {
return;
}
const $sections = $list.find(".section");
const listHeight = $list.innerHeight();
let $selectedSection;
$visibleSections = _.filter($sections, section => {
const $section = $(section);
const sectionTop = $section.position().top;
return sectionTop + $section.height() > 0 && sectionTop < listHeight;
});
if (!_.isEmpty(this.get("recentEmojis")) && scrollPosition === 0) {
$selectedSection = $(_.first($visibleSections));
} else {
$selectedSection = $(_.last($visibleSections));
}
if($selectedSection) {
$picker.find(".category-icon").removeClass("current");
$picker.find(`.category-icon button[data-section='${$selectedSection.data("section")}']`)
.parent()
.addClass("current");
this._loadVisibleSections();
}
},
_loadVisibleSections() {
if(!$visibleSections) {
return;
}
const listHeight = $list.innerHeight();
$visibleSections.forEach(visibleSection => {
const $unloadedEmojis = $(visibleSection).find("button.emoji[data-loaded!='1']");
$.each($unloadedEmojis, (_, button) => {
const $button = $(button);
const buttonTop = $button.position().top;
const buttonHeight = $button.height();
if(buttonTop + buttonHeight > 0 && buttonTop - buttonHeight < listHeight) {
this._setButtonBackground($button);
}
});
});
},
_bindDiversityClick() {
const $diversityScales = $picker.find(".diversity-picker .diversity-scale");
$diversityScales.on("click", (event) => {
const $selectedDiversity = $(event.currentTarget);
this.set("selectedDiversity", parseInt($selectedDiversity.data("level")));
return false;
});
},
_isReplyControlExpanded() {
const verticalSpace = this.$(window).height() -
Ember.$(".d-header").height() -
Ember.$("#reply-control").height();
return verticalSpace < $picker.height() - 48;
},
_positionPicker(){
if(!this.get("active")) { return; }
let windowWidth = this.$(window).width();
const desktopModalePositioning = options => {
let attributes = {
width: Math.min(windowWidth, 400) - 12,
marginLeft: -(Math.min(windowWidth, 400)/2) + 6,
marginTop: -130,
left: "50%",
bottom: "",
top: "50%",
display: "flex"
};
this.$(".emoji-picker-modal").addClass("fadeIn");
$picker.css(_.merge(attributes, options));
};
const mobilePositioning = options => {
let attributes = {
width: windowWidth - 12,
marginLeft: 5,
marginTop: -130,
left: 0,
bottom: "",
top: "50%",
display: "flex"
};
this.$(".emoji-picker-modal").addClass("fadeIn");
$picker.css(_.merge(attributes, options));
};
const desktopPositioning = options => {
let attributes = {
width: windowWidth < 485 ? windowWidth - 12 : 400,
marginLeft: "",
marginTop: "",
right: "",
left: "",
bottom: 32,
top: "",
display:
"flex"
};
this.$(".emoji-picker-modal").removeClass("fadeIn");
$picker.css(_.merge(attributes, options));
};
if(Ember.testing) {
desktopPositioning();
return;
}
if(this.site.isMobileDevice) {
mobilePositioning();
} else {
if(this._isReplyControlExpanded()) {
let $editorWrapper = Ember.$(".d-editor-preview-wrapper");
if(($editorWrapper.is(":visible") && $editorWrapper.width() < 400) || windowWidth < 485) {
desktopModalePositioning();
} else {
if($editorWrapper.is(":visible")) {
let previewOffset = Ember.$(".d-editor-preview-wrapper").offset();
let replyControlOffset = Ember.$("#reply-control").offset();
let left = previewOffset.left - replyControlOffset.left;
desktopPositioning({left});
} else {
desktopPositioning({
right: (Ember.$("#reply-control").width() - Ember.$(".d-editor-container").width()) / 2
});
}
}
} else {
if(windowWidth < 485) {
desktopModalePositioning();
} else {
let previewInputOffset = Ember.$(".d-editor-input").offset();
let replyControlOffset = Ember.$("#reply-control").offset() || {left: 0};
let left = previewInputOffset.left - replyControlOffset.left;
desktopPositioning({left, bottom: Ember.$("#reply-control").height() - 48});
}
}
}
const infoMaxWidth = $picker.width() -
$picker.find(".categories-column").width() -
$picker.find(".diversity-picker").width() -
32;
$picker.find(".info").css("max-width", infoMaxWidth);
},
_codeWithDiversity(code, diversity) {
if(diversity && this.get("selectedDiversity") !== 1) {
return `${code}:t${this.get("selectedDiversity")}`;
} else {
return code;
}
},
_trackEmojiUsage(code) {
let recent = keyValueStore.getObject(EMOJI_USAGE) || [];
recent = recent.filter(r => r !== code);
recent.unshift(code);
recent.length = Math.min(recent.length, PER_ROW);
keyValueStore.setObject({ key: EMOJI_USAGE, value: recent });
this.set("recentEmojis", recent);
},
_scrollTo(y) {
const yPosition = _.isUndefined(y) ? scrollPosition : y;
$list.scrollTop(yPosition);
// if we dont actually scroll we need to force it
if(yPosition === 0) {
$list.scroll();
}
},
_codeForEmojiButton($button) {
const title = $button.attr("title");
return this._codeWithDiversity(title, $button.hasClass("diversity"));
},
_setButtonBackground(button, diversity) {
const $button = $(button);
const code = this._codeWithDiversity(
$button.attr("title"),
diversity || $button.hasClass("diversity")
);
// force visual reloading if needed
if($button.css("background-image") !== "none") {
$button.css("background-image", "");
}
$button
.attr("data-loaded", 1)
.css("background-image", `url("${emojiUrlFor(code)}")`);
},
});

View File

@ -0,0 +1,16 @@
import { default as computed } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
tagName: 'button',
classNames: ['btn-flat'],
attributeBindings: ['disabled', 'translatedTitle:title'],
@computed("title")
translatedTitle(title) {
if (title) return I18n.t(title);
},
click() {
return this.attrs.action();
}
});

View File

@ -1,5 +1,5 @@
import { on } from 'ember-addons/ember-computed-decorators'; import { on } from 'ember-addons/ember-computed-decorators';
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import LogsNotice from 'discourse/services/logs-notice'; import LogsNotice from 'discourse/services/logs-notice';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';

View File

@ -1,4 +1,4 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(bufferedRender({ export default Ember.Component.extend(bufferedRender({

View File

@ -4,6 +4,7 @@ import { propertyEqual } from 'discourse/lib/computed';
export default Ember.Component.extend({ export default Ember.Component.extend({
classNames: ["group-members-input"], classNames: ["group-members-input"],
addButton: true,
@computed('model.limit', 'model.offset', 'model.user_count') @computed('model.limit', 'model.offset', 'model.user_count')
currentPage(limit, offset, userCount) { currentPage(limit, offset, userCount) {

View File

@ -5,9 +5,14 @@ import DiscourseURL from 'discourse/lib/url';
export default Ember.Component.extend({ export default Ember.Component.extend({
loading: false, loading: false,
@computed("model.public") @computed("model.public_admission", "userIsGroupUser")
canJoinGroup(publicGroup) { canJoinGroup(publicAdmission, userIsGroupUser) {
return publicGroup; return publicAdmission && !userIsGroupUser;
},
@computed("model.public_exit", "userIsGroupUser")
canLeaveGroup(publicExit, userIsGroupUser) {
return publicExit && userIsGroupUser;
}, },
@computed("model.is_group_user", "model.id", "groupUserIds") @computed("model.is_group_user", "model.id", "groupUserIds")

View File

@ -15,31 +15,27 @@ export default Ember.Component.extend({
@on('didInsertElement') @on('didInsertElement')
_initializeAutocomplete(opts) { _initializeAutocomplete(opts) {
var self = this; let selectedGroups;
var selectedGroups; let groupNames = this.get('groupNames');
var groupNames = this.get('groupNames');
self.$('input').autocomplete({ this.$('input').autocomplete({
allowAny: false, allowAny: false,
items: _.isArray(groupNames) ? groupNames : (Ember.isEmpty(groupNames)) ? [] : [groupNames], items: _.isArray(groupNames) ? groupNames : (Ember.isEmpty(groupNames)) ? [] : [groupNames],
single: this.get('single'), single: this.get('single'),
updateData: (opts && opts.updateData) ? opts.updateData : false, updateData: (opts && opts.updateData) ? opts.updateData : false,
onChangeItems: function(items){ onChangeItems: items => {
selectedGroups = items; selectedGroups = items;
self.set("groupNames", items.join(",")); this.set("groupNames", items.join(","));
}, },
transformComplete: function(g) { transformComplete: g => {
return g.name; return g.name;
}, },
dataSource: function(term) { dataSource: term => {
return self.get("groupFinder")(term).then(function(groups){ return this.get("groupFinder")(term).then(groups => {
if(!selectedGroups) return groups;
if(!selectedGroups){ return groups.filter(group => {
return groups; return !selectedGroups.any(s => s === group.name);
}
return groups.filter(function(group){
return !selectedGroups.any(function(s){return s === group.name;});
}); });
}); });
}, },

View File

@ -1,6 +1,6 @@
export default Ember.Component.extend({ export default Ember.Component.extend({
didInsertElement() { didInsertElement() {
this._super(); this._super();
$('#discourse-modal').modal('hide'); $('#discourse-modal').modal('hide').addClass('hidden');
} }
}); });

View File

@ -1,13 +1,11 @@
import highlightText from 'discourse/lib/highlight-text';
export default Ember.Component.extend({ export default Ember.Component.extend({
tagName: 'span', tagName: 'span',
_highlightOnInsert: function() { _highlightOnInsert: function() {
const term = this.get('highlight'); const term = this.get('highlight');
const self = this; highlightText(this.$(), term);
if(!_.isEmpty(term)) {
self.$().highlight(term.split(/\s+/), {className: 'search-highlight'});
}
}.observes('highlight').on('didInsertElement') }.observes('highlight').on('didInsertElement')
}); });

View File

@ -1,5 +1,5 @@
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
export default Ember.Component.extend(bufferedRender({ export default Ember.Component.extend(bufferedRender({
classNameBindings: [':tip', 'good', 'bad'], classNameBindings: [':tip', 'good', 'bad'],

View File

@ -1,6 +1,6 @@
import DropdownButton from 'discourse/components/dropdown-button'; import DropdownButton from 'discourse/components/dropdown-button';
import { allLevels, buttonDetails } from 'discourse/lib/notification-levels'; import { allLevels, buttonDetails } from 'discourse/lib/notification-levels';
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import computed from 'ember-addons/ember-computed-decorators'; import computed from 'ember-addons/ember-computed-decorators';
export default DropdownButton.extend({ export default DropdownButton.extend({
@ -22,7 +22,8 @@ export default DropdownButton.extend({
id: l.id, id: l.id,
title: I18n.t(`${start}.title`), title: I18n.t(`${start}.title`),
description: I18n.t(`${start}.description`), description: I18n.t(`${start}.description`),
styleClasses: `${l.key.dasherize()} fa fa-${l.icon}` icon: l.icon,
iconClass: l.key.dasherize(),
}; };
}); });
}, },

View File

@ -25,7 +25,7 @@ export default Ember.Component.extend(CleansUp, {
if (!this.get('showPeriods')) { if (!this.get('showPeriods')) {
if (!this.site.mobileView) { if (!this.site.mobileView) {
const $chevron = this.$('i.fa-caret-down'); const $chevron = this.$('.d-icon-caret-down');
this.$('#period-popup').css($chevron.position()); this.$('#period-popup').css($chevron.position());
} else { } else {
this.$('#period-popup').css({top: this.$().height()}); this.$('#period-popup').css({top: this.$().height()});

View File

@ -1,3 +1,5 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import computed from 'ember-addons/ember-computed-decorators';
import DropdownButton from 'discourse/components/dropdown-button'; import DropdownButton from 'discourse/components/dropdown-button';
export default DropdownButton.extend({ export default DropdownButton.extend({
@ -28,21 +30,28 @@ export default DropdownButton.extend({
{id: 'pinned', {id: 'pinned',
title: I18n.t('topic_statuses.pinned' + globally + '.title'), title: I18n.t('topic_statuses.pinned' + globally + '.title'),
description: I18n.t('topic_statuses.pinned' + globally + '.help'), description: I18n.t('topic_statuses.pinned' + globally + '.help'),
styleClasses: 'fa fa-thumb-tack' }, icon: 'thumb-tack' },
{id: 'unpinned', {id: 'unpinned',
title: I18n.t('topic_statuses.unpinned.title'), title: I18n.t('topic_statuses.unpinned.title'),
description: I18n.t('topic_statuses.unpinned.help'), description: I18n.t('topic_statuses.unpinned.help'),
styleClasses: 'fa fa-thumb-tack unpinned' } icon: 'thumb-tack',
iconClass: 'unpinned' }
]; ];
}.property(), }.property(),
text: function() { @computed('topic.pinned', 'topic.pinned_globally')
const globally = this.get('topic.pinned_globally') ? '_globally' : ''; text(pinned, pinnedGlobally) {
const state = this.get('topic.pinned') ? 'pinned' + globally : 'unpinned'; const globally = pinnedGlobally ? '_globally' : '';
const state = pinned ? 'pinned' + globally : 'unpinned';
return '<span class="fa fa-thumb-tack' + (state === 'unpinned' ? ' unpinned' : "") + '"></span> ' + const icon = iconHTML(
'thumb-tack',
{ tagName: 'span', class: (state === 'unpinned' ? 'unpinned' : null) }
);
return icon +
I18n.t('topic_statuses.' + state + '.title') + "<span class='caret'></span>"; I18n.t('topic_statuses.' + state + '.title') + "<span class='caret'></span>";
}.property('topic.pinned', 'topic.unpinned'), },
clicked(id) { clicked(id) {
const topic = this.get('topic'); const topic = this.get('topic');

View File

@ -1,4 +1,4 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators'; import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';

View File

@ -10,13 +10,13 @@ const REGEXP_CATEGORY_PREFIX = /^(category:|#)/ig;
const REGEXP_GROUP_PREFIX = /^group:/ig; const REGEXP_GROUP_PREFIX = /^group:/ig;
const REGEXP_BADGE_PREFIX = /^badge:/ig; const REGEXP_BADGE_PREFIX = /^badge:/ig;
const REGEXP_TAGS_PREFIX = /^(tags?:|#(?=[a-z0-9\-]+::tag))/ig; const REGEXP_TAGS_PREFIX = /^(tags?:|#(?=[a-z0-9\-]+::tag))/ig;
const REGEXP_IN_PREFIX = /^in:/ig; const REGEXP_IN_PREFIX = /^(in|with):/ig;
const REGEXP_STATUS_PREFIX = /^status:/ig; const REGEXP_STATUS_PREFIX = /^status:/ig;
const REGEXP_MIN_POST_COUNT_PREFIX = /^min_post_count:/ig; const REGEXP_MIN_POST_COUNT_PREFIX = /^min_post_count:/ig;
const REGEXP_POST_TIME_PREFIX = /^(before|after):/ig; const REGEXP_POST_TIME_PREFIX = /^(before|after):/ig;
const REGEXP_TAGS_REPLACE = /(^(tags?:|#(?=[a-z0-9\-]+::tag))|::tag\s?$)/ig; const REGEXP_TAGS_REPLACE = /(^(tags?:|#(?=[a-z0-9\-]+::tag))|::tag\s?$)/ig;
const REGEXP_IN_MATCH = /^in:(posted|watching|tracking|bookmarks|first|pinned|unpinned|wiki|unseen)/ig; const REGEXP_IN_MATCH = /^(in|with):(posted|watching|tracking|bookmarks|first|pinned|unpinned|wiki|unseen|image)/ig;
const REGEXP_SPECIAL_IN_LIKES_MATCH = /^in:likes/ig; const REGEXP_SPECIAL_IN_LIKES_MATCH = /^in:likes/ig;
const REGEXP_SPECIAL_IN_PRIVATE_MATCH = /^in:private/ig; const REGEXP_SPECIAL_IN_PRIVATE_MATCH = /^in:private/ig;
const REGEXP_SPECIAL_IN_SEEN_MATCH = /^in:seen/ig; const REGEXP_SPECIAL_IN_SEEN_MATCH = /^in:seen/ig;
@ -25,6 +25,8 @@ const REGEXP_CATEGORY_SLUG = /^(\#[a-zA-Z0-9\-:]+)/ig;
const REGEXP_CATEGORY_ID = /^(category:[0-9]+)/ig; const REGEXP_CATEGORY_ID = /^(category:[0-9]+)/ig;
const REGEXP_POST_TIME_WHEN = /^(before|after)/ig; const REGEXP_POST_TIME_WHEN = /^(before|after)/ig;
const IN_OPTIONS_MAPPING = {'images': 'with'};
export default Em.Component.extend({ export default Em.Component.extend({
classNames: ['search-advanced-options'], classNames: ['search-advanced-options'],
@ -38,6 +40,7 @@ export default Em.Component.extend({
{name: I18n.t('search.advanced.filters.pinned'), value: "pinned"}, {name: I18n.t('search.advanced.filters.pinned'), value: "pinned"},
{name: I18n.t('search.advanced.filters.unpinned'), value: "unpinned"}, {name: I18n.t('search.advanced.filters.unpinned'), value: "unpinned"},
{name: I18n.t('search.advanced.filters.wiki'), value: "wiki"}, {name: I18n.t('search.advanced.filters.wiki'), value: "wiki"},
{name: I18n.t('search.advanced.filters.images'), value: "images"},
], ],
statusOptions: [ statusOptions: [
{name: I18n.t('search.advanced.statuses.open'), value: "open"}, {name: I18n.t('search.advanced.statuses.open'), value: "open"},
@ -398,13 +401,17 @@ export default Em.Component.extend({
updateSearchTermForIn() { updateSearchTermForIn() {
const match = this.filterBlocks(REGEXP_IN_MATCH); const match = this.filterBlocks(REGEXP_IN_MATCH);
const inFilter = this.get('searchedTerms.in'); const inFilter = this.get('searchedTerms.in');
let keyword = 'in';
if(inFilter in IN_OPTIONS_MAPPING) {
keyword = IN_OPTIONS_MAPPING[inFilter];
}
let searchTerm = this.get('searchTerm') || ''; let searchTerm = this.get('searchTerm') || '';
if (inFilter) { if (inFilter) {
if (match.length !== 0) { if (match.length !== 0) {
searchTerm = searchTerm.replace(match[0], `in:${inFilter}`); searchTerm = searchTerm.replace(match[0], `${keyword}:${inFilter}`);
} else { } else {
searchTerm += ` in:${inFilter}`; searchTerm += ` ${keyword}:${inFilter}`;
} }
this.set('searchTerm', searchTerm.trim()); this.set('searchTerm', searchTerm.trim());
@ -527,7 +534,7 @@ export default Em.Component.extend({
}, },
groupFinder(term) { groupFinder(term) {
return Group.findAll({search: term, ignore_automatic: false}); return Group.findAll({ term: term, ignore_automatic: false });
}, },
badgeFinder(term) { badgeFinder(term) {

View File

@ -21,9 +21,8 @@ export default Ember.Component.extend({
}, },
@computed('expanded') @computed('expanded')
iconClass() { expandedIcon(expanded) {
if (this.get('expanded')) { return "fa fa-caret-down"; } return expanded ? 'caret-down' : 'caret-right';
return "fa fa-caret-right";
}, },
@computed('tagId') @computed('tagId')
@ -70,7 +69,7 @@ export default Ember.Component.extend({
@computed('tag') @computed('tag')
dropdownButtonClass() { dropdownButtonClass() {
var result = 'badge-category category-dropdown-button'; let result = 'dropdown-header category-dropdown-button';
if (Em.isNone(this.get('tag'))) { if (Em.isNone(this.get('tag'))) {
result += ' home'; result += ' home';
} }

View File

@ -1,4 +1,4 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import DropdownButton from 'discourse/components/dropdown-button'; import DropdownButton from 'discourse/components/dropdown-button';
import computed from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators";
@ -14,7 +14,7 @@ export default DropdownButton.extend({
{ id: 'manageGroups', { id: 'manageGroups',
title: I18n.t('tagging.manage_groups'), title: I18n.t('tagging.manage_groups'),
description: I18n.t('tagging.manage_groups_description'), description: I18n.t('tagging.manage_groups_description'),
styleClasses: 'fa fa-wrench' } icon: 'wrench' }
]; ];
return items; return items;
}, },

View File

@ -1,4 +1,4 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import Combobox from 'discourse-common/components/combo-box'; import Combobox from 'discourse-common/components/combo-box';
import { observes } from 'ember-addons/ember-computed-decorators'; import { observes } from 'ember-addons/ember-computed-decorators';

View File

@ -1,4 +1,4 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon'; import { iconHTML } from 'discourse-common/lib/icon-library';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { escapeExpression } from 'discourse/lib/utilities'; import { escapeExpression } from 'discourse/lib/utilities';
@ -8,7 +8,7 @@ export default Ember.Component.extend(bufferedRender({
rerenderTriggers: ['topic.archived', 'topic.closed', 'topic.pinned', 'topic.visible', 'topic.unpinned', 'topic.is_warning'], rerenderTriggers: ['topic.archived', 'topic.closed', 'topic.pinned', 'topic.visible', 'topic.unpinned', 'topic.is_warning'],
click(e) { click(e) {
if ($(e.target).hasClass('fa-thumb-tack')) { if ($(e.target).hasClass('d-icon-thumb-tack')) {
const topic = this.get('topic'); const topic = this.get('topic');
// only pin unpin for now // only pin unpin for now

View File

@ -1,3 +1,4 @@
import { iconHTML } from 'discourse-common/lib/icon-library';
import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { bufferedRender } from 'discourse-common/lib/buffered-render';
import Category from 'discourse/models/category'; import Category from 'discourse/models/category';
@ -35,7 +36,7 @@ export default Ember.Component.extend(bufferedRender({
let autoCloseHours = this.get("duration") || 0; let autoCloseHours = this.get("duration") || 0;
buffer.push('<h3><i class="fa fa-clock-o"></i> '); buffer.push(`<h3>${iconHTML('clock-o')} `);
let options = { let options = {
timeLeft: duration.humanize(true), timeLeft: duration.humanize(true),

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