From 27539e15e1fb30ce44e93980a95331465b42cae2 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 26 Jul 2017 15:54:20 +0100 Subject: [PATCH 01/59] Allow whitespace around % scaler in markdown image syntax (#5000) --- .../engines/discourse-markdown-it.js.es6 | 2 +- spec/components/pretty_text_spec.rb | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/pretty-text/engines/discourse-markdown-it.js.es6 b/app/assets/javascripts/pretty-text/engines/discourse-markdown-it.js.es6 index 6ffa5092572..1174be579c4 100644 --- a/app/assets/javascripts/pretty-text/engines/discourse-markdown-it.js.es6 +++ b/app/assets/javascripts/pretty-text/engines/discourse-markdown-it.js.es6 @@ -104,7 +104,7 @@ function setupHoister(md) { md.renderer.rules.html_raw = renderHoisted; } -const IMG_SIZE_REGEX = /^([1-9]+[0-9]*)x([1-9]+[0-9]*)(,([1-9][0-9]?)%)?$/; +const IMG_SIZE_REGEX = /^([1-9]+[0-9]*)x([1-9]+[0-9]*)(\s*,\s*([1-9][0-9]?)%)?$/; function renderImage(tokens, idx, options, env, slf) { var token = tokens[idx]; diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index 7853babee63..72beb51030d 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -970,6 +970,21 @@ HTML expect(cooked).to eq(html.strip) end + + it "allows whitespace before the percent scaler" do + cooked = PrettyText.cook <<~MD + ![|220x100, 50%](http://png.com/my.png) + ![|220x100 , 50%](http://png.com/my.png) + ![|220x100 ,50%](http://png.com/my.png) + MD + + html = <<~HTML +

+ +

+ HTML + end + end describe "inline onebox" do From 9d774a951af5442ee990b6c4b1f49f03f731b5c0 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 26 Jul 2017 10:59:32 -0400 Subject: [PATCH 02/59] Update libv8 and benchmark --- Gemfile.lock | 4 +- script/benchmarks/markdown/bench.rb | 85 +++++++++++++++++++---------- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index dce01c3c166..40d1fd0c262 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -137,7 +137,7 @@ GEM thor (>= 0.14, < 2.0) jwt (1.5.6) kgio (2.11.0) - libv8 (5.7.492.65.1) + libv8 (5.9.211.38.1) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -481,4 +481,4 @@ DEPENDENCIES webmock BUNDLED WITH - 1.15.1 + 1.15.3 diff --git a/script/benchmarks/markdown/bench.rb b/script/benchmarks/markdown/bench.rb index a1e0cda2851..4f70fa6dc48 100644 --- a/script/benchmarks/markdown/bench.rb +++ b/script/benchmarks/markdown/bench.rb @@ -42,32 +42,61 @@ Benchmark.ips do |x| end -# 18-07-2017 - Sam's NUC - -# Calculating ------------------------------------- -# tiny post sanitize: true -# 162.766 (±13.5%) i/s - 812.000 in 5.101429s -# giant post sanitize: true -# 133.957 (±11.2%) i/s - 663.000 in 5.029386s -# most features sanitize: true -# 55.319 (±10.8%) i/s - 276.000 in 5.054290s -# lots of mentions sanitize: true -# 0.313 (± 0.0%) i/s - 2.000 in 6.394343s -# tiny post sanitize: false -# 456.209 (±13.6%) i/s - 2.288k in 5.117314s -# giant post sanitize: false -# 331.357 (±10.9%) i/s - 1.650k in 5.046322s -# most features sanitize: false -# 77.038 (±10.4%) i/s - 385.000 in 5.055062s -# lots of mentions sanitize: false -# 0.312 (± 0.0%) i/s - 2.000 in 6.430657s -# markdown it no extensions commonmark tiny post -# 6.916k (± 5.5%) i/s - 34.540k in 5.010354s -# markdown it no extensions commonmark giant post -# 1.044k (± 9.3%) i/s - 5.247k in 5.090534s -# markdown it no extensions commonmark most features -# 1.457k (± 5.0%) i/s - 7.314k in 5.034401s -# markdown it no extensions commonmark lots of mentions -# 2.004k (± 5.2%) i/s - 10.192k in 5.100657s -# sam@ubuntu markdown % +# 27-07-2017 - Sam's NUC # +# v8 5.7 +# +# +# tiny post sanitize: true +# 160.678 (±19.9%) i/s - 760.000 in 5.005630s +# giant post sanitize: true +# 132.195 (±14.4%) i/s - 649.000 in 5.042695s +# most features sanitize: true +# 56.205 (± 8.9%) i/s - 280.000 in 5.038138s +# lots of mentions sanitize: true +# 0.318 (± 0.0%) i/s - 2.000 in 6.293644s +# tiny post sanitize: false +# 404.304 (±20.8%) i/s - 1.920k in 5.019903s +# giant post sanitize: false +# 327.721 (±11.9%) i/s - 1.624k in 5.033749s +# most features sanitize: false +# 76.649 (±10.4%) i/s - 385.000 in 5.085552s +# lots of mentions sanitize: false +# 0.306 (± 0.0%) i/s - 2.000 in 6.525968s +# markdown it no extensions commonmark tiny post +# 5.871k (±19.1%) i/s - 28.544k in 5.073585s +# markdown it no extensions commonmark giant post +# 1.006k (±12.5%) i/s - 4.960k in 5.041623s +# markdown it no extensions commonmark most features +# 1.447k (± 8.9%) i/s - 7.205k in 5.029094s +# markdown it no extensions commonmark lots of mentions +# 1.962k (± 8.3%) i/s - 9.850k in 5.061684s +# +# +# v8 5.9 +# +# +# tiny post sanitize: true +# 156.179 (±16.0%) i/s - 765.000 in 5.059401s +# giant post sanitize: true +# 129.972 (±10.8%) i/s - 650.000 in 5.071824s +# most features sanitize: true +# 54.960 (± 9.1%) i/s - 275.000 in 5.051284s +# lots of mentions sanitize: true +# 0.321 (± 0.0%) i/s - 2.000 in 6.251009s +# tiny post sanitize: false +# 431.159 (±10.4%) i/s - 2.166k in 5.085303s +# giant post sanitize: false +# 300.236 (±11.7%) i/s - 1.479k in 5.029557s +# most features sanitize: false +# 73.808 (±10.8%) i/s - 371.000 in 5.092310s +# lots of mentions sanitize: false +# 0.297 (± 0.0%) i/s - 2.000 in 6.729708s +# markdown it no extensions commonmark tiny post +# 6.421k (±13.0%) i/s - 32.012k in 5.084672s +# markdown it no extensions commonmark giant post +# 901.622 (± 9.2%) i/s - 4.452k in 5.016748s +# markdown it no extensions commonmark most features +# 1.410k (± 6.5%) i/s - 7.112k in 5.070053s +# markdown it no extensions commonmark lots of mentions +# 1.934k (± 6.4%) i/s - 9.672k in 5.025858s From 24cb950432e1fa321d3adf5511543ec57813925a Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Wed, 28 Jun 2017 16:56:44 -0400 Subject: [PATCH 03/59] FEATURE: Watched Words: when posts contain words, do one of flag, require approval, censor, or block --- .../components/admin-watched-word.js.es6 | 19 ++ .../admin/components/watched-word-form.js.es6 | 49 +++++ .../components/watched-word-uploader.js.es6 | 25 +++ .../admin-watched-words-action.js.es6 | 65 +++++++ .../controllers/admin-watched-words.js.es6 | 51 ++++++ .../admin/models/watched-word.js.es6 | 37 ++++ .../admin/routes/admin-route-map.js.es6 | 5 + .../routes/admin-watched-words-action.js.es6 | 11 ++ .../routes/admin-watched-words-index.js.es6 | 5 + .../admin/routes/admin-watched-words.js.es6 | 15 ++ .../javascripts/admin/templates/admin.hbs | 1 + .../components/watched-word-form.hbs | 7 + .../components/watched-word-uploader.hbs | 7 + .../admin/templates/watched-words-action.hbs | 18 ++ .../admin/templates/watched-words.hbs | 31 ++++ .../javascripts/discourse/lib/text.js.es6 | 4 +- .../javascripts/discourse/models/topic.js.es6 | 6 +- .../discourse-markdown/censored.js.es6 | 1 - .../pretty-text/pretty-text.js.es6 | 4 +- .../stylesheets/common/admin/admin_base.scss | 41 +++++ .../admin/watched_words_controller.rb | 47 +++++ app/jobs/onceoff/migrate_censored_words.rb | 12 ++ app/jobs/regular/process_post.rb | 8 + app/models/watched_word.rb | 54 ++++++ app/serializers/site_serializer.rb | 7 +- .../watched_word_list_serializer.rb | 13 ++ app/serializers/watched_word_serializer.rb | 7 + app/services/word_watcher.rb | 51 ++++++ config/locales/client.en.yml | 25 +++ config/locales/server.en.yml | 5 + config/routes.rb | 6 + config/site_settings.yml | 5 - .../20170628152322_create_watched_words.rb | 11 ++ lib/new_post_manager.rb | 23 ++- lib/post_revisor.rb | 6 + lib/pretty_text.rb | 1 + lib/validators/censored_words_validator.rb | 7 +- .../lib/details-cooked-test.js.es6 | 2 +- spec/components/new_post_manager_spec.rb | 10 +- spec/components/pretty_text_spec.rb | 6 +- spec/fabricators/watched_word_fabricator.rb | 4 + spec/integration/watched_words_spec.rb | 169 ++++++++++++++++++ spec/models/topic_spec.rb | 14 +- spec/models/watched_word_spec.rb | 92 ++++++++++ spec/services/word_watcher_spec.rb | 53 ++++++ .../admin-watched-words-test.js.es6 | 68 +++++++ .../fixtures/watched-words-fixtures.js.es6 | 12 ++ .../helpers/create-pretender.js.es6 | 11 ++ test/javascripts/lib/pretty-text-test.js.es6 | 2 +- 49 files changed, 1096 insertions(+), 37 deletions(-) create mode 100644 app/assets/javascripts/admin/components/admin-watched-word.js.es6 create mode 100644 app/assets/javascripts/admin/components/watched-word-form.js.es6 create mode 100644 app/assets/javascripts/admin/components/watched-word-uploader.js.es6 create mode 100644 app/assets/javascripts/admin/controllers/admin-watched-words-action.js.es6 create mode 100644 app/assets/javascripts/admin/controllers/admin-watched-words.js.es6 create mode 100644 app/assets/javascripts/admin/models/watched-word.js.es6 create mode 100644 app/assets/javascripts/admin/routes/admin-watched-words-action.js.es6 create mode 100644 app/assets/javascripts/admin/routes/admin-watched-words-index.js.es6 create mode 100644 app/assets/javascripts/admin/routes/admin-watched-words.js.es6 create mode 100644 app/assets/javascripts/admin/templates/components/watched-word-form.hbs create mode 100644 app/assets/javascripts/admin/templates/components/watched-word-uploader.hbs create mode 100644 app/assets/javascripts/admin/templates/watched-words-action.hbs create mode 100644 app/assets/javascripts/admin/templates/watched-words.hbs create mode 100644 app/controllers/admin/watched_words_controller.rb create mode 100644 app/jobs/onceoff/migrate_censored_words.rb create mode 100644 app/models/watched_word.rb create mode 100644 app/serializers/watched_word_list_serializer.rb create mode 100644 app/serializers/watched_word_serializer.rb create mode 100644 app/services/word_watcher.rb create mode 100644 db/migrate/20170628152322_create_watched_words.rb create mode 100644 spec/fabricators/watched_word_fabricator.rb create mode 100644 spec/integration/watched_words_spec.rb create mode 100644 spec/models/watched_word_spec.rb create mode 100644 spec/services/word_watcher_spec.rb create mode 100644 test/javascripts/acceptance/admin-watched-words-test.js.es6 create mode 100644 test/javascripts/fixtures/watched-words-fixtures.js.es6 diff --git a/app/assets/javascripts/admin/components/admin-watched-word.js.es6 b/app/assets/javascripts/admin/components/admin-watched-word.js.es6 new file mode 100644 index 00000000000..9a454ad57ee --- /dev/null +++ b/app/assets/javascripts/admin/components/admin-watched-word.js.es6 @@ -0,0 +1,19 @@ +import { iconHTML } from 'discourse-common/helpers/fa-icon'; +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})); + });; + } +})); diff --git a/app/assets/javascripts/admin/components/watched-word-form.js.es6 b/app/assets/javascripts/admin/components/watched-word-form.js.es6 new file mode 100644 index 00000000000..95f859abf4b --- /dev/null +++ b/app/assets/javascripts/admin/components/watched-word-form.js.es6 @@ -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'); + } + }); + }); + } +}); diff --git a/app/assets/javascripts/admin/components/watched-word-uploader.js.es6 b/app/assets/javascripts/admin/components/watched-word-uploader.js.es6 new file mode 100644 index 00000000000..c6d39c52a9a --- /dev/null +++ b/app/assets/javascripts/admin/components/watched-word-uploader.js.es6 @@ -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"); + } + } +}); diff --git a/app/assets/javascripts/admin/controllers/admin-watched-words-action.js.es6 b/app/assets/javascripts/admin/controllers/admin-watched-words-action.js.es6 new file mode 100644 index 00000000000..bd11f15fcff --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-watched-words-action.js.es6 @@ -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); + }); + } + } + +}); diff --git a/app/assets/javascripts/admin/controllers/admin-watched-words.js.es6 b/app/assets/javascripts/admin/controllers/admin-watched-words.js.es6 new file mode 100644 index 00000000000..07d53fd2301 --- /dev/null +++ b/app/assets/javascripts/admin/controllers/admin-watched-words.js.es6 @@ -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: '' }); + } + } + +}); diff --git a/app/assets/javascripts/admin/models/watched-word.js.es6 b/app/assets/javascripts/admin/models/watched-word.js.es6 new file mode 100644 index 00000000000..f5d42df1b92 --- /dev/null +++ b/app/assets/javascripts/admin/models/watched-word.js.es6 @@ -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; diff --git a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 index dd87207156a..86d624bab79 100644 --- a/app/assets/javascripts/admin/routes/admin-route-map.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-route-map.js.es6 @@ -90,5 +90,10 @@ export default function() { this.route('adminPlugins', { path: '/plugins', resetNamespace: true }, function() { this.route('index', { path: '/' }); }); + + this.route('adminWatchedWords', { path: '/watched_words', resetNamespace: true}, function() { + this.route('index', { path: '/' } ); + this.route('action', { path: '/action/:action_id' } ); + }); }); }; diff --git a/app/assets/javascripts/admin/routes/admin-watched-words-action.js.es6 b/app/assets/javascripts/admin/routes/admin-watched-words-action.js.es6 new file mode 100644 index 00000000000..123884d6fc6 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-watched-words-action.js.es6 @@ -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 + }); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-watched-words-index.js.es6 b/app/assets/javascripts/admin/routes/admin-watched-words-index.js.es6 new file mode 100644 index 00000000000..0103744b52d --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-watched-words-index.js.es6 @@ -0,0 +1,5 @@ +export default Discourse.Route.extend({ + beforeModel() { + this.replaceWith('adminWatchedWords.action', this.modelFor('adminWatchedWords')[0].nameKey); + } +}); diff --git a/app/assets/javascripts/admin/routes/admin-watched-words.js.es6 b/app/assets/javascripts/admin/routes/admin-watched-words.js.es6 new file mode 100644 index 00000000000..77f9082b576 --- /dev/null +++ b/app/assets/javascripts/admin/routes/admin-watched-words.js.es6 @@ -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); + } +}); diff --git a/app/assets/javascripts/admin/templates/admin.hbs b/app/assets/javascripts/admin/templates/admin.hbs index 780aad41fcf..525a62626e6 100644 --- a/app/assets/javascripts/admin/templates/admin.hbs +++ b/app/assets/javascripts/admin/templates/admin.hbs @@ -22,6 +22,7 @@ {{nav-item route='adminApi' label='admin.api.title'}} {{nav-item route='admin.backups' label='admin.backups.title'}} {{/if}} + {{nav-item route='adminWatchedWords' label='admin.watched_words.title'}} {{nav-item route='adminPlugins' label='admin.plugins.title'}} {{plugin-outlet name="admin-menu" connectorTagName="li"}} diff --git a/app/assets/javascripts/admin/templates/components/watched-word-form.hbs b/app/assets/javascripts/admin/templates/components/watched-word-form.hbs new file mode 100644 index 00000000000..64fbcb6570a --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/watched-word-form.hbs @@ -0,0 +1,7 @@ +{{i18n 'admin.watched_words.form.label'}} +{{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}} + {{i18n 'admin.watched_words.form.success'}} +{{/if}} diff --git a/app/assets/javascripts/admin/templates/components/watched-word-uploader.hbs b/app/assets/javascripts/admin/templates/components/watched-word-uploader.hbs new file mode 100644 index 00000000000..2582bdcbb0a --- /dev/null +++ b/app/assets/javascripts/admin/templates/components/watched-word-uploader.hbs @@ -0,0 +1,7 @@ + +
+One word per line diff --git a/app/assets/javascripts/admin/templates/watched-words-action.hbs b/app/assets/javascripts/admin/templates/watched-words-action.hbs new file mode 100644 index 00000000000..3a7351d9ed4 --- /dev/null +++ b/app/assets/javascripts/admin/templates/watched-words-action.hbs @@ -0,0 +1,18 @@ +

{{model.name}}

+ +

{{actionDescription}}

+ +{{watched-word-form actionKey=actionNameKey action="recordAdded"}} + +{{watched-word-uploader uploading=uploading actionKey=actionNameKey done="uploadComplete"}} + +
+
+ {{#if showWordsList}} + {{#each filteredContent as |word| }} +
{{admin-watched-word word=word action="recordRemoved"}}
+ {{/each}} + {{else}} + {{i18n 'admin.watched_words.word_count' count=model.words.length}} + {{/if}} +
diff --git a/app/assets/javascripts/admin/templates/watched-words.hbs b/app/assets/javascripts/admin/templates/watched-words.hbs new file mode 100644 index 00000000000..501e4af97b6 --- /dev/null +++ b/app/assets/javascripts/admin/templates/watched-words.hbs @@ -0,0 +1,31 @@ +
+ +
+ {{text-field value=filter placeholderKey="admin.watched_words.search" class="no-blur"}} + {{d-button action="clearFilter" label="admin.watched_words.clear_filter"}} +
+
+ +
+ +
+ +
+ {{outlet}} +
+ +
diff --git a/app/assets/javascripts/discourse/lib/text.js.es6 b/app/assets/javascripts/discourse/lib/text.js.es6 index 970bbbae5ae..874f519010b 100644 --- a/app/assets/javascripts/discourse/lib/text.js.es6 +++ b/app/assets/javascripts/discourse/lib/text.js.es6 @@ -5,11 +5,13 @@ import { sanitize as textSanitize } from 'pretty-text/sanitizer'; import loadScript from 'discourse/lib/load-script'; function getOpts(opts) { - const siteSettings = Discourse.__container__.lookup('site-settings:main'); + const siteSettings = Discourse.__container__.lookup('site-settings:main'), + site = Discourse.__container__.lookup('site:main'); opts = _.merge({ getURL: Discourse.getURLWithCDN, currentUser: Discourse.__container__.lookup('current-user:main'), + censoredWords: site.censored_words, siteSettings }, opts); diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6 index 5949766a915..f8e75e517f9 100644 --- a/app/assets/javascripts/discourse/models/topic.js.es6 +++ b/app/assets/javascripts/discourse/models/topic.js.es6 @@ -50,11 +50,7 @@ const Topic = RestModel.extend({ @computed('fancy_title') fancyTitle(title) { - // TODO: `siteSettings` should always be present, but there are places in the code - // that call Discourse.Topic.create instead of using the store. - // When the store is used, remove this. - const siteSettings = this.siteSettings || Discourse.SiteSettings; - return censor(emojiUnescape(title || ""), siteSettings.censored_words); + return censor(emojiUnescape(title || ""), Discourse.Site.currentProp('censored_words')); }, // returns createdAt if there's no bumped date diff --git a/app/assets/javascripts/pretty-text/engines/discourse-markdown/censored.js.es6 b/app/assets/javascripts/pretty-text/engines/discourse-markdown/censored.js.es6 index cd0d7bb7926..91a0d297ba0 100644 --- a/app/assets/javascripts/pretty-text/engines/discourse-markdown/censored.js.es6 +++ b/app/assets/javascripts/pretty-text/engines/discourse-markdown/censored.js.es6 @@ -24,7 +24,6 @@ function censorTree(state, censor) { export function setup(helper) { helper.registerOptions((opts, siteSettings) => { - opts.censoredWords = siteSettings.censored_words; opts.censoredPattern = siteSettings.censored_pattern; }); diff --git a/app/assets/javascripts/pretty-text/pretty-text.js.es6 b/app/assets/javascripts/pretty-text/pretty-text.js.es6 index 21708cbe211..68fd360dc66 100644 --- a/app/assets/javascripts/pretty-text/pretty-text.js.es6 +++ b/app/assets/javascripts/pretty-text/pretty-text.js.es6 @@ -22,7 +22,8 @@ export function buildOptions(state) { emojiUnicodeReplacer, lookupInlineOnebox, previewing, - linkify + linkify, + censoredWords } = state; let features = { @@ -57,6 +58,7 @@ export function buildOptions(state) { mentionLookup: state.mentionLookup, emojiUnicodeReplacer, lookupInlineOnebox, + censoredWords, allowedHrefSchemes: siteSettings.allowed_href_schemes ? siteSettings.allowed_href_schemes.split('|') : null, markdownIt: true, previewing diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index e462a8328a8..58ee43ef773 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -1847,6 +1847,47 @@ table#user-badges { } } +.watched-word-box { + display: inline-block; + width: 250px; + margin-bottom: 1em; + float: left; +} + +.watched-words-list { + margin-top: 40px; +} +.watched-word { + display: inline-block; + cursor: pointer; + i.fa { + margin-right: 0.25em; + color: dark-light-diff($primary, $secondary, 50%, -50%); + } + &:hover i.fa { + color: $primary; + } +} +.watched-word-form { + display: inline-block; + .success-message { + margin-left: 1em; + } +} +.watched-words-uploader { + float: right; + text-align: right; + .instructions { + font-size: 12px; + } +} +.watched-words-detail { + .about { + margin-top: 24px; + margin-bottom: 40px; + } +} + // Mobile specific styles // Mobile view text-inputs need some padding .mobile-view .admin-contents { diff --git a/app/controllers/admin/watched_words_controller.rb b/app/controllers/admin/watched_words_controller.rb new file mode 100644 index 00000000000..a2f777d8f55 --- /dev/null +++ b/app/controllers/admin/watched_words_controller.rb @@ -0,0 +1,47 @@ +class Admin::WatchedWordsController < Admin::AdminController + + def index + render_json_dump WatchedWordListSerializer.new(WatchedWord.by_action, scope: guardian, root: false) + end + + def create + watched_word = WatchedWord.create_or_update_word(watched_words_params) + if watched_word.valid? + render json: watched_word, root: false + else + render_json_error(watched_word) + end + end + + def destroy + watched_word = WatchedWord.find(params[:id]) + watched_word.destroy + render json: success_json + end + + def upload + file = params[:file] || params[:files].first + action_key = params[:action_key].to_sym + + Scheduler::Defer.later("Upload watched words") do + begin + File.open(file.tempfile, encoding: "ISO-8859-1").each_line do |line| + WatchedWord.create_or_update_word(word: line, action_key: action_key) unless line.empty? + end + data = {url: '/ok'} + rescue => e + data = failed_json.merge(errors: [e.message]) + end + MessageBus.publish("/uploads/csv", data.as_json, client_ids: [params[:client_id]]) + end + + render json: success_json + end + + private + + def watched_words_params + params.permit(:id, :word, :action_key) + end + +end diff --git a/app/jobs/onceoff/migrate_censored_words.rb b/app/jobs/onceoff/migrate_censored_words.rb new file mode 100644 index 00000000000..03ce4b358cb --- /dev/null +++ b/app/jobs/onceoff/migrate_censored_words.rb @@ -0,0 +1,12 @@ +module Jobs + class MigrateCensoredWords < Jobs::Onceoff + def execute_onceoff(args) + row = WatchedWord.exec_sql("SELECT value FROM site_settings WHERE name = 'censored_words'") + if row.count > 0 + row.first["value"].split('|').each do |word| + WatchedWord.create(word: word, action: WatchedWord.actions[:censor]) + end + end + end + end +end diff --git a/app/jobs/regular/process_post.rb b/app/jobs/regular/process_post.rb index ddfb0ff3d58..1230dbc7526 100644 --- a/app/jobs/regular/process_post.rb +++ b/app/jobs/regular/process_post.rb @@ -37,6 +37,14 @@ module Jobs post.publish_change_to_clients! :revised end end + + if !post.user.staff? && !post.user.staged + s = post.cooked + s << " #{post.topic.title}" if post.post_number == 1 + if WordWatcher.new(s).should_flag? + PostAction.act(Discourse.system_user, post, PostActionType.types[:inappropriate]) rescue PostAction::AlreadyActed + end + end end # onebox may have added some links, so extract them now diff --git a/app/models/watched_word.rb b/app/models/watched_word.rb new file mode 100644 index 00000000000..d9cbd4edc19 --- /dev/null +++ b/app/models/watched_word.rb @@ -0,0 +1,54 @@ +require_dependency 'enum' + +class WatchedWord < ActiveRecord::Base + + def self.actions + @actions ||= Enum.new( + block: 1, + censor: 2, + require_approval: 3, + flag: 4 + ) + end + + MAX_WORDS_PER_ACTION = 1000 + + before_validation do + self.word = self.class.normalize_word(self.word) + end + + validates :word, presence: true, uniqueness: true, length: { maximum: 50 } + validates :action, presence: true + validates_each :word do |record, attr, val| + if WatchedWord.where(action: record.action).count >= MAX_WORDS_PER_ACTION + record.errors.add(:word, :too_many) + end + end + + after_save :clear_cache + after_destroy :clear_cache + + scope :by_action, -> { order("action ASC, word ASC") } + + + def self.normalize_word(w) + w.strip.downcase.squeeze('*') + end + + def self.create_or_update_word(params) + w = find_or_initialize_by(word: normalize_word(params[:word])) + w.action_key = params[:action_key] if params[:action_key] + w.action = params[:action] if params[:action] + w.save + w + end + + def action_key=(arg) + self.action = self.class.actions[arg.to_sym] + end + + def clear_cache + WordWatcher.clear_cache! + end + +end diff --git a/app/serializers/site_serializer.rb b/app/serializers/site_serializer.rb index 5ea12e8cbe7..1b474185336 100644 --- a/app/serializers/site_serializer.rb +++ b/app/serializers/site_serializer.rb @@ -25,7 +25,8 @@ class SiteSerializer < ApplicationSerializer :top_tags, :wizard_required, :topic_featured_link_allowed_category_ids, - :user_themes + :user_themes, + :censored_words has_many :categories, serializer: BasicCategorySerializer, embed: :objects has_many :trust_levels, embed: :objects @@ -142,4 +143,8 @@ class SiteSerializer < ApplicationSerializer def topic_featured_link_allowed_category_ids scope.topic_featured_link_allowed_category_ids end + + def censored_words + WordWatcher.words_for_action(:censor).join('|') + end end diff --git a/app/serializers/watched_word_list_serializer.rb b/app/serializers/watched_word_list_serializer.rb new file mode 100644 index 00000000000..e38d56365ca --- /dev/null +++ b/app/serializers/watched_word_list_serializer.rb @@ -0,0 +1,13 @@ +class WatchedWordListSerializer < ApplicationSerializer + attributes :actions, :words + + def actions + WatchedWord.actions.keys + end + + def words + object.map do |word| + WatchedWordSerializer.new(word, root: false) + end + end +end diff --git a/app/serializers/watched_word_serializer.rb b/app/serializers/watched_word_serializer.rb new file mode 100644 index 00000000000..ffa46da5125 --- /dev/null +++ b/app/serializers/watched_word_serializer.rb @@ -0,0 +1,7 @@ +class WatchedWordSerializer < ApplicationSerializer + attributes :id, :word, :action + + def action + WatchedWord.actions[object.action] + end +end diff --git a/app/services/word_watcher.rb b/app/services/word_watcher.rb new file mode 100644 index 00000000000..625e51e999d --- /dev/null +++ b/app/services/word_watcher.rb @@ -0,0 +1,51 @@ +class WordWatcher + + def initialize(raw) + @raw = raw + end + + def self.words_for_action(action) + WatchedWord.where(action: WatchedWord.actions[action.to_sym]).limit(1000).pluck(:word) + end + + def self.words_for_action_exists?(action) + WatchedWord.where(action: WatchedWord.actions[action.to_sym]).exists? + end + + def self.word_matcher_regexp(action) + s = Discourse.cache.fetch(word_matcher_regexp_key(action), expires_in: 1.day) do + words = words_for_action(action) + words.empty? ? nil : '\b(' + words.map { |w| Regexp.escape(w).gsub("\\*", '\S*') }.join('|'.freeze) + ')\b' + end + + s.present? ? Regexp.new(s, Regexp::IGNORECASE) : nil + end + + def self.word_matcher_regexp_key(action) + "watched-words-regexp:#{action}" + end + + def self.clear_cache! + WatchedWord.actions.sum do |a,i| + Discourse.cache.delete word_matcher_regexp_key(a) + end + end + + def requires_approval? + word_matches_for_action?(:require_approval) + end + + def should_flag? + word_matches_for_action?(:flag) + end + + def should_block? + word_matches_for_action?(:block) + end + + def word_matches_for_action?(action) + r = self.class.word_matcher_regexp(action) + r ? r.match(@raw) : false + end + +end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index ec2d5398d93..e13e91344d8 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -3148,6 +3148,31 @@ en: logster: title: "Error Logs" + watched_words: + title: "Watched Words" + search: "search" + clear_filter: "Clear" + show_words: "show words" + word_count: + one: "1 word" + other: "%{count} words" + actions: + block: 'Block' + censor: 'Censor' + require_approval: 'Require Approval' + flag: 'Flag' + action_descriptions: + block: 'Prevent posts containing these words from being posted. The user will see an error message when they try to submit their post.' + censor: 'Allow posts containing these words, but replace them with characters that hide the censored words.' + require_approval: 'Posts containing these words will require approval by staff before they can be seen.' + flag: 'Allow posts containing these words, but flag them as inappropriate so moderators can review them.' + form: + label: 'New Word:' + add: 'Add' + success: 'Success' + upload: "Upload" + upload_successful: "Upload successful. Words have been added." + impersonate: title: "Impersonate" help: "Use this tool to impersonate a user account for debugging purposes. You will have to log out once finished." diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index e05db140c1f..516b639b20f 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -208,6 +208,7 @@ en: too_many_links: one: "Sorry, new users can only put one link in a post." other: "Sorry, new users can only put %{count} links in a post." + contains_blocked_words: "Your post contains words that aren't allowed." spamming_host: "Sorry you cannot post a link to that host." user_is_suspended: "Suspended users are not allowed to post." @@ -414,6 +415,10 @@ en: attributes: value: missing_interpolation_keys: 'The following interpolation key(s) are missing: "%{keys}"' + watched_word: + attributes: + word: + too_many: "Too many words for that action" user_profile: no_info_me: "
the About Me field of your profile is currently blank, would you like to fill it out?
" diff --git a/config/routes.rb b/config/routes.rb index a66dadb4895..b95fff1a39d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -270,6 +270,12 @@ Discourse::Application.routes.draw do get "dump_heap"=> "diagnostics#dump_heap", constraints: AdminConstraint.new get "dump_statement_cache"=> "diagnostics#dump_statement_cache", constraints: AdminConstraint.new + resources :watched_words, only: [:index, :create, :update, :destroy], constraints: AdminConstraint.new do + collection do + get "action/:id" => "watched_words#index" + end + end + post "watched_words/upload" => "watched_words#upload" end # admin namespace get "email_preferences" => "email#preferences_redirect", :as => "email_preferences_redirect" diff --git a/config/site_settings.yml b/config/site_settings.yml index 53ed6d41ea1..4e0dbdc5caa 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -555,11 +555,6 @@ posting: type: list client: true delete_old_hidden_posts: true - censored_words: - client: true - default: '' - refresh: true - type: list censored_pattern: client: true default: '' diff --git a/db/migrate/20170628152322_create_watched_words.rb b/db/migrate/20170628152322_create_watched_words.rb new file mode 100644 index 00000000000..212a3d2b9f6 --- /dev/null +++ b/db/migrate/20170628152322_create_watched_words.rb @@ -0,0 +1,11 @@ +class CreateWatchedWords < ActiveRecord::Migration + def change + create_table :watched_words do |t| + t.string :word, null: false + t.integer :action, null: false + t.timestamps + end + + add_index :watched_words, [:action, :word], unique: true + end +end diff --git a/lib/new_post_manager.rb b/lib/new_post_manager.rb index 537c7faff04..7d99a8b4ed6 100644 --- a/lib/new_post_manager.rb +++ b/lib/new_post_manager.rb @@ -1,6 +1,7 @@ require_dependency 'post_creator' require_dependency 'new_post_result' require_dependency 'post_enqueuer' +require_dependency 'word_watcher' # Determines what actions should be taken with new posts. # @@ -66,21 +67,25 @@ class NewPostManager end - def self.user_needs_approval?(manager) + def self.exempt_user?(user) + user.staff? || user.staged + end + + def self.post_needs_approval?(manager) user = manager.user - return false if user.staff? || user.staged + return false if exempt_user?(user) (user.trust_level <= TrustLevel.levels[:basic] && user.post_count < SiteSetting.approve_post_count) || (user.trust_level < SiteSetting.approve_unless_trust_level.to_i) || (manager.args[:title].present? && user.trust_level < SiteSetting.approve_new_topics_unless_trust_level.to_i) || is_fast_typer?(manager) || - matches_auto_block_regex?(manager) + matches_auto_block_regex?(manager) || + WordWatcher.new("#{manager.args[:title]} #{manager.args[:raw]}").requires_approval? end def self.default_handler(manager) - if user_needs_approval?(manager) - + if post_needs_approval?(manager) validator = Validators::PostValidator.new post = Post.new(raw: manager.args[:raw]) post.user = manager.user @@ -118,6 +123,7 @@ class NewPostManager SiteSetting.approve_post_count > 0 || SiteSetting.approve_unless_trust_level.to_i > 0 || SiteSetting.approve_new_topics_unless_trust_level.to_i > 0 || + WordWatcher.words_for_action_exists?(:require_approval) || handlers.size > 1 end @@ -127,8 +133,15 @@ class NewPostManager end def perform + if !self.class.exempt_user?(@user) && WordWatcher.new("#{@args[:title]} #{@args[:raw]}").should_block? + result = NewPostResult.new(:created_post, false) + result.errors[:base] << I18n.t('contains_blocked_words') + return result + end + # We never queue private messages return perform_create_post if @args[:archetype] == Archetype.private_message + if args[:topic_id] && Topic.where(id: args[:topic_id], archetype: Archetype.private_message).exists? return perform_create_post end diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index 831faa47b43..dea5948df57 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -273,6 +273,12 @@ class PostRevisor @post.word_count = @fields[:raw].scan(/[[:word:]]+/).size if @fields.has_key?(:raw) @post.self_edits += 1 if self_edit? + if !@post.acting_user.staff? && !@post.acting_user.staged && WordWatcher.new(@post.raw).should_block? + @post.errors[:base] << I18n.t('contains_blocked_words') + @post_successfully_saved = false + return + end + remove_flags_and_unhide_post @post.extract_quoted_post_numbers diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb index 69e60f1bed7..8c74bd6fe1f 100644 --- a/lib/pretty_text.rb +++ b/lib/pretty_text.rb @@ -166,6 +166,7 @@ module PrettyText __optInput.emojiUnicodeReplacer = __emojiUnicodeReplacer; __optInput.lookupInlineOnebox = __lookupInlineOnebox; #{opts[:linkify] == false ? "__optInput.linkify = false;": ""} + __optInput.censoredWords = #{WordWatcher.words_for_action(:censor).join('|').to_json}; JS if opts[:topicId] diff --git a/lib/validators/censored_words_validator.rb b/lib/validators/censored_words_validator.rb index 66e03f5bdd4..2fa5f0e0c0f 100644 --- a/lib/validators/censored_words_validator.rb +++ b/lib/validators/censored_words_validator.rb @@ -1,6 +1,6 @@ class CensoredWordsValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - if SiteSetting.censored_words.present? && (censored_words = censor_words(value, censored_words_regexp)).present? + if WordWatcher.words_for_action(:censor).present? && (censored_words = censor_words(value, censored_words_regexp)).present? record.errors.add( attribute, :contains_censored_words, censored_words: join_censored_words(censored_words) @@ -32,9 +32,6 @@ class CensoredWordsValidator < ActiveModel::EachValidator end def censored_words_regexp - Regexp.new( - '\b(' + SiteSetting.censored_words.split('|'.freeze).map! { |w| Regexp.escape(w) }.join('|'.freeze) + ')\b', - true - ) + WordWatcher.word_matcher_regexp :censor end end diff --git a/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js.es6 b/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js.es6 index 4087ea22b21..ab49a5a92cb 100644 --- a/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js.es6 +++ b/plugins/discourse-details/test/javascripts/lib/details-cooked-test.js.es6 @@ -8,8 +8,8 @@ const defaultOpts = buildOptions({ emoji_set: 'emoji_one', highlighted_languages: 'json|ruby|javascript', default_code_lang: 'auto', - censored_words: '' }, + censoredWords: 'shucks|whiz|whizzer', getURL: url => url }); diff --git a/spec/components/new_post_manager_spec.rb b/spec/components/new_post_manager_spec.rb index 3b38304d177..79d11707fd9 100644 --- a/spec/components/new_post_manager_spec.rb +++ b/spec/components/new_post_manager_spec.rb @@ -253,22 +253,22 @@ describe NewPostManager do - it "handles user_needs_approval? correctly" do + it "handles post_needs_approval? correctly" do u = user default = NewPostManager.new(u,{}) - expect(NewPostManager.user_needs_approval?(default)).to eq(false) + expect(NewPostManager.post_needs_approval?(default)).to eq(false) with_check = NewPostManager.new(u, first_post_checks: true) - expect(NewPostManager.user_needs_approval?(with_check)).to eq(true) + expect(NewPostManager.post_needs_approval?(with_check)).to eq(true) u.user_stat.post_count = 1 with_check_and_post = NewPostManager.new(u, first_post_checks: true) - expect(NewPostManager.user_needs_approval?(with_check_and_post)).to eq(false) + expect(NewPostManager.post_needs_approval?(with_check_and_post)).to eq(false) u.user_stat.post_count = 0 u.trust_level = 1 with_check_tl1 = NewPostManager.new(u, first_post_checks: true) - expect(NewPostManager.user_needs_approval?(with_check_tl1)).to eq(false) + expect(NewPostManager.post_needs_approval?(with_check_tl1)).to eq(false) end end diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index 72beb51030d..6e40972adeb 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -247,8 +247,9 @@ describe PrettyText do end it 'does censor code fences' do - SiteSetting.censored_words = 'apple|banana' + ['apple', 'banana'].each { |w| Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) } expect(PrettyText.cook("# banana")).not_to include('banana') + $redis.flushall end end @@ -787,11 +788,12 @@ HTML end it 'can censor words correctly' do - SiteSetting.censored_words = 'apple|banana' + ['apple', 'banana'].each { |w| Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) } expect(PrettyText.cook('yay banana yay')).not_to include('banana') expect(PrettyText.cook('yay `banana` yay')).not_to include('banana') expect(PrettyText.cook("# banana")).not_to include('banana') expect(PrettyText.cook("# banana")).to include("\u25a0\u25a0") + $redis.flushall end it 'supports typographer' do diff --git a/spec/fabricators/watched_word_fabricator.rb b/spec/fabricators/watched_word_fabricator.rb new file mode 100644 index 00000000000..f85547d8a2d --- /dev/null +++ b/spec/fabricators/watched_word_fabricator.rb @@ -0,0 +1,4 @@ +Fabricator(:watched_word) do + word { sequence(:word) { |i| "word#{i}"} } + action { WatchedWord.actions[:block] } +end diff --git a/spec/integration/watched_words_spec.rb b/spec/integration/watched_words_spec.rb new file mode 100644 index 00000000000..95787054048 --- /dev/null +++ b/spec/integration/watched_words_spec.rb @@ -0,0 +1,169 @@ +require 'rails_helper' + +describe WatchedWord do + let(:tl2_user) { Fabricate(:user, trust_level: TrustLevel[2]) } + let(:admin) { Fabricate(:admin) } + let(:moderator) { Fabricate(:moderator) } + + let(:topic) { Fabricate(:topic) } + let(:first_post) { Fabricate(:post, topic: topic) } + + let(:require_approval_word) { Fabricate(:watched_word, action: WatchedWord.actions[:require_approval]) } + let(:flag_word) { Fabricate(:watched_word, action: WatchedWord.actions[:flag]) } + let(:block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) } + + context "block" do + def should_block_post(manager) + expect { + result = manager.perform + expect(result).to_not be_success + expect(result.errors[:base]&.first).to eq(I18n.t('contains_blocked_words')) + }.to_not change { Post.count } + end + + it "should prevent the post from being created" do + manager = NewPostManager.new(tl2_user, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id) + should_block_post(manager) + end + + it "look at title too" do + manager = NewPostManager.new(tl2_user, title: "We sell #{block_word.word} online", raw: "Want some poutine for cheap?", topic_id: topic.id) + should_block_post(manager) + end + + it "should not block the post from admin" do + manager = NewPostManager.new(admin, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id) + result = manager.perform + expect(result).to be_success + expect(result.action).to eq(:create_post) + end + + it "should not block the post from moderator" do + manager = NewPostManager.new(moderator, raw: "Want some #{block_word.word} for cheap?", topic_id: topic.id) + result = manager.perform + expect(result).to be_success + expect(result.action).to eq(:create_post) + end + + it "should block in a private message too" do + manager = NewPostManager.new( + tl2_user, + raw: "Want some #{block_word.word} for cheap?", + title: 'this is a new title', + archetype: Archetype.private_message, + target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username + ) + should_block_post(manager) + end + + it "blocks on revisions" do + post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user) + expect { + PostRevisor.new(post).revise!(post.user, { raw: "Want some #{block_word.word} for cheap?" }, revised_at: post.updated_at + 10.seconds) + expect(post.errors).to be_present + post.reload + }.to_not change { post.raw } + end + end + + context "require_approval" do + it "should queue the post for approval" do + manager = NewPostManager.new(tl2_user, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id) + result = manager.perform + expect(result.action).to eq(:enqueued) + end + + it "looks at title too" do + manager = NewPostManager.new(tl2_user, title: "You won't believe these #{require_approval_word.word} dog names!", raw: "My dog's name is Porkins.", topic_id: topic.id) + result = manager.perform + expect(result.action).to eq(:enqueued) + end + + it "should not queue posts from admin" do + manager = NewPostManager.new(admin, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id) + result = manager.perform + expect(result).to be_success + expect(result.action).to eq(:create_post) + end + + it "should not queue posts from moderator" do + manager = NewPostManager.new(moderator, raw: "My dog's name is #{require_approval_word.word}.", topic_id: topic.id) + result = manager.perform + expect(result).to be_success + expect(result.action).to eq(:create_post) + end + + it "doesn't need approval in a private message" do + manager = NewPostManager.new( + tl2_user, + raw: "Want some #{require_approval_word.word} for cheap?", + title: 'this is a new title', + archetype: Archetype.private_message, + target_usernames: Fabricate(:user, trust_level: TrustLevel[2]).username + ) + result = manager.perform + expect(result).to be_success + expect(result.action).to eq(:create_post) + end + end + + context "flag" do + def should_flag_post(author, raw, topic) + post = Fabricate(:post, raw: raw, topic: topic, user: author) + expect { + Jobs::ProcessPost.new.execute(post_id: post.id) + }.to change { PostAction.count }.by(1) + expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true) + end + + def should_not_flag_post(author, raw, topic) + post = Fabricate(:post, raw: raw, topic: topic, user: author) + expect { + Jobs::ProcessPost.new.execute(post_id: post.id) + }.to_not change { PostAction.count } + end + + it "should flag the post as inappropriate" do + should_flag_post(tl2_user, "I thought the #{flag_word.word} was bad.", Fabricate(:topic, user: tl2_user)) + end + + it "should look at the title too" do + should_flag_post(tl2_user, "I thought the movie was not bad actually.", Fabricate(:topic, user: tl2_user, title: "Read my #{flag_word.word} review!")) + end + + it "shouldn't flag posts by admin" do + should_not_flag_post(admin, "I thought the #{flag_word.word} was bad.", Fabricate(:topic, user: admin)) + end + + it "shouldn't flag posts by moderator" do + should_not_flag_post(moderator, "I thought the #{flag_word.word} was bad.", Fabricate(:topic, user: moderator)) + end + + it "is compatible with flag_sockpuppets" do + # e.g., handle PostAction::AlreadyActed + SiteSetting.flag_sockpuppets = true + ip_address = '182.189.119.174' + user1 = Fabricate(:user, ip_address: ip_address, created_at: 2.days.ago) + user2 = Fabricate(:user, ip_address: ip_address) + first = create_post(user: user1, created_at: 2.days.ago) + sockpuppet_post = create_post(user: user2, topic: first.topic, raw: "I thought the #{flag_word.word} was bad.") + expect(PostAction.where(post_id: sockpuppet_post.id).count).to eq(1) + end + + it "flags in private message too" do + post = Fabricate(:private_message_post, raw: "Want some #{flag_word.word} for cheap?", user: tl2_user) + expect { + Jobs::ProcessPost.new.execute(post_id: post.id) + }.to change { PostAction.count }.by(1) + expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true) + end + + it "flags on revisions" do + post = Fabricate(:post, topic: Fabricate(:topic, user: tl2_user), user: tl2_user) + expect { + PostRevisor.new(post).revise!(post.user, { raw: "Want some #{flag_word.word} for cheap?" }, revised_at: post.updated_at + 10.seconds) + }.to change { PostAction.count }.by(1) + expect(PostAction.where(post_id: post.id, post_action_type_id: PostActionType.types[:inappropriate]).exists?).to eq(true) + end + end +end diff --git a/spec/models/topic_spec.rb b/spec/models/topic_spec.rb index c93d6d3e393..b4b43759df4 100644 --- a/spec/models/topic_spec.rb +++ b/spec/models/topic_spec.rb @@ -30,9 +30,13 @@ describe Topic do end describe 'censored words' do + after do + $redis.flushall + end + describe 'when title contains censored words' do it 'should not be valid' do - SiteSetting.censored_words = 'pineapple|pen' + ['pineapple', 'pen'].each { |w| Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) } topic.title = 'pen PinEapple apple pen is a complete sentence' @@ -46,7 +50,7 @@ describe Topic do describe 'titles with censored words not on boundaries' do it "should be valid" do - SiteSetting.censored_words = 'apple' + Fabricate(:watched_word, word: 'apple', action: WatchedWord.actions[:censor]) topic.title = "Pineapples are great fruit! Applebee's is a great restaurant" expect(topic).to be_valid end @@ -62,10 +66,12 @@ describe Topic do describe 'escape special characters in censored words' do before do - SiteSetting.censored_words = 'co(onut|coconut|a**le' + ['co(onut', 'coconut', 'a**le'].each do |w| + Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) + end end - it 'should not valid' do + it 'should not be valid' do topic.title = "I have a co(onut a**le" expect(topic.valid?).to eq(false) diff --git a/spec/models/watched_word_spec.rb b/spec/models/watched_word_spec.rb new file mode 100644 index 00000000000..7bc52d47e5e --- /dev/null +++ b/spec/models/watched_word_spec.rb @@ -0,0 +1,92 @@ +require 'rails_helper' + +describe WatchedWord do + + it "can't have duplicate words" do + Fabricate(:watched_word, word: "darn", action: described_class.actions[:block]) + w = Fabricate.build(:watched_word, word: "darn", action: described_class.actions[:block]) + expect(w.save).to eq(false) + w = Fabricate.build(:watched_word, word: "darn", action: described_class.actions[:flag]) + expect(w.save).to eq(false) + expect(described_class.count).to eq(1) + end + + it "downcases words" do + expect(described_class.create(word: "ShooT").word).to eq('shoot') + end + + it "strips leading and trailing spaces" do + expect(described_class.create(word: " poutine ").word).to eq('poutine') + end + + it "squeezes multiple asterisks" do + expect(described_class.create(word: "a**les").word).to eq('a*les') + end + + describe "action_key=" do + let(:w) { WatchedWord.new(word: "troll") } + + it "sets action attr from symbol" do + described_class.actions.keys.each do |k| + w.action_key = k + expect(w.action).to eq(described_class.actions[k]) + end + end + + it "sets action attr from string" do + described_class.actions.keys.each do |k| + w.action_key = k.to_s + expect(w.action).to eq(described_class.actions[k]) + end + end + + it "sets error for invalid key" do + w.action_key = "shame" + expect(w).to_not be_valid + expect(w.errors[:action]).to be_present + end + end + + describe '#create_or_update_word' do + it "can create a new record" do + expect { + w = described_class.create_or_update_word(word: 'nickelback', action_key: :block) + expect(w.reload.action).to eq(described_class.actions[:block]) + }.to change { described_class.count }.by(1) + end + + it "can update an existing record with different action" do + existing = Fabricate(:watched_word, action: described_class.actions[:flag]) + expect { + w = described_class.create_or_update_word(word: existing.word, action_key: :block) + expect(w.reload.action).to eq(described_class.actions[:block]) + expect(w.id).to eq(existing.id) + }.to_not change { described_class.count } + end + + it "doesn't error for existing record with same action" do + existing = Fabricate(:watched_word, action: described_class.actions[:flag], created_at: 1.day.ago, updated_at: 1.day.ago) + expect { + w = described_class.create_or_update_word(word: existing.word, action_key: :flag) + expect(w.id).to eq(existing.id) + expect(w.updated_at).to eq(w.updated_at) + }.to_not change { described_class.count } + end + + it "allows action param instead of action_key" do + expect { + w = described_class.create_or_update_word(word: 'nickelback', action: described_class.actions[:block]) + expect(w.reload.action).to eq(described_class.actions[:block]) + }.to change { described_class.count }.by(1) + end + + it "normalizes input" do + existing = Fabricate(:watched_word, action: described_class.actions[:flag]) + expect { + w = described_class.create_or_update_word(word: " #{existing.word.upcase} ", action_key: :block) + expect(w.reload.action).to eq(described_class.actions[:block]) + expect(w.id).to eq(existing.id) + }.to_not change { described_class.count } + end + end +end diff --git a/spec/services/word_watcher_spec.rb b/spec/services/word_watcher_spec.rb new file mode 100644 index 00000000000..98c2776d8ee --- /dev/null +++ b/spec/services/word_watcher_spec.rb @@ -0,0 +1,53 @@ +require 'rails_helper' + +describe WordWatcher do + + let(:raw) { "Do you like liquorice?\n\nI really like them. One could even say that I am *addicted* to liquorice. Anf if\nyou can mix it up with some anise, then I'm in heaven ;)" } + + after do + $redis.flushall + end + + describe "word_matches_for_action?" do + it "is falsey when there are no watched words" do + expect(WordWatcher.new(raw).word_matches_for_action?(:require_approval)).to be_falsey + end + + context "with watched words" do + let!(:anise) { Fabricate(:watched_word, word: "anise", action: WatchedWord.actions[:require_approval]) } + + it "is falsey without a match" do + expect(WordWatcher.new("No liquorice for me, thanks...").word_matches_for_action?(:require_approval)).to be_falsey + end + + it "is returns matched words if there's a match" do + m = WordWatcher.new(raw).word_matches_for_action?(:require_approval) + expect(m).to be_truthy + expect(m[1]).to eq(anise.word) + end + + it "finds at start of string" do + m = WordWatcher.new("#{anise.word} is garbage").word_matches_for_action?(:require_approval) + expect(m[1]).to eq(anise.word) + end + + it "finds at end of string" do + m = WordWatcher.new("who likes #{anise.word}").word_matches_for_action?(:require_approval) + expect(m[1]).to eq(anise.word) + end + + it "finds non-letters in place of letters" do + Fabricate(:watched_word, word: "co(onut", action: WatchedWord.actions[:require_approval]) + m = WordWatcher.new("This co(onut is delicious.").word_matches_for_action?(:require_approval) + expect(m[1]).to eq("co(onut") + end + + it "handles * for wildcards" do + Fabricate(:watched_word, word: "a**le*", action: WatchedWord.actions[:require_approval]) + m = WordWatcher.new("I acknowledge you.").word_matches_for_action?(:require_approval) + expect(m[1]).to eq("acknowledge") + end + end + end + +end diff --git a/test/javascripts/acceptance/admin-watched-words-test.js.es6 b/test/javascripts/acceptance/admin-watched-words-test.js.es6 new file mode 100644 index 00000000000..ac24215f56f --- /dev/null +++ b/test/javascripts/acceptance/admin-watched-words-test.js.es6 @@ -0,0 +1,68 @@ +import { acceptance } from "helpers/qunit-helpers"; +acceptance("Admin - Watched Words", { loggedIn: true }); + +QUnit.test("list words in groups", assert => { + visit("/admin/watched_words/action/block"); + andThen(() => { + assert.ok(exists('.watched-words-list')); + assert.ok(!exists('.watched-words-list .watched-word'), "Don't show bad words by default."); + }); + + fillIn('.admin-controls .controls input[type=text]', 'li'); + andThen(() => { + assert.equal(find('.watched-words-list .watched-word').length, 1, "When filtering, show words even if checkbox is unchecked."); + }); + + fillIn('.admin-controls .controls input[type=text]', ''); + andThen(() => { + assert.ok(!exists('.watched-words-list .watched-word'), "Clearing the filter hides words again."); + }); + + click('.show-words-checkbox'); + andThen(() => { + assert.ok(exists('.watched-words-list .watched-word'), "Always show the words when checkbox is checked."); + }); + + click('.nav-stacked .censor'); + andThen(() => { + assert.ok(exists('.watched-words-list')); + assert.ok(!exists('.watched-words-list .watched-word'), "Empty word list."); + }); +}); + +QUnit.test("add words", assert => { + visit("/admin/watched_words/action/block"); + andThen(() => { + click('.show-words-checkbox'); + fillIn('.watched-word-form input', 'poutine'); + }); + click('.watched-word-form button'); + andThen(() => { + let found = []; + _.each(find('.watched-words-list .watched-word'), i => { + if ($(i).text().trim() === 'poutine') { + found.push(true); + } + }); + assert.equal(found.length, 1); + }); +}); + +QUnit.test("remove words", assert => { + visit("/admin/watched_words/action/block"); + click('.show-words-checkbox'); + + let word = null; + andThen(() => { + _.each(find('.watched-words-list .watched-word'), i => { + if ($(i).text().trim() === 'anise') { + word = i; + } + }); + click('#' + $(word).attr('id')); + }); + andThen(() => { + assert.equal(find('.watched-words-list .watched-word').length, 1); + }); +}); + diff --git a/test/javascripts/fixtures/watched-words-fixtures.js.es6 b/test/javascripts/fixtures/watched-words-fixtures.js.es6 new file mode 100644 index 00000000000..e73505f1851 --- /dev/null +++ b/test/javascripts/fixtures/watched-words-fixtures.js.es6 @@ -0,0 +1,12 @@ +export default { + "/admin/watched_words.json": { + "actions": ["block", "censor", "require_approval", "flag"], + "words": [ + {id: 1, word: "liquorice", action: "block"}, + {id: 2, word: "anise", action: "block"}, + {id: 3, word: "pyramid", action: "flag"}, + {id: 4, word: "scheme", action: "flag"}, + {id: 5, word: "coupon", action: "require_approval"} + ] + } +}; diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6 index bfa32ba4af4..2b5141ce9ed 100644 --- a/test/javascripts/helpers/create-pretender.js.es6 +++ b/test/javascripts/helpers/create-pretender.js.es6 @@ -334,6 +334,17 @@ export default function() { this.post('/admin/badges', success); this.delete('/admin/badges/:id', success); + this.get('/admin/watched_words', () => { + return response(200, fixturesByUrl['/admin/watched_words.json']); + }); + this.delete('/admin/watched_words/:id.json', success); + + this.post('/admin/watched_words.json', request => { + const result = parsePostData(request.requestBody); + result.id = new Date().getTime(); + return response(200, result); + }); + this.get('/onebox', request => { if (request.queryParams.url === 'http://www.example.com/has-title.html' || request.queryParams.url === 'http://www.example.com/has-title-and-a-url-that-is-more-than-80-characters-because-thats-good-for-seo-i-guess.html') { diff --git a/test/javascripts/lib/pretty-text-test.js.es6 b/test/javascripts/lib/pretty-text-test.js.es6 index 8594976b73a..d53fcc346e1 100644 --- a/test/javascripts/lib/pretty-text-test.js.es6 +++ b/test/javascripts/lib/pretty-text-test.js.es6 @@ -11,9 +11,9 @@ const rawOpts = { emoji_set: 'emoji_one', highlighted_languages: 'json|ruby|javascript', default_code_lang: 'auto', - censored_words: 'shucks|whiz|whizzer|a**le', censored_pattern: '\\d{3}-\\d{4}|tech\\w*' }, + censoredWords: 'shucks|whiz|whizzer|a**le', getURL: url => url }; From 68d09e831557d8bbf67488c726a34a481ce740b5 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 26 Jul 2017 12:41:19 -0400 Subject: [PATCH 04/59] noturbo bench added --- script/benchmarks/markdown/bench.rb | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/script/benchmarks/markdown/bench.rb b/script/benchmarks/markdown/bench.rb index 4f70fa6dc48..a69318ec19d 100644 --- a/script/benchmarks/markdown/bench.rb +++ b/script/benchmarks/markdown/bench.rb @@ -1,6 +1,9 @@ require 'benchmark/ips' require File.expand_path('../../../../config/environment', __FILE__) +# set any flags here +# MiniRacer::Platform.set_flags! :noturbo + tests = [ ["tiny post", "**hello**"], @@ -100,3 +103,32 @@ end # 1.410k (± 6.5%) i/s - 7.112k in 5.070053s # markdown it no extensions commonmark lots of mentions # 1.934k (± 6.4%) i/s - 9.672k in 5.025858s +# +# v8 noturbo 5.9 +# +# +# tiny post sanitize: true +# 105.152 (±17.1%) i/s - 512.000 in 5.034419s +# giant post sanitize: true +# 97.002 (±12.4%) i/s - 480.000 in 5.038382s +# most features sanitize: true +# 46.355 (±12.9%) i/s - 228.000 in 5.009251s +# lots of mentions sanitize: true +# 0.278 (± 0.0%) i/s - 2.000 in 7.205837s +# tiny post sanitize: false +# 201.166 (±13.4%) i/s - 990.000 in 5.017725s +# giant post sanitize: false +# 174.212 (±10.9%) i/s - 867.000 in 5.040859s +# most features sanitize: false +# 60.272 (±14.9%) i/s - 295.000 in 5.029353s +# lots of mentions sanitize: false +# 0.309 (± 0.0%) i/s - 2.000 in 6.483433s +# markdown it no extensions commonmark tiny post +# 6.331k (±13.8%) i/s - 31.065k in 5.023613s +# markdown it no extensions commonmark giant post +# 1.045k (± 9.6%) i/s - 5.208k in 5.053733s +# markdown it no extensions commonmark most features +# 1.448k (± 6.7%) i/s - 7.239k in 5.024831s +# markdown it no extensions commonmark lots of mentions +# 1.986k (± 5.2%) i/s - 9.990k in 5.044624s + From 2c6ed64ebe81da2dcd4e103f4534e0ecd3ad5798 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Wed, 26 Jul 2017 17:54:56 +0100 Subject: [PATCH 05/59] Spawn a new rake process to run tests, so that LOAD_PLUGINS gets re-checked (#5001) --- lib/tasks/plugin.rake | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/tasks/plugin.rake b/lib/tasks/plugin.rake index 9666ed747c4..99f15cb517f 100644 --- a/lib/tasks/plugin.rake +++ b/lib/tasks/plugin.rake @@ -101,14 +101,20 @@ desc 'run plugin qunit tests' task 'plugin:qunit', [:plugin, :timeout] do |t, args| args.with_defaults(plugin: "*") - ENV['LOAD_PLUGINS'] = '1' - ENV['QUNIT_SKIP_CORE'] = '1' + rake = `which rake`.strip + + cmd = 'LOAD_PLUGINS=1 ' + cmd += 'QUNIT_SKIP_CORE=1 ' + if args[:plugin] == "*" puts "Running qunit tests for all plugins" else puts "Running qunit tests for #{args[:plugin]}" - ENV['QUNIT_SINGLE_PLUGIN'] = args[:plugin] + cmd += "QUNIT_SINGLE_PLUGIN='#{args[:plugin]}' " end - Rake::Task["qunit:test"].invoke(args[:timeout]) + cmd += "#{rake} qunit:test" + cmd += "[#{args[:timeout]}" if args[:timeout] + + sh cmd end From 2d41c5ed3c0cf14fd61c16d4c9f2b8fc12776650 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 26 Jul 2017 13:11:08 -0400 Subject: [PATCH 06/59] missing bracket --- lib/tasks/plugin.rake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tasks/plugin.rake b/lib/tasks/plugin.rake index 99f15cb517f..51c674132f0 100644 --- a/lib/tasks/plugin.rake +++ b/lib/tasks/plugin.rake @@ -105,16 +105,16 @@ task 'plugin:qunit', [:plugin, :timeout] do |t, args| cmd = 'LOAD_PLUGINS=1 ' cmd += 'QUNIT_SKIP_CORE=1 ' - + if args[:plugin] == "*" puts "Running qunit tests for all plugins" else puts "Running qunit tests for #{args[:plugin]}" cmd += "QUNIT_SINGLE_PLUGIN='#{args[:plugin]}' " end - + cmd += "#{rake} qunit:test" - cmd += "[#{args[:timeout]}" if args[:timeout] + cmd += "[#{args[:timeout]}]" if args[:timeout] sh cmd end From e87125b63c7e7b5d763317bfcdfdd214f74c7492 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 26 Jul 2017 12:13:49 -0400 Subject: [PATCH 07/59] FEATURE: Plugins can overwrite and add support for new icons --- .../components/admin-directory-toggle.js.es6 | 2 +- .../components/admin-web-hook-status.js.es6 | 2 +- .../discourse-common/helpers/fa-icon.js.es6 | 21 +----- .../discourse-common/lib/icon-library.js.es6 | 68 +++++++++++++++++++ .../categories-admin-dropdown.js.es6 | 2 +- .../components/directory-toggle.js.es6 | 2 +- .../discourse/components/global-notice.js.es6 | 2 +- .../components/group-index-toggle.js.es6 | 2 +- .../discourse/components/input-tip.js.es6 | 2 +- .../components/notifications-button.js.es6 | 2 +- .../components/popup-input-tip.js.es6 | 2 +- .../components/tags-admin-dropdown.js.es6 | 2 +- .../topic-footer-mobile-dropdown.js.es6 | 2 +- .../discourse/components/topic-status.js.es6 | 2 +- .../controllers/full-page-search.js.es6 | 2 +- .../discourse/helpers/category-link.js.es6 | 2 +- .../discourse/helpers/fa-icon-node.js.es6 | 21 +----- .../discourse/helpers/user-status.js.es6 | 2 +- .../discourse/lib/plugin-api.js.es6 | 29 +++++++- .../discourse/widgets/actions-summary.js.es6 | 2 +- .../discourse/widgets/button.js.es6 | 2 +- .../discourse/widgets/embedded-post.js.es6 | 2 +- .../widgets/header-topic-info.js.es6 | 2 +- .../discourse/widgets/header.js.es6 | 2 +- .../discourse/widgets/home-logo.js.es6 | 2 +- .../javascripts/discourse/widgets/link.js.es6 | 2 +- .../discourse/widgets/post-admin-menu.js.es6 | 2 +- .../widgets/post-edits-indicator.js.es6 | 2 +- .../discourse/widgets/post-links.js.es6 | 2 +- .../widgets/post-small-action.js.es6 | 2 +- .../javascripts/discourse/widgets/post.js.es6 | 2 +- .../discourse/widgets/poster-name.js.es6 | 2 +- .../widgets/private-message-map.js.es6 | 2 +- .../widgets/search-menu-results.js.es6 | 2 +- .../discourse/widgets/time-gap.js.es6 | 2 +- .../discourse/widgets/topic-status.js.es6 | 2 +- .../discourse/widgets/topic-timeline.js.es6 | 2 +- .../javascripts/widgets/discourse-poll.js.es6 | 2 +- 38 files changed, 136 insertions(+), 71 deletions(-) create mode 100644 app/assets/javascripts/discourse-common/lib/icon-library.js.es6 diff --git a/app/assets/javascripts/admin/components/admin-directory-toggle.js.es6 b/app/assets/javascripts/admin/components/admin-directory-toggle.js.es6 index e4d19613b53..8d43474a9d5 100644 --- a/app/assets/javascripts/admin/components/admin-directory-toggle.js.es6 +++ b/app/assets/javascripts/admin/components/admin-directory-toggle.js.es6 @@ -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'; export default Ember.Component.extend(bufferedRender({ diff --git a/app/assets/javascripts/admin/components/admin-web-hook-status.js.es6 b/app/assets/javascripts/admin/components/admin-web-hook-status.js.es6 index d176b5a40ab..c5f1a4a2447 100644 --- a/app/assets/javascripts/admin/components/admin-web-hook-status.js.es6 +++ b/app/assets/javascripts/admin/components/admin-web-hook-status.js.es6 @@ -1,5 +1,5 @@ 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'; export default Ember.Component.extend(bufferedRender({ diff --git a/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6 b/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6 index 3c1f8b9b5ab..b8dc58f23a3 100644 --- a/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6 +++ b/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6 @@ -1,23 +1,8 @@ import { registerUnbound } from 'discourse-common/lib/helpers'; +import { renderIcon } from 'discourse-common/lib/icon-library'; -export function iconClasses(icon, params) { - let classes = "fa fa-" + icon; - if (params.modifier) { classes += " fa-" + params.modifier; } - if (params['class']) { classes += ' ' + params['class']; } - return classes; -} - -export function iconHTML(icon, params) { - params = params || {}; - - var html = ""; - } - return html; +export function iconHTML(id, params) { + return renderIcon('string', id, params); } registerUnbound('fa-icon', function(icon, params) { diff --git a/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 b/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 new file mode 100644 index 00000000000..04d4629d16d --- /dev/null +++ b/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 @@ -0,0 +1,68 @@ +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}`; + 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 html = `"; + } + return html; + }, + + node(id, params) { + const properties = { + className: faClasses(id, params), + attributes: { "aria-hidden": true } + }; + + if (params.title) { properties.attributes.title = params.title; } + if (params.label) { + return h('i', properties, h('span.sr-only', I18n.t(params.label))); + } else { + return h('i', properties); + } + } +}); diff --git a/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 b/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 index 8dbb4d34231..52eca3fce33 100644 --- a/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 +++ b/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 @@ -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 computed from "ember-addons/ember-computed-decorators"; diff --git a/app/assets/javascripts/discourse/components/directory-toggle.js.es6 b/app/assets/javascripts/discourse/components/directory-toggle.js.es6 index 3f55ec0543a..8da529957a4 100644 --- a/app/assets/javascripts/discourse/components/directory-toggle.js.es6 +++ b/app/assets/javascripts/discourse/components/directory-toggle.js.es6 @@ -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'; export default Ember.Component.extend(bufferedRender({ diff --git a/app/assets/javascripts/discourse/components/global-notice.js.es6 b/app/assets/javascripts/discourse/components/global-notice.js.es6 index d3ae4235484..06f7e68a166 100644 --- a/app/assets/javascripts/discourse/components/global-notice.js.es6 +++ b/app/assets/javascripts/discourse/components/global-notice.js.es6 @@ -1,5 +1,5 @@ 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 { bufferedRender } from 'discourse-common/lib/buffered-render'; diff --git a/app/assets/javascripts/discourse/components/group-index-toggle.js.es6 b/app/assets/javascripts/discourse/components/group-index-toggle.js.es6 index fb7c9d24c50..f5b24684087 100644 --- a/app/assets/javascripts/discourse/components/group-index-toggle.js.es6 +++ b/app/assets/javascripts/discourse/components/group-index-toggle.js.es6 @@ -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'; export default Ember.Component.extend(bufferedRender({ diff --git a/app/assets/javascripts/discourse/components/input-tip.js.es6 b/app/assets/javascripts/discourse/components/input-tip.js.es6 index 0178ad7cac5..4120544afeb 100644 --- a/app/assets/javascripts/discourse/components/input-tip.js.es6 +++ b/app/assets/javascripts/discourse/components/input-tip.js.es6 @@ -1,5 +1,5 @@ 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({ classNameBindings: [':tip', 'good', 'bad'], diff --git a/app/assets/javascripts/discourse/components/notifications-button.js.es6 b/app/assets/javascripts/discourse/components/notifications-button.js.es6 index d01f17e43d5..7983e535664 100644 --- a/app/assets/javascripts/discourse/components/notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/components/notifications-button.js.es6 @@ -1,6 +1,6 @@ import DropdownButton from 'discourse/components/dropdown-button'; 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'; export default DropdownButton.extend({ diff --git a/app/assets/javascripts/discourse/components/popup-input-tip.js.es6 b/app/assets/javascripts/discourse/components/popup-input-tip.js.es6 index 59e5856db09..e3e27621f93 100644 --- a/app/assets/javascripts/discourse/components/popup-input-tip.js.es6 +++ b/app/assets/javascripts/discourse/components/popup-input-tip.js.es6 @@ -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 { bufferedRender } from 'discourse-common/lib/buffered-render'; diff --git a/app/assets/javascripts/discourse/components/tags-admin-dropdown.js.es6 b/app/assets/javascripts/discourse/components/tags-admin-dropdown.js.es6 index 6d1c7ae7b31..f06174d0886 100644 --- a/app/assets/javascripts/discourse/components/tags-admin-dropdown.js.es6 +++ b/app/assets/javascripts/discourse/components/tags-admin-dropdown.js.es6 @@ -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 computed from "ember-addons/ember-computed-decorators"; diff --git a/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 b/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 index d5370e416e8..0e2329fb4c4 100644 --- a/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-footer-mobile-dropdown.js.es6 @@ -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 { observes } from 'ember-addons/ember-computed-decorators'; diff --git a/app/assets/javascripts/discourse/components/topic-status.js.es6 b/app/assets/javascripts/discourse/components/topic-status.js.es6 index bede7d57a6a..6725b1d9d7b 100644 --- a/app/assets/javascripts/discourse/components/topic-status.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-status.js.es6 @@ -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 { escapeExpression } from 'discourse/lib/utilities'; diff --git a/app/assets/javascripts/discourse/controllers/full-page-search.js.es6 b/app/assets/javascripts/discourse/controllers/full-page-search.js.es6 index d85760f6325..f508479b83d 100644 --- a/app/assets/javascripts/discourse/controllers/full-page-search.js.es6 +++ b/app/assets/javascripts/discourse/controllers/full-page-search.js.es6 @@ -4,7 +4,7 @@ import { default as computed, observes } from 'ember-addons/ember-computed-decor import Category from 'discourse/models/category'; import { escapeExpression } from 'discourse/lib/utilities'; import { setTransient } from 'discourse/lib/page-tracker'; -import { iconHTML } from 'discourse-common/helpers/fa-icon'; +import { iconHTML } from 'discourse-common/lib/icon-library'; const SortOrders = [ {name: I18n.t('search.relevance'), id: 0}, diff --git a/app/assets/javascripts/discourse/helpers/category-link.js.es6 b/app/assets/javascripts/discourse/helpers/category-link.js.es6 index 618eca3211a..87aa1f7d77b 100644 --- a/app/assets/javascripts/discourse/helpers/category-link.js.es6 +++ b/app/assets/javascripts/discourse/helpers/category-link.js.es6 @@ -1,5 +1,5 @@ import { registerUnbound } from 'discourse-common/lib/helpers'; -import { iconHTML } from 'discourse-common/helpers/fa-icon'; +import { iconHTML } from 'discourse-common/lib/icon-library'; var get = Em.get, escapeExpression = Handlebars.Utils.escapeExpression; diff --git a/app/assets/javascripts/discourse/helpers/fa-icon-node.js.es6 b/app/assets/javascripts/discourse/helpers/fa-icon-node.js.es6 index 84f15420258..9b5d989f5a0 100644 --- a/app/assets/javascripts/discourse/helpers/fa-icon-node.js.es6 +++ b/app/assets/javascripts/discourse/helpers/fa-icon-node.js.es6 @@ -1,20 +1,5 @@ -import { h } from 'virtual-dom'; -import { iconClasses } from 'discourse-common/helpers/fa-icon'; +import { renderIcon } from 'discourse-common/lib/icon-library'; -export function iconNode(icon, params) { - params = params || {}; - - const properties = { - className: iconClasses(icon, params), - attributes: { "aria-hidden": true } - }; - - if (params.title) { properties.attributes.title = params.title; } - - if (params.label) { - return h('i', properties, h('span.sr-only', I18n.t(params.label))); - } else { - return h('i', properties); - } +export function iconNode(id, params) { + return renderIcon('node', id, params); } - diff --git a/app/assets/javascripts/discourse/helpers/user-status.js.es6 b/app/assets/javascripts/discourse/helpers/user-status.js.es6 index fb1a05efeed..c1b64fe8335 100644 --- a/app/assets/javascripts/discourse/helpers/user-status.js.es6 +++ b/app/assets/javascripts/discourse/helpers/user-status.js.es6 @@ -1,4 +1,4 @@ -import { iconHTML } from 'discourse-common/helpers/fa-icon'; +import { iconHTML } from 'discourse-common/lib/icon-library'; import { htmlHelper } from 'discourse-common/lib/helpers'; import { escapeExpression } from 'discourse/lib/utilities'; diff --git a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 b/app/assets/javascripts/discourse/lib/plugin-api.js.es6 index 5d382e4ac04..238e0275999 100644 --- a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 +++ b/app/assets/javascripts/discourse/lib/plugin-api.js.es6 @@ -1,4 +1,4 @@ -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { addDecorator } from 'discourse/widgets/post-cooked'; import ComposerEditor from 'discourse/components/composer-editor'; import { addButton } from 'discourse/widgets/post-menu'; @@ -19,6 +19,7 @@ import { addUserMenuGlyph } from 'discourse/widgets/user-menu'; import { addPostClassesCallback } from 'discourse/widgets/post'; import { addPostTransformCallback } from 'discourse/widgets/post-stream'; import { attachAdditionalPanel } from 'discourse/widgets/header'; +import { registerIconRenderer } from 'discourse-common/lib/icon-library'; // If you add any methods to the API ensure you bump up this number @@ -58,6 +59,32 @@ class PluginApi { return klass; } + /** + * If you want to use custom icons in your discourse application, + * you can register a renderer that will return an icon in the + * format required. + * + * For example, the follwing resolver will render a smile in the place + * of every icon on Discourse. + * + * api.registerIconRenderer({ + * name: 'smile-icons', + * + * // for the place in code that render a string + * string() { + * return ""; + * }, + * + * // for the places in code that render virtual dom elements + * node() { + * return h('i', { className: 'fa fa-smile-o' }); + * } + * }); + **/ + registerIconRenderer(fn) { + registerIconRenderer(fn); + } + /** * Used for decorating the `cooked` content of a post after it is rendered using * jQuery. diff --git a/app/assets/javascripts/discourse/widgets/actions-summary.js.es6 b/app/assets/javascripts/discourse/widgets/actions-summary.js.es6 index df852621b99..0e8212f35ad 100644 --- a/app/assets/javascripts/discourse/widgets/actions-summary.js.es6 +++ b/app/assets/javascripts/discourse/widgets/actions-summary.js.es6 @@ -1,6 +1,6 @@ import { createWidget } from 'discourse/widgets/widget'; import { avatarFor } from 'discourse/widgets/post'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { h } from 'virtual-dom'; import { dateNode } from 'discourse/helpers/node'; import { userPath } from 'discourse/lib/url'; diff --git a/app/assets/javascripts/discourse/widgets/button.js.es6 b/app/assets/javascripts/discourse/widgets/button.js.es6 index 0fd705e7974..ece396abaa3 100644 --- a/app/assets/javascripts/discourse/widgets/button.js.es6 +++ b/app/assets/javascripts/discourse/widgets/button.js.es6 @@ -1,5 +1,5 @@ import { createWidget } from 'discourse/widgets/widget'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; const ButtonClass = { diff --git a/app/assets/javascripts/discourse/widgets/embedded-post.js.es6 b/app/assets/javascripts/discourse/widgets/embedded-post.js.es6 index 6f572388ab1..11036d85413 100644 --- a/app/assets/javascripts/discourse/widgets/embedded-post.js.es6 +++ b/app/assets/javascripts/discourse/widgets/embedded-post.js.es6 @@ -2,7 +2,7 @@ import PostCooked from 'discourse/widgets/post-cooked'; import DecoratorHelper from 'discourse/widgets/decorator-helper'; import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import DiscourseURL from 'discourse/lib/url'; createWidget('post-link-arrow', { diff --git a/app/assets/javascripts/discourse/widgets/header-topic-info.js.es6 b/app/assets/javascripts/discourse/widgets/header-topic-info.js.es6 index 8a083df6608..65ae654c5df 100644 --- a/app/assets/javascripts/discourse/widgets/header-topic-info.js.es6 +++ b/app/assets/javascripts/discourse/widgets/header-topic-info.js.es6 @@ -1,6 +1,6 @@ import { applyDecorators, createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import DiscourseURL from 'discourse/lib/url'; import RawHtml from 'discourse/widgets/raw-html'; import renderTags from 'discourse/lib/render-tags'; diff --git a/app/assets/javascripts/discourse/widgets/header.js.es6 b/app/assets/javascripts/discourse/widgets/header.js.es6 index 4c8df62ae5c..600bd753084 100644 --- a/app/assets/javascripts/discourse/widgets/header.js.es6 +++ b/app/assets/javascripts/discourse/widgets/header.js.es6 @@ -1,5 +1,5 @@ import { createWidget } from 'discourse/widgets/widget'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { avatarImg } from 'discourse/widgets/post'; import DiscourseURL from 'discourse/lib/url'; import { wantsNewWindow } from 'discourse/lib/intercept-click'; diff --git a/app/assets/javascripts/discourse/widgets/home-logo.js.es6 b/app/assets/javascripts/discourse/widgets/home-logo.js.es6 index ad4c59b26a0..c226d944eb5 100644 --- a/app/assets/javascripts/discourse/widgets/home-logo.js.es6 +++ b/app/assets/javascripts/discourse/widgets/home-logo.js.es6 @@ -1,6 +1,6 @@ import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { wantsNewWindow } from 'discourse/lib/intercept-click'; import DiscourseURL from 'discourse/lib/url'; diff --git a/app/assets/javascripts/discourse/widgets/link.js.es6 b/app/assets/javascripts/discourse/widgets/link.js.es6 index 4f6beef6bf2..c6426a86238 100644 --- a/app/assets/javascripts/discourse/widgets/link.js.es6 +++ b/app/assets/javascripts/discourse/widgets/link.js.es6 @@ -1,6 +1,6 @@ import { wantsNewWindow } from 'discourse/lib/intercept-click'; import { createWidget } from 'discourse/widgets/widget'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { h } from 'virtual-dom'; import DiscourseURL from 'discourse/lib/url'; diff --git a/app/assets/javascripts/discourse/widgets/post-admin-menu.js.es6 b/app/assets/javascripts/discourse/widgets/post-admin-menu.js.es6 index eb868b03439..4fb293745a0 100644 --- a/app/assets/javascripts/discourse/widgets/post-admin-menu.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-admin-menu.js.es6 @@ -1,4 +1,4 @@ -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; diff --git a/app/assets/javascripts/discourse/widgets/post-edits-indicator.js.es6 b/app/assets/javascripts/discourse/widgets/post-edits-indicator.js.es6 index 531e4396850..7d8f4928071 100644 --- a/app/assets/javascripts/discourse/widgets/post-edits-indicator.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-edits-indicator.js.es6 @@ -1,5 +1,5 @@ import { createWidget } from 'discourse/widgets/widget'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { longDate } from 'discourse/lib/formatter'; import { h } from 'virtual-dom'; diff --git a/app/assets/javascripts/discourse/widgets/post-links.js.es6 b/app/assets/javascripts/discourse/widgets/post-links.js.es6 index 91821914513..69340bc86b2 100644 --- a/app/assets/javascripts/discourse/widgets/post-links.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-links.js.es6 @@ -1,4 +1,4 @@ -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; import { replaceEmoji } from 'discourse/widgets/emoji'; diff --git a/app/assets/javascripts/discourse/widgets/post-small-action.js.es6 b/app/assets/javascripts/discourse/widgets/post-small-action.js.es6 index b128b246ce6..680a6259116 100644 --- a/app/assets/javascripts/discourse/widgets/post-small-action.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-small-action.js.es6 @@ -1,6 +1,6 @@ import { createWidget } from 'discourse/widgets/widget'; import RawHtml from 'discourse/widgets/raw-html'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { h } from 'virtual-dom'; import { actionDescriptionHtml } from 'discourse/components/small-action'; import { avatarFor } from 'discourse/widgets/post'; diff --git a/app/assets/javascripts/discourse/widgets/post.js.es6 b/app/assets/javascripts/discourse/widgets/post.js.es6 index c6c9f581f4d..2652b4954af 100644 --- a/app/assets/javascripts/discourse/widgets/post.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post.js.es6 @@ -1,7 +1,7 @@ import PostCooked from 'discourse/widgets/post-cooked'; import DecoratorHelper from 'discourse/widgets/decorator-helper'; import { createWidget, applyDecorators } from 'discourse/widgets/widget'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { transformBasicPost } from 'discourse/lib/transform-post'; import { h } from 'virtual-dom'; import DiscourseURL from 'discourse/lib/url'; diff --git a/app/assets/javascripts/discourse/widgets/poster-name.js.es6 b/app/assets/javascripts/discourse/widgets/poster-name.js.es6 index f514b9ac8bf..2bf64d738db 100644 --- a/app/assets/javascripts/discourse/widgets/poster-name.js.es6 +++ b/app/assets/javascripts/discourse/widgets/poster-name.js.es6 @@ -1,4 +1,4 @@ -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; diff --git a/app/assets/javascripts/discourse/widgets/private-message-map.js.es6 b/app/assets/javascripts/discourse/widgets/private-message-map.js.es6 index 1c43153798a..60441867e17 100644 --- a/app/assets/javascripts/discourse/widgets/private-message-map.js.es6 +++ b/app/assets/javascripts/discourse/widgets/private-message-map.js.es6 @@ -1,4 +1,4 @@ -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; import { avatarFor } from 'discourse/widgets/post'; diff --git a/app/assets/javascripts/discourse/widgets/search-menu-results.js.es6 b/app/assets/javascripts/discourse/widgets/search-menu-results.js.es6 index d210b2e2d99..c195c962f6b 100644 --- a/app/assets/javascripts/discourse/widgets/search-menu-results.js.es6 +++ b/app/assets/javascripts/discourse/widgets/search-menu-results.js.es6 @@ -3,7 +3,7 @@ import { dateNode } from 'discourse/helpers/node'; import RawHtml from 'discourse/widgets/raw-html'; import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import highlightText from 'discourse/lib/highlight-text'; class Highlighted extends RawHtml { diff --git a/app/assets/javascripts/discourse/widgets/time-gap.js.es6 b/app/assets/javascripts/discourse/widgets/time-gap.js.es6 index f2ce1cfdb93..30cfd244830 100644 --- a/app/assets/javascripts/discourse/widgets/time-gap.js.es6 +++ b/app/assets/javascripts/discourse/widgets/time-gap.js.es6 @@ -1,6 +1,6 @@ import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; function description(attrs) { const daysSince = attrs.daysSince; diff --git a/app/assets/javascripts/discourse/widgets/topic-status.js.es6 b/app/assets/javascripts/discourse/widgets/topic-status.js.es6 index d0b8ac5ef64..763ce7e2160 100644 --- a/app/assets/javascripts/discourse/widgets/topic-status.js.es6 +++ b/app/assets/javascripts/discourse/widgets/topic-status.js.es6 @@ -1,5 +1,5 @@ import { createWidget } from 'discourse/widgets/widget'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import { h } from 'virtual-dom'; import { escapeExpression } from 'discourse/lib/utilities'; diff --git a/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 b/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 index f71e7b892d5..bccb90f80f4 100644 --- a/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 +++ b/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 @@ -1,7 +1,7 @@ import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; import { relativeAge } from 'discourse/lib/formatter'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import RawHtml from 'discourse/widgets/raw-html'; const SCROLLAREA_HEIGHT = 300; diff --git a/plugins/poll/assets/javascripts/widgets/discourse-poll.js.es6 b/plugins/poll/assets/javascripts/widgets/discourse-poll.js.es6 index 5bac8d63b86..0f087d2570d 100644 --- a/plugins/poll/assets/javascripts/widgets/discourse-poll.js.es6 +++ b/plugins/poll/assets/javascripts/widgets/discourse-poll.js.es6 @@ -1,6 +1,6 @@ import { createWidget } from 'discourse/widgets/widget'; import { h } from 'virtual-dom'; -import { iconNode } from 'discourse/helpers/fa-icon-node'; +import { iconNode } from 'discourse-common/lib/icon-library'; import RawHtml from 'discourse/widgets/raw-html'; import { ajax } from 'discourse/lib/ajax'; import evenRound from "discourse/plugins/poll/lib/even-round"; From dc42d4411d9dc3521172b7d61a21301446433deb Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 26 Jul 2017 13:21:33 -0400 Subject: [PATCH 08/59] correct flaky spec --- spec/components/discourse_spec.rb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/spec/components/discourse_spec.rb b/spec/components/discourse_spec.rb index 3b458e3131a..a6c8e763aca 100644 --- a/spec/components/discourse_spec.rb +++ b/spec/components/discourse_spec.rb @@ -103,17 +103,25 @@ describe Discourse do expect($redis.get(key)).to eq(nil) end + def get_readonly_message + messages = MessageBus.track_publish do + yield + end + + messages.first{|m| m.channel == Discourse.readonly_channel} + end + describe ".enable_readonly_mode" do it "adds a key in redis and publish a message through the message bus" do expect($redis.get(readonly_mode_key)).to eq(nil) - message = MessageBus.track_publish { Discourse.enable_readonly_mode }.first + message = get_readonly_message { Discourse.enable_readonly_mode } assert_readonly_mode(message, readonly_mode_key, readonly_mode_ttl) end context 'user enabled readonly mode' do it "adds a key in redis and publish a message through the message bus" do expect($redis.get(user_readonly_mode_key)).to eq(nil) - message = MessageBus.track_publish { Discourse.enable_readonly_mode(user_readonly_mode_key) }.first + message = get_readonly_message { Discourse.enable_readonly_mode(user_readonly_mode_key) } assert_readonly_mode(message, user_readonly_mode_key) end end @@ -123,9 +131,9 @@ describe Discourse do it "removes a key from redis and publish a message through the message bus" do Discourse.enable_readonly_mode - message = MessageBus.track_publish do + message = get_readonly_message do Discourse.disable_readonly_mode - end.first + end assert_readonly_mode_disabled(message, readonly_mode_key) end @@ -133,7 +141,7 @@ describe Discourse do context 'user disabled readonly mode' do it "removes readonly key in redis and publish a message through the message bus" do Discourse.enable_readonly_mode(user_enabled: true) - message = MessageBus.track_publish { Discourse.disable_readonly_mode(user_enabled: true) }.first + message = get_readonly_message { Discourse.disable_readonly_mode(user_enabled: true) } assert_readonly_mode_disabled(message, user_readonly_mode_key) end end From 6fc5ece6281488beee69929b83318f8329fadded Mon Sep 17 00:00:00 2001 From: Blake Erickson Date: Wed, 26 Jul 2017 14:37:54 -0600 Subject: [PATCH 09/59] FIX: onebox for dropbox video links not working add dropbox to the list of ignore redirects for onebox links --- lib/oneboxer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oneboxer.rb b/lib/oneboxer.rb index c08d619d6af..08d3ac4149b 100644 --- a/lib/oneboxer.rb +++ b/lib/oneboxer.rb @@ -18,7 +18,7 @@ module Oneboxer end def self.ignore_redirects - @ignore_redirects ||= ['http://store.steampowered.com', Discourse.base_url] + @ignore_redirects ||= ['http://www.dropbox.com','http://store.steampowered.com', Discourse.base_url] end def self.preview(url, options=nil) From fd95c971ec105d77de6cdd7a29af99693f66992d Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Wed, 26 Jul 2017 17:22:21 +0900 Subject: [PATCH 10/59] REFACTOR: Better variable name. --- app/models/group.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 5ca913f876e..e99d3482977 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -457,18 +457,18 @@ class Group < ActiveRecord::Base Group.exec_sql(sql, group_id: self.id, user_ids: user_ids) - new_attributes = {} + user_attributes = {} if self.primary_group? - new_attributes[:primary_group_id] = self.id + user_attributes[:primary_group_id] = self.id end if self.title.present? - new_attributes[:title] = self.title + user_attributes[:title] = self.title end - if new_attributes.present? - User.where(id: user_ids).update_all(new_attributes) + if user_attributes.present? + User.where(id: user_ids).update_all(user_attributes) end if self.grant_trust_level.present? From 2442bba1314f08566e33df739ea77785bcc88911 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 27 Jul 2017 15:39:47 +0900 Subject: [PATCH 11/59] UX: Better group creation workflow. * Owners and users can now be added to a group during creation. https://meta.discourse.org/t/you-cannot-allow-membership-requests-without-any-owners/64760/3 --- .../javascripts/admin/templates/group.hbs | 50 +++++++++++-------- .../components/group-members-input.js.es6 | 1 + .../javascripts/discourse/models/group.js.es6 | 23 +++++++-- .../components/group-members-input.hbs | 12 +++-- app/controllers/admin/groups_controller.rb | 34 +++++++++---- app/models/group.rb | 38 ++++++++++---- .../admin/groups_controller_spec.rb | 15 ------ spec/integration/admin/groups_spec.rb | 29 +++++++++++ spec/models/group_spec.rb | 31 ++++++++++++ 9 files changed, 168 insertions(+), 65 deletions(-) create mode 100644 spec/integration/admin/groups_spec.rb diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs index d1e8f3df185..acf6b9e6d07 100644 --- a/app/assets/javascripts/admin/templates/group.hbs +++ b/app/assets/javascripts/admin/templates/group.hbs @@ -9,19 +9,18 @@ {{/if}} - {{#if model.id}} - {{#unless model.automatic}} -
- - {{input type='text' name='full_name' value=model.full_name class='group-edit-full-name'}} -
+ {{#unless model.automatic}} +
+ + {{input type='text' name='full_name' value=model.full_name class='group-edit-full-name'}} +
-
- - {{d-editor value=model.bio_raw}} -
+
+ + {{d-editor value=model.bio_raw}} +
- {{#if model.hasOwners}} + {{#if model.hasOwners}}
@@ -30,17 +29,28 @@ {{/each}}
- {{/if}} -
- - {{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"}} -
- {{/unless}} + {{/if}} +
- {{group-members-input model=model}} + + + {{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}}
- {{/if}} + {{/unless}} + +
+ {{group-members-input model=model addButton=model.id}} +
diff --git a/app/assets/javascripts/discourse/components/group-members-input.js.es6 b/app/assets/javascripts/discourse/components/group-members-input.js.es6 index b2d892095b9..691eb051bbc 100644 --- a/app/assets/javascripts/discourse/components/group-members-input.js.es6 +++ b/app/assets/javascripts/discourse/components/group-members-input.js.es6 @@ -4,6 +4,7 @@ import { propertyEqual } from 'discourse/lib/computed'; export default Ember.Component.extend({ classNames: ["group-members-input"], + addButton: true, @computed('model.limit', 'model.offset', 'model.user_count') currentPage(limit, offset, userCount) { diff --git a/app/assets/javascripts/discourse/models/group.js.es6 b/app/assets/javascripts/discourse/models/group.js.es6 index c156613ef56..51ad388a1f3 100644 --- a/app/assets/javascripts/discourse/models/group.js.es6 +++ b/app/assets/javascripts/discourse/models/group.js.es6 @@ -130,7 +130,7 @@ const Group = RestModel.extend({ }, asJSON() { - return { + const attrs = { name: this.get('name'), alias_level: this.get('alias_level'), visibility_level: this.get('visibility_level'), @@ -149,13 +149,26 @@ const Group = RestModel.extend({ full_name: this.get('full_name'), default_notification_level: this.get('default_notification_level') }; + + if (!this.get('id')) { + attrs['usernames'] = this.get('usernames'); + attrs['owner_usernames'] = this.get('ownerUsernames'); + } + + return attrs; }, create() { - var self = this; - return ajax("/admin/groups", { type: "POST", data: { group: this.asJSON() } }).then(function(resp) { - self.set('id', resp.basic_group.id); - }); + return ajax("/admin/groups", { type: "POST", data: { group: this.asJSON() } }) + .then(resp => { + this.setProperties({ + id: resp.basic_group.id, + usernames: null, + ownerUsernames: null + }); + + this.findMembers(); + }); }, save() { diff --git a/app/assets/javascripts/discourse/templates/components/group-members-input.hbs b/app/assets/javascripts/discourse/templates/components/group-members-input.hbs index 24db7b5c877..9127b7096de 100644 --- a/app/assets/javascripts/discourse/templates/components/group-members-input.hbs +++ b/app/assets/javascripts/discourse/templates/components/group-members-input.hbs @@ -17,10 +17,12 @@
{{user-selector usernames=model.usernames}} - {{d-button action="addMembers" - class="add" - icon="plus" - disabled=disableAddButton - label="groups.edit.add_members"}} + {{#if addButton}} + {{d-button action="addMembers" + class="add" + icon="plus" + disabled=disableAddButton + label="groups.edit.add_members"}} + {{/if}}
{{/unless}} diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 2a1f9a36e02..3da78834fd4 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -32,10 +32,7 @@ class Admin::GroupsController < Admin::AdminController end def create - group = Group.new - - group.name = (group_params[:name] || '').strip - save_group(group) + save_group(Group.new) end def update @@ -47,6 +44,7 @@ class Admin::GroupsController < Admin::AdminController end def save_group(group) + group.name = group_params[:name] if group_params[:name].present? && !group.automatic group.alias_level = group_params[:alias_level].to_i if group_params[:alias_level].present? if group_params[:visibility_level] @@ -81,8 +79,27 @@ class Admin::GroupsController < Admin::AdminController group.allow_membership_requests = group_params[:allow_membership_requests] end + if group_params[:owner_usernames].present? + owner_ids = User.where( + username: group_params[:owner_usernames].split(",") + ).pluck(:id) + + owner_ids.each do |user_id| + group.group_users.build(user_id: user_id, owner: true) + end + end + + if group_params[:usernames].present? + user_ids = User.where(username: group_params[:usernames].split(",")).pluck(:id) + user_ids -= owner_ids if owner_ids + + user_ids.each do |user_id| + group.group_users.build(user_id: user_id) + end + end + if group.save - Group.reset_counters(group.id, :group_users) + group.restore_user_count! yield(group) if block_given? @@ -111,8 +128,7 @@ class Admin::GroupsController < Admin::AdminController def add_owners group = Group.find(params.require(:id)) return can_not_modify_automatic if group.automatic - - users = User.where(username: params[:usernames].split(",")) + users = User.where(username: group_params[:usernames].split(",")) users.each do |user| group_action_logger = GroupActionLogger.new(current_user, group) @@ -125,7 +141,7 @@ class Admin::GroupsController < Admin::AdminController group_action_logger.log_make_user_group_owner(user) end - Group.reset_counters(group.id, :group_users) + group.restore_user_count! render json: success_json end @@ -157,7 +173,7 @@ class Admin::GroupsController < Admin::AdminController :automatic_membership_retroactive, :title, :primary_group, :grant_trust_level, :incoming_email, :flair_url, :flair_bg_color, :flair_color, :bio_raw, :public, :allow_membership_requests, :full_name, - :default_notification_level + :default_notification_level, :usernames, :owner_usernames ) end end diff --git a/app/models/group.rb b/app/models/group.rb index e99d3482977..517247beb6d 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -37,10 +37,10 @@ class Group < ActiveRecord::Base end validate :name_format_validator - validates_uniqueness_of :name, case_sensitive: false + validates :name, presence: true, uniqueness: { case_sensitive: false } validate :automatic_membership_email_domains_format_validator validate :incoming_email_validator - validate :can_allow_membership_requests + validate :can_allow_membership_requests, if: :allow_membership_requests validates :flair_url, url: true, if: Proc.new { |g| g.flair_url && g.flair_url[0,3] != 'fa-' } AUTO_GROUPS = { @@ -426,9 +426,9 @@ class Group < ActiveRecord::Base def add_owner(user) if group_user = self.group_users.find_by(user: user) - group_user.update_attributes!(owner: true) if !group_user.owner + group_user.update!(owner: true) if !group_user.owner else - GroupUser.create!(user: user, group: self, owner: true) + self.group_users.create!(user: user, owner: true) end end @@ -437,7 +437,9 @@ class Group < ActiveRecord::Base end def bulk_add(user_ids) - if user_ids.present? + return unless user_ids.present? + + Group.transaction do sql = <<~SQL INSERT INTO group_users (group_id, user_id, created_at, updated_at) @@ -470,12 +472,16 @@ class Group < ActiveRecord::Base if user_attributes.present? User.where(id: user_ids).update_all(user_attributes) end - - if self.grant_trust_level.present? - Jobs.enqueue(:bulk_grant_trust_level, user_ids: user_ids, trust_level: self.grant_trust_level) - end end - true + + if self.grant_trust_level.present? + Jobs.enqueue(:bulk_grant_trust_level, + user_ids: user_ids, + trust_level: self.grant_trust_level + ) + end + + self end def staff? @@ -485,6 +491,7 @@ class Group < ActiveRecord::Base protected def name_format_validator + self.name.strip! UsernameValidator.perform_validation(self, 'name') end @@ -565,7 +572,16 @@ class Group < ActiveRecord::Base private def can_allow_membership_requests - if self.allow_membership_requests && !self.group_users.where(owner: true).exists? + valid = true + + valid = + if self.persisted? + self.group_users.where(owner: true).exists? + else + self.group_users.any?(&:owner) + end + + if !valid self.errors.add(:base, I18n.t('groups.errors.cant_allow_membership_requests')) end end diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index 5e6b51581ff..833613133e1 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -39,21 +39,6 @@ describe Admin::GroupsController do end end - context "#create" do - - it "strip spaces on the group name" do - xhr :post, :create, { group: { name: " bob " } } - - expect(response.status).to eq(200) - - groups = Group.where(name: "bob").to_a - - expect(groups.count).to eq(1) - expect(groups[0].name).to eq("bob") - end - - end - context "#update" do it 'should update a group' do group.add_owner(user) diff --git a/spec/integration/admin/groups_spec.rb b/spec/integration/admin/groups_spec.rb new file mode 100644 index 00000000000..4f41f6d219a --- /dev/null +++ b/spec/integration/admin/groups_spec.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +RSpec.describe "Managing groups as an admin" do + let(:admin) { Fabricate(:admin) } + let(:user) { Fabricate(:user) } + + before do + sign_in(admin) + end + + describe 'creating a new group' do + it 'should work' do + post "/admin/groups.json", group: { + name: 'testing', + usernames: [admin.username, user.username].join(","), + owner_usernames: [user.username].join(","), + allow_membership_requests: true + } + + expect(response).to be_success + + group = Group.last + + expect(group.name).to eq('testing') + expect(group.users).to contain_exactly(admin, user) + expect(group.allow_membership_requests).to eq(true) + end + end +end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 3a53b68bd07..dece2cf4e38 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -47,6 +47,13 @@ describe Group do expect(group.valid?).to eq false end + it 'strips trailing and leading spaces' do + group.name = ' dragon ' + + expect(group.save).to eq(true) + expect(group.reload.name).to eq('dragon') + end + it "is invalid for case-insensitive existing names" do build(:group, name: 'this_is_a_name').save group.name = 'This_Is_A_Name' @@ -79,6 +86,22 @@ describe Group do end context 'when a group has no owners' do + describe 'group has not been persisted' do + it 'should not allow membership requests' do + group = Fabricate.build(:group, allow_membership_requests: true) + + expect(group.valid?).to eq(false) + + expect(group.errors.full_messages).to include(I18n.t( + "groups.errors.cant_allow_membership_requests" + )) + + group.group_users.build(user_id: user.id, owner: true) + + expect(group.valid?).to eq(true) + end + end + it 'should not allow membership requests' do group.allow_membership_requests = true @@ -533,4 +556,12 @@ describe Group do expect(Group.search_group('test2')).to eq([]) end end + + describe '#bulk_add' do + it 'should be able to add multiple users' do + group.bulk_add([user.id, admin.id]) + + expect(group.group_users.map(&:user_id)).to contain_exactly(user.id, admin.id) + end + end end From 4d25d61e9c2cd749db0ddc0f76d78af88a9f0953 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 27 Jul 2017 16:22:12 +0900 Subject: [PATCH 12/59] UX: Change group membership requests icons on groups pages. https://meta.discourse.org/t/make-it-easier-to-send-a-message-to-groups/65065 --- .../templates/components/group-membership-button.hbs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/components/group-membership-button.hbs b/app/assets/javascripts/discourse/templates/components/group-membership-button.hbs index ed17b2a79bd..2d05e166889 100644 --- a/app/assets/javascripts/discourse/templates/components/group-membership-button.hbs +++ b/app/assets/javascripts/discourse/templates/components/group-membership-button.hbs @@ -2,13 +2,13 @@ {{#if userIsGroupUser}} {{d-button action="leaveGroup" class="btn-danger group-index-leave" - icon="minus" + icon="user-times" label="groups.leave" disabled=updatingMembership}} {{else}} {{d-button action="joinGroup" class="group-index-join" - icon="plus" + icon="user-plus" label="groups.join" disabled=updatingMembership}} {{/if}} @@ -25,7 +25,7 @@ {{d-button action="requestMembership" class="group-index-request" disabled=loading - icon="envelope" + icon="user-plus" label="groups.request"}} {{#if loading}} From 9c93a20cf1d4df2d04cd16bff552f724d1396768 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 27 Jul 2017 17:05:08 +0900 Subject: [PATCH 13/59] Fix incorrect assertion in JS tests. --- test/javascripts/models/group-test.js.es6 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/javascripts/models/group-test.js.es6 b/test/javascripts/models/group-test.js.es6 index 3a74d635dd3..6b1b726dd58 100644 --- a/test/javascripts/models/group-test.js.es6 +++ b/test/javascripts/models/group-test.js.es6 @@ -5,9 +5,9 @@ QUnit.module("model:group"); QUnit.test('displayName', assert => { const group = Group.create({ name: "test", display_name: 'donkey' }); - assert.ok(group.get('displayName'), "donkey", 'it should return the display name'); + assert.equal(group.get('displayName'), "donkey", 'it should return the display name'); group.set('display_name', null); - assert.ok(group.get('displayName'), "test", "it should return the group's name"); -}); \ No newline at end of file + assert.equal(group.get('displayName'), "test", "it should return the group's name"); +}); From 75374c76b316c6cc8b3008e79b720d2d8bad78ac Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 27 Jul 2017 17:51:25 +0900 Subject: [PATCH 14/59] UX: Display button to message group on group page. https://meta.discourse.org/t/make-it-easier-to-send-a-message-to-groups/65065 --- .../discourse/controllers/group.js.es6 | 11 ++++++++++ .../javascripts/discourse/templates/group.hbs | 8 ++++++++ app/serializers/group_show_serializer.rb | 18 ++++++++++++++--- config/locales/client.en.yml | 1 + .../serializers/group_show_serializer_spec.rb | 20 ++++++++++++++++--- .../javascripts/acceptance/groups-test.js.es6 | 19 ++++++++++++++++-- .../fixtures/group-fixtures.js.es6 | 5 +++-- 7 files changed, 72 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/discourse/controllers/group.js.es6 b/app/assets/javascripts/discourse/controllers/group.js.es6 index 09466b4c4f9..d38f15290ec 100644 --- a/app/assets/javascripts/discourse/controllers/group.js.es6 +++ b/app/assets/javascripts/discourse/controllers/group.js.es6 @@ -48,6 +48,11 @@ export default Ember.Controller.extend({ }; }, + @computed("model.mentionable") + displayGroupMessageButton(mentionable) { + return this.currentUser && mentionable; + }, + @observes('model.user_count') _setMembersTabCount() { this.get('tabs')[0].set('count', this.get('model.user_count')); @@ -66,5 +71,11 @@ export default Ember.Controller.extend({ return canSee; }); + }, + + actions: { + messageGroup() { + this.send('createNewMessageViaParams', this.get('model.name')); + } } }); diff --git a/app/assets/javascripts/discourse/templates/group.hbs b/app/assets/javascripts/discourse/templates/group.hbs index 24afa5a0c70..2da7d242cb3 100644 --- a/app/assets/javascripts/discourse/templates/group.hbs +++ b/app/assets/javascripts/discourse/templates/group.hbs @@ -43,6 +43,14 @@ {{/each}} {{/mobile-nav}} + {{#if displayGroupMessageButton}} + {{d-button + action="messageGroup" + class="btn-primary group-message-button" + icon="envelope" + label="groups.message"}} + {{/if}} + {{group-membership-button model=model showLogin='showLogin'}}
diff --git a/app/serializers/group_show_serializer.rb b/app/serializers/group_show_serializer.rb index dc9e0c7649a..3bd6bf31d15 100644 --- a/app/serializers/group_show_serializer.rb +++ b/app/serializers/group_show_serializer.rb @@ -1,8 +1,8 @@ class GroupShowSerializer < BasicGroupSerializer - attributes :is_group_user, :is_group_owner + attributes :is_group_user, :is_group_owner, :mentionable def include_is_group_user? - scope.authenticated? + authenticated? end def is_group_user @@ -10,15 +10,27 @@ class GroupShowSerializer < BasicGroupSerializer end def include_is_group_owner? - scope.authenticated? + authenticated? end def is_group_owner scope.is_admin? || fetch_group_user&.owner end + def include_mentionable? + authenticated? + end + + def mentionable + Group.mentionable(scope.user).exists?(id: object.id) + end + private + def authenticated? + scope.authenticated? + end + def fetch_group_user @group_user ||= object.group_users.find_by(user: scope.user) end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index e13e91344d8..467e3a799b5 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -421,6 +421,7 @@ en: join: "Join Group" leave: "Leave Group" request: "Request to Join Group" + message: "Message" automatic_group: Automatic Group closed_group: Closed Group is_group_user: "You are a member of this group" diff --git a/spec/serializers/group_show_serializer_spec.rb b/spec/serializers/group_show_serializer_spec.rb index 5641ccd8d09..b90db108e5c 100644 --- a/spec/serializers/group_show_serializer_spec.rb +++ b/spec/serializers/group_show_serializer_spec.rb @@ -1,6 +1,9 @@ require 'rails_helper' describe GroupShowSerializer do + let(:user) { Fabricate(:user) } + let(:group) { Fabricate(:group) } + context 'admin user' do let(:user) { Fabricate(:admin) } let(:group) { Fabricate(:group, users: [user]) } @@ -14,9 +17,6 @@ describe GroupShowSerializer do end context 'group owner' do - let(:user) { Fabricate(:user) } - let(:group) { Fabricate(:group) } - before do group.add_owner(user) end @@ -28,4 +28,18 @@ describe GroupShowSerializer do expect(json[:group_show][:is_group_user]).to eq(true) end end + + describe '#mentionable' do + let(:group) { Fabricate(:group, alias_level: Group::ALIAS_LEVELS[:everyone]) } + + it 'should return the right value' do + json = GroupShowSerializer.new(group, scope: Guardian.new).as_json + + expect(json[:group_show][:mentionable]).to eq(nil) + + json = GroupShowSerializer.new(group, scope: Guardian.new(user)).as_json + + expect(json[:group_show][:mentionable]).to eq(true) + end + end end diff --git a/test/javascripts/acceptance/groups-test.js.es6 b/test/javascripts/acceptance/groups-test.js.es6 index 0534127ea7b..017f672a73e 100644 --- a/test/javascripts/acceptance/groups-test.js.es6 +++ b/test/javascripts/acceptance/groups-test.js.es6 @@ -42,12 +42,13 @@ QUnit.test("Browsing Groups", assert => { }); }); -QUnit.test("Viewing Group", assert => { +QUnit.test("Anonymous Viewing Group", assert => { visit("/groups/discourse"); andThen(() => { assert.ok(count('.avatar-flair .fa-adjust') === 1, "it displays the group's avatar flair"); assert.ok(count('.group-members tr') > 0, "it lists group members"); + assert.ok(count('.group-message-button') === 0, 'it does not show group message button'); }); click(".nav-pills li a[title='Activity']"); @@ -80,6 +81,20 @@ QUnit.test("Viewing Group", assert => { }); }); +QUnit.test("User Viewing Group", assert => { + logIn(); + Discourse.reset(); + + visit("/groups/discourse"); + + click('.group-message-button'); + + andThen(() => { + assert.ok(count('#reply-control') === 1, 'it opens the composer'); + assert.equal(find('.ac-wrap .item').text(), 'discourse', 'it prefills the group name'); + }); +}); + QUnit.test("Admin Viewing Group", assert => { logIn(); Discourse.reset(); @@ -102,4 +117,4 @@ QUnit.test("Admin Viewing Group", assert => { 'it should show messages tab if user is admin' ); }); -}); \ No newline at end of file +}); diff --git a/test/javascripts/fixtures/group-fixtures.js.es6 b/test/javascripts/fixtures/group-fixtures.js.es6 index a55d8303cb3..dbc9d32d579 100644 --- a/test/javascripts/fixtures/group-fixtures.js.es6 +++ b/test/javascripts/fixtures/group-fixtures.js.es6 @@ -6,11 +6,12 @@ export default { "name":"discourse", "full_name":"Awesome Team", "user_count":8, - "alias_level":0, + "alias_level":99, "visible":true, "public":true, "flair_url": 'fa-adjust', - "is_group_owner":true + "is_group_owner":true, + "mentionable":true } }, "/groups/discourse/counts.json":{ From 32c9ad6f7f4ddab8e1339ea51a9c36a3a57cc9ee Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 27 Jul 2017 10:48:31 -0400 Subject: [PATCH 15/59] FIX: Wizard was broken --- app/assets/javascripts/wizard-vendor.js | 2 ++ .../javascripts/wizard/test/acceptance/wizard-test.js.es6 | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/wizard-vendor.js b/app/assets/javascripts/wizard-vendor.js index 2236d33669b..ecb8d2bd7e0 100644 --- a/app/assets/javascripts/wizard-vendor.js +++ b/app/assets/javascripts/wizard-vendor.js @@ -3,3 +3,5 @@ //= require jquery.ui.widget.js //= require jquery.fileupload.js //= require sweetalert.js +//= require virtual-dom +//= require virtual-dom-amd diff --git a/app/assets/javascripts/wizard/test/acceptance/wizard-test.js.es6 b/app/assets/javascripts/wizard/test/acceptance/wizard-test.js.es6 index ca42183bb67..3ea019a062b 100644 --- a/app/assets/javascripts/wizard/test/acceptance/wizard-test.js.es6 +++ b/app/assets/javascripts/wizard/test/acceptance/wizard-test.js.es6 @@ -1,7 +1,7 @@ import startApp from 'wizard/test/helpers/start-app'; var wizard; -module("Acceptance: wizard", { +QUnit.module("Acceptance: wizard", { beforeEach() { wizard = startApp(); }, From 5cfc2d8972754d79b0c3e6a121f309161fff74ae Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 27 Jul 2017 11:29:18 -0400 Subject: [PATCH 16/59] Run wizard specs in docker:test --- app/controllers/wizard_controller.rb | 5 +++-- lib/tasks/docker.rake | 5 +++-- lib/tasks/qunit.rake | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/controllers/wizard_controller.rb b/app/controllers/wizard_controller.rb index 4b7bc82faa4..88edb7af8d4 100644 --- a/app/controllers/wizard_controller.rb +++ b/app/controllers/wizard_controller.rb @@ -3,8 +3,8 @@ require_dependency 'wizard/builder' class WizardController < ApplicationController before_filter :ensure_wizard_enabled, only: [:index] - before_filter :ensure_logged_in - before_filter :ensure_admin + before_filter :ensure_logged_in, except: [:qunit] + before_filter :ensure_admin, except: [:qunit] skip_before_filter :check_xhr, :preload_json @@ -21,6 +21,7 @@ class WizardController < ApplicationController end def qunit + raise Discourse::InvalidAccess.new if Rails.env.production? end end diff --git a/lib/tasks/docker.rake b/lib/tasks/docker.rake index 80af4e74d74..b6089394478 100644 --- a/lib/tasks/docker.rake +++ b/lib/tasks/docker.rake @@ -92,8 +92,9 @@ task 'docker:test' do @good &&= run_or_fail("eslint --ext .es6 test/javascripts") @good &&= run_or_fail("eslint test/javascripts") @good &&= run_or_fail("bundle exec rake qunit:test['600000']") + @good &&= run_or_fail("bundle exec rake qunit:test['600000','/wizard/qunit']") end - + unless ENV["SKIP_PLUGINS"] if ENV["SINGLE_PLUGIN"] @good &&= run_or_fail("bundle exec rake plugin:qunit['#{ENV['SINGLE_PLUGIN']}','600000']") @@ -101,7 +102,7 @@ task 'docker:test' do @good &&= run_or_fail("bundle exec rake plugin:qunit['*','600000']") end end - + end ensure diff --git a/lib/tasks/qunit.rake b/lib/tasks/qunit.rake index af08cb58565..715af0ed6ef 100644 --- a/lib/tasks/qunit.rake +++ b/lib/tasks/qunit.rake @@ -1,6 +1,6 @@ desc "Runs the qunit test suite" -task "qunit:test", [:timeout] => :environment do |_, args| +task "qunit:test", [:timeout, :qunit_path] => :environment do |_, args| require "rack" require "socket" @@ -35,7 +35,8 @@ task "qunit:test", [:timeout] => :environment do |_, args| begin success = true test_path = "#{Rails.root}/vendor/assets/javascripts" - cmd = "phantomjs #{test_path}/run-qunit.js http://localhost:#{port}/qunit" + qunit_path = args[:qunit_path] || "/qunit" + cmd = "phantomjs #{test_path}/run-qunit.js http://localhost:#{port}#{qunit_path}" options = {} From 39e394de15870b96f56959d2c3c0aed8527af6a4 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 27 Jul 2017 11:42:11 -0400 Subject: [PATCH 17/59] FIX: We need to precompile the wizard test helper --- config/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application.rb b/config/application.rb index f49a372783f..0c6755c353e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -83,7 +83,7 @@ module Discourse browser-update.js break_string.js ember_jquery.js pretty-text-bundle.js wizard-application.js wizard-vendor.js plugin.js plugin-third-party.js - markdown-it-bundle.js + markdown-it-bundle.js wizard/test/test_helper.js } # Precompile all available locales From 6a8f5d497e179fce32934d9d8fcca44eb1ac9cd1 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 27 Jul 2017 11:51:04 -0400 Subject: [PATCH 18/59] Revert "FIX: We need to precompile the wizard test helper" This reverts commit 39e394de15870b96f56959d2c3c0aed8527af6a4. --- config/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application.rb b/config/application.rb index 0c6755c353e..f49a372783f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -83,7 +83,7 @@ module Discourse browser-update.js break_string.js ember_jquery.js pretty-text-bundle.js wizard-application.js wizard-vendor.js plugin.js plugin-third-party.js - markdown-it-bundle.js wizard/test/test_helper.js + markdown-it-bundle.js } # Precompile all available locales From ad04d188ae792ddb365eaf4bb8e0eadf23ac78b2 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 27 Jul 2017 12:08:10 -0400 Subject: [PATCH 19/59] FIX: Precompile errors for wizard tests --- config/application.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/config/application.rb b/config/application.rb index f49a372783f..153ef545cd7 100644 --- a/config/application.rb +++ b/config/application.rb @@ -209,6 +209,17 @@ module Discourse if plugins = Discourse.plugins plugins.each{|plugin| plugin.notify_after_initialize} end + + # This nasty hack is required for not precompiling QUnit assets + # in test mode. see: https://github.com/rails/sprockets-rails/issues/299#issuecomment-167701012 + ActiveSupport.on_load(:action_view) do + default_checker = ActionView::Base.precompiled_asset_checker + + ActionView::Base.precompiled_asset_checker = -> logical_path do + default_checker[logical_path] || + %w{qunit.js qunit.css test_helper.css test_helper.js wizard/test/test_helper.js}.include?(logical_path) + end + end end if ENV['RBTRACE'] == "1" From 68b3dd43cea1a54a2b3a6e81fcbf6c02a867e9ce Mon Sep 17 00:00:00 2001 From: Neil Lalonde Date: Thu, 27 Jul 2017 12:26:55 -0400 Subject: [PATCH 20/59] fix intermittent failing tests, some watched word refactoring --- .../components/admin-watched-word.js.es6 | 2 +- lib/post_revisor.rb | 6 ----- lib/validators/post_validator.rb | 7 ++++++ spec/components/pretty_text_spec.rb | 24 ++++++++++++------- spec/integration/watched_words_spec.rb | 4 ++++ 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/admin/components/admin-watched-word.js.es6 b/app/assets/javascripts/admin/components/admin-watched-word.js.es6 index 9a454ad57ee..eda556e38e2 100644 --- a/app/assets/javascripts/admin/components/admin-watched-word.js.es6 +++ b/app/assets/javascripts/admin/components/admin-watched-word.js.es6 @@ -13,7 +13,7 @@ export default Ember.Component.extend(bufferedRender({ 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})); + bootbox.alert(I18n.t("generic_error_with_reason", {error: `http: ${e.status} - ${e.body}`})); });; } })); diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index dea5948df57..831faa47b43 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -273,12 +273,6 @@ class PostRevisor @post.word_count = @fields[:raw].scan(/[[:word:]]+/).size if @fields.has_key?(:raw) @post.self_edits += 1 if self_edit? - if !@post.acting_user.staff? && !@post.acting_user.staged && WordWatcher.new(@post.raw).should_block? - @post.errors[:base] << I18n.t('contains_blocked_words') - @post_successfully_saved = false - return - end - remove_flags_and_unhide_post @post.extract_quoted_post_numbers diff --git a/lib/validators/post_validator.rb b/lib/validators/post_validator.rb index 3ed62e37406..dec44496a8a 100644 --- a/lib/validators/post_validator.rb +++ b/lib/validators/post_validator.rb @@ -33,6 +33,7 @@ class Validators::PostValidator < ActiveModel::Validator return if options[:skip_post_body] || post.topic&.pm_with_non_human_user? stripped_length(post) raw_quality(post) + watched_words(post) end def stripped_length(post) @@ -55,6 +56,12 @@ class Validators::PostValidator < ActiveModel::Validator post.errors.add(:raw, I18n.t(:is_invalid)) unless sentinel.valid? end + def watched_words(post) + if !post.acting_user&.staff? && !post.acting_user&.staged && WordWatcher.new(post.raw).should_block? + post.errors[:base] << I18n.t('contains_blocked_words') + end + end + # Ensure maximum amount of mentions in a post def max_mention_validator(post) return if post.acting_user.try(:staff?) diff --git a/spec/components/pretty_text_spec.rb b/spec/components/pretty_text_spec.rb index 6e40972adeb..4a944900ab3 100644 --- a/spec/components/pretty_text_spec.rb +++ b/spec/components/pretty_text_spec.rb @@ -247,9 +247,12 @@ describe PrettyText do end it 'does censor code fences' do - ['apple', 'banana'].each { |w| Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) } - expect(PrettyText.cook("# banana")).not_to include('banana') - $redis.flushall + begin + ['apple', 'banana'].each { |w| Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) } + expect(PrettyText.cook("# banana")).not_to include('banana') + ensure + $redis.flushall + end end end @@ -788,12 +791,15 @@ HTML end it 'can censor words correctly' do - ['apple', 'banana'].each { |w| Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) } - expect(PrettyText.cook('yay banana yay')).not_to include('banana') - expect(PrettyText.cook('yay `banana` yay')).not_to include('banana') - expect(PrettyText.cook("# banana")).not_to include('banana') - expect(PrettyText.cook("# banana")).to include("\u25a0\u25a0") - $redis.flushall + begin + ['apple', 'banana'].each { |w| Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor]) } + expect(PrettyText.cook('yay banana yay')).not_to include('banana') + expect(PrettyText.cook('yay `banana` yay')).not_to include('banana') + expect(PrettyText.cook("# banana")).not_to include('banana') + expect(PrettyText.cook("# banana")).to include("\u25a0\u25a0") + ensure + $redis.flushall + end end it 'supports typographer' do diff --git a/spec/integration/watched_words_spec.rb b/spec/integration/watched_words_spec.rb index 95787054048..bfef752a6de 100644 --- a/spec/integration/watched_words_spec.rb +++ b/spec/integration/watched_words_spec.rb @@ -12,6 +12,10 @@ describe WatchedWord do let(:flag_word) { Fabricate(:watched_word, action: WatchedWord.actions[:flag]) } let(:block_word) { Fabricate(:watched_word, action: WatchedWord.actions[:block]) } + after do + $redis.flushall + end + context "block" do def should_block_post(manager) expect { From d0c41a578e2a0df2cc95e2b06bcd7827270a3fca Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 26 Jul 2017 16:25:09 -0400 Subject: [PATCH 21/59] Rename `{{fa-icon}}` to `{{d-icon}}` so it can be more generic --- .../components/admin-watched-word.js.es6 | 2 +- .../admin/templates/badges-index.hbs | 2 +- .../admin/templates/badges-show.hbs | 2 +- .../javascripts/admin/templates/badges.hbs | 2 +- .../components/admin-report-counts.hbs | 8 +++---- .../components/setting-validation-message.hbs | 2 +- .../components/watched-word-uploader.hbs | 2 +- .../admin/templates/customize-colors.hbs | 4 ++-- .../admin/templates/customize-themes-edit.hbs | 6 ++--- .../admin/templates/customize-themes-show.hbs | 8 +++---- .../admin/templates/customize-themes.hbs | 4 ++-- .../javascripts/admin/templates/dashboard.hbs | 12 +++++----- .../admin/templates/email-sent.hbs | 2 +- .../javascripts/admin/templates/emojis.hbs | 2 +- .../admin/templates/flags-list.hbs | 2 +- .../javascripts/admin/templates/group.hbs | 2 +- .../admin/templates/groups-type-index.hbs | 2 +- .../admin/templates/groups-type.hbs | 2 +- .../admin/templates/logs/screened-emails.hbs | 2 +- .../templates/logs/screened-ip-addresses.hbs | 4 ++-- .../admin/templates/logs/screened-urls.hbs | 2 +- .../templates/logs/staff-action-logs.hbs | 8 +++---- .../modal/admin-color-scheme-select-base.hbs | 2 +- .../modal/admin-edit-badge-groupings.hbs | 10 ++++----- .../admin/templates/site-text-edit.hbs | 2 +- .../admin/templates/user-index.hbs | 6 ++--- .../admin/templates/users-list-show.hbs | 4 ++-- .../admin/templates/version-checks.hbs | 12 +++++----- .../admin/templates/web-hooks-show-events.hbs | 4 ++-- .../admin/templates/web-hooks-show.hbs | 6 ++--- .../javascripts/admin/templates/web-hooks.hbs | 4 ++-- .../discourse-common/helpers/d-icon.js.es6 | 6 +++++ .../discourse-common/helpers/fa-icon.js.es6 | 2 ++ .../discourse-common/lib/icon-library.js.es6 | 2 +- .../javascripts/discourse/templates/about.hbs | 8 +++---- .../category-tag-autocomplete.raw.hbs | 2 +- .../components/auto-update-input.hbs | 6 ++--- .../templates/components/avatar-uploader.hbs | 2 +- .../templates/components/badge-card.hbs | 2 +- .../categories-boxes-with-topics.hbs | 2 +- .../templates/components/categories-boxes.hbs | 2 +- .../templates/components/category-drop.hbs | 2 +- .../components/category-title-link.hbs | 2 +- .../templates/components/composer-editor.hbs | 2 +- .../templates/components/csv-uploader.hbs | 2 +- .../templates/components/d-button.hbs | 2 +- .../templates/components/disabled-icon.hbs | 4 ++-- .../components/edit-category-security.hbs | 2 +- .../components/edit-category-settings.hbs | 2 +- .../templates/components/emoji-uploader.hbs | 2 +- .../templates/components/expand-post.hbs | 2 +- .../templates/components/flat-button.hbs | 2 +- .../components/group-logs-filter.hbs | 2 +- .../templates/components/group-logs-row.hbs | 4 ++-- .../templates/components/group-member.hbs | 2 +- .../components/group-members-input.hbs | 4 ++-- .../templates/components/image-uploader.hbs | 4 ++-- .../templates/components/ip-lookup.hbs | 6 ++--- .../templates/components/period-chooser.hbs | 2 +- .../templates/components/share-popup.hbs | 6 ++--- .../templates/components/small-action.hbs | 4 ++-- .../templates/components/stream-item.hbs | 4 ++-- .../templates/components/topic-entrance.hbs | 4 ++-- .../components/topic-footer-buttons.hbs | 2 +- .../components/user-card-contents.hbs | 14 ++++++------ .../templates/components/user-stat.hbs | 2 +- .../discourse/templates/composer.hbs | 2 +- .../templates/composer/custom-body.hbs | 2 +- .../templates/composer/education.hbs | 2 +- .../templates/composer/group-mentioned.hbs | 2 +- .../templates/composer/similar-topics.hbs | 2 +- .../templates/emoji-picker.raw.hbs.erb | 8 +++---- .../discourse/templates/flat-button.raw.hbs | 2 +- .../discourse/templates/full-page-search.hbs | 2 +- .../javascripts/discourse/templates/group.hbs | 2 +- .../javascripts/discourse/templates/modal.hbs | 2 +- .../templates/modal/feature-topic.hbs | 4 ++-- .../discourse/templates/modal/history.hbs | 2 +- .../discourse/templates/modal/merge-topic.hbs | 2 +- .../discourse/templates/modal/split-topic.hbs | 2 +- .../templates/preferences/account.hbs | 6 ++--- .../templates/preferences/profile.hbs | 2 +- .../discourse/templates/tags/show.hbs | 2 +- .../javascripts/discourse/templates/topic.hbs | 8 +++---- .../discourse/templates/user-invited-show.hbs | 2 +- .../javascripts/discourse/templates/user.hbs | 22 +++++++++---------- .../discourse/templates/user/summary.hbs | 10 ++++----- .../templates/components/invite-list-user.hbs | 2 +- .../templates/components/invite-list.hbs | 2 +- .../templates/components/radio-button.hbs | 2 +- .../components/wizard-field-image.hbs | 2 +- .../templates/components/wizard-step.hbs | 2 +- 92 files changed, 178 insertions(+), 170 deletions(-) create mode 100644 app/assets/javascripts/discourse-common/helpers/d-icon.js.es6 diff --git a/app/assets/javascripts/admin/components/admin-watched-word.js.es6 b/app/assets/javascripts/admin/components/admin-watched-word.js.es6 index eda556e38e2..3c78becb493 100644 --- a/app/assets/javascripts/admin/components/admin-watched-word.js.es6 +++ b/app/assets/javascripts/admin/components/admin-watched-word.js.es6 @@ -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'; export default Ember.Component.extend(bufferedRender({ diff --git a/app/assets/javascripts/admin/templates/badges-index.hbs b/app/assets/javascripts/admin/templates/badges-index.hbs index c94f59fe105..674eae8de29 100644 --- a/app/assets/javascripts/admin/templates/badges-index.hbs +++ b/app/assets/javascripts/admin/templates/badges-index.hbs @@ -3,7 +3,7 @@
{{#link-to 'adminBadges.show' 'new' class="btn"}} - {{fa-icon "plus"}} {{i18n 'admin.badges.new'}} + {{d-icon "plus"}} {{i18n 'admin.badges.new'}} {{/link-to}}
{{/d-section}} diff --git a/app/assets/javascripts/admin/templates/badges-show.hbs b/app/assets/javascripts/admin/templates/badges-show.hbs index e0ae7001df0..e40e6bfec08 100644 --- a/app/assets/javascripts/admin/templates/badges-show.hbs +++ b/app/assets/javascripts/admin/templates/badges-show.hbs @@ -38,7 +38,7 @@ content=badgeGroupings optionValuePath="content.id" optionLabelPath="content.displayName"}} -   +   diff --git a/app/assets/javascripts/admin/templates/badges.hbs b/app/assets/javascripts/admin/templates/badges.hbs index 3ca7f912940..aeeaac9c9e2 100644 --- a/app/assets/javascripts/admin/templates/badges.hbs +++ b/app/assets/javascripts/admin/templates/badges.hbs @@ -15,7 +15,7 @@ {{/each}} {{#link-to 'adminBadges.show' 'new' class="btn"}} - {{fa-icon "plus"}} {{i18n 'admin.badges.new'}} + {{d-icon "plus"}} {{i18n 'admin.badges.new'}} {{/link-to}}

diff --git a/app/assets/javascripts/admin/templates/components/admin-report-counts.hbs b/app/assets/javascripts/admin/templates/components/admin-report-counts.hbs index 304570c0a52..82fbf0d7996 100644 --- a/app/assets/javascripts/admin/templates/components/admin-report-counts.hbs +++ b/app/assets/javascripts/admin/templates/components/admin-report-counts.hbs @@ -1,6 +1,6 @@ {{#if report.icon}} - {{fa-icon report.icon}} + {{d-icon report.icon}} {{/if}} {{report.title}} @@ -8,15 +8,15 @@ {{number report.todayCount}} - {{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"}} - {{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"}} - {{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"}} {{number report.total}} diff --git a/app/assets/javascripts/admin/templates/components/setting-validation-message.hbs b/app/assets/javascripts/admin/templates/components/setting-validation-message.hbs index 92451dbbf92..88d59816fe3 100644 --- a/app/assets/javascripts/admin/templates/components/setting-validation-message.hbs +++ b/app/assets/javascripts/admin/templates/components/setting-validation-message.hbs @@ -1,4 +1,4 @@
- {{fa-icon "times"}} + {{d-icon "times"}} {{message}}
diff --git a/app/assets/javascripts/admin/templates/components/watched-word-uploader.hbs b/app/assets/javascripts/admin/templates/components/watched-word-uploader.hbs index 2582bdcbb0a..caa74a86152 100644 --- a/app/assets/javascripts/admin/templates/components/watched-word-uploader.hbs +++ b/app/assets/javascripts/admin/templates/components/watched-word-uploader.hbs @@ -1,5 +1,5 @@ diff --git a/app/assets/javascripts/admin/templates/customize-colors.hbs b/app/assets/javascripts/admin/templates/customize-colors.hbs index 1ed39b8fcf2..920f6f19540 100644 --- a/app/assets/javascripts/admin/templates/customize-colors.hbs +++ b/app/assets/javascripts/admin/templates/customize-colors.hbs @@ -4,12 +4,12 @@ {{#each model as |scheme|}} {{#unless scheme.is_base}}
  • - {{#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}}
  • {{/unless}} {{/each}} - + {{outlet}} diff --git a/app/assets/javascripts/admin/templates/customize-themes-edit.hbs b/app/assets/javascripts/admin/templates/customize-themes-edit.hbs index fafce666a6f..ba65a6d33fd 100644 --- a/app/assets/javascripts/admin/templates/customize-themes-edit.hbs +++ b/app/assets/javascripts/admin/templates/customize-themes-edit.hbs @@ -19,7 +19,7 @@
  • {{#link-to 'adminCustomizeThemes.edit' model.id 'desktop' fieldName replace=true title=field.title}} {{i18n 'admin.customize.theme.desktop'}} - {{fa-icon 'desktop'}} + {{d-icon 'desktop'}} {{/link-to}}
  • {{/if}} @@ -27,7 +27,7 @@
  • {{#link-to 'adminCustomizeThemes.edit' model.id 'mobile' fieldName replace=true title=field.title}} {{i18n 'admin.customize.theme.mobile'}} - {{fa-icon 'mobile'}} + {{d-icon 'mobile'}} {{/link-to}}
  • {{/if}} @@ -46,7 +46,7 @@ {{#each fields as |field|}}
  • {{#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}} {{/link-to}}
  • diff --git a/app/assets/javascripts/admin/templates/customize-themes-show.hbs b/app/assets/javascripts/admin/templates/customize-themes-show.hbs index 6e89af47678..638d7f24d42 100644 --- a/app/assets/javascripts/admin/templates/customize-themes-show.hbs +++ b/app/assets/javascripts/admin/templates/customize-themes-show.hbs @@ -5,7 +5,7 @@ {{d-button action="finishedEditingName" class="btn-primary btn-small submit-edit" icon="check"}} {{d-button action="cancelEditingName" class="btn-small cancel-edit" icon="times"}} {{else}} - {{model.name}} {{fa-icon "pencil"}} + {{model.name}} {{d-icon "pencil"}} {{/if}} @@ -15,7 +15,7 @@

    {{#if model.remote_theme.license_url}}

    - {{i18n "admin.customize.theme.license"}} {{fa-icon "copyright"}} + {{i18n "admin.customize.theme.license"}} {{d-icon "copyright"}}

    {{/if}} {{/if}} @@ -133,8 +133,8 @@ {{/if}} {{/if}} - {{fa-icon 'desktop'}}{{i18n 'admin.customize.theme.preview'}} - {{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}} + {{d-icon 'desktop'}}{{i18n 'admin.customize.theme.preview'}} + {{d-icon "download"}} {{i18n 'admin.export_json.button_text'}} {{d-button action="destroy" label="admin.customize.delete" icon="trash" class="btn-danger"}} diff --git a/app/assets/javascripts/admin/templates/customize-themes.hbs b/app/assets/javascripts/admin/templates/customize-themes.hbs index 43129006a51..c9474917ef6 100644 --- a/app/assets/javascripts/admin/templates/customize-themes.hbs +++ b/app/assets/javascripts/admin/templates/customize-themes.hbs @@ -7,10 +7,10 @@ {{#link-to 'adminCustomizeThemes.show' theme replace=true}} {{theme.name}} {{#if theme.user_selectable}} - {{fa-icon "user"}} + {{d-icon "user"}} {{/if}} {{#if theme.default}} - {{fa-icon "asterisk"}} + {{d-icon "asterisk"}} {{/if}} {{/link-to}} diff --git a/app/assets/javascripts/admin/templates/dashboard.hbs b/app/assets/javascripts/admin/templates/dashboard.hbs index c6c44870094..b8c0297d48d 100644 --- a/app/assets/javascripts/admin/templates/dashboard.hbs +++ b/app/assets/javascripts/admin/templates/dashboard.hbs @@ -29,15 +29,15 @@
    - + - + - + - +
    {{fa-icon "shield"}} {{i18n 'admin.dashboard.admins'}}{{d-icon "shield"}} {{i18n 'admin.dashboard.admins'}} {{#link-to 'adminUsersList.show' 'admins'}}{{admins}}{{/link-to}}{{fa-icon "ban"}} {{i18n 'admin.dashboard.suspended'}}{{d-icon "ban"}} {{i18n 'admin.dashboard.suspended'}} {{#link-to 'adminUsersList.show' 'suspended'}}{{suspended}}{{/link-to}}
    {{fa-icon "shield"}} {{i18n 'admin.dashboard.moderators'}}{{d-icon "shield"}} {{i18n 'admin.dashboard.moderators'}} {{#link-to 'adminUsersList.show' 'moderators'}}{{moderators}}{{/link-to}}{{fa-icon "ban"}} {{i18n 'admin.dashboard.blocked'}}{{d-icon "ban"}} {{i18n 'admin.dashboard.blocked'}} {{#link-to 'adminUsersList.show' 'blocked'}}{{blocked}}{{/link-to}}
    @@ -87,7 +87,7 @@ - + @@ -176,7 +176,7 @@
    {{#if foundProblems}}
    -
    {{fa-icon "exclamation-triangle"}}
    +
    {{d-icon "exclamation-triangle"}}
    {{#conditional-loading-spinner condition=loadingProblems}}

    diff --git a/app/assets/javascripts/admin/templates/email-sent.hbs b/app/assets/javascripts/admin/templates/email-sent.hbs index 847076f098a..9dd7996ff54 100644 --- a/app/assets/javascripts/admin/templates/email-sent.hbs +++ b/app/assets/javascripts/admin/templates/email-sent.hbs @@ -30,7 +30,7 @@ {{/if}}

    diff --git a/app/assets/javascripts/admin/templates/emojis.hbs b/app/assets/javascripts/admin/templates/emojis.hbs index 6f35c097a96..675e8249be0 100644 --- a/app/assets/javascripts/admin/templates/emojis.hbs +++ b/app/assets/javascripts/admin/templates/emojis.hbs @@ -20,7 +20,7 @@ - + {{/each}} diff --git a/app/assets/javascripts/admin/templates/flags-list.hbs b/app/assets/javascripts/admin/templates/flags-list.hbs index 8796f31ff3c..796552d4d2f 100644 --- a/app/assets/javascripts/admin/templates/flags-list.hbs +++ b/app/assets/javascripts/admin/templates/flags-list.hbs @@ -31,7 +31,7 @@

    {{#if flaggedPost.topic.isPrivateMessage}} - {{fa-icon "envelope"}} + {{d-icon "envelope"}} {{/if}} {{topic-status topic=flaggedPost.topic}} {{{unbound flaggedPost.topic.fancyTitle}}} diff --git a/app/assets/javascripts/admin/templates/group.hbs b/app/assets/javascripts/admin/templates/group.hbs index acf6b9e6d07..7ff651e7c9d 100644 --- a/app/assets/javascripts/admin/templates/group.hbs +++ b/app/assets/javascripts/admin/templates/group.hbs @@ -133,7 +133,7 @@
    {{#unless model.automatic}} - + {{/unless}} {{savingStatus}} diff --git a/app/assets/javascripts/admin/templates/groups-type-index.hbs b/app/assets/javascripts/admin/templates/groups-type-index.hbs index 88d870c1f6e..196ded96d2e 100644 --- a/app/assets/javascripts/admin/templates/groups-type-index.hbs +++ b/app/assets/javascripts/admin/templates/groups-type-index.hbs @@ -3,7 +3,7 @@
    {{#link-to 'adminGroup' 'new' class="btn"}} - {{fa-icon "plus"}} {{i18n 'admin.groups.new'}} + {{d-icon "plus"}} {{i18n 'admin.groups.new'}} {{/link-to}}
    diff --git a/app/assets/javascripts/admin/templates/groups-type.hbs b/app/assets/javascripts/admin/templates/groups-type.hbs index b4d2a0acf35..a1fa8298970 100644 --- a/app/assets/javascripts/admin/templates/groups-type.hbs +++ b/app/assets/javascripts/admin/templates/groups-type.hbs @@ -18,7 +18,7 @@ {{d-button action="refreshAutoGroups" icon="refresh" label="admin.groups.refresh" disabled=refreshingAutoGroups}} {{else}} {{#link-to 'adminGroup' 'new' class="btn"}} - {{fa-icon "plus"}} {{i18n 'admin.groups.new'}} + {{d-icon "plus"}} {{i18n 'admin.groups.new'}} {{/link-to}} {{/if}}

    diff --git a/app/assets/javascripts/admin/templates/logs/screened-emails.hbs b/app/assets/javascripts/admin/templates/logs/screened-emails.hbs index 685d8455c39..f99ff865e15 100644 --- a/app/assets/javascripts/admin/templates/logs/screened-emails.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened-emails.hbs @@ -1,6 +1,6 @@

    {{i18n 'admin.logs.screened_emails.description'}} - +


    diff --git a/app/assets/javascripts/admin/templates/logs/screened-ip-addresses.hbs b/app/assets/javascripts/admin/templates/logs/screened-ip-addresses.hbs index ca6c216f502..00e67a9aa5c 100644 --- a/app/assets/javascripts/admin/templates/logs/screened-ip-addresses.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened-ip-addresses.hbs @@ -41,9 +41,9 @@
    {{#if item.isBlocked}} - {{fa-icon "ban"}} + {{d-icon "ban"}} {{else}} - {{fa-icon "check"}} + {{d-icon "check"}} {{/if}} {{item.actionName}}
    diff --git a/app/assets/javascripts/admin/templates/logs/screened-urls.hbs b/app/assets/javascripts/admin/templates/logs/screened-urls.hbs index 40aaceba70d..7b1048f1b44 100644 --- a/app/assets/javascripts/admin/templates/logs/screened-urls.hbs +++ b/app/assets/javascripts/admin/templates/logs/screened-urls.hbs @@ -1,6 +1,6 @@

    {{i18n 'admin.logs.screened_urls.description'}} - +


    diff --git a/app/assets/javascripts/admin/templates/logs/staff-action-logs.hbs b/app/assets/javascripts/admin/templates/logs/staff-action-logs.hbs index f469ec266e8..50f02f9f5b1 100644 --- a/app/assets/javascripts/admin/templates/logs/staff-action-logs.hbs +++ b/app/assets/javascripts/admin/templates/logs/staff-action-logs.hbs @@ -7,25 +7,25 @@ {{#if actionFilter}} {{i18n 'admin.logs.action'}}: {{actionFilter}} - {{fa-icon "times-circle"}} + {{d-icon "times-circle"}} {{/if}} {{#if filters.acting_user}} {{i18n 'admin.logs.staff_actions.staff_user'}}: {{filters.acting_user}} - {{fa-icon "times-circle"}} + {{d-icon "times-circle"}} {{/if}} {{#if filters.target_user}} {{i18n 'admin.logs.staff_actions.target_user'}}: {{filters.target_user}} - {{fa-icon "times-circle"}} + {{d-icon "times-circle"}} {{/if}} {{#if filters.subject}} {{i18n 'admin.logs.staff_actions.subject'}}: {{filters.subject}} - {{fa-icon "times-circle"}} + {{d-icon "times-circle"}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs b/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs index 5286bbf0b07..d58a63c1342 100644 --- a/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin-color-scheme-select-base.hbs @@ -7,6 +7,6 @@ valueAttribute="base_scheme_id"}} {{/d-modal-body}} diff --git a/app/assets/javascripts/admin/templates/modal/admin-edit-badge-groupings.hbs b/app/assets/javascripts/admin/templates/modal/admin-edit-badge-groupings.hbs index 80eb0844b30..f37c1f414d1 100644 --- a/app/assets/javascripts/admin/templates/modal/admin-edit-badge-groupings.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin-edit-badge-groupings.hbs @@ -5,15 +5,15 @@
  • {{#if wc.editing}} {{input value=wc.name}} - + {{else}} {{wc.displayName}} {{/if}}
    - - - - + + + +
  • {{/each}} diff --git a/app/assets/javascripts/admin/templates/site-text-edit.hbs b/app/assets/javascripts/admin/templates/site-text-edit.hbs index 5e02eb07316..4e134433d84 100644 --- a/app/assets/javascripts/admin/templates/site-text-edit.hbs +++ b/app/assets/javascripts/admin/templates/site-text-edit.hbs @@ -13,7 +13,7 @@ {{/save-controls}} {{#link-to 'adminSiteText.index' class="go-back"}} - {{fa-icon 'arrow-left'}} + {{d-icon 'arrow-left'}} {{i18n 'admin.site_text.go_back'}} {{/link-to}} diff --git a/app/assets/javascripts/admin/templates/user-index.hbs b/app/assets/javascripts/admin/templates/user-index.hbs index f6a7a2d75e1..eecc669e6cc 100644 --- a/app/assets/javascripts/admin/templates/user-index.hbs +++ b/app/assets/javascripts/admin/templates/user-index.hbs @@ -3,7 +3,7 @@
    {{#if model.canViewProfile}} {{#link-to 'user' model class="btn"}} - {{fa-icon "user"}} + {{d-icon "user"}} {{i18n 'admin.user.show_public_profile'}} {{/link-to}} {{/if}} @@ -151,7 +151,7 @@ {{i18n 'badges.badge_count' count=model.badge_count}}
    - {{#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}}
    {{/if}} @@ -487,7 +487,7 @@

    - {{fa-icon "exclamation-triangle"}} {{model.deleteExplanation}} + {{d-icon "exclamation-triangle"}} {{model.deleteExplanation}}
    {{/if}} diff --git a/app/assets/javascripts/admin/templates/users-list-show.hbs b/app/assets/javascripts/admin/templates/users-list-show.hbs index cc0c5ce09ef..9bb578af7de 100644 --- a/app/assets/javascripts/admin/templates/users-list-show.hbs +++ b/app/assets/javascripts/admin/templates/users-list-show.hbs @@ -92,10 +92,10 @@ {{/if}} diff --git a/app/assets/javascripts/admin/templates/version-checks.hbs b/app/assets/javascripts/admin/templates/version-checks.hbs index 25c560ad6ae..4f392650267 100644 --- a/app/assets/javascripts/admin/templates/version-checks.hbs +++ b/app/assets/javascripts/admin/templates/version-checks.hbs @@ -18,7 +18,7 @@ {{#if versionCheck.noCheckPerformed}} diff --git a/app/assets/javascripts/discourse-common/helpers/d-icon.js.es6 b/app/assets/javascripts/discourse-common/helpers/d-icon.js.es6 new file mode 100644 index 00000000000..b85b9ebf269 --- /dev/null +++ b/app/assets/javascripts/discourse-common/helpers/d-icon.js.es6 @@ -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)); +}); diff --git a/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6 b/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6 index b8dc58f23a3..bd736c84821 100644 --- a/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6 +++ b/app/assets/javascripts/discourse-common/helpers/fa-icon.js.es6 @@ -1,10 +1,12 @@ import { registerUnbound } from 'discourse-common/lib/helpers'; import { renderIcon } from 'discourse-common/lib/icon-library'; +import deprecated from 'discourse-common/lib/deprecated'; export function iconHTML(id, params) { return renderIcon('string', id, params); } registerUnbound('fa-icon', function(icon, params) { + deprecated("Use `{{d-icon}}` instead of `{{fa-icon}}"); return new Handlebars.SafeString(iconHTML(icon, params)); }); diff --git a/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 b/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 index 04d4629d16d..eac3ea194b5 100644 --- a/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 +++ b/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 @@ -29,7 +29,7 @@ export function registerIconRenderer(renderer) { // Support for font awesome icons function faClasses(id, params) { - let classNames = `fa fa-${id}`; + 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']; } diff --git a/app/assets/javascripts/discourse/templates/about.hbs b/app/assets/javascripts/discourse/templates/about.hbs index b5191b5696a..aa5027486f6 100644 --- a/app/assets/javascripts/discourse/templates/about.hbs +++ b/app/assets/javascripts/discourse/templates/about.hbs @@ -21,7 +21,7 @@ {{#if model.admins}}
    -

    {{fa-icon "users"}} {{i18n 'about.our_admins'}}

    +

    {{d-icon "users"}} {{i18n 'about.our_admins'}}

    {{#each model.admins as |a|}} {{user-info user=a}} @@ -33,7 +33,7 @@ {{#if model.moderators}}
    -

    {{fa-icon "users"}} {{i18n 'about.our_moderators'}}

    +

    {{d-icon "users"}} {{i18n 'about.our_moderators'}}

    {{#each model.moderators as |m|}} @@ -45,7 +45,7 @@ {{/if}}
    -

    {{fa-icon "bar-chart"}} {{i18n 'about.stats'}}

    +

    {{d-icon "bar-chart"}} {{i18n 'about.stats'}}

    {{fa-icon "envelope"}} {{i18n 'admin.dashboard.private_messages_short'}}{{d-icon "envelope"}} {{i18n 'admin.dashboard.private_messages_short'}} {{i18n 'admin.dashboard.reports.today'}} {{i18n 'admin.dashboard.reports.yesterday'}} {{i18n 'admin.dashboard.reports.last_7_days'}} - {{#if l.bounced}}{{fa-icon "repeat" title="admin.email.bounced"}}{{/if}} + {{#if l.bounced}}{{d-icon "repeat" title="admin.email.bounced"}}{{/if}} {{l.to_address}} {{l.email_type}}
    :{{e.name}}:
    {{#if user.admin}} - {{fa-icon "shield" title="admin.title" }} + {{d-icon "shield" title="admin.title" }} {{/if}} {{#if user.moderator}} - {{fa-icon "shield" title="admin.moderator" }} + {{d-icon "shield" title="admin.moderator" }} {{/if}}
    - {{fa-icon "frown-o"}} + {{d-icon "frown-o"}} {{i18n 'admin.dashboard.no_check_performed'}} @@ -28,9 +28,9 @@ {{#if versionCheck.version_check_pending}}{{dash-if-empty versionCheck.installed_version}}{{/if}} {{#if versionCheck.version_check_pending}} - {{fa-icon "smile-o"}} + {{d-icon "smile-o"}} {{else}} - {{fa-icon "frown-o"}} + {{d-icon "frown-o"}} {{/if}} @@ -46,13 +46,13 @@ {{dash-if-empty versionCheck.latest_version}} {{#if versionCheck.upToDate }} - {{fa-icon "smile-o"}} + {{d-icon "smile-o"}} {{else}} {{#if versionCheck.behindByOneVersion}} - {{fa-icon "meh-o"}} + {{d-icon "meh-o"}} {{else}} - {{fa-icon "frown-o"}} + {{d-icon "frown-o"}} {{/if}} {{/if}} diff --git a/app/assets/javascripts/admin/templates/web-hooks-show-events.hbs b/app/assets/javascripts/admin/templates/web-hooks-show-events.hbs index f3b8825e5bf..e0d06098713 100644 --- a/app/assets/javascripts/admin/templates/web-hooks-show-events.hbs +++ b/app/assets/javascripts/admin/templates/web-hooks-show-events.hbs @@ -1,10 +1,10 @@
    {{#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}} {{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'}} - {{fa-icon 'edit'}} {{i18n 'admin.web_hooks.events.go_details'}} + {{d-icon 'edit'}} {{i18n 'admin.web_hooks.events.go_details'}} {{/link-to}}
    diff --git a/app/assets/javascripts/admin/templates/web-hooks-show.hbs b/app/assets/javascripts/admin/templates/web-hooks-show.hbs index c7a8e030888..8530de5f129 100644 --- a/app/assets/javascripts/admin/templates/web-hooks-show.hbs +++ b/app/assets/javascripts/admin/templates/web-hooks-show.hbs @@ -1,5 +1,5 @@ {{#link-to 'adminWebHooks' class="go-back"}} - {{fa-icon 'arrow-left'}} + {{d-icon 'arrow-left'}} {{i18n 'admin.web_hooks.go_back'}} {{/link-to}} @@ -49,12 +49,12 @@
    - + {{category-selector categories=model.categories blacklist=model.categories}}
    {{i18n 'admin.web_hooks.categories_filter_instructions'}}
    - + {{group-selector groupNames=model.groupsFilterInName groupFinder=model.groupFinder}}
    {{i18n 'admin.web_hooks.groups_filter_instructions'}}
    diff --git a/app/assets/javascripts/admin/templates/web-hooks.hbs b/app/assets/javascripts/admin/templates/web-hooks.hbs index 082cf170b41..81d4b86e4dc 100644 --- a/app/assets/javascripts/admin/templates/web-hooks.hbs +++ b/app/assets/javascripts/admin/templates/web-hooks.hbs @@ -1,6 +1,6 @@
    {{#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}}
    @@ -24,7 +24,7 @@
    {{#link-to 'adminWebHooks.show' webHook}}{{webHook.payload_url}}{{/link-to}} {{webHook.description}} - {{#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"}}
    @@ -89,7 +89,7 @@ {{#if contactInfo}}
    -

    {{fa-icon "envelope-o"}} {{i18n 'about.contact'}}

    +

    {{d-icon "envelope-o"}} {{i18n 'about.contact'}}

    {{{contactInfo}}}

    {{/if}} diff --git a/app/assets/javascripts/discourse/templates/category-tag-autocomplete.raw.hbs b/app/assets/javascripts/discourse/templates/category-tag-autocomplete.raw.hbs index efbf18a33bf..1c429ddb39d 100644 --- a/app/assets/javascripts/discourse/templates/category-tag-autocomplete.raw.hbs +++ b/app/assets/javascripts/discourse/templates/category-tag-autocomplete.raw.hbs @@ -5,7 +5,7 @@ {{#if option.model}} {{category-link option.model allowUncategorized="true" link="false"}} {{else}} - {{fa-icon 'tag'}}{{option.text}} x {{option.count}} + {{d-icon 'tag'}}{{option.text}} x {{option.count}} {{/if}} {{/each}} diff --git a/app/assets/javascripts/discourse/templates/components/auto-update-input.hbs b/app/assets/javascripts/discourse/templates/components/auto-update-input.hbs index 1e6fbe778ba..50f0a8ee258 100644 --- a/app/assets/javascripts/discourse/templates/components/auto-update-input.hbs +++ b/app/assets/javascripts/discourse/templates/components/auto-update-input.hbs @@ -14,11 +14,11 @@ {{#if isCustom}}
    - {{fa-icon "calendar"}} {{date-picker-future value=date defaultDate=date}} + {{d-icon "calendar"}} {{date-picker-future value=date defaultDate=date}}
    - {{fa-icon "clock-o"}} + {{d-icon "clock-o"}} {{input type="time" value=time}}
    {{/if}} @@ -33,7 +33,7 @@ {{#if willCloseImmediately}}
    - {{fa-icon "warning"}} + {{d-icon "warning"}} {{willCloseI18n}}
    {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/avatar-uploader.hbs b/app/assets/javascripts/discourse/templates/components/avatar-uploader.hbs index 89c46363e34..b43cbb0d567 100644 --- a/app/assets/javascripts/discourse/templates/components/avatar-uploader.hbs +++ b/app/assets/javascripts/discourse/templates/components/avatar-uploader.hbs @@ -1,5 +1,5 @@ {{#if uploading}} diff --git a/app/assets/javascripts/discourse/templates/components/badge-card.hbs b/app/assets/javascripts/discourse/templates/components/badge-card.hbs index 93d3c490b6e..3c519d85ddf 100644 --- a/app/assets/javascripts/discourse/templates/components/badge-card.hbs +++ b/app/assets/javascripts/discourse/templates/components/badge-card.hbs @@ -2,7 +2,7 @@ {{displayCount}} {{/if}} {{#if badge.has_badge}} - {{fa-icon "check"}} + {{d-icon "check"}} {{/if}}
    diff --git a/app/assets/javascripts/discourse/templates/components/categories-boxes-with-topics.hbs b/app/assets/javascripts/discourse/templates/components/categories-boxes-with-topics.hbs index 8909e8a9bab..434e18fe629 100644 --- a/app/assets/javascripts/discourse/templates/components/categories-boxes-with-topics.hbs +++ b/app/assets/javascripts/discourse/templates/components/categories-boxes-with-topics.hbs @@ -9,7 +9,7 @@

    {{#if c.read_restricted}} - {{fa-icon 'lock'}} + {{d-icon 'lock'}} {{/if}} {{c.name}}

    diff --git a/app/assets/javascripts/discourse/templates/components/categories-boxes.hbs b/app/assets/javascripts/discourse/templates/components/categories-boxes.hbs index 3bb4fde6653..c473668648f 100644 --- a/app/assets/javascripts/discourse/templates/components/categories-boxes.hbs +++ b/app/assets/javascripts/discourse/templates/components/categories-boxes.hbs @@ -9,7 +9,7 @@

    {{#if c.read_restricted}} - {{fa-icon 'lock'}} + {{d-icon 'lock'}} {{/if}} {{c.name}}

    diff --git a/app/assets/javascripts/discourse/templates/components/category-drop.hbs b/app/assets/javascripts/discourse/templates/components/category-drop.hbs index 9e9cc410e57..339d4927b60 100644 --- a/app/assets/javascripts/discourse/templates/components/category-drop.hbs +++ b/app/assets/javascripts/discourse/templates/components/category-drop.hbs @@ -2,7 +2,7 @@ {{#if category.read_restricted}} - {{fa-icon "lock"}} + {{d-icon "lock"}} {{/if}} {{category.name}} diff --git a/app/assets/javascripts/discourse/templates/components/category-title-link.hbs b/app/assets/javascripts/discourse/templates/components/category-title-link.hbs index 49e3b088f18..fcb0bdeb071 100644 --- a/app/assets/javascripts/discourse/templates/components/category-title-link.hbs +++ b/app/assets/javascripts/discourse/templates/components/category-title-link.hbs @@ -1,6 +1,6 @@ {{#if category.read_restricted}} - {{fa-icon 'lock'}} + {{d-icon 'lock'}} {{/if}} {{category.name}} diff --git a/app/assets/javascripts/discourse/templates/components/composer-editor.hbs b/app/assets/javascripts/discourse/templates/components/composer-editor.hbs index 46daaeb10e6..3d3f6fc8eb9 100644 --- a/app/assets/javascripts/discourse/templates/components/composer-editor.hbs +++ b/app/assets/javascripts/discourse/templates/components/composer-editor.hbs @@ -30,7 +30,7 @@ {{loading-spinner size="small"}} {{i18n 'upload_selector.uploading'}} {{uploadProgress}}% {{#if isCancellable}} - {{fa-icon "times"}} + {{d-icon "times"}} {{/if}}
    {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/csv-uploader.hbs b/app/assets/javascripts/discourse/templates/components/csv-uploader.hbs index a29839a7909..859433546c3 100644 --- a/app/assets/javascripts/discourse/templates/components/csv-uploader.hbs +++ b/app/assets/javascripts/discourse/templates/components/csv-uploader.hbs @@ -1,5 +1,5 @@ {{#if uploading}} diff --git a/app/assets/javascripts/discourse/templates/components/d-button.hbs b/app/assets/javascripts/discourse/templates/components/d-button.hbs index e01a9bb5c82..d06d14e66df 100644 --- a/app/assets/javascripts/discourse/templates/components/d-button.hbs +++ b/app/assets/javascripts/discourse/templates/components/d-button.hbs @@ -1,5 +1,5 @@ {{#if icon}} - {{fa-icon icon}} + {{d-icon icon}} {{/if}} {{#if translatedLabel}} diff --git a/app/assets/javascripts/discourse/templates/components/disabled-icon.hbs b/app/assets/javascripts/discourse/templates/components/disabled-icon.hbs index b7749feceec..100849f3123 100644 --- a/app/assets/javascripts/discourse/templates/components/disabled-icon.hbs +++ b/app/assets/javascripts/discourse/templates/components/disabled-icon.hbs @@ -1,5 +1,5 @@ -{{fa-icon icon modifier="stack-2x"}} +{{d-icon icon modifier="stack-2x"}} {{#if disabled}} - {{fa-icon "ban" modifier="stack-2x"}} + {{d-icon "ban" modifier="stack-2x"}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/edit-category-security.hbs b/app/assets/javascripts/discourse/templates/components/edit-category-security.hbs index 760863befee..a389e2ceab0 100644 --- a/app/assets/javascripts/discourse/templates/components/edit-category-security.hbs +++ b/app/assets/javascripts/discourse/templates/components/edit-category-security.hbs @@ -9,7 +9,7 @@ {{{i18n "category.can"}}} {{p.permission.description}} {{#if editingPermissions}} - {{fa-icon "times-circle"}} + {{d-icon "times-circle"}} {{/if}} {{/each}} diff --git a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs index ddfb1ce11be..05f70e3b27a 100644 --- a/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs +++ b/app/assets/javascripts/discourse/templates/components/edit-category-settings.hbs @@ -107,7 +107,7 @@
    diff --git a/app/assets/javascripts/discourse/templates/components/emoji-uploader.hbs b/app/assets/javascripts/discourse/templates/components/emoji-uploader.hbs index 1fb6a26eac4..85ad4157bb3 100644 --- a/app/assets/javascripts/discourse/templates/components/emoji-uploader.hbs +++ b/app/assets/javascripts/discourse/templates/components/emoji-uploader.hbs @@ -1,6 +1,6 @@ {{text-field name="name" placeholderKey="admin.emoji.name" value=name}} diff --git a/app/assets/javascripts/discourse/templates/components/expand-post.hbs b/app/assets/javascripts/discourse/templates/components/expand-post.hbs index 4d3192668d1..94ba1d62907 100644 --- a/app/assets/javascripts/discourse/templates/components/expand-post.hbs +++ b/app/assets/javascripts/discourse/templates/components/expand-post.hbs @@ -1,5 +1,5 @@ {{#if item.truncated}} - {{fa-icon "chevron-down"}} + {{d-icon "chevron-down"}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/flat-button.hbs b/app/assets/javascripts/discourse/templates/components/flat-button.hbs index 41ee1dc6407..5c8be2b5eab 100644 --- a/app/assets/javascripts/discourse/templates/components/flat-button.hbs +++ b/app/assets/javascripts/discourse/templates/components/flat-button.hbs @@ -1 +1 @@ -{{fa-icon icon}} +{{d-icon icon}} diff --git a/app/assets/javascripts/discourse/templates/components/group-logs-filter.hbs b/app/assets/javascripts/discourse/templates/components/group-logs-filter.hbs index 11c1eb7b558..f9f13474c7f 100644 --- a/app/assets/javascripts/discourse/templates/components/group-logs-filter.hbs +++ b/app/assets/javascripts/discourse/templates/components/group-logs-filter.hbs @@ -1,6 +1,6 @@ {{#if value}} {{#d-button class="btn-small group-logs-filter" action="clearFilter" actionParam=type}} {{label}}: {{filterText}} - {{fa-icon "times-circle"}} + {{d-icon "times-circle"}} {{/d-button}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/group-logs-row.hbs b/app/assets/javascripts/discourse/templates/components/group-logs-row.hbs index 31d63039952..6fe9bfd858a 100644 --- a/app/assets/javascripts/discourse/templates/components/group-logs-row.hbs +++ b/app/assets/javascripts/discourse/templates/components/group-logs-row.hbs @@ -36,9 +36,9 @@
    diff --git a/app/assets/javascripts/discourse/templates/components/group-member.hbs b/app/assets/javascripts/discourse/templates/components/group-member.hbs index d476fdf033a..ddc6456b833 100644 --- a/app/assets/javascripts/discourse/templates/components/group-member.hbs +++ b/app/assets/javascripts/discourse/templates/components/group-member.hbs @@ -1 +1 @@ -{{avatar member imageSize="small"}} {{member.username}} {{#unless automatic}}{{fa-icon "times"}}{{/unless}} +{{avatar member imageSize="small"}} {{member.username}} {{#unless automatic}}{{d-icon "times"}}{{/unless}} diff --git a/app/assets/javascripts/discourse/templates/components/group-members-input.hbs b/app/assets/javascripts/discourse/templates/components/group-members-input.hbs index 9127b7096de..e4f4e0c1adb 100644 --- a/app/assets/javascripts/discourse/templates/components/group-members-input.hbs +++ b/app/assets/javascripts/discourse/templates/components/group-members-input.hbs @@ -2,9 +2,9 @@ {{#if model.members}}
    {{#each model.members as |member|}} diff --git a/app/assets/javascripts/discourse/templates/components/image-uploader.hbs b/app/assets/javascripts/discourse/templates/components/image-uploader.hbs index 14f0695fd2e..5a12cff39bc 100644 --- a/app/assets/javascripts/discourse/templates/components/image-uploader.hbs +++ b/app/assets/javascripts/discourse/templates/components/image-uploader.hbs @@ -1,11 +1,11 @@
    {{#if backgroundStyle}} - + {{/if}} {{i18n 'upload_selector.uploading'}} {{uploadProgress}}%
    diff --git a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs index 35d5afd0bb4..44e1b1809b9 100644 --- a/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs +++ b/app/assets/javascripts/discourse/templates/components/ip-lookup.hbs @@ -1,11 +1,11 @@ {{#if ip}} {{/if}} {{#if show}}
    - {{fa-icon "times"}} + {{d-icon "times"}}

    {{i18n 'ip_lookup.title'}}

    {{#if location}} @@ -42,7 +42,7 @@ {{totalOthersWithSameIP}} {{#if other_accounts.length}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/period-chooser.hbs b/app/assets/javascripts/discourse/templates/components/period-chooser.hbs index 3defac6a02c..ce4d6066c57 100644 --- a/app/assets/javascripts/discourse/templates/components/period-chooser.hbs +++ b/app/assets/javascripts/discourse/templates/components/period-chooser.hbs @@ -1,5 +1,5 @@

    {{period-title period showDateRange=true}}

    - +
      diff --git a/app/assets/javascripts/discourse/templates/components/share-popup.hbs b/app/assets/javascripts/discourse/templates/components/share-popup.hbs index b6f77b67c3d..ad3dc852dd4 100644 --- a/app/assets/javascripts/discourse/templates/components/share-popup.hbs +++ b/app/assets/javascripts/discourse/templates/components/share-popup.hbs @@ -16,13 +16,13 @@ {{#if topic.details.can_reply_as_new_topic}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/components/small-action.hbs b/app/assets/javascripts/discourse/templates/components/small-action.hbs index 821ed4ff910..852ef95abaa 100644 --- a/app/assets/javascripts/discourse/templates/components/small-action.hbs +++ b/app/assets/javascripts/discourse/templates/components/small-action.hbs @@ -1,10 +1,10 @@
      {{#if post}} {{#if post.can_delete}} - + {{/if}} {{#if post.can_edit}} - + {{/if}} {{avatar post imageSize="small"}} diff --git a/app/assets/javascripts/discourse/templates/components/stream-item.hbs b/app/assets/javascripts/discourse/templates/components/stream-item.hbs index ea3c9dcec98..36d572a488b 100644 --- a/app/assets/javascripts/discourse/templates/components/stream-item.hbs +++ b/app/assets/javascripts/discourse/templates/components/stream-item.hbs @@ -10,7 +10,7 @@ {{#if item.deleted_by}} - {{fa-icon "trash-o"}} + {{d-icon "trash-o"}} {{avatar item.deleted_by imageSize="tiny" extraClasses="actor" ignoreTitle="true"}} {{format-date item.deleted_at leaveAgo="true"}} @@ -31,7 +31,7 @@ {{#each child.items as |grandChild|}} {{#if grandChild.removableBookmark}} {{else}}
      {{avatar grandChild imageSize="tiny" extraClasses="actor" ignoreTitle="true" avatarTemplatePath="acting_avatar_template"}}
      diff --git a/app/assets/javascripts/discourse/templates/components/topic-entrance.hbs b/app/assets/javascripts/discourse/templates/components/topic-entrance.hbs index 399ac0bdafd..c6885392c4c 100644 --- a/app/assets/javascripts/discourse/templates/components/topic-entrance.hbs +++ b/app/assets/javascripts/discourse/templates/components/topic-entrance.hbs @@ -1,7 +1,7 @@ {{#d-button action="enterTop" class="full jump-top"}} - {{fa-icon 'caret-up'}} {{{topDate}}} + {{d-icon 'caret-up'}} {{{topDate}}} {{/d-button}} {{#d-button action="enterBottom" class="full jump-button"}} - {{{bottomDate}}} {{fa-icon 'caret-down'}} + {{{bottomDate}}} {{d-icon 'caret-down'}} {{/d-button}} diff --git a/app/assets/javascripts/discourse/templates/components/topic-footer-buttons.hbs b/app/assets/javascripts/discourse/templates/components/topic-footer-buttons.hbs index 7e28cc0f9a6..15b5882a004 100644 --- a/app/assets/javascripts/discourse/templates/components/topic-footer-buttons.hbs +++ b/app/assets/javascripts/discourse/templates/components/topic-footer-buttons.hbs @@ -28,7 +28,7 @@ action=toggleBookmark}} diff --git a/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs b/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs index a7041a50657..1c948ba800a 100644 --- a/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs +++ b/app/assets/javascripts/discourse/templates/components/user-card-contents.hbs @@ -36,25 +36,25 @@ {{#if isSuspended}}
      - {{fa-icon "ban"}} + {{d-icon "ban"}} {{i18n 'user.suspended_notice' date=user.suspendedTillDate}}
      {{i18n 'user.suspended_reason'}} {{user.suspend_reason}}
      @@ -71,12 +71,12 @@ {{#if hasLocationOrWebsite}}
      {{#if user.location}} - {{fa-icon "map-marker"}} {{user.location}} + {{d-icon "map-marker"}} {{user.location}} {{/if}} {{#if user.website_name}} - {{fa-icon "globe"}} + {{d-icon "globe"}} {{#if linkWebsite}} {{user.website_name}} {{else}} diff --git a/app/assets/javascripts/discourse/templates/components/user-stat.hbs b/app/assets/javascripts/discourse/templates/components/user-stat.hbs index 67d28d72763..72bfaedac6e 100644 --- a/app/assets/javascripts/discourse/templates/components/user-stat.hbs +++ b/app/assets/javascripts/discourse/templates/components/user-stat.hbs @@ -1,5 +1,5 @@ - {{#if icon}}{{fa-icon icon}}{{/if}} + {{#if icon}}{{d-icon icon}}{{/if}} {{number value}} {{{i18n label count=value}}} diff --git a/app/assets/javascripts/discourse/templates/composer.hbs b/app/assets/javascripts/discourse/templates/composer.hbs index 24912fed6be..7a57b20d638 100644 --- a/app/assets/javascripts/discourse/templates/composer.hbs +++ b/app/assets/javascripts/discourse/templates/composer.hbs @@ -130,7 +130,7 @@
      {{#if model.topic}} - {{fa-icon "mail-forward"}} {{{draftTitle}}} + {{d-icon "mail-forward"}} {{{draftTitle}}} {{else}} {{i18n "composer.saved_draft"}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/composer/custom-body.hbs b/app/assets/javascripts/discourse/templates/composer/custom-body.hbs index 4b2be21c2de..60493d75265 100644 --- a/app/assets/javascripts/discourse/templates/composer/custom-body.hbs +++ b/app/assets/javascripts/discourse/templates/composer/custom-body.hbs @@ -1,3 +1,3 @@ -{{fa-icon "close"}} +{{d-icon "close"}} {{#if message.title}}

      {{message.title}}

      {{/if}}

      {{{message.body}}}

      diff --git a/app/assets/javascripts/discourse/templates/composer/education.hbs b/app/assets/javascripts/discourse/templates/composer/education.hbs index a461a9b5127..c2d574cb690 100644 --- a/app/assets/javascripts/discourse/templates/composer/education.hbs +++ b/app/assets/javascripts/discourse/templates/composer/education.hbs @@ -1,2 +1,2 @@ -{{fa-icon "times"}} +{{d-icon "times"}} {{{message.body}}} diff --git a/app/assets/javascripts/discourse/templates/composer/group-mentioned.hbs b/app/assets/javascripts/discourse/templates/composer/group-mentioned.hbs index 8497ac5dbdc..a91a313e0f8 100644 --- a/app/assets/javascripts/discourse/templates/composer/group-mentioned.hbs +++ b/app/assets/javascripts/discourse/templates/composer/group-mentioned.hbs @@ -1,2 +1,2 @@ -{{fa-icon "close"}} +{{d-icon "close"}} {{{message.body}}} diff --git a/app/assets/javascripts/discourse/templates/composer/similar-topics.hbs b/app/assets/javascripts/discourse/templates/composer/similar-topics.hbs index 304352f5496..bdd2cd5a3f2 100644 --- a/app/assets/javascripts/discourse/templates/composer/similar-topics.hbs +++ b/app/assets/javascripts/discourse/templates/composer/similar-topics.hbs @@ -1,4 +1,4 @@ -{{fa-icon "close"}} +{{d-icon "close"}}

      {{i18n 'composer.similar_topics'}}

        diff --git a/app/assets/javascripts/discourse/templates/emoji-picker.raw.hbs.erb b/app/assets/javascripts/discourse/templates/emoji-picker.raw.hbs.erb index 74c0327485b..e2570e95b05 100644 --- a/app/assets/javascripts/discourse/templates/emoji-picker.raw.hbs.erb +++ b/app/assets/javascripts/discourse/templates/emoji-picker.raw.hbs.erb @@ -18,10 +18,10 @@
        - {{fa-icon 'search'}} + {{d-icon 'search'}}
        @@ -31,7 +31,7 @@
        {{i18n 'emoji_picker.recent'}} - {{fa-icon 'trash'}} + {{d-icon 'trash'}}
        @@ -67,7 +67,7 @@
        <% ['default', 'light', 'medium-light', 'medium', 'medium-dark', 'dark'].each.with_index do |diversity, index| %> - {{fa-icon "check"}} + {{d-icon "check"}} <% end %>
        diff --git a/app/assets/javascripts/discourse/templates/flat-button.raw.hbs b/app/assets/javascripts/discourse/templates/flat-button.raw.hbs index 74384745a80..9b5f10eebc3 100644 --- a/app/assets/javascripts/discourse/templates/flat-button.raw.hbs +++ b/app/assets/javascripts/discourse/templates/flat-button.raw.hbs @@ -1,3 +1,3 @@ diff --git a/app/assets/javascripts/discourse/templates/full-page-search.hbs b/app/assets/javascripts/discourse/templates/full-page-search.hbs index e3962efe12f..60b3fad1612 100644 --- a/app/assets/javascripts/discourse/templates/full-page-search.hbs +++ b/app/assets/javascripts/discourse/templates/full-page-search.hbs @@ -116,7 +116,7 @@ {{#if showLikeCount}} {{#if result.like_count}} {{/if}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/group.hbs b/app/assets/javascripts/discourse/templates/group.hbs index 2da7d242cb3..aa34b15c1a0 100644 --- a/app/assets/javascripts/discourse/templates/group.hbs +++ b/app/assets/javascripts/discourse/templates/group.hbs @@ -35,7 +35,7 @@ {{#each getTabs as |tab|}}
      • {{#link-to tab.location model title=tab.message}} - {{#if tab.icon}}{{fa-icon tab.icon}}{{/if}} + {{#if tab.icon}}{{d-icon tab.icon}}{{/if}} {{tab.message}} {{#if tab.count}}({{tab.count}}){{/if}} {{/link-to}} diff --git a/app/assets/javascripts/discourse/templates/modal.hbs b/app/assets/javascripts/discourse/templates/modal.hbs index 0366d78a541..cd7b2c616ab 100644 --- a/app/assets/javascripts/discourse/templates/modal.hbs +++ b/app/assets/javascripts/discourse/templates/modal.hbs @@ -4,7 +4,7 @@
        - {{fa-icon "pencil"}} + {{d-icon "pencil"}} {{#link-to 'user' model.username}} {{bound-avatar-template model.avatar_template "small"}} {{model.username}} {{/link-to}} diff --git a/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs b/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs index 8cd51857b7c..a94f8b40e41 100644 --- a/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs +++ b/app/assets/javascripts/discourse/templates/modal/merge-topic.hbs @@ -8,6 +8,6 @@ diff --git a/app/assets/javascripts/discourse/templates/modal/split-topic.hbs b/app/assets/javascripts/discourse/templates/modal/split-topic.hbs index b18a2d793b2..3902c3daf1a 100644 --- a/app/assets/javascripts/discourse/templates/modal/split-topic.hbs +++ b/app/assets/javascripts/discourse/templates/modal/split-topic.hbs @@ -12,6 +12,6 @@ diff --git a/app/assets/javascripts/discourse/templates/preferences/account.hbs b/app/assets/javascripts/discourse/templates/preferences/account.hbs index 27e50337a38..a1fd8b1d3d4 100644 --- a/app/assets/javascripts/discourse/templates/preferences/account.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/account.hbs @@ -34,7 +34,7 @@
        {{model.email}} {{#if model.can_edit_email}} - {{#link-to "preferences.email" class="btn btn-small pad-left no-text"}}{{fa-icon "pencil"}}{{/link-to}} + {{#link-to "preferences.email" class="btn btn-small pad-left no-text"}}{{d-icon "pencil"}}{{/link-to}} {{/if}}
        diff --git a/app/assets/javascripts/discourse/templates/tags/show.hbs b/app/assets/javascripts/discourse/templates/tags/show.hbs index 5ad165da8cb..9a7e54844a0 100644 --- a/app/assets/javascripts/discourse/templates/tags/show.hbs +++ b/app/assets/javascripts/discourse/templates/tags/show.hbs @@ -30,7 +30,7 @@ {{else}}

        {{#link-to 'tags'}}{{i18n "tagging.tags"}}{{/link-to}} - {{fa-icon "angle-right"}} + {{d-icon "angle-right"}} {{discourse-tag-bound tagRecord=tag style="simple"}} {{#each additionalTags as |tag|}} & diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs index aa0627534b5..db748edd814 100644 --- a/app/assets/javascripts/discourse/templates/topic.hbs +++ b/app/assets/javascripts/discourse/templates/topic.hbs @@ -13,7 +13,7 @@ {{#topic-title cancelled="cancelEditingTopic" save="finishedEditingTopic" model=model}} {{#if editingTopic}} {{#if model.isPrivateMessage}} - {{fa-icon "envelope"}} + {{d-icon "envelope"}} {{/if}} {{text-field id="edit-title" value=buffered.title maxlength=siteSettings.max_topic_title_length autofocus="true"}} @@ -36,7 +36,7 @@

        {{#unless model.is_warning}} - {{fa-icon "envelope"}} + {{d-icon "envelope"}} {{/unless}} @@ -48,7 +48,7 @@ {{/if}} {{#if model.details.can_edit}} - {{fa-icon "pencil"}} + {{d-icon "pencil"}} {{/if}}

        @@ -230,7 +230,7 @@ {{#if currentUser.show_queued_posts}} {{#link-to "queued-posts"}} - {{fa-icon "check"}} + {{d-icon "check"}} {{i18n "queue.view_pending"}} {{/link-to}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/user-invited-show.hbs b/app/assets/javascripts/discourse/templates/user-invited-show.hbs index 12a26aa3a0c..fab58ece1f6 100644 --- a/app/assets/javascripts/discourse/templates/user-invited-show.hbs +++ b/app/assets/javascripts/discourse/templates/user-invited-show.hbs @@ -17,7 +17,7 @@ {{d-button icon="plus" action="showInvite" label="user.invited.create" class="btn"}} {{#if canBulkInvite}} {{csv-uploader uploading=uploading}} - {{fa-icon "question-circle"}} + {{d-icon "question-circle"}} {{/if}} {{#if showBulkActionButtons}} {{#if rescindedAll}} diff --git a/app/assets/javascripts/discourse/templates/user.hbs b/app/assets/javascripts/discourse/templates/user.hbs index 7970c3f9a00..5634f641028 100644 --- a/app/assets/javascripts/discourse/templates/user.hbs +++ b/app/assets/javascripts/discourse/templates/user.hbs @@ -39,13 +39,13 @@ {{#if model.can_send_private_message_to_user}}
      • - {{fa-icon "envelope"}} + {{d-icon "envelope"}} {{i18n 'user.private_message'}}
      • {{/if}} {{#if currentUser.staff}} -
      • {{fa-icon "wrench"}}{{i18n 'admin.user.show_admin_profile'}}
      • +
      • {{d-icon "wrench"}}{{i18n 'admin.user.show_admin_profile'}}
      • {{/if}} {{plugin-outlet name="user-profile-controls" connectorTagName="li" @@ -53,7 +53,7 @@ {{#if collapsedInfo}} {{#if viewingSelf}} -
      • {{fa-icon "angle-double-down"}}{{i18n 'user.expand_profile'}}
      • +
      • {{d-icon "angle-double-down"}}{{i18n 'user.expand_profile'}}
      • {{/if}} {{/if}} @@ -68,9 +68,9 @@ {{/if}} {{plugin-outlet name="user-post-names" args=(hash model=model)}}

        - {{#if model.location}}{{fa-icon "map-marker"}} {{model.location}}{{/if}} + {{#if model.location}}{{d-icon "map-marker"}} {{model.location}}{{/if}} {{#if model.website_name}} - {{fa-icon "globe"}} + {{d-icon "globe"}} {{#if linkWebsite}} {{model.website_name}} {{else}} @@ -83,7 +83,7 @@
        {{#if model.isSuspended}}
        - {{fa-icon "ban"}} + {{d-icon "ban"}} {{i18n 'user.suspended_notice' date=model.suspendedTillDate}}
        {{i18n 'user.suspended_reason'}} {{model.suspend_reason}}
        @@ -167,22 +167,22 @@ {{#if showNotificationsTab}}
      • {{#link-to 'userNotifications'}} - {{fa-icon "comment" class="glyph"}}{{i18n 'user.notifications'}} + {{d-icon "comment" class="glyph"}}{{i18n 'user.notifications'}} {{/link-to}}
      • {{/if}} {{#if showPrivateMessages}} -
      • {{#link-to 'userPrivateMessages'}}{{fa-icon "envelope-o"}}{{i18n 'user.private_messages'}}{{/link-to}}
      • +
      • {{#link-to 'userPrivateMessages'}}{{d-icon "envelope-o"}}{{i18n 'user.private_messages'}}{{/link-to}}
      • {{/if}} {{#if canInviteToForum}} -
      • {{#link-to 'userInvited'}}{{fa-icon "user-plus"}}{{i18n 'user.invited.title'}}{{/link-to}}
      • +
      • {{#link-to 'userInvited'}}{{d-icon "user-plus"}}{{i18n 'user.invited.title'}}{{/link-to}}
      • {{/if}} {{#if showBadges}} -
      • {{#link-to 'user.badges'}}{{fa-icon "certificate"}}{{i18n 'badges.title'}}{{/link-to}}
      • +
      • {{#link-to 'user.badges'}}{{d-icon "certificate"}}{{i18n 'badges.title'}}{{/link-to}}
      • {{/if}} {{plugin-outlet name="user-main-nav" connectorTagName='li' args=(hash model=model)}} {{#if model.can_edit}} -
      • {{#link-to 'preferences'}}{{fa-icon "cog"}}{{i18n 'user.preferences'}}{{/link-to}}
      • +
      • {{#link-to 'preferences'}}{{d-icon "cog"}}{{i18n 'user.preferences'}}{{/link-to}}
      • {{/if}} {{/mobile-nav}} diff --git a/app/assets/javascripts/discourse/templates/user/summary.hbs b/app/assets/javascripts/discourse/templates/user/summary.hbs index 19150c2be89..d28eef4a30a 100644 --- a/app/assets/javascripts/discourse/templates/user/summary.hbs +++ b/app/assets/javascripts/discourse/templates/user/summary.hbs @@ -54,7 +54,7 @@ {{format-date reply.createdAt format="tiny" noTitle="true"}} {{#if reply.like_count}} · - {{fa-icon 'heart'}}  + {{d-icon 'heart'}}  {{/if}}
        @@ -79,7 +79,7 @@ {{format-date topic.createdAt format="tiny" noTitle="true"}} {{#if topic.like_count}} · - {{fa-icon 'heart'}}  + {{d-icon 'heart'}}  {{/if}}
        @@ -127,7 +127,7 @@ {{#each model.most_replied_to_users as |user|}}
      • {{#user-info user=user}} - {{fa-icon "reply"}} + {{d-icon "reply"}} {{number user.count}} {{/user-info}}
      • @@ -147,7 +147,7 @@ {{#each model.most_liked_by_users as |user|}}
      • {{#user-info user=user}} - {{fa-icon "heart"}} + {{d-icon "heart"}} {{/user-info}}
      • @@ -164,7 +164,7 @@ {{#each model.most_liked_users as |user|}}
      • {{#user-info user=user}} - {{fa-icon "heart"}} + {{d-icon "heart"}} {{/user-info}}
      • diff --git a/app/assets/javascripts/wizard/templates/components/invite-list-user.hbs b/app/assets/javascripts/wizard/templates/components/invite-list-user.hbs index b957827ad1b..c31d80349ad 100644 --- a/app/assets/javascripts/wizard/templates/components/invite-list-user.hbs +++ b/app/assets/javascripts/wizard/templates/components/invite-list-user.hbs @@ -2,5 +2,5 @@ {{roleName}} diff --git a/app/assets/javascripts/wizard/templates/components/invite-list.hbs b/app/assets/javascripts/wizard/templates/components/invite-list.hbs index 6b383776682..718ef5bd16f 100644 --- a/app/assets/javascripts/wizard/templates/components/invite-list.hbs +++ b/app/assets/javascripts/wizard/templates/components/invite-list.hbs @@ -15,6 +15,6 @@ {{combo-box value=inviteRole content=roles nameProperty="label" width="200px"}}
        diff --git a/app/assets/javascripts/wizard/templates/components/radio-button.hbs b/app/assets/javascripts/wizard/templates/components/radio-button.hbs index 3973d91324a..e93347513a1 100644 --- a/app/assets/javascripts/wizard/templates/components/radio-button.hbs +++ b/app/assets/javascripts/wizard/templates/components/radio-button.hbs @@ -2,7 +2,7 @@ {{#if icon}} - {{fa-icon icon}} + {{d-icon icon}} {{/if}} {{label}} diff --git a/app/assets/javascripts/wizard/templates/components/wizard-field-image.hbs b/app/assets/javascripts/wizard/templates/components/wizard-field-image.hbs index 209f344e0f5..05f1cb29517 100644 --- a/app/assets/javascripts/wizard/templates/components/wizard-field-image.hbs +++ b/app/assets/javascripts/wizard/templates/components/wizard-field-image.hbs @@ -7,7 +7,7 @@ {{i18n "wizard.uploading"}} {{else}} {{i18n "wizard.upload"}} - {{fa-icon "picture-o"}} + {{d-icon "picture-o"}} {{/if}} diff --git a/app/assets/javascripts/wizard/templates/components/wizard-step.hbs b/app/assets/javascripts/wizard/templates/components/wizard-step.hbs index 9c945cf60a2..b7371dc1b5a 100644 --- a/app/assets/javascripts/wizard/templates/components/wizard-step.hbs +++ b/app/assets/javascripts/wizard/templates/components/wizard-step.hbs @@ -39,7 +39,7 @@ {{#if showNextButton}} {{/if}} From 5b590b96373a8c2f1f211aef63bc8317f1cb776a Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 26 Jul 2017 16:33:17 -0400 Subject: [PATCH 22/59] REFACTOR: Replace some `fa-*` uses with helpers --- .../admin/components/resumable-upload.js.es6 | 3 +- .../admin/helpers/check-icon.js.es6 | 7 ++++ .../admin/models/admin-user.js.es6 | 11 ++--- .../admin/models/flagged-post.js.es6 | 13 +++--- .../javascripts/admin/templates/api-keys.hbs | 2 +- .../admin/templates/customize-colors-show.hbs | 6 +-- .../admin/templates/customize-themes-edit.hbs | 2 +- .../admin/templates/flags-list.hbs | 19 +++++---- .../templates/modal/admin-agree-flag.hbs | 8 ++-- .../templates/modal/admin-badge-preview.hbs | 2 +- .../templates/modal/admin-delete-flag.hbs | 6 +-- .../templates/modal/admin-suspend-user.hbs | 2 +- .../admin/templates/user-badges.hbs | 2 +- .../admin/templates/user-index.hbs | 6 ++- .../admin/templates/user-tl3-requirements.hbs | 40 +++++++++---------- .../components/combo-box.js.es6 | 3 +- .../discourse-common/lib/icon-library.js.es6 | 5 ++- .../auto-update-input-selector.js.es6 | 5 +-- .../categories-admin-dropdown.js.es6 | 4 +- .../discourse/components/category-drop.js.es6 | 10 +++-- .../components/dropdown-button.js.es6 | 13 +++++- .../components/notifications-button.js.es6 | 3 +- .../components/period-chooser.js.es6 | 2 +- .../discourse/components/pinned-button.js.es6 | 23 +++++++---- .../discourse/components/tag-drop.js.es6 | 5 +-- .../components/tags-admin-dropdown.js.es6 | 2 +- .../discourse/components/topic-status.js.es6 | 2 +- .../components/topic-timer-info.js.es6 | 3 +- .../discourse/controllers/flag.js.es6 | 5 ++- .../controllers/preferences/account.js.es6 | 3 +- .../discourse/controllers/topic.js.es6 | 3 +- .../discourse/helpers/icon-or-image.js.es6 | 3 +- .../initializers/sharing-sources.js.es6 | 8 ++-- .../discourse/lib/autocomplete.js.es6 | 3 +- .../javascripts/discourse/lib/sharing.js.es6 | 12 ++++-- .../discourse/models/composer.js.es6 | 11 ++--- .../discourse/models/user-action.js.es6 | 8 ++-- .../raw-views/topic-list-header-column.js.es6 | 4 +- .../templates/components/category-drop.hbs | 4 +- .../templates/components/discourse-banner.hbs | 4 +- .../templates/components/queued-post.hbs | 2 +- .../templates/components/share-source.hbs | 4 +- .../templates/components/stream-item.hbs | 2 +- .../templates/components/tag-drop.hbs | 4 +- .../discourse/templates/composer.hbs | 4 +- .../discourse/templates/discovery/topics.hbs | 5 ++- .../discourse/templates/exception.hbs | 2 +- .../discourse/templates/invites/show.hbs | 3 +- .../templates/list/action-list.raw.hbs | 2 +- .../templates/list/topic-list-item.raw.hbs | 4 +- .../mobile/components/mobile-nav.hbs | 4 +- .../mobile/components/navigation-bar.hbs | 2 +- .../templates/mobile/discovery/topics.hbs | 4 +- .../templates/mobile/modal/login.hbs | 2 +- .../templates/modal/create-account.hbs | 3 +- .../templates/modal/dismiss-read.hbs | 3 +- .../discourse/templates/modal/flag.hbs | 6 +-- .../discourse/templates/modal/login.hbs | 4 +- .../templates/navigation/default.hbs | 3 +- .../discourse/templates/password-reset.hbs | 3 +- .../templates/preferences/account.hbs | 3 +- .../templates/preferences/categories.hbs | 8 ++-- .../templates/preferences/notifications.hbs | 2 +- .../discourse/templates/preferences/tags.hbs | 8 ++-- .../discourse/templates/tag-groups-show.hbs | 4 +- .../discourse/templates/tag-groups.hbs | 4 +- .../discourse/templates/tags/show.hbs | 2 +- .../topic-list-header-column.raw.hbs | 2 +- .../discourse/templates/topic-status.raw.hbs | 4 +- .../user-selector-autocomplete.raw.hbs | 2 +- .../discourse/templates/user/activity.hbs | 6 +-- .../discourse/templates/user/messages.hbs | 4 +- .../templates/user/notifications.hbs | 8 ++-- .../discourse/widgets/post-cooked.js.es6 | 5 ++- .../discourse/widgets/post-menu.js.es6 | 2 +- .../javascripts/discourse/widgets/post.js.es6 | 2 +- .../widgets/topic-notifications-button.js.es6 | 3 +- .../acceptance/details-button-test.js.es6 | 8 ++-- .../widgets/discourse-poll-option-test.js.es6 | 8 ++-- .../acceptance/composer-test.js.es6 | 6 +-- test/javascripts/acceptance/topic-test.js.es6 | 8 ++-- .../widgets/actions-summary-test.js.es6 | 2 +- .../javascripts/widgets/home-logo-test.js.es6 | 2 +- test/javascripts/widgets/post-test.js.es6 | 6 +-- .../widgets/poster-name-test.js.es6 | 2 +- 85 files changed, 262 insertions(+), 192 deletions(-) create mode 100644 app/assets/javascripts/admin/helpers/check-icon.js.es6 diff --git a/app/assets/javascripts/admin/components/resumable-upload.js.es6 b/app/assets/javascripts/admin/components/resumable-upload.js.es6 index 73a46eded3c..36f420d1009 100644 --- a/app/assets/javascripts/admin/components/resumable-upload.js.es6 +++ b/app/assets/javascripts/admin/components/resumable-upload.js.es6 @@ -1,3 +1,4 @@ +import { iconHTML } from 'discourse-common/lib/icon-library'; import { bufferedRender } from 'discourse-common/lib/buffered-render'; /*global Resumable:true */ @@ -40,7 +41,7 @@ export default Ember.Component.extend(bufferedRender({ buildBuffer(buffer) { const icon = this.get("isUploading") ? "times" : "upload"; - buffer.push(``); + buffer.push(iconHTML(icon)); buffer.push("" + this.get("text") + ""); buffer.push(""); }, diff --git a/app/assets/javascripts/admin/helpers/check-icon.js.es6 b/app/assets/javascripts/admin/helpers/check-icon.js.es6 new file mode 100644 index 00000000000..0a68c0be0b3 --- /dev/null +++ b/app/assets/javascripts/admin/helpers/check-icon.js.es6 @@ -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)); +}); diff --git a/app/assets/javascripts/admin/models/admin-user.js.es6 b/app/assets/javascripts/admin/models/admin-user.js.es6 index 0720976e69b..662f0b046ca 100644 --- a/app/assets/javascripts/admin/models/admin-user.js.es6 +++ b/app/assets/javascripts/admin/models/admin-user.js.es6 @@ -1,3 +1,4 @@ +import { iconHTML } from 'discourse-common/lib/icon-library'; import { ajax } from 'discourse/lib/ajax'; import computed from 'ember-addons/ember-computed-decorators'; import { propertyNotEqual } from 'discourse/lib/computed'; @@ -108,7 +109,7 @@ const AdminUser = Discourse.User.extend({ "class": "cancel-inline", "link": true }, { - "label": ' ' + I18n.t("admin.user.delete_all_posts"), + "label": `${iconHTML('exclamation-triangle')} ` + I18n.t("admin.user.delete_all_posts"), "class": "btn btn-danger", "callback": function() { ajax("/admin/users/" + user.get('id') + "/delete_all_posts", { @@ -337,7 +338,7 @@ const AdminUser = Discourse.User.extend({ "class": "cancel", "link": true }, { - "label": '' + I18n.t('admin.user.block_accept'), + "label": `${iconHTML('exclamation-triangle')} ` + I18n.t('admin.user.block_accept'), "class": "btn btn-danger", "callback": function() { performBlock(); } }]; @@ -386,7 +387,7 @@ const AdminUser = Discourse.User.extend({ "class": "cancel", "link": true }, { - "label": '' + I18n.t('admin.user.anonymize_yes'), + "label": `${iconHTML('exclamation-triangle')} ` + I18n.t('admin.user.anonymize_yes'), "class": "btn btn-danger", "callback": function() { performAnonymize(); } }]; @@ -450,7 +451,7 @@ const AdminUser = Discourse.User.extend({ "class": "btn", "link": true }, { - "label": '' + I18n.t('admin.user.delete_and_block'), + "label": `${iconHTML('exclamation-triangle')} ` + I18n.t('admin.user.delete_and_block'), "class": "btn btn-danger", "callback": function(){ performDestroy(true); } }, { @@ -479,7 +480,7 @@ const AdminUser = Discourse.User.extend({ "class": "cancel-inline", "link": true }, { - "label": ' ' + I18n.t("flagging.yes_delete_spammer"), + "label": `${iconHTML('exclamation-triangle')} ` + I18n.t("flagging.yes_delete_spammer"), "class": "btn btn-danger", "callback": function() { return ajax("/admin/users/" + user.get('id') + '.json', { diff --git a/app/assets/javascripts/admin/models/flagged-post.js.es6 b/app/assets/javascripts/admin/models/flagged-post.js.es6 index c7a654608f8..7a627f1569b 100644 --- a/app/assets/javascripts/admin/models/flagged-post.js.es6 +++ b/app/assets/javascripts/admin/models/flagged-post.js.es6 @@ -2,7 +2,7 @@ import { ajax } from 'discourse/lib/ajax'; import AdminUser from 'admin/models/admin-user'; import Topic from 'discourse/models/topic'; import Post from 'discourse/models/post'; - +import { iconHTML } from 'discourse-common/lib/icon-library'; const FlaggedPost = Post.extend({ @@ -35,13 +35,14 @@ const FlaggedPost = Post.extend({ dispositionIcon: function (disposition) { if (!disposition) { return null; } - var icon, title = I18n.t('admin.flags.dispositions.' + disposition); + let icon; + let title = 'admin.flags.dispositions.' + disposition; switch (disposition) { - case "deferred": { icon = "fa-external-link"; break; } - case "agreed": { icon = "fa-thumbs-o-up"; break; } - case "disagreed": { icon = "fa-thumbs-o-down"; break; } + case "deferred": { icon = "external-link"; break; } + case "agreed": { icon = "thumbs-o-up"; break; } + case "disagreed": { icon = "thumbs-o-down"; break; } } - return ""; + return iconHTML(icon, { title }); }, wasEdited: function () { diff --git a/app/assets/javascripts/admin/templates/api-keys.hbs b/app/assets/javascripts/admin/templates/api-keys.hbs index 60449052bb9..b25eebd69a6 100644 --- a/app/assets/javascripts/admin/templates/api-keys.hbs +++ b/app/assets/javascripts/admin/templates/api-keys.hbs @@ -29,5 +29,5 @@ {{/if}} {{#unless hasMasterKey}} - + {{/unless}} diff --git a/app/assets/javascripts/admin/templates/customize-colors-show.hbs b/app/assets/javascripts/admin/templates/customize-colors-show.hbs index 6b3c8a3e91c..e688e133166 100644 --- a/app/assets/javascripts/admin/templates/customize-colors-show.hbs +++ b/app/assets/javascripts/admin/templates/customize-colors-show.hbs @@ -5,13 +5,13 @@ {{#unless model.theme_id}} {{/unless}} - - + + {{#if model.theme_id}} {{i18n "admin.customize.theme_owner"}} {{#link-to "adminCustomizeThemes.show" model.theme_id}}{{model.theme_name}}{{/link-to}} {{else}} - + {{/if}} {{model.savingStatus}}

        diff --git a/app/assets/javascripts/admin/templates/customize-themes-edit.hbs b/app/assets/javascripts/admin/templates/customize-themes-edit.hbs index ba65a6d33fd..83eebb83d08 100644 --- a/app/assets/javascripts/admin/templates/customize-themes-edit.hbs +++ b/app/assets/javascripts/admin/templates/customize-themes-edit.hbs @@ -53,7 +53,7 @@ {{/each}}
      • - + {{d-icon maximizeIcon}}
      diff --git a/app/assets/javascripts/admin/templates/flags-list.hbs b/app/assets/javascripts/admin/templates/flags-list.hbs index 796552d4d2f..f7a6ef19e18 100644 --- a/app/assets/javascripts/admin/templates/flags-list.hbs +++ b/app/assets/javascripts/admin/templates/flags-list.hbs @@ -19,7 +19,9 @@ {{#if flaggedPost.postAuthorFlagged}} {{#if flaggedPost.user}} {{#link-to 'adminUser' flaggedPost.user}}{{avatar flaggedPost.user imageSize="large"}}{{/link-to}} - {{#if flaggedPost.wasEdited}}{{/if}} + {{#if flaggedPost.wasEdited}} + {{d-icon "pencil" title="admin.flags.was_edited"}} + {{/if}} {{/if}} {{/if}} {{#if adminActiveFlagsView}} @@ -90,7 +92,7 @@ {{format-age flagger.disposedAt}} {{{flagger.dispositionIcon}}} {{#if flagger.tookAction}} - + {{d-icon "gavel" title="admin.flags.took_action"}} {{/if}}
    @@ -129,7 +131,8 @@

    {{/if}} - + + {{/if}} @@ -141,14 +144,14 @@ diff --git a/app/assets/javascripts/admin/templates/modal/admin-agree-flag.hbs b/app/assets/javascripts/admin/templates/modal/admin-agree-flag.hbs index 6f845b22c80..63ce16601bd 100644 --- a/app/assets/javascripts/admin/templates/modal/admin-agree-flag.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin-agree-flag.hbs @@ -1,13 +1,13 @@ {{#d-modal-body title="admin.flags.agree_flag_modal_title"}} {{#if model.user_deleted}} - + {{else}} {{#unless model.postHidden}} - + {{/unless}} {{/if}} - + {{#if model.canDeleteAsSpammer}} - + {{/if}} {{/d-modal-body}} diff --git a/app/assets/javascripts/admin/templates/modal/admin-badge-preview.hbs b/app/assets/javascripts/admin/templates/modal/admin-badge-preview.hbs index c22bd9d99d2..70b0c1c3fc2 100644 --- a/app/assets/javascripts/admin/templates/modal/admin-badge-preview.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin-badge-preview.hbs @@ -24,7 +24,7 @@ {{#if count_warning}}
    -

    {{i18n 'admin.badges.preview.bad_count_warning.header'}}

    +

    {{d-icon "warning"}} {{i18n 'admin.badges.preview.bad_count_warning.header'}}

    {{i18n 'admin.badges.preview.bad_count_warning.text'}}

    {{/if}} diff --git a/app/assets/javascripts/admin/templates/modal/admin-delete-flag.hbs b/app/assets/javascripts/admin/templates/modal/admin-delete-flag.hbs index c2abeae36d7..89cf7452a4c 100644 --- a/app/assets/javascripts/admin/templates/modal/admin-delete-flag.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin-delete-flag.hbs @@ -1,7 +1,7 @@ {{#d-modal-body title="admin.flags.delete_flag_modal_title"}} - - + + {{#if model.canDeleteAsSpammer}} - + {{/if}} {{/d-modal-body}} diff --git a/app/assets/javascripts/admin/templates/modal/admin-suspend-user.hbs b/app/assets/javascripts/admin/templates/modal/admin-suspend-user.hbs index 9d9326ad3ee..afb7525fb59 100644 --- a/app/assets/javascripts/admin/templates/modal/admin-suspend-user.hbs +++ b/app/assets/javascripts/admin/templates/modal/admin-suspend-user.hbs @@ -11,6 +11,6 @@ {{/d-modal-body}} diff --git a/app/assets/javascripts/admin/templates/user-badges.hbs b/app/assets/javascripts/admin/templates/user-badges.hbs index 393695e8422..9977aca39d2 100644 --- a/app/assets/javascripts/admin/templates/user-badges.hbs +++ b/app/assets/javascripts/admin/templates/user-badges.hbs @@ -1,7 +1,7 @@
    diff --git a/app/assets/javascripts/admin/templates/user-index.hbs b/app/assets/javascripts/admin/templates/user-index.hbs index eecc669e6cc..7a016f150f1 100644 --- a/app/assets/javascripts/admin/templates/user-index.hbs +++ b/app/assets/javascripts/admin/templates/user-index.hbs @@ -281,9 +281,11 @@
    {{#if model.canLockTrustLevel}} {{#if model.trust_level_locked}} - {{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}} - {{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 model.tl3Requirements}} diff --git a/app/assets/javascripts/admin/templates/user-tl3-requirements.hbs b/app/assets/javascripts/admin/templates/user-tl3-requirements.hbs index e0536ddd087..1561a95670b 100644 --- a/app/assets/javascripts/admin/templates/user-tl3-requirements.hbs +++ b/app/assets/javascripts/admin/templates/user-tl3-requirements.hbs @@ -1,7 +1,7 @@
    @@ -24,7 +24,7 @@
    - + @@ -32,67 +32,67 @@ - + - + - + - + - + - + - + - + - + - + - + @@ -105,16 +105,16 @@ {{#if model.tl3Requirements.requirements_lost}} {{! tl implicitly not locked }} {{#if model.tl3Requirements.on_grace_period}} - {{i18n 'admin.user.tl3_requirements.on_grace_period'}} + {{d-icon "times"}} {{i18n 'admin.user.tl3_requirements.on_grace_period'}} {{else}} {{! not on grace period }} - {{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'}} {{/if}} {{else}} {{! requirements not lost - remains tl3 }} {{#if model.tl3Requirements.trust_level_locked}} - {{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 }} - {{i18n 'admin.user.tl3_requirements.qualifies'}} + {{d-icon "check"}} {{i18n 'admin.user.tl3_requirements.qualifies'}} {{#if model.tl3Requirements.on_grace_period}} {{i18n 'admin.user.tl3_requirements.on_grace_period'}} {{/if}} @@ -123,13 +123,13 @@ {{else}} {{! is not tl3 }} {{#if model.tl3Requirements.requirements_met}} {{! met & not tl3 - will be promoted}} - {{i18n 'admin.user.tl3_requirements.qualifies'}} + {{d-icon "check"}} {{i18n 'admin.user.tl3_requirements.qualifies'}} {{i18n 'admin.user.tl3_requirements.will_be_promoted'}} {{else}} {{! requirements not met - remains regular }} {{#if model.tl3Requirements.trust_level_locked}} - {{i18n 'admin.user.tl3_requirements.locked_will_not_be_promoted'}} + {{d-icon "lock"}} {{i18n 'admin.user.tl3_requirements.locked_will_not_be_promoted'}} {{else}} - {{i18n 'admin.user.tl3_requirements.does_not_qualify'}} + {{d-icon "times"}} {{i18n 'admin.user.tl3_requirements.does_not_qualify'}} {{/if}} {{/if}} {{/if}} diff --git a/app/assets/javascripts/discourse-common/components/combo-box.js.es6 b/app/assets/javascripts/discourse-common/components/combo-box.js.es6 index e5828e8fc72..0d8cd538b8a 100644 --- a/app/assets/javascripts/discourse-common/components/combo-box.js.es6 +++ b/app/assets/javascripts/discourse-common/components/combo-box.js.es6 @@ -1,5 +1,6 @@ import { bufferedRender } from 'discourse-common/lib/buffered-render'; import { on, observes } from 'ember-addons/ember-computed-decorators'; +import { iconHTML } from 'discourse-common/lib/icon-library'; export default Ember.Component.extend(bufferedRender({ tagName: 'select', @@ -97,7 +98,7 @@ export default Ember.Component.extend(bufferedRender({ this.selectionTemplate = (item) => { let name = Em.get(item, 'text'); name = Handlebars.escapeExpression(name); - return `${name}`; + return iconHTML(this.get('selectionIcon')) + name; }; } diff --git a/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 b/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 index eac3ea194b5..be4f0040551 100644 --- a/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 +++ b/app/assets/javascripts/discourse-common/lib/icon-library.js.es6 @@ -42,10 +42,11 @@ registerIconRenderer({ name: 'font-awesome', string(id, params) { - let html = ``; if (params.label) { html += "" + I18n.t(params.label) + ""; } diff --git a/app/assets/javascripts/discourse/components/auto-update-input-selector.js.es6 b/app/assets/javascripts/discourse/components/auto-update-input-selector.js.es6 index 60978f39592..0ad58a506cc 100644 --- a/app/assets/javascripts/discourse/components/auto-update-input-selector.js.es6 +++ b/app/assets/javascripts/discourse/components/auto-update-input-selector.js.es6 @@ -1,3 +1,4 @@ +import { iconHTML } from 'discourse-common/lib/icon-library'; import { default as computed, observes } from "ember-addons/ember-computed-decorators"; import Combobox from 'discourse-common/components/combo-box'; import { CLOSE_STATUS_TYPE } from 'discourse/controllers/edit-topic-timer'; @@ -111,9 +112,7 @@ export default Combobox.extend({ let icons; if (icon) { - icons = icon.split(',').map(i => { - return ``; - }).join(" "); + icons = icon.split(',').map(i => iconHTML(i)).join(" "); } if (time) { diff --git a/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 b/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 index 52eca3fce33..6a6c1a33532 100644 --- a/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 +++ b/app/assets/javascripts/discourse/components/categories-admin-dropdown.js.es6 @@ -15,14 +15,14 @@ export default DropdownButton.extend({ { id: 'create', title: I18n.t('category.create'), description: I18n.t('category.create_long'), - styleClasses: 'fa fa-plus' } + icon: 'plus' } ]; if (includeReorder) { items.push({ id: 'reorder', title: I18n.t('categories.reorder.title'), description: I18n.t('categories.reorder.title_long'), - styleClasses: 'fa fa-random' + icon: 'random' }); } return items; diff --git a/app/assets/javascripts/discourse/components/category-drop.js.es6 b/app/assets/javascripts/discourse/components/category-drop.js.es6 index e9f05c33fe2..3e4c218b51c 100644 --- a/app/assets/javascripts/discourse/components/category-drop.js.es6 +++ b/app/assets/javascripts/discourse/components/category-drop.js.es6 @@ -1,4 +1,6 @@ import { setting } from 'discourse/lib/computed'; +import computed from 'ember-addons/ember-computed-decorators'; + var get = Ember.get; export default Ember.Component.extend({ @@ -8,10 +10,10 @@ export default Ember.Component.extend({ tagName: 'li', - iconClass: function() { - if (this.get('expanded')) { return "fa fa-caret-down"; } - return "fa fa-caret-right"; - }.property('expanded'), + @computed('expanded') + expandIcon(expanded) { + return expanded ? 'caret-down' : 'caret-right'; + }, allCategoriesUrl: function() { if (this.get('subCategory')) { diff --git a/app/assets/javascripts/discourse/components/dropdown-button.js.es6 b/app/assets/javascripts/discourse/components/dropdown-button.js.es6 index 032c80e2aff..bffeaa6cdc2 100644 --- a/app/assets/javascripts/discourse/components/dropdown-button.js.es6 +++ b/app/assets/javascripts/discourse/components/dropdown-button.js.es6 @@ -1,3 +1,4 @@ +import { iconHTML } from 'discourse-common/lib/icon-library'; import { bufferedRender } from 'discourse-common/lib/buffered-render'; export default Ember.Component.extend(bufferedRender({ @@ -29,7 +30,7 @@ export default Ember.Component.extend(bufferedRender({ buffer.push("

    " + title + "

    "); } - buffer.push(``); + buffer.push(``); buffer.push("
    + {{number topic.like_count}} {{d-icon "heart"}} {{/if}} {{/if}} @@ -54,7 +54,7 @@ + {{number topic.op_like_count}} {{d-icon "heart"}} {{/if}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/mobile/components/mobile-nav.hbs b/app/assets/javascripts/discourse/templates/mobile/components/mobile-nav.hbs index 8a58906d192..83bdf44a111 100644 --- a/app/assets/javascripts/discourse/templates/mobile/components/mobile-nav.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/components/mobile-nav.hbs @@ -1,5 +1,7 @@ {{#if selectedHtml}} -
  • {{{selectedHtml}}}
  • +
  • {{{selectedHtml}}} + {{d-icon "caret-down"}} +
  • {{/if}}
      {{yield}} diff --git a/app/assets/javascripts/discourse/templates/mobile/components/navigation-bar.hbs b/app/assets/javascripts/discourse/templates/mobile/components/navigation-bar.hbs index 456db2abce7..f52a6b16ca8 100644 --- a/app/assets/javascripts/discourse/templates/mobile/components/navigation-bar.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/components/navigation-bar.hbs @@ -1,7 +1,7 @@
    • {{selectedNavItem.displayName}} - + {{d-icon "caret-down"}}
    • {{#if expanded}} diff --git a/app/assets/javascripts/discourse/templates/mobile/discovery/topics.hbs b/app/assets/javascripts/discourse/templates/mobile/discovery/topics.hbs index c17937e618c..4eb7503e29b 100644 --- a/app/assets/javascripts/discourse/templates/mobile/discovery/topics.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/discovery/topics.hbs @@ -35,7 +35,9 @@ {{/if}} {{#if showResetNew}} - + {{/if}} {{#if latest}} diff --git a/app/assets/javascripts/discourse/templates/mobile/modal/login.hbs b/app/assets/javascripts/discourse/templates/mobile/modal/login.hbs index d156ca66dde..0e191a6c8da 100644 --- a/app/assets/javascripts/discourse/templates/mobile/modal/login.hbs +++ b/app/assets/javascripts/discourse/templates/mobile/modal/login.hbs @@ -46,7 +46,7 @@ {{#if showSignupLink}} diff --git a/app/assets/javascripts/discourse/templates/modal/create-account.hbs b/app/assets/javascripts/discourse/templates/modal/create-account.hbs index bdaa3d82394..c32fe8e3c1e 100644 --- a/app/assets/javascripts/discourse/templates/modal/create-account.hbs +++ b/app/assets/javascripts/discourse/templates/modal/create-account.hbs @@ -70,7 +70,8 @@
    {{/if}} diff --git a/app/assets/javascripts/discourse/templates/modal/dismiss-read.hbs b/app/assets/javascripts/discourse/templates/modal/dismiss-read.hbs index 4dc7a96b32c..367359d9e78 100644 --- a/app/assets/javascripts/discourse/templates/modal/dismiss-read.hbs +++ b/app/assets/javascripts/discourse/templates/modal/dismiss-read.hbs @@ -5,5 +5,6 @@ {{/d-modal-body}} diff --git a/app/assets/javascripts/discourse/templates/modal/flag.hbs b/app/assets/javascripts/discourse/templates/modal/flag.hbs index 3b0da391233..465b0a0ed4c 100644 --- a/app/assets/javascripts/discourse/templates/modal/flag.hbs +++ b/app/assets/javascripts/discourse/templates/modal/flag.hbs @@ -16,14 +16,14 @@ {{#if canSendWarning}} - + {{/if}} {{#if canTakeAction}} - + {{/if}} {{#if canDeleteSpammer}} - + {{/if}} diff --git a/app/assets/javascripts/discourse/templates/modal/login.hbs b/app/assets/javascripts/discourse/templates/modal/login.hbs index f7a7d616bce..81b3fb611df 100644 --- a/app/assets/javascripts/discourse/templates/modal/login.hbs +++ b/app/assets/javascripts/discourse/templates/modal/login.hbs @@ -27,7 +27,7 @@ - +
    {{#if log.prev_value}} {{#if expandDetails}} - {{fa-icon 'ellipsis-v'}} + {{d-icon 'ellipsis-v'}} {{else}} - {{fa-icon 'ellipsis-h'}} + {{d-icon 'ellipsis-h'}} {{/if}} {{/if}}
    {{#if adminActiveFlagsView}} - + {{#if flaggedPost.postHidden}} - + {{else}} - + {{/if}} - - + + {{/if}}
    {{i18n 'admin.user.tl3_requirements.visits'}}{{check-icon model.tl3Requirements.met.days_visited}} {{model.tl3Requirements.days_visited_percent}}% ({{model.tl3Requirements.days_visited}} / {{model.tl3Requirements.time_period}} {{i18n 'admin.user.tl3_requirements.days'}})
    {{i18n 'admin.user.tl3_requirements.topics_replied_to'}}{{check-icon model.tl3Requirements.met.topics_replied_to}} {{model.tl3Requirements.num_topics_replied_to}} {{model.tl3Requirements.min_topics_replied_to}}
    {{i18n 'admin.user.tl3_requirements.topics_viewed'}}{{check-icon model.tl3Requirements.met.topics_viewed}} {{model.tl3Requirements.topics_viewed}} {{model.tl3Requirements.min_topics_viewed}}
    {{i18n 'admin.user.tl3_requirements.topics_viewed_all_time'}}{{check-icon model.tl3Requirements.met.topics_viewed_all_time}} {{model.tl3Requirements.topics_viewed_all_time}} {{model.tl3Requirements.min_topics_viewed_all_time}}
    {{i18n 'admin.user.tl3_requirements.posts_read'}}{{check-icon model.tl3Requirements.met.posts_read}} {{model.tl3Requirements.posts_read}} {{model.tl3Requirements.min_posts_read}}
    {{i18n 'admin.user.tl3_requirements.posts_read_all_time'}}{{check-icon model.tl3Requirements.met.posts_read_all_time}} {{model.tl3Requirements.posts_read_all_time}} {{model.tl3Requirements.min_posts_read_all_time}}
    {{i18n 'admin.user.tl3_requirements.flagged_posts'}}{{check-icon model.tl3Requirements.met.flagged_posts}} {{model.tl3Requirements.num_flagged_posts}} {{i18n 'max_of_count' count=model.tl3Requirements.max_flagged_posts}}
    {{i18n 'admin.user.tl3_requirements.flagged_by_users'}}{{check-icon model.tl3Requirements.met.flagged_by_users}} {{model.tl3Requirements.num_flagged_by_users}} {{i18n 'max_of_count' count=model.tl3Requirements.max_flagged_by_users}}
    {{i18n 'admin.user.tl3_requirements.likes_given'}}{{check-icon model.tl3Requirements.met.likes_given}} {{model.tl3Requirements.num_likes_given}} {{model.tl3Requirements.min_likes_given}}
    {{i18n 'admin.user.tl3_requirements.likes_received'}}{{check-icon model.tl3Requirements.met.likes_received}} {{model.tl3Requirements.num_likes_received}} {{model.tl3Requirements.min_likes_received}}
    {{i18n 'admin.user.tl3_requirements.likes_received_days'}}{{check-icon model.tl3Requirements.met.likes_received_days}} {{model.tl3Requirements.num_likes_received_days}} {{model.tl3Requirements.min_likes_received_days}}
    {{i18n 'admin.user.tl3_requirements.likes_received_users'}}{{check-icon model.tl3Requirements.met.likes_received_users}} {{model.tl3Requirements.num_likes_received_users}} {{model.tl3Requirements.min_likes_received_users}}
    -
    {{i18n 'login.caps_lock_warning'}}
    +
    + {{d-icon "exclamation-triangle"}} {{i18n 'login.caps_lock_warning'}}
    {{i18n 'login.caps_lock_warning'}}
    {{d-icon "exclamation-triangle"}} {{i18n 'login.caps_lock_warning'}}
    @@ -43,7 +43,7 @@ {{#if showSignupLink}} diff --git a/app/assets/javascripts/discourse/templates/navigation/default.hbs b/app/assets/javascripts/discourse/templates/navigation/default.hbs index 07fb2f693db..57f01646512 100644 --- a/app/assets/javascripts/discourse/templates/navigation/default.hbs +++ b/app/assets/javascripts/discourse/templates/navigation/default.hbs @@ -4,6 +4,7 @@ {{navigation-bar navItems=navItems filterMode=filterMode}} {{#if canCreateTopic}} - + {{/if}} {{/d-section}} diff --git a/app/assets/javascripts/discourse/templates/password-reset.hbs b/app/assets/javascripts/discourse/templates/password-reset.hbs index e101ed0b948..e999fedb1ef 100644 --- a/app/assets/javascripts/discourse/templates/password-reset.hbs +++ b/app/assets/javascripts/discourse/templates/password-reset.hbs @@ -25,7 +25,8 @@
    -
    {{i18n 'login.caps_lock_warning'}}
    +
    + {{d-icon "exclamation-triangle"}} {{i18n 'login.caps_lock_warning'}}
    diff --git a/app/assets/javascripts/discourse/templates/preferences/account.hbs b/app/assets/javascripts/discourse/templates/preferences/account.hbs index a1fd8b1d3d4..239c8d0ecfa 100644 --- a/app/assets/javascripts/discourse/templates/preferences/account.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/account.hbs @@ -3,7 +3,8 @@
    {{model.username}} {{#if model.can_edit_username}} - {{#link-to "preferences.username" class="btn btn-small pad-left no-text"}}{{/link-to}} + {{#link-to "preferences.username" class="btn btn-small pad-left no-text"}} + {{d-icon "pencil"}} {{/link-to}} {{/if}}
    diff --git a/app/assets/javascripts/discourse/templates/preferences/categories.hbs b/app/assets/javascripts/discourse/templates/preferences/categories.hbs index d9be7335240..856feedcf7d 100644 --- a/app/assets/javascripts/discourse/templates/preferences/categories.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/categories.hbs @@ -3,7 +3,7 @@
    - + {{category-selector categories=model.watchedCategories blacklist=selectedCategories}}
    {{i18n 'user.watched_categories_instructions'}}
    @@ -12,7 +12,7 @@
    - + {{category-selector categories=model.trackedCategories blacklist=selectedCategories}}
    {{i18n 'user.tracked_categories_instructions'}}
    @@ -21,13 +21,13 @@
    - + {{category-selector categories=model.watchedFirstPostCategories}}
    {{i18n 'user.watched_first_post_categories_instructions'}}
    - + {{category-selector categories=model.mutedCategories blacklist=selectedCategories}}
    {{i18n 'user.muted_categories_instructions'}}
    diff --git a/app/assets/javascripts/discourse/templates/preferences/notifications.hbs b/app/assets/javascripts/discourse/templates/preferences/notifications.hbs index 6c81046e279..c9cf37ac4d1 100644 --- a/app/assets/javascripts/discourse/templates/preferences/notifications.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/notifications.hbs @@ -29,7 +29,7 @@
    - + {{user-selector excludeCurrentUser=true usernames=model.muted_usernames class="user-selector"}}
    {{i18n 'user.muted_users_instructions'}}
    diff --git a/app/assets/javascripts/discourse/templates/preferences/tags.hbs b/app/assets/javascripts/discourse/templates/preferences/tags.hbs index e2e9507b5bd..390429833ff 100644 --- a/app/assets/javascripts/discourse/templates/preferences/tags.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/tags.hbs @@ -4,25 +4,25 @@
    - + {{tag-chooser tags=model.watched_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
    {{i18n 'user.watched_tags_instructions'}}
    - + {{tag-chooser tags=model.tracked_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
    {{i18n 'user.tracked_tags_instructions'}}
    - + {{tag-chooser tags=model.watching_first_post_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
    {{i18n 'user.watched_first_post_tags_instructions'}}
    - + {{tag-chooser tags=model.muted_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
    {{i18n 'user.muted_tags_instructions'}}
    diff --git a/app/assets/javascripts/discourse/templates/tag-groups-show.hbs b/app/assets/javascripts/discourse/templates/tag-groups-show.hbs index 58b80d565e2..26fde8830f4 100644 --- a/app/assets/javascripts/discourse/templates/tag-groups-show.hbs +++ b/app/assets/javascripts/discourse/templates/tag-groups-show.hbs @@ -20,6 +20,6 @@ - + {{model.savingStatus}} -
    \ No newline at end of file + diff --git a/app/assets/javascripts/discourse/templates/tag-groups.hbs b/app/assets/javascripts/discourse/templates/tag-groups.hbs index 139874e75d0..a4c0ec21de7 100644 --- a/app/assets/javascripts/discourse/templates/tag-groups.hbs +++ b/app/assets/javascripts/discourse/templates/tag-groups.hbs @@ -7,10 +7,10 @@
  • {{tagGroup.name}}
  • {{/each}} - + {{outlet}}
    -
    \ No newline at end of file + diff --git a/app/assets/javascripts/discourse/templates/tags/show.hbs b/app/assets/javascripts/discourse/templates/tags/show.hbs index 9a7e54844a0..18659601d17 100644 --- a/app/assets/javascripts/discourse/templates/tags/show.hbs +++ b/app/assets/javascripts/discourse/templates/tags/show.hbs @@ -16,7 +16,7 @@ {{d-button action="renameTag" actionParam=tag icon="pencil" class="admin-tag"}} {{else}} {{#if canCreateTopic}} - + {{/if}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/topic-list-header-column.raw.hbs b/app/assets/javascripts/discourse/templates/topic-list-header-column.raw.hbs index 7d7b6b62bb3..762ed1e9968 100644 --- a/app/assets/javascripts/discourse/templates/topic-list-header-column.raw.hbs +++ b/app/assets/javascripts/discourse/templates/topic-list-header-column.raw.hbs @@ -12,6 +12,6 @@ {{/if ~}} {{view.localizedName}} {{~#if view.isSorting}} - + {{d-icon sortIcon}} {{/if ~}} diff --git a/app/assets/javascripts/discourse/templates/topic-status.raw.hbs b/app/assets/javascripts/discourse/templates/topic-status.raw.hbs index 621415dfa16..cfcbe836cfb 100644 --- a/app/assets/javascripts/discourse/templates/topic-status.raw.hbs +++ b/app/assets/javascripts/discourse/templates/topic-status.raw.hbs @@ -3,9 +3,9 @@ {{/if ~}} {{~#each view.statuses as |status|~}} {{~#if status.href ~}} - +{{d-icon status.icon}} {{~else ~}} -<{{status.openTag}} title='{{status.title}}' class='topic-status'> +<{{status.openTag}} title='{{status.title}}' class='topic-status'>{{d-icon status.icon}} {{~/if ~}} {{~/each}} {{~#if view.renderDiv ~}} diff --git a/app/assets/javascripts/discourse/templates/user-selector-autocomplete.raw.hbs b/app/assets/javascripts/discourse/templates/user-selector-autocomplete.raw.hbs index 02a8b005dd5..4c353fbd065 100644 --- a/app/assets/javascripts/discourse/templates/user-selector-autocomplete.raw.hbs +++ b/app/assets/javascripts/discourse/templates/user-selector-autocomplete.raw.hbs @@ -13,7 +13,7 @@ {{#each options.groups as |group|}}
  • - + {{d-icon "users"}} {{group.name}} {{group.full_name}} diff --git a/app/assets/javascripts/discourse/templates/user/activity.hbs b/app/assets/javascripts/discourse/templates/user/activity.hbs index b0fa84ba144..165e0cfbcfd 100644 --- a/app/assets/javascripts/discourse/templates/user/activity.hbs +++ b/app/assets/javascripts/discourse/templates/user/activity.hbs @@ -8,18 +8,18 @@
  • {{#link-to 'userActivity.replies'}} - {{i18n 'user_action_groups.5'}} + {{d-icon "reply" class="glyph"}}{{i18n 'user_action_groups.5'}} {{/link-to}}
  • {{#link-to 'userActivity.likesGiven'}} - {{i18n 'user_action_groups.1'}} + {{d-icon "heart" class="glyph"}}{{i18n 'user_action_groups.1'}} {{/link-to}}
  • {{#if showBookmarks}}
  • {{#link-to 'userActivity.bookmarks'}} - {{i18n 'user_action_groups.3'}} + {{d-icon "bookmark" class="glyph"}}{{i18n 'user_action_groups.3'}} {{/link-to}}
  • {{/if}} diff --git a/app/assets/javascripts/discourse/templates/user/messages.hbs b/app/assets/javascripts/discourse/templates/user/messages.hbs index ece0c407b7f..3e19ee722a1 100644 --- a/app/assets/javascripts/discourse/templates/user/messages.hbs +++ b/app/assets/javascripts/discourse/templates/user/messages.hbs @@ -26,7 +26,7 @@ {{#if group.has_messages}}
  • {{#link-to 'userPrivateMessages.group' group.name}} - + {{d-icon "group" class="glyph"}} {{capitalize-string group.name}} {{/link-to}}
  • @@ -44,7 +44,7 @@
    {{#if site.mobileView}} diff --git a/app/assets/javascripts/discourse/templates/user/notifications.hbs b/app/assets/javascripts/discourse/templates/user/notifications.hbs index ce0128519f4..19e549d6c16 100644 --- a/app/assets/javascripts/discourse/templates/user/notifications.hbs +++ b/app/assets/javascripts/discourse/templates/user/notifications.hbs @@ -5,17 +5,17 @@
  • {{#link-to 'userNotifications.responses'}} - + {{d-icon "reply" class="glyph"}} {{i18n 'user_action_groups.6'}} {{/link-to}}
  • {{#link-to 'userNotifications.likesReceived'}} - {{i18n 'user_action_groups.2'}} + {{d-icon "heart" class="glyph"}}{{i18n 'user_action_groups.2'}} {{/link-to}}
  • -
  • {{#link-to 'userNotifications.mentions'}}{{i18n 'user_action_groups.7'}}{{/link-to}}
  • -
  • {{#link-to 'userNotifications.edits'}}{{i18n 'user_action_groups.11'}}{{/link-to}}
  • +
  • {{#link-to 'userNotifications.mentions'}}{{d-icon "at" class="glyph"}}{{i18n 'user_action_groups.7'}}{{/link-to}}
  • +
  • {{#link-to 'userNotifications.edits'}}{{d-icon "pencil" class="glyph"}}{{i18n 'user_action_groups.11'}}{{/link-to}}
  • {{/mobile-nav}} {{#if model}} diff --git a/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 b/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 index 2786f7df734..11eaf277dc8 100644 --- a/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-cooked.js.es6 @@ -1,3 +1,4 @@ +import { iconHTML } from 'discourse-common/lib/icon-library'; import { ajax } from 'discourse/lib/ajax'; import { isValidLink } from 'discourse/lib/click-track'; import { number } from 'discourse/lib/formatter'; @@ -140,7 +141,7 @@ export default class PostCooked { $blockQuote.showHtml(div, 'fast', finished); }).catch((e) => { if (e.jqXHR.status === 404) { - $blockQuote.showHtml($("
    "), 'fast', finished); + $blockQuote.showHtml($(`
    ${iconHTML('trash-o')}
    `), 'fast', finished); } }); } else { @@ -178,7 +179,7 @@ export default class PostCooked { // Only add the expand/contract control if it's not a full post let expandContract = ""; if (!$aside.data('full')) { - expandContract = ``; + expandContract = iconHTML(desc, { title: "post.expand_collapse" }); $('.title', $aside).css('cursor', 'pointer'); } $('.quote-controls', $aside).html(expandContract + navLink); diff --git a/app/assets/javascripts/discourse/widgets/post-menu.js.es6 b/app/assets/javascripts/discourse/widgets/post-menu.js.es6 index 7f542a5d0cc..b7f97b697e0 100644 --- a/app/assets/javascripts/discourse/widgets/post-menu.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post-menu.js.es6 @@ -360,7 +360,7 @@ export default createWidget('post-menu', { return this.sendWidgetAction('toggleLike'); } - const $heart = $(`[data-post-id=${attrs.id}] .fa-heart`); + const $heart = $(`[data-post-id=${attrs.id}] .d-icon-heart`); $heart.closest('button').addClass('has-like'); if (!Ember.testing) { diff --git a/app/assets/javascripts/discourse/widgets/post.js.es6 b/app/assets/javascripts/discourse/widgets/post.js.es6 index 2652b4954af..016a2c4030e 100644 --- a/app/assets/javascripts/discourse/widgets/post.js.es6 +++ b/app/assets/javascripts/discourse/widgets/post.js.es6 @@ -88,7 +88,7 @@ createWidget('post-avatar', { html(attrs) { let body; if (!attrs.user_id) { - body = h('i', { className: 'fa fa-trash-o deleted-user-avatar' }); + body = iconNode('trash-o', { class: 'deleted-user-avatar' }); } else { body = avatarFor.call(this, this.settings.size, { template: attrs.avatar_template, diff --git a/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 b/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 index bd37c8b4d8a..74ae08c4957 100644 --- a/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 +++ b/app/assets/javascripts/discourse/widgets/topic-notifications-button.js.es6 @@ -2,6 +2,7 @@ import { createWidget } from 'discourse/widgets/widget'; import { topicLevels, buttonDetails } from 'discourse/lib/notification-levels'; import { h } from 'virtual-dom'; import RawHTML from 'discourse/widgets/raw-html'; +import { iconNode } from 'discourse-common/lib/icon-library'; createWidget('notification-option', { buildKey: attrs => `topic-notifications-button-${attrs.id}`, @@ -9,7 +10,7 @@ createWidget('notification-option', { html(attrs) { return h('a', [ - h('span.icon', { className: `fa fa-${attrs.icon} ${attrs.key}`}), + iconNode(attrs.icon, { class: attrs.key }), h('div', [ h('span.title', I18n.t(`topic.notifications.${attrs.key}.title`)), h('span.desc', I18n.t(`topic.notifications.${attrs.key}.description`)), diff --git a/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6 b/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6 index 61ffac21528..cbb30b82e64 100644 --- a/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6 +++ b/plugins/discourse-details/test/javascripts/acceptance/details-button-test.js.es6 @@ -11,7 +11,7 @@ test('details button', (assert) => { click('#create-topic'); click('button.options'); - click('.popup-menu .fa-caret-right'); + click('.popup-menu .d-icon-caret-right'); andThen(() => { assert.equal( @@ -30,7 +30,7 @@ test('details button', (assert) => { }); click('button.options'); - click('.popup-menu .fa-caret-right'); + click('.popup-menu .d-icon-caret-right'); andThen(() => { assert.equal( @@ -53,7 +53,7 @@ test('details button', (assert) => { }); click('button.options'); - click('.popup-menu .fa-caret-right'); + click('.popup-menu .d-icon-caret-right'); andThen(() => { assert.equal( @@ -76,7 +76,7 @@ test('details button', (assert) => { }); click('button.options'); - click('.popup-menu .fa-caret-right'); + click('.popup-menu .d-icon-caret-right'); andThen(() => { assert.equal( diff --git a/plugins/poll/test/javascripts/widgets/discourse-poll-option-test.js.es6 b/plugins/poll/test/javascripts/widgets/discourse-poll-option-test.js.es6 index 0d646199c7a..75035b6651c 100644 --- a/plugins/poll/test/javascripts/widgets/discourse-poll-option-test.js.es6 +++ b/plugins/poll/test/javascripts/widgets/discourse-poll-option-test.js.es6 @@ -14,7 +14,7 @@ widgetTest('single, not selected', { }, test(assert) { - assert.ok(find('li .fa-circle-o:eq(0)').length === 1); + assert.ok(find('li .d-icon-circle-o:eq(0)').length === 1); } }); @@ -27,7 +27,7 @@ widgetTest('single, selected', { }, test(assert) { - assert.ok(find('li .fa-dot-circle-o:eq(0)').length === 1); + assert.ok(find('li .d-icon-dot-circle-o:eq(0)').length === 1); } }); @@ -43,7 +43,7 @@ widgetTest('multi, not selected', { }, test(assert) { - assert.ok(find('li .fa-square-o:eq(0)').length === 1); + assert.ok(find('li .d-icon-square-o:eq(0)').length === 1); } }); @@ -59,6 +59,6 @@ widgetTest('multi, selected', { }, test(assert) { - assert.ok(find('li .fa-check-square-o:eq(0)').length === 1); + assert.ok(find('li .d-icon-check-square-o:eq(0)').length === 1); } }); diff --git a/test/javascripts/acceptance/composer-test.js.es6 b/test/javascripts/acceptance/composer-test.js.es6 index b155418b78e..fcbd971f230 100644 --- a/test/javascripts/acceptance/composer-test.js.es6 +++ b/test/javascripts/acceptance/composer-test.js.es6 @@ -264,7 +264,7 @@ QUnit.test("Composer can toggle between reply and createTopic", assert => { click('.topic-post:eq(0) button.reply'); click('button.options'); - click('.popup-menu .fa-eye-slash'); + click('.popup-menu .d-icon-eye-slash'); andThen(() => { assert.ok( find('.composer-fields .whisper').text().indexOf(I18n.t("composer.whisper")) > 0, @@ -286,7 +286,7 @@ QUnit.test("Composer can toggle between reply and createTopic", assert => { }); click('button.options'); - click('.popup-menu .fa-eye-slash'); + click('.popup-menu .d-icon-eye-slash'); andThen(() => { assert.ok( find('.composer-fields .whisper').text().indexOf(I18n.t("composer.unlist")) > 0, @@ -334,4 +334,4 @@ QUnit.test("Composer draft with dirty reply can toggle to edit", assert => { andThen(() => { assert.equal(find('.d-editor-input').val().indexOf('This is the first post.'), 0, 'it populates the input with the post text'); }); -}); \ No newline at end of file +}); diff --git a/test/javascripts/acceptance/topic-test.js.es6 b/test/javascripts/acceptance/topic-test.js.es6 index 08b54bfd519..581ef00cd38 100644 --- a/test/javascripts/acceptance/topic-test.js.es6 +++ b/test/javascripts/acceptance/topic-test.js.es6 @@ -33,7 +33,7 @@ QUnit.test("Share Popup", assert => { QUnit.test("Showing and hiding the edit controls", assert => { visit("/t/internationalization-localization/280"); - click('#topic-title .fa-pencil'); + click('#topic-title .d-icon-pencil'); andThen(() => { assert.ok(exists('#edit-title'), 'it shows the editing controls'); @@ -48,7 +48,7 @@ QUnit.test("Showing and hiding the edit controls", assert => { QUnit.test("Updating the topic title and category", assert => { visit("/t/internationalization-localization/280"); - click('#topic-title .fa-pencil'); + click('#topic-title .d-icon-pencil'); fillIn('#edit-title', 'this is the new title'); selectDropdown('.category-combobox', 4); @@ -140,7 +140,7 @@ QUnit.test("Reply as new message", assert => { QUnit.test("Updating the topic title with emojis", assert => { visit("/t/internationalization-localization/280"); - click('#topic-title .fa-pencil'); + click('#topic-title .d-icon-pencil'); fillIn('#edit-title', 'emojis title :bike: :blonde_woman:t6:'); @@ -149,4 +149,4 @@ QUnit.test("Updating the topic title with emojis", assert => { andThen(() => { assert.equal(find('.fancy-title').html().trim(), 'emojis title \"bike\" \"blonde_woman:t6\"', 'it displays the new title with emojis'); }); -}); \ No newline at end of file +}); diff --git a/test/javascripts/widgets/actions-summary-test.js.es6 b/test/javascripts/widgets/actions-summary-test.js.es6 index 4e900f14741..f4eb8b78821 100644 --- a/test/javascripts/widgets/actions-summary-test.js.es6 +++ b/test/javascripts/widgets/actions-summary-test.js.es6 @@ -74,7 +74,7 @@ widgetTest('post deleted', { }); }, test(assert) { - assert.ok(this.$('.post-action .fa-trash-o').length === 1, 'it has the deleted icon'); + assert.ok(this.$('.post-action .d-icon-trash-o').length === 1, 'it has the deleted icon'); assert.ok(this.$('.avatar[title=eviltrout]').length === 1, 'it has the deleted by avatar'); } }); diff --git a/test/javascripts/widgets/home-logo-test.js.es6 b/test/javascripts/widgets/home-logo-test.js.es6 index ddc032391ed..96bd889f50f 100644 --- a/test/javascripts/widgets/home-logo-test.js.es6 +++ b/test/javascripts/widgets/home-logo-test.js.es6 @@ -66,7 +66,7 @@ widgetTest('no logo - minimized', { }, test(assert) { - assert.ok(this.$('i.fa-home').length === 1); + assert.ok(this.$('.d-icon-home').length === 1); } }); diff --git a/test/javascripts/widgets/post-test.js.es6 b/test/javascripts/widgets/post-test.js.es6 index c7702f68c9d..0296c9dcf3d 100644 --- a/test/javascripts/widgets/post-test.js.es6 +++ b/test/javascripts/widgets/post-test.js.es6 @@ -436,7 +436,7 @@ widgetTest("reply directly above", { click('a.reply-to-tab'); andThen(() => { assert.equal(this.$('section.embedded-posts.top .cooked').length, 1); - assert.equal(this.$('section.embedded-posts i.fa-arrow-up').length, 1); + assert.equal(this.$('section.embedded-posts .d-icon-arrow-up').length, 1); }); } }); @@ -673,7 +673,7 @@ widgetTest("replies - one below, not suppressed", { click('button.show-replies'); andThen(() => { assert.equal(this.$('section.embedded-posts.bottom .cooked').length, 1); - assert.equal(this.$('section.embedded-posts i.fa-arrow-down').length, 1); + assert.equal(this.$('section.embedded-posts .d-icon-arrow-down').length, 1); }); } }); @@ -760,7 +760,7 @@ widgetTest("topic map - links", { click('nav.buttons button'); andThen(() => { assert.equal(this.$('.map.map-collapsed').length, 0); - assert.equal(this.$('.topic-map i.fa-chevron-up').length, 1); + assert.equal(this.$('.topic-map .d-icon-chevron-up').length, 1); assert.equal(this.$('.topic-map-expanded').length, 1); assert.equal(this.$('.topic-map-expanded .topic-link').length, 5, 'it limits the links displayed'); }); diff --git a/test/javascripts/widgets/poster-name-test.js.es6 b/test/javascripts/widgets/poster-name-test.js.es6 index d399e91fef2..c8ab8391d7a 100644 --- a/test/javascripts/widgets/poster-name-test.js.es6 +++ b/test/javascripts/widgets/poster-name-test.js.es6 @@ -38,7 +38,7 @@ widgetTest('extra classes and glyphs', { assert.ok(this.$('span.staff').length); assert.ok(this.$('span.admin').length); assert.ok(this.$('span.moderator').length); - assert.ok(this.$('i.fa-shield').length); + assert.ok(this.$('.d-icon-shield').length); assert.ok(this.$('span.new-user').length); assert.ok(this.$('span.fish').length); } From 6dfa3625dfec711a6fb20d0ea826ce9f65fc06cb Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 27 Jul 2017 15:03:41 -0400 Subject: [PATCH 23/59] REFACTOR: Replace `fa-*` in SCSS with `d-icon-*` --- .../stylesheets/common/admin/admin_base.scss | 18 +++++++++--------- .../stylesheets/common/admin/customize.scss | 2 +- .../stylesheets/common/base/_topic-list.scss | 4 ++-- .../stylesheets/common/base/combobox.scss | 2 +- .../stylesheets/common/base/compose.scss | 2 +- .../stylesheets/common/base/directory.scss | 4 ++-- app/assets/stylesheets/common/base/emoji.scss | 4 ++-- .../stylesheets/common/base/exception.scss | 2 +- app/assets/stylesheets/common/base/header.scss | 5 +++-- .../stylesheets/common/base/history.scss | 2 +- .../stylesheets/common/base/menu-panel.scss | 4 ++-- app/assets/stylesheets/common/base/modal.scss | 2 +- app/assets/stylesheets/common/base/rtl.scss | 4 ++-- .../stylesheets/common/base/tagging.scss | 2 +- .../stylesheets/common/base/topic-post.scss | 2 +- app/assets/stylesheets/common/base/user.scss | 6 +++--- .../stylesheets/common/components/badges.scss | 2 +- .../stylesheets/common/components/buttons.scss | 6 +++--- app/assets/stylesheets/desktop/discourse.scss | 2 +- app/assets/stylesheets/desktop/header.scss | 2 +- app/assets/stylesheets/desktop/topic-list.scss | 10 +++++----- app/assets/stylesheets/desktop/topic-post.scss | 14 +++++++------- app/assets/stylesheets/desktop/topic.scss | 2 +- app/assets/stylesheets/mobile/directory.scss | 2 +- app/assets/stylesheets/mobile/discourse.scss | 2 +- app/assets/stylesheets/mobile/topic-list.scss | 4 ++-- app/assets/stylesheets/mobile/topic-post.scss | 8 ++++---- app/assets/stylesheets/mobile/topic.scss | 2 +- app/assets/stylesheets/wizard.scss | 6 +++--- 29 files changed, 64 insertions(+), 63 deletions(-) diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 58ee43ef773..6ad6b69dcec 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -51,8 +51,8 @@ $mobile-breakpoint: 700px; background-color: lighten($primary, 80%); } - i.fa-chevron-down, - i.fa-chevron-up { + .d-icon-chevron-down, + .d-icon-chevron-up { margin-left: 0.5em; } } @@ -869,7 +869,7 @@ section.details { } .update-nag { - i.fa { + .d-icon { font-size: 1.429em; } } @@ -904,10 +904,10 @@ table.api-keys { width: 100%; .title { - i.fa { + .d-icon { color: $primary; } - i.fa-heart { + .d-icon-heart { color: $love; } } @@ -1363,10 +1363,10 @@ table.api-keys { } .tl3-requirements { - .fa-check { + .d-icon-check { color: $success; } - .fa-times { + .d-icon-times { color: $danger; } } @@ -1860,11 +1860,11 @@ table#user-badges { .watched-word { display: inline-block; cursor: pointer; - i.fa { + .d-icon { margin-right: 0.25em; color: dark-light-diff($primary, $secondary, 50%, -50%); } - &:hover i.fa { + &:hover .d-icon { color: $primary; } } diff --git a/app/assets/stylesheets/common/admin/customize.scss b/app/assets/stylesheets/common/admin/customize.scss index b550f65b3fb..1ba3bbc507f 100644 --- a/app/assets/stylesheets/common/admin/customize.scss +++ b/app/assets/stylesheets/common/admin/customize.scss @@ -98,7 +98,7 @@ li.mobile a { padding-right: 25px; } - .fa-mobile { + .d-icon-mobile { position: absolute; right: 10px; top: 3px; diff --git a/app/assets/stylesheets/common/base/_topic-list.scss b/app/assets/stylesheets/common/base/_topic-list.scss index 6a202efceab..f918da40ee2 100644 --- a/app/assets/stylesheets/common/base/_topic-list.scss +++ b/app/assets/stylesheets/common/base/_topic-list.scss @@ -72,7 +72,7 @@ html.anon .topic-list a.title:visited:not(.badge-notification) {color: $primary- color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); font-weight: normal; font-size: 1em; - button i.fa {color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));} + button .d-icon {color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%));} } td { @@ -257,7 +257,7 @@ ol.category-breadcrumb { } } -.fa-thumb-tack.unpinned { +.d-icon-thumb-tack.unpinned { @include fa-icon-rotate(180deg, 1); color: $primary; /* because it is rotated, right becomes left! */ diff --git a/app/assets/stylesheets/common/base/combobox.scss b/app/assets/stylesheets/common/base/combobox.scss index e9c2fcda7b4..4c80abc5c6d 100644 --- a/app/assets/stylesheets/common/base/combobox.scss +++ b/app/assets/stylesheets/common/base/combobox.scss @@ -25,7 +25,7 @@ .select2-drop { background: $secondary; - i.fa { + .d-icon { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } } diff --git a/app/assets/stylesheets/common/base/compose.scss b/app/assets/stylesheets/common/base/compose.scss index d0079f72346..233519fcc8c 100644 --- a/app/assets/stylesheets/common/base/compose.scss +++ b/app/assets/stylesheets/common/base/compose.scss @@ -10,7 +10,7 @@ margin: 0; li { - .fa-users { + .d-icon-users { color: lighten($primary, 40%); padding: 0 2px; } diff --git a/app/assets/stylesheets/common/base/directory.scss b/app/assets/stylesheets/common/base/directory.scss index b0c7ae08983..8a31885b8cb 100644 --- a/app/assets/stylesheets/common/base/directory.scss +++ b/app/assets/stylesheets/common/base/directory.scss @@ -49,11 +49,11 @@ white-space: nowrap; width: 13%; - i.fa-heart { + .d-icon-heart { color: $love; margin-right: 0.5em; } - i.fa-chevron-down, i.fa-chevron-up { + .d-icon-chevron-down, .d-icon-chevron-up { margin-left: 0.5em; } diff --git a/app/assets/stylesheets/common/base/emoji.scss b/app/assets/stylesheets/common/base/emoji.scss index cfbfbaff504..df240bc38fc 100644 --- a/app/assets/stylesheets/common/base/emoji.scss +++ b/app/assets/stylesheets/common/base/emoji.scss @@ -140,7 +140,7 @@ body img.emoji { display: none; } -.emoji-picker .diversity-picker i.fa { +.emoji-picker .diversity-picker .d-icon { color: #fff; font-size: 12px; text-shadow: 0.5px 1.5px 0 rgba(0,0,0,0.3); @@ -196,7 +196,7 @@ body img.emoji { align-items: center; } -.emoji-picker .filter .fa.fa-search { +.emoji-picker .filter .d-icon-search { color: dark-light-choose(scale-color($header_primary, $lightness: 50%), $header_primary); font-size: 16px; margin-left: 5px; diff --git a/app/assets/stylesheets/common/base/exception.scss b/app/assets/stylesheets/common/base/exception.scss index c9a70876513..9bdc0df4bb8 100644 --- a/app/assets/stylesheets/common/base/exception.scss +++ b/app/assets/stylesheets/common/base/exception.scss @@ -16,7 +16,7 @@ } .desc { margin-top: 16px; - .fa-check-circle { + .d-icon-check-circle { color: $success; } } diff --git a/app/assets/stylesheets/common/base/header.scss b/app/assets/stylesheets/common/base/header.scss index 4a674e8d1f9..2245734e849 100644 --- a/app/assets/stylesheets/common/base/header.scss +++ b/app/assets/stylesheets/common/base/header.scss @@ -26,7 +26,7 @@ max-height: 40px; } - .fa-home { + .d-icon-home { font-size: 1.643em; } @@ -113,7 +113,8 @@ } } } - [class^="fa fa-"] { + + .d-icon { width: 32px; height: 32px; font-size: 1.714em; diff --git a/app/assets/stylesheets/common/base/history.scss b/app/assets/stylesheets/common/base/history.scss index 23a094624fc..0de3abad178 100644 --- a/app/assets/stylesheets/common/base/history.scss +++ b/app/assets/stylesheets/common/base/history.scss @@ -80,7 +80,7 @@ background-color: lighten($highlight, 23%); padding: 3px 5px 5px 5px; } - .fa-ban { + .d-icon-ban { color: #f00; } .hidden-revision-either { diff --git a/app/assets/stylesheets/common/base/menu-panel.scss b/app/assets/stylesheets/common/base/menu-panel.scss index 0e19a8a1175..21e80fb44e6 100644 --- a/app/assets/stylesheets/common/base/menu-panel.scss +++ b/app/assets/stylesheets/common/base/menu-panel.scss @@ -226,7 +226,7 @@ } } .is-warning { - i.fa-envelope-o { + .d-icon-envelope-o { &:before { content: "\f0e0"; } @@ -301,7 +301,7 @@ div.menu-links-header { a { font-size: 1.1em; } - .fa-user { + .d-icon-user { margin-right: 0.2em; } } diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss index c5a6647d07f..0a73791b3e1 100644 --- a/app/assets/stylesheets/common/base/modal.scss +++ b/app/assets/stylesheets/common/base/modal.scss @@ -292,7 +292,7 @@ .permission { margin-left: 20px; } - .fa-times-circle { + .d-icon-times-circle { margin-left: 5px; } li { diff --git a/app/assets/stylesheets/common/base/rtl.scss b/app/assets/stylesheets/common/base/rtl.scss index 045df633a6f..71163ef835c 100644 --- a/app/assets/stylesheets/common/base/rtl.scss +++ b/app/assets/stylesheets/common/base/rtl.scss @@ -7,7 +7,7 @@ right: 0 !important; } -// This is used to flip the .fa-caret-right icon +// This is used to flip the .d-icon-caret-right @mixin rotate( $degrees ) { -webkit-transform: rotate(#{$degrees}deg); -moz-transform: rotate(#{$degrees}deg); @@ -21,7 +21,7 @@ } // Get the right caret to point left -.rtl .fa-caret-right { +.rtl .d-icon-caret-right { @include rotate(180); } diff --git a/app/assets/stylesheets/common/base/tagging.scss b/app/assets/stylesheets/common/base/tagging.scss index f26c0623175..be2a28cc3f3 100644 --- a/app/assets/stylesheets/common/base/tagging.scss +++ b/app/assets/stylesheets/common/base/tagging.scss @@ -207,7 +207,7 @@ header .discourse-tag {color: $tag-color } } .autocomplete { - .fa-tag { + .d-icon-tag { color: dark-light-choose($primary, scale-color($primary, $lightness: 70%)); padding-right: 5px; } diff --git a/app/assets/stylesheets/common/base/topic-post.scss b/app/assets/stylesheets/common/base/topic-post.scss index ce52e4bb19e..3f34809d20a 100644 --- a/app/assets/stylesheets/common/base/topic-post.scss +++ b/app/assets/stylesheets/common/base/topic-post.scss @@ -242,7 +242,7 @@ aside.quote { .post-info { &.via-email, &.whisper { margin-right: 5px; - i.fa { + .d-icon { font-size: 1em; } } diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index 72316b34ad1..70926d68a3e 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -21,7 +21,7 @@ } .user-main { - i.fa-heart { + .d-icon-heart { color: $love !important; } .nav-pills { @@ -177,10 +177,10 @@ .user-nav { margin: 5px 0px; padding-top: 10px; - .fa { + .d-icon { margin-right: 5px; } - .fa.fa-comment { + .d-icon-comment { margin-right: 2px; } } diff --git a/app/assets/stylesheets/common/components/badges.scss b/app/assets/stylesheets/common/components/badges.scss index c160fc53538..0dd3a5d3a4d 100644 --- a/app/assets/stylesheets/common/components/badges.scss +++ b/app/assets/stylesheets/common/components/badges.scss @@ -158,7 +158,7 @@ padding: 5px; width: 13px; - .fa-caret-right { + .d-icon-caret-right { margin-left: 2px; } } diff --git a/app/assets/stylesheets/common/components/buttons.scss b/app/assets/stylesheets/common/components/buttons.scss index bce45c1263a..f73c5e3ea09 100644 --- a/app/assets/stylesheets/common/components/buttons.scss +++ b/app/assets/stylesheets/common/components/buttons.scss @@ -61,10 +61,10 @@ cursor: not-allowed; } - i.fa { + .d-icon { opacity: 0.7; } - &.btn-primary i.fa { + &.btn-primary .d-icon { opacity: 1; } } @@ -194,7 +194,7 @@ background: transparent; border: 0; outline: 0; - i.fa { + .d-icon { opacity: 0.7; } } diff --git a/app/assets/stylesheets/desktop/discourse.scss b/app/assets/stylesheets/desktop/discourse.scss index aac66f0295a..aa120ec8a95 100644 --- a/app/assets/stylesheets/desktop/discourse.scss +++ b/app/assets/stylesheets/desktop/discourse.scss @@ -96,7 +96,7 @@ body { } } - i.fa-envelope { + .d-icon-envelope { color: $danger; } } diff --git a/app/assets/stylesheets/desktop/header.scss b/app/assets/stylesheets/desktop/header.scss index f4ce15a0222..4c4d3da302a 100644 --- a/app/assets/stylesheets/desktop/header.scss +++ b/app/assets/stylesheets/desktop/header.scss @@ -7,7 +7,7 @@ z-index: 1000; padding-top: 3px; height: 60px; - .fa-home { + .d-icon-home { padding:8px; font-size: 2.1em; } diff --git a/app/assets/stylesheets/desktop/topic-list.scss b/app/assets/stylesheets/desktop/topic-list.scss index edd58ce1e0c..de6b1ef496e 100644 --- a/app/assets/stylesheets/desktop/topic-list.scss +++ b/app/assets/stylesheets/desktop/topic-list.scss @@ -36,10 +36,10 @@ .topic-list { margin: 0 0 10px; - .fa-thumb-tack { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } - .fa-thumb-tack.unpinned { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + .d-icon-thumb-tack { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + .d-icon-thumb-tack.unpinned { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } a.title {color: $primary;} - .fa-bookmark { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + .d-icon-bookmark { color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } th, td { padding: 12px 5px; @@ -51,7 +51,7 @@ } } th { - button i.fa {color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } + button .d-icon {color: dark-light-choose(scale-color($primary, $lightness: 50%), scale-color($secondary, $lightness: 50%)); } } > tbody > tr { @@ -68,7 +68,7 @@ .star { width: 20px; padding-right: 0; - .fa-star { + .d-icon-star { position: relative; } + .main-link { diff --git a/app/assets/stylesheets/desktop/topic-post.scss b/app/assets/stylesheets/desktop/topic-post.scss index fd0dea67181..5229331141f 100644 --- a/app/assets/stylesheets/desktop/topic-post.scss +++ b/app/assets/stylesheets/desktop/topic-post.scss @@ -67,7 +67,7 @@ nav.post-controls { a, button { color: dark-light-choose(scale-color($primary, $lightness: 75%), scale-color($secondary, $lightness: 25%)); - i.fa { + .d-icon { opacity: 1.0; } margin-right: 2px; @@ -207,7 +207,7 @@ nav.post-controls { width: 176px; margin-bottom: 5px; - i.fa { + .d-icon { width: 14px; margin-right: 14px; } @@ -261,7 +261,7 @@ nav.post-controls { } .post-date { color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); } - .fa-arrow-up, .fa-arrow-down { margin-left: 5px; } + .d-icon-arrow-up, .d-icon-arrow-down { margin-left: 5px; } .reply:first-of-type .row { border-top: none; } .topic-meta-data { @@ -385,7 +385,7 @@ a.star { font-weight: bold; font-size: 0.929em; } - i.fa-times { + .d-icon-times { color: dark-light-choose(scale-color($primary, $lightness: 60%), scale-color($secondary, $lightness: 40%)); } } @@ -446,10 +446,10 @@ a.star { .btn { margin-bottom: 5px; margin-right: 10px; - .fa-bookmark.bookmarked { color: $tertiary; } + .d-icon-bookmark.bookmarked { color: $tertiary; } } - .bookmark.bookmarked .fa-bookmark { + .bookmark.bookmarked .d-icon-bookmark { color: $tertiary; } @@ -526,7 +526,7 @@ video { .topic-statuses { i { color: $header_primary; } - i.fa-envelope { color: $danger; } + .d-icon-envelope { color: $danger; } .unpinned { color: $header_primary; } } diff --git a/app/assets/stylesheets/desktop/topic.scss b/app/assets/stylesheets/desktop/topic.scss index af7c2da9026..4adbbcd4d0d 100644 --- a/app/assets/stylesheets/desktop/topic.scss +++ b/app/assets/stylesheets/desktop/topic.scss @@ -167,7 +167,7 @@ text-align: center; z-index: 1; } - i.fa { + .d-icon { position: absolute; right: 8px; bottom: 9px; diff --git a/app/assets/stylesheets/mobile/directory.scss b/app/assets/stylesheets/mobile/directory.scss index ba1617ea54a..4cd15c89009 100644 --- a/app/assets/stylesheets/mobile/directory.scss +++ b/app/assets/stylesheets/mobile/directory.scss @@ -40,7 +40,7 @@ margin-left: 0.2em; color: blend-primary-secondary(50%); } - i.fa-heart { + .d-icon-heart { color: $love; } } diff --git a/app/assets/stylesheets/mobile/discourse.scss b/app/assets/stylesheets/mobile/discourse.scss index 39162e34a05..18726ee67e6 100644 --- a/app/assets/stylesheets/mobile/discourse.scss +++ b/app/assets/stylesheets/mobile/discourse.scss @@ -136,7 +136,7 @@ h2#site-text-logo box-sizing: border-box; display: block; } - .fa-caret-down { + .d-icon-caret-down { position: absolute; right: 0px; } diff --git a/app/assets/stylesheets/mobile/topic-list.scss b/app/assets/stylesheets/mobile/topic-list.scss index 5c1d3e2bddf..7e8c56da2de 100644 --- a/app/assets/stylesheets/mobile/topic-list.scss +++ b/app/assets/stylesheets/mobile/topic-list.scss @@ -47,8 +47,8 @@ } .nav-pills > li { - background: $primary-low; - i.fa-caret-down { + background: $primary-low; + .d-icon-caret-down { margin-left: 8px; } } diff --git a/app/assets/stylesheets/mobile/topic-post.scss b/app/assets/stylesheets/mobile/topic-post.scss index b8227b21c1b..4068eb00aee 100644 --- a/app/assets/stylesheets/mobile/topic-post.scss +++ b/app/assets/stylesheets/mobile/topic-post.scss @@ -47,7 +47,7 @@ span.badge-posts { nav.post-controls { clear: both; - i.fa { + .d-icon { opacity: 1.0; } } @@ -134,7 +134,7 @@ nav.post-controls { width: 176px; margin-bottom: 5px; - i.fa { + .d-icon { width: 14px; margin-right: 14px; } @@ -323,7 +323,7 @@ a.star { #topic-footer-buttons { @include clearfix; padding: 20px 0 0 0; - .fa-bookmark.bookmarked { color: $tertiary; } + .d-icon-bookmark.bookmarked { color: $tertiary; } .combobox { float: left; @@ -371,7 +371,7 @@ span.post-count { .btn { margin-bottom: 5px; margin-right: 10px; - .fa-star {margin-right: 5px;} + .d-icon-star {margin-right: 5px;} } } diff --git a/app/assets/stylesheets/mobile/topic.scss b/app/assets/stylesheets/mobile/topic.scss index 0d719d7f032..f52b3ce0fc8 100644 --- a/app/assets/stylesheets/mobile/topic.scss +++ b/app/assets/stylesheets/mobile/topic.scss @@ -133,7 +133,7 @@ font-size: 1.286em; line-height: 15px; } - i.fa { + .d-icon { position: absolute; right: 8px; bottom: 9px; diff --git a/app/assets/stylesheets/wizard.scss b/app/assets/stylesheets/wizard.scss index 245478866a7..922f5afe5db 100644 --- a/app/assets/stylesheets/wizard.scss +++ b/app/assets/stylesheets/wizard.scss @@ -224,11 +224,11 @@ body.wizard { background-color: #ccc; } - i.fa-chevron-right { + .d-icon-chevron-right { margin-left: 0.25em; font-size: 0.8em; } - i.fa-chevron-left { + .d-icon-chevron-left { margin-right: 0.25em; font-size: 0.8em; } @@ -287,7 +287,7 @@ body.wizard { .wizard-btn.next { min-width: 70px; - i.fa-chevron-right { + .d-icon-chevron-right { margin-left: 0.25em; font-size: 0.8em; } From 41afd9c81802438c932f5aa4d647ecefa6998332 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 27 Jul 2017 15:13:03 -0400 Subject: [PATCH 24/59] Bump Plugin API version for icon api --- app/assets/javascripts/discourse/lib/plugin-api.js.es6 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 b/app/assets/javascripts/discourse/lib/plugin-api.js.es6 index 238e0275999..b96b5514218 100644 --- a/app/assets/javascripts/discourse/lib/plugin-api.js.es6 +++ b/app/assets/javascripts/discourse/lib/plugin-api.js.es6 @@ -23,7 +23,7 @@ import { registerIconRenderer } from 'discourse-common/lib/icon-library'; // If you add any methods to the API ensure you bump up this number -const PLUGIN_API_VERSION = '0.8.7'; +const PLUGIN_API_VERSION = '0.8.8'; class PluginApi { constructor(version, container) { @@ -550,7 +550,7 @@ function cmpVersions (a, b) { function getPluginApi(version) { version = version.toString(); - if (cmpVersions(version,PLUGIN_API_VERSION) <= 0) { + if (cmpVersions(version, PLUGIN_API_VERSION) <= 0) { if (!_pluginv01) { _pluginv01 = new PluginApi(version, Discourse.__container__); } From 1b1fd646390e5bcee2ba02f3ebfbdcabc32b93d4 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 27 Jul 2017 16:57:26 -0400 Subject: [PATCH 25/59] FIX: Badge icons disappeared --- app/assets/javascripts/discourse/helpers/icon-or-image.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/helpers/icon-or-image.js.es6 b/app/assets/javascripts/discourse/helpers/icon-or-image.js.es6 index 4f1ccf0d7e3..d5439c7323f 100644 --- a/app/assets/javascripts/discourse/helpers/icon-or-image.js.es6 +++ b/app/assets/javascripts/discourse/helpers/icon-or-image.js.es6 @@ -3,5 +3,5 @@ import { iconHTML } from 'discourse-common/lib/icon-library'; export default htmlHelper(function(str) { if (Ember.isEmpty(str)) { return ""; } - return (str.indexOf('fa-') === 0) ? iconHTML(str) : ``; + return (str.indexOf('fa-') === 0) ? iconHTML(str.replace('fa-', '')) : ``; }); From c3b7419a08349e324143c38241d0205493309bcc Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 27 Jul 2017 17:14:41 -0400 Subject: [PATCH 26/59] UX: Remove `hidden` from the `#discourse-modal` when the modal is shown --- app/assets/javascripts/discourse/components/d-modal.js.es6 | 3 ++- .../javascripts/discourse/components/hide-modal-trigger.js.es6 | 2 +- app/assets/javascripts/discourse/templates/modal.hbs | 2 +- app/assets/stylesheets/common/base/modal.scss | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/discourse/components/d-modal.js.es6 b/app/assets/javascripts/discourse/components/d-modal.js.es6 index 475c79d0495..1e1a40cdc34 100644 --- a/app/assets/javascripts/discourse/components/d-modal.js.es6 +++ b/app/assets/javascripts/discourse/components/d-modal.js.es6 @@ -2,7 +2,7 @@ import { on } from "ember-addons/ember-computed-decorators"; export default Ember.Component.extend({ elementId: 'discourse-modal', - classNameBindings: [':modal', ':hidden', 'modalClass'], + classNameBindings: [':modal', 'modalClass'], attributeBindings: ['data-keyboard'], // We handle ESC ourselves @@ -17,6 +17,7 @@ export default Ember.Component.extend({ }); this.appEvents.on('modal:body-shown', data => { + this.$().removeClass('hidden'); if (data.title) { this.set('title', I18n.t(data.title)); } else if (data.rawTitle) { diff --git a/app/assets/javascripts/discourse/components/hide-modal-trigger.js.es6 b/app/assets/javascripts/discourse/components/hide-modal-trigger.js.es6 index 0db92cc4617..b077ab7048d 100644 --- a/app/assets/javascripts/discourse/components/hide-modal-trigger.js.es6 +++ b/app/assets/javascripts/discourse/components/hide-modal-trigger.js.es6 @@ -1,6 +1,6 @@ export default Ember.Component.extend({ didInsertElement() { this._super(); - $('#discourse-modal').modal('hide'); + $('#discourse-modal').modal('hide').addClass('hidden'); } }); diff --git a/app/assets/javascripts/discourse/templates/modal.hbs b/app/assets/javascripts/discourse/templates/modal.hbs index cd7b2c616ab..a52aa8276fe 100644 --- a/app/assets/javascripts/discourse/templates/modal.hbs +++ b/app/assets/javascripts/discourse/templates/modal.hbs @@ -1,4 +1,4 @@ -{{#d-modal modalClass=modalClass title=title}} +{{#d-modal modalClass=modalClass title=title class="hidden"}}