From de88be2fbcd305cc82358a9e819a9b049515ff75 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 30 Nov 2015 15:22:58 -0500 Subject: [PATCH] Support for "Only show overridden" in site text customization --- .../admin/components/site-text-summary.js.es6 | 1 + .../controllers/admin-site-text-index.js.es6 | 17 +++++++++-- .../admin/routes/admin-site-text-index.js.es6 | 5 ++-- .../admin/templates/site-text-index.hbs | 10 +++++-- .../discourse/components/d-checkbox.js.es6 | 18 ++++++++++++ .../discourse/components/text-field.js.es6 | 7 ----- .../javascripts/discourse/models/store.js.es6 | 29 +++++++++++++------ .../templates/components/d-checkbox.hbs | 2 ++ .../stylesheets/common/admin/admin_base.scss | 14 ++++++++- .../admin/site_texts_controller.rb | 10 +++++-- app/controllers/application_controller.rb | 2 ++ app/serializers/site_text_serializer.rb | 6 ++-- config/locales/client.en.yml | 1 + lib/freedom_patches/translate_accelerator.rb | 2 +- .../acceptance/admin-site-text-test.js.es6 | 15 +++++++++- .../helpers/create-pretender.js.es6 | 13 +++++++-- test/javascripts/models/store-test.js.es6 | 2 +- 17 files changed, 119 insertions(+), 35 deletions(-) create mode 100644 app/assets/javascripts/discourse/components/d-checkbox.js.es6 create mode 100644 app/assets/javascripts/discourse/templates/components/d-checkbox.hbs diff --git a/app/assets/javascripts/admin/components/site-text-summary.js.es6 b/app/assets/javascripts/admin/components/site-text-summary.js.es6 index a54e615ef88..642164f8710 100644 --- a/app/assets/javascripts/admin/components/site-text-summary.js.es6 +++ b/app/assets/javascripts/admin/components/site-text-summary.js.es6 @@ -2,6 +2,7 @@ import { on } from 'ember-addons/ember-computed-decorators'; export default Ember.Component.extend({ classNames: ['site-text'], + classNameBindings: ['siteText.overridden'], @on('didInsertElement') highlightTerm() { diff --git a/app/assets/javascripts/admin/controllers/admin-site-text-index.js.es6 b/app/assets/javascripts/admin/controllers/admin-site-text-index.js.es6 index d5450c0d442..7be42698844 100644 --- a/app/assets/javascripts/admin/controllers/admin-site-text-index.js.es6 +++ b/app/assets/javascripts/admin/controllers/admin-site-text-index.js.es6 @@ -5,8 +5,20 @@ export default Ember.Controller.extend({ searching: false, siteTexts: null, preferred: false, + _overridden: null, + queryParams: ['q', 'overridden'], - queryParams: ['q'], + @computed + overridden: { + set(value) { + if (!value || value === "false") { value = false; } + this._overridden = value; + return value; + }, + get() { + return this._overridden; + } + }, @computed q: { @@ -21,8 +33,7 @@ export default Ember.Controller.extend({ }, _performSearch() { - const q = this.get('q'); - this.store.find('site-text', { q }).then(results => { + this.store.find('site-text', this.getProperties('q', 'overridden')).then(results => { this.set('siteTexts', results); }).finally(() => this.set('searching', false)); }, diff --git a/app/assets/javascripts/admin/routes/admin-site-text-index.js.es6 b/app/assets/javascripts/admin/routes/admin-site-text-index.js.es6 index e312d389dfc..510fd93deea 100644 --- a/app/assets/javascripts/admin/routes/admin-site-text-index.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-site-text-index.js.es6 @@ -1,10 +1,11 @@ export default Ember.Route.extend({ queryParams: { - q: { replace: true } + q: { replace: true }, + overridden: { replace: true } }, model(params) { - return this.store.find('site-text', { q: params.q }); + return this.store.find('site-text', Ember.getProperties(params, 'q', 'overridden')); }, setupController(controller, model) { diff --git a/app/assets/javascripts/admin/templates/site-text-index.hbs b/app/assets/javascripts/admin/templates/site-text-index.hbs index 85b007dcc97..7caf9b124e8 100644 --- a/app/assets/javascripts/admin/templates/site-text-index.hbs +++ b/app/assets/javascripts/admin/templates/site-text-index.hbs @@ -5,13 +5,17 @@ placeholderKey="admin.site_text.search" class="no-blur site-text-search" autofocus="true" - keyUpAction="search"}} + key-up="search"}} + +
+ {{d-checkbox label="admin.site_text.show_overriden" checked=overridden change="search"}} +
{{#conditional-loading-spinner condition=searching}} - {{#unless siteTexts.findArgs.q}} + {{#if siteTexts.extras.recommended}}

{{i18n "admin.site_text.recommended"}}

- {{/unless}} + {{/if}} {{#each siteTexts as |siteText|}} {{site-text-summary siteText=siteText editAction="edit" term=q}} diff --git a/app/assets/javascripts/discourse/components/d-checkbox.js.es6 b/app/assets/javascripts/discourse/components/d-checkbox.js.es6 new file mode 100644 index 00000000000..217ec2c3aec --- /dev/null +++ b/app/assets/javascripts/discourse/components/d-checkbox.js.es6 @@ -0,0 +1,18 @@ +import { on } from "ember-addons/ember-computed-decorators"; + +export default Ember.Component.extend({ + tagName: 'label', + + @on('didInsertElement') + _watchChanges() { + // In Ember 13.3 we can use action on the checkbox `{{input}}` but not in 1.11 + this.$('input').on('click.d-checkbox', () => { + Ember.run.scheduleOnce('afterRender', () => this.sendAction('change')); + }); + }, + + @on('willDestroyElement') + _stopWatching() { + this.$('input').off('click.d-checkbox'); + } +}); diff --git a/app/assets/javascripts/discourse/components/text-field.js.es6 b/app/assets/javascripts/discourse/components/text-field.js.es6 index cf572dd121c..a9246efa7db 100644 --- a/app/assets/javascripts/discourse/components/text-field.js.es6 +++ b/app/assets/javascripts/discourse/components/text-field.js.es6 @@ -6,12 +6,5 @@ export default Ember.TextField.extend({ @computed("placeholderKey") placeholder(placeholderKey) { return placeholderKey ? I18n.t(placeholderKey) : ""; - }, - - keyUp() { - const act = this.get('keyUpAction'); - if (act) { - this.sendAction('keyUpAction'); - } } }); diff --git a/app/assets/javascripts/discourse/models/store.js.es6 b/app/assets/javascripts/discourse/models/store.js.es6 index b29b1033065..a7f4edcf9be 100644 --- a/app/assets/javascripts/discourse/models/store.js.es6 +++ b/app/assets/javascripts/discourse/models/store.js.es6 @@ -88,9 +88,9 @@ export default Ember.Object.extend({ refreshResults(resultSet, type, url) { const self = this; - return Discourse.ajax(url).then(function(result) { - const typeName = Ember.String.underscore(self.pluralize(type)), - content = result[typeName].map(obj => self._hydrate(type, obj, result)); + return Discourse.ajax(url).then(result => { + const typeName = Ember.String.underscore(self.pluralize(type)); + const content = result[typeName].map(obj => self._hydrate(type, obj, result)); resultSet.set('content', content); }); }, @@ -143,13 +143,24 @@ export default Ember.Object.extend({ }, _resultSet(type, result, findArgs) { - const typeName = Ember.String.underscore(this.pluralize(type)), - content = result[typeName].map(obj => this._hydrate(type, obj, result)), - totalRows = result["total_rows_" + typeName] || content.length, - loadMoreUrl = result["load_more_" + typeName], - refreshUrl = result['refresh_' + typeName]; + const typeName = Ember.String.underscore(this.pluralize(type)); + const content = result[typeName].map(obj => this._hydrate(type, obj, result)); - return ResultSet.create({ content, totalRows, loadMoreUrl, refreshUrl, findArgs, store: this, __type: type }); + const createArgs = { + content, + findArgs, + totalRows: result["total_rows_" + typeName] || content.length, + loadMoreUrl: result["load_more_" + typeName], + refreshUrl: result['refresh_' + typeName], + store: this, + __type: type + }; + + if (result.extras) { + createArgs.extras = result.extras; + } + + return ResultSet.create(createArgs); }, _build(type, obj) { diff --git a/app/assets/javascripts/discourse/templates/components/d-checkbox.hbs b/app/assets/javascripts/discourse/templates/components/d-checkbox.hbs new file mode 100644 index 00000000000..b4bbae64a24 --- /dev/null +++ b/app/assets/javascripts/discourse/templates/components/d-checkbox.hbs @@ -0,0 +1,2 @@ +{{input type="checkbox" checked=checked}} +{{i18n label}} diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 9bec8c8c3a8..646e2b3dfbc 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -50,11 +50,19 @@ td.flaggers td { margin-top: 0; } - input { + .site-text-search { padding: 0.5em; font-size: 1em; width: 50%; } + + .extra-options { + float: right; + input[type=checkbox] { + margin-right: 0.5em; + } + } + } .text-highlight { font-weight: bold; @@ -65,6 +73,10 @@ td.flaggers td { border-bottom: 1px solid dark-light-diff($primary, $secondary, 90%, -60%); margin-bottom: 0.5em; + &.overridden { + background-color: dark-light-diff($highlight, $secondary, 50%, -60%); + } + h3 { font-weight: normal; font-size: 1.1em; diff --git a/app/controllers/admin/site_texts_controller.rb b/app/controllers/admin/site_texts_controller.rb index 0a70519f25b..18b9841d851 100644 --- a/app/controllers/admin/site_texts_controller.rb +++ b/app/controllers/admin/site_texts_controller.rb @@ -8,11 +8,15 @@ class Admin::SiteTextsController < Admin::AdminController end def index - if params[:q].blank? + overridden = params[:overridden] == 'true' + extras = {} + + if params[:q].blank? && !overridden + extras[:recommended] = true results = self.class.preferred_keys.map {|k| {id: k, value: I18n.t(k) }} else results = [] - translations = I18n.search(params[:q]) + translations = I18n.search(params[:q], overridden: overridden) translations.each do |k, v| results << {id: k, value: v} end @@ -21,7 +25,7 @@ class Admin::SiteTextsController < Admin::AdminController end end - render_serialized(results[0..50], SiteTextSerializer, root: 'site_texts', rest_serializer: true) + render_serialized(results[0..50], SiteTextSerializer, root: 'site_texts', rest_serializer: true, extras: extras) end def show diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b3244816580..8bbbc55a2cd 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -229,6 +229,8 @@ class ApplicationController < ActionController::Base opts.each do |k, v| obj[k] = v if k.to_s.start_with?("refresh_") end + + obj['extras'] = opts[:extras] if opts[:extras] end render json: MultiJson.dump(obj), status: opts[:status] || 200 diff --git a/app/serializers/site_text_serializer.rb b/app/serializers/site_text_serializer.rb index 0bd13d7520a..c686a080087 100644 --- a/app/serializers/site_text_serializer.rb +++ b/app/serializers/site_text_serializer.rb @@ -1,5 +1,5 @@ class SiteTextSerializer < ApplicationSerializer - attributes :id, :value, :can_revert? + attributes :id, :value, :overridden?, :can_revert? def id object[:id] @@ -9,12 +9,14 @@ class SiteTextSerializer < ApplicationSerializer object[:value] end - def can_revert? + def overridden? current_val = value I18n.overrides_disabled do return I18n.t(object[:id]) != current_val end end + + alias_method :can_revert?, :overridden? end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 61302b7e8c3..e1d371ac200 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2492,6 +2492,7 @@ en: revert_confirm: "Are you sure you want to revert your changes?" go_back: "Back to Search" recommended: "We recommend customizing the following text to suit your needs:" + show_overriden: 'Only show overridden' site_settings: show_overriden: 'Only show overridden' diff --git a/lib/freedom_patches/translate_accelerator.rb b/lib/freedom_patches/translate_accelerator.rb index 46b5973ed5b..0cabd15c7d9 100644 --- a/lib/freedom_patches/translate_accelerator.rb +++ b/lib/freedom_patches/translate_accelerator.rb @@ -56,7 +56,7 @@ module I18n opts ||= {} target = opts[:backend] || backend - results = target.search(config.locale, query) + results = opts[:overridden] ? {} : target.search(config.locale, query) regexp = /#{query}/i (overrides_by_locale || {}).each do |k, v| diff --git a/test/javascripts/acceptance/admin-site-text-test.js.es6 b/test/javascripts/acceptance/admin-site-text-test.js.es6 index 65c4fb1deee..0cd17959c18 100644 --- a/test/javascripts/acceptance/admin-site-text-test.js.es6 +++ b/test/javascripts/acceptance/admin-site-text-test.js.es6 @@ -6,9 +6,22 @@ test("search for a key", () => { visit("/admin/customize/site_texts"); fillIn('.site-text-search', 'Test'); - andThen(() => ok(exists('.site-text'))); + andThen(() => { + ok(exists('.site-text')); + ok(exists(".site-text:not(.overridden)")); + ok(exists('.site-text.overridden')); + }); + + + // Only show overridden + click('.extra-options input'); + andThen(() => { + ok(!exists(".site-text:not(.overridden)")); + ok(exists('.site-text.overridden')); + }); }); + test("edit and revert a site text by key", () => { visit("/admin/customize/site_texts/site.test"); andThen(() => { diff --git a/test/javascripts/helpers/create-pretender.js.es6 b/test/javascripts/helpers/create-pretender.js.es6 index 1c1e034cfbe..5c8d9168f49 100644 --- a/test/javascripts/helpers/create-pretender.js.es6 +++ b/test/javascripts/helpers/create-pretender.js.es6 @@ -210,7 +210,7 @@ export default function() { }); this.get('/fruits', function() { - return response({ __rest_serializer: "1", fruits, farmers, colors }); + return response({ __rest_serializer: "1", fruits, farmers, colors, extras: {hello: 'world'} }); }); this.get('/widgets/:widget_id', function(request) { @@ -262,7 +262,16 @@ export default function() { this.post('/topics/timings', () => response(200, {})); const siteText = {id: 'site.test', value: 'Test McTest'}; - this.get('/admin/customize/site_texts', () => response(200, {site_texts: [siteText] })); + const overridden = {id: 'site.overridden', value: 'Overridden', overridden: true }; + this.get('/admin/customize/site_texts', request => { + + if (request.queryParams.overridden) { + return response(200, {site_texts: [overridden] }) + } else { + return response(200, {site_texts: [siteText, overridden] }) + } + }); + this.get('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText })); this.delete('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText })); diff --git a/test/javascripts/models/store-test.js.es6 b/test/javascripts/models/store-test.js.es6 index 6f39c2186ad..4c72c2ae738 100644 --- a/test/javascripts/models/store-test.js.es6 +++ b/test/javascripts/models/store-test.js.es6 @@ -116,7 +116,6 @@ test('destroyRecord when new', function(assert) { }); }); - test('find embedded', function(assert) { const store = createStore(); return store.find('fruit', 2).then(function(f) { @@ -136,6 +135,7 @@ test('findAll embedded', function(assert) { return store.findAll('fruit').then(function(fruits) { assert.equal(fruits.objectAt(0).get('farmer.name'), 'Old MacDonald'); assert.equal(fruits.objectAt(0).get('farmer'), fruits.objectAt(1).get('farmer'), 'points at the same object'); + assert.equal(fruits.get('extras.hello'), 'world', 'it can supply extra information'); const fruitCols = fruits.objectAt(0).get('colors'); assert.equal(fruitCols.length, 2);