mirror of
https://github.com/discourse/discourse.git
synced 2025-03-15 02:45:24 +08:00
Support for "Only show overridden" in site text customization
This commit is contained in:
parent
04593b8fef
commit
de88be2fbc
@ -2,6 +2,7 @@ import { on } from 'ember-addons/ember-computed-decorators';
|
|||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: ['site-text'],
|
classNames: ['site-text'],
|
||||||
|
classNameBindings: ['siteText.overridden'],
|
||||||
|
|
||||||
@on('didInsertElement')
|
@on('didInsertElement')
|
||||||
highlightTerm() {
|
highlightTerm() {
|
||||||
|
@ -5,8 +5,20 @@ export default Ember.Controller.extend({
|
|||||||
searching: false,
|
searching: false,
|
||||||
siteTexts: null,
|
siteTexts: null,
|
||||||
preferred: false,
|
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
|
@computed
|
||||||
q: {
|
q: {
|
||||||
@ -21,8 +33,7 @@ export default Ember.Controller.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_performSearch() {
|
_performSearch() {
|
||||||
const q = this.get('q');
|
this.store.find('site-text', this.getProperties('q', 'overridden')).then(results => {
|
||||||
this.store.find('site-text', { q }).then(results => {
|
|
||||||
this.set('siteTexts', results);
|
this.set('siteTexts', results);
|
||||||
}).finally(() => this.set('searching', false));
|
}).finally(() => this.set('searching', false));
|
||||||
},
|
},
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
queryParams: {
|
queryParams: {
|
||||||
q: { replace: true }
|
q: { replace: true },
|
||||||
|
overridden: { replace: true }
|
||||||
},
|
},
|
||||||
|
|
||||||
model(params) {
|
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) {
|
setupController(controller, model) {
|
||||||
|
@ -5,13 +5,17 @@
|
|||||||
placeholderKey="admin.site_text.search"
|
placeholderKey="admin.site_text.search"
|
||||||
class="no-blur site-text-search"
|
class="no-blur site-text-search"
|
||||||
autofocus="true"
|
autofocus="true"
|
||||||
keyUpAction="search"}}
|
key-up="search"}}
|
||||||
|
|
||||||
|
<div class='extra-options'>
|
||||||
|
{{d-checkbox label="admin.site_text.show_overriden" checked=overridden change="search"}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#conditional-loading-spinner condition=searching}}
|
{{#conditional-loading-spinner condition=searching}}
|
||||||
{{#unless siteTexts.findArgs.q}}
|
{{#if siteTexts.extras.recommended}}
|
||||||
<p><b>{{i18n "admin.site_text.recommended"}}</b></p>
|
<p><b>{{i18n "admin.site_text.recommended"}}</b></p>
|
||||||
{{/unless}}
|
{{/if}}
|
||||||
|
|
||||||
{{#each siteTexts as |siteText|}}
|
{{#each siteTexts as |siteText|}}
|
||||||
{{site-text-summary siteText=siteText editAction="edit" term=q}}
|
{{site-text-summary siteText=siteText editAction="edit" term=q}}
|
||||||
|
@ -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');
|
||||||
|
}
|
||||||
|
});
|
@ -6,12 +6,5 @@ export default Ember.TextField.extend({
|
|||||||
@computed("placeholderKey")
|
@computed("placeholderKey")
|
||||||
placeholder(placeholderKey) {
|
placeholder(placeholderKey) {
|
||||||
return placeholderKey ? I18n.t(placeholderKey) : "";
|
return placeholderKey ? I18n.t(placeholderKey) : "";
|
||||||
},
|
|
||||||
|
|
||||||
keyUp() {
|
|
||||||
const act = this.get('keyUpAction');
|
|
||||||
if (act) {
|
|
||||||
this.sendAction('keyUpAction');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -88,9 +88,9 @@ export default Ember.Object.extend({
|
|||||||
|
|
||||||
refreshResults(resultSet, type, url) {
|
refreshResults(resultSet, type, url) {
|
||||||
const self = this;
|
const self = this;
|
||||||
return Discourse.ajax(url).then(function(result) {
|
return Discourse.ajax(url).then(result => {
|
||||||
const typeName = Ember.String.underscore(self.pluralize(type)),
|
const typeName = Ember.String.underscore(self.pluralize(type));
|
||||||
content = result[typeName].map(obj => self._hydrate(type, obj, result));
|
const content = result[typeName].map(obj => self._hydrate(type, obj, result));
|
||||||
resultSet.set('content', content);
|
resultSet.set('content', content);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -143,13 +143,24 @@ export default Ember.Object.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
_resultSet(type, result, findArgs) {
|
_resultSet(type, result, findArgs) {
|
||||||
const typeName = Ember.String.underscore(this.pluralize(type)),
|
const typeName = Ember.String.underscore(this.pluralize(type));
|
||||||
content = result[typeName].map(obj => this._hydrate(type, obj, result)),
|
const 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];
|
|
||||||
|
|
||||||
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) {
|
_build(type, obj) {
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
{{input type="checkbox" checked=checked}}
|
||||||
|
{{i18n label}}
|
@ -50,11 +50,19 @@ td.flaggers td {
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
.site-text-search {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.extra-options {
|
||||||
|
float: right;
|
||||||
|
input[type=checkbox] {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.text-highlight {
|
.text-highlight {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -65,6 +73,10 @@ td.flaggers td {
|
|||||||
border-bottom: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
border-bottom: 1px solid dark-light-diff($primary, $secondary, 90%, -60%);
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
|
&.overridden {
|
||||||
|
background-color: dark-light-diff($highlight, $secondary, 50%, -60%);
|
||||||
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
|
@ -8,11 +8,15 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def index
|
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) }}
|
results = self.class.preferred_keys.map {|k| {id: k, value: I18n.t(k) }}
|
||||||
else
|
else
|
||||||
results = []
|
results = []
|
||||||
translations = I18n.search(params[:q])
|
translations = I18n.search(params[:q], overridden: overridden)
|
||||||
translations.each do |k, v|
|
translations.each do |k, v|
|
||||||
results << {id: k, value: v}
|
results << {id: k, value: v}
|
||||||
end
|
end
|
||||||
@ -21,7 +25,7 @@ class Admin::SiteTextsController < Admin::AdminController
|
|||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
@ -229,6 +229,8 @@ class ApplicationController < ActionController::Base
|
|||||||
opts.each do |k, v|
|
opts.each do |k, v|
|
||||||
obj[k] = v if k.to_s.start_with?("refresh_")
|
obj[k] = v if k.to_s.start_with?("refresh_")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
obj['extras'] = opts[:extras] if opts[:extras]
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: MultiJson.dump(obj), status: opts[:status] || 200
|
render json: MultiJson.dump(obj), status: opts[:status] || 200
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
class SiteTextSerializer < ApplicationSerializer
|
class SiteTextSerializer < ApplicationSerializer
|
||||||
attributes :id, :value, :can_revert?
|
attributes :id, :value, :overridden?, :can_revert?
|
||||||
|
|
||||||
def id
|
def id
|
||||||
object[:id]
|
object[:id]
|
||||||
@ -9,12 +9,14 @@ class SiteTextSerializer < ApplicationSerializer
|
|||||||
object[:value]
|
object[:value]
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_revert?
|
def overridden?
|
||||||
current_val = value
|
current_val = value
|
||||||
|
|
||||||
I18n.overrides_disabled do
|
I18n.overrides_disabled do
|
||||||
return I18n.t(object[:id]) != current_val
|
return I18n.t(object[:id]) != current_val
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias_method :can_revert?, :overridden?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2492,6 +2492,7 @@ en:
|
|||||||
revert_confirm: "Are you sure you want to revert your changes?"
|
revert_confirm: "Are you sure you want to revert your changes?"
|
||||||
go_back: "Back to Search"
|
go_back: "Back to Search"
|
||||||
recommended: "We recommend customizing the following text to suit your needs:"
|
recommended: "We recommend customizing the following text to suit your needs:"
|
||||||
|
show_overriden: 'Only show overridden'
|
||||||
|
|
||||||
site_settings:
|
site_settings:
|
||||||
show_overriden: 'Only show overridden'
|
show_overriden: 'Only show overridden'
|
||||||
|
@ -56,7 +56,7 @@ module I18n
|
|||||||
opts ||= {}
|
opts ||= {}
|
||||||
|
|
||||||
target = opts[:backend] || backend
|
target = opts[:backend] || backend
|
||||||
results = target.search(config.locale, query)
|
results = opts[:overridden] ? {} : target.search(config.locale, query)
|
||||||
|
|
||||||
regexp = /#{query}/i
|
regexp = /#{query}/i
|
||||||
(overrides_by_locale || {}).each do |k, v|
|
(overrides_by_locale || {}).each do |k, v|
|
||||||
|
@ -6,9 +6,22 @@ test("search for a key", () => {
|
|||||||
visit("/admin/customize/site_texts");
|
visit("/admin/customize/site_texts");
|
||||||
|
|
||||||
fillIn('.site-text-search', 'Test');
|
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", () => {
|
test("edit and revert a site text by key", () => {
|
||||||
visit("/admin/customize/site_texts/site.test");
|
visit("/admin/customize/site_texts/site.test");
|
||||||
andThen(() => {
|
andThen(() => {
|
||||||
|
@ -210,7 +210,7 @@ export default function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.get('/fruits', 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) {
|
this.get('/widgets/:widget_id', function(request) {
|
||||||
@ -262,7 +262,16 @@ export default function() {
|
|||||||
this.post('/topics/timings', () => response(200, {}));
|
this.post('/topics/timings', () => response(200, {}));
|
||||||
|
|
||||||
const siteText = {id: 'site.test', value: 'Test McTest'};
|
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.get('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText }));
|
||||||
this.delete('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText }));
|
this.delete('/admin/customize/site_texts/:key', () => response(200, {site_text: siteText }));
|
||||||
|
|
||||||
|
@ -116,7 +116,6 @@ test('destroyRecord when new', function(assert) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('find embedded', function(assert) {
|
test('find embedded', function(assert) {
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
return store.find('fruit', 2).then(function(f) {
|
return store.find('fruit', 2).then(function(f) {
|
||||||
@ -136,6 +135,7 @@ test('findAll embedded', function(assert) {
|
|||||||
return store.findAll('fruit').then(function(fruits) {
|
return store.findAll('fruit').then(function(fruits) {
|
||||||
assert.equal(fruits.objectAt(0).get('farmer.name'), 'Old MacDonald');
|
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.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');
|
const fruitCols = fruits.objectAt(0).get('colors');
|
||||||
assert.equal(fruitCols.length, 2);
|
assert.equal(fruitCols.length, 2);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user