From caa38aaaad625d4479c9dc82353624ee52b289cc Mon Sep 17 00:00:00 2001 From: scossar Date: Sun, 28 Jan 2018 18:33:55 -0800 Subject: [PATCH 001/202] Add support for mixed text directions --- app/assets/javascripts/application.js | 1 + .../discourse/components/d-editor.js.es6 | 21 +++++++++++++ .../discourse/components/text-field.js.es6 | 30 ++++++++++++++++++- .../discourse/helpers/category-link.js.es6 | 10 +++++-- .../discourse/helpers/dir-span.js.es6 | 14 +++++++++ .../initializers/post-decorations.js.es6 | 7 ++++- .../discourse/lib/text-direction.js.es6 | 28 +++++++++++++++++ .../javascripts/discourse/models/topic.js.es6 | 9 +++++- .../templates/components/categories-only.hbs | 2 +- .../components/category-title-link.hbs | 2 +- .../templates/list/topic-excerpt.raw.hbs | 2 +- .../templates/navigation/category.hbs | 2 +- .../templates/components/category-row.hbs | 2 +- config/locales/client.en.yml | 1 + config/locales/server.en.yml | 1 + config/site_settings.yml | 3 ++ 16 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 app/assets/javascripts/discourse/helpers/dir-span.js.es6 create mode 100644 app/assets/javascripts/discourse/lib/text-direction.js.es6 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 02bc257b12..c6ca0b0c90 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -25,6 +25,7 @@ //= require ./discourse/lib/key-value-store //= require ./discourse/lib/computed //= require ./discourse/lib/formatter +//= require ./discourse/lib/text-direction //= require ./discourse/lib/eyeline //= require ./discourse/lib/show-modal //= require ./discourse/mixins/scrolling diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6 index c70f0b3bc9..10d1a4fa53 100644 --- a/app/assets/javascripts/discourse/components/d-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/d-editor.js.es6 @@ -8,6 +8,7 @@ import { emojiSearch, isSkinTonableEmoji } from 'pretty-text/emoji'; import { emojiUrlFor } from 'discourse/lib/text'; import { getRegister } from 'discourse-common/lib/get-owner'; import { findRawTemplate } from 'discourse/lib/raw-templates'; +import { siteDir } from 'discourse/lib/text-direction'; import { determinePostReplaceSelection, clipboardData } from 'discourse/lib/utilities'; import toMarkdown from 'discourse/lib/to-markdown'; import deprecated from 'discourse-common/lib/deprecated'; @@ -107,6 +108,17 @@ class Toolbar { perform: e => e.applyList(i => !i ? "1. " : `${parseInt(i) + 1}. `, 'list_item') }); + if (Discourse.SiteSettings.support_mixed_text_direction) { + this.addButton({ + id: 'toggle-direction', + group: 'extras', + icon: 'arrows-h', + shortcut: 'Shift+6', + title: 'composer.toggle_direction', + perform: e => e.toggleDirection(), + }); + } + if (site.mobileView) { this.groups.push({group: 'mobileExtras', buttons: []}); } @@ -647,6 +659,14 @@ export default Ember.Component.extend({ return null; }, + _toggleDirection() { + const $textArea = $(".d-editor-input"); + let currentDir = $textArea.attr('dir') ? $textArea.attr('dir') : siteDir(), + newDir = currentDir === 'ltr' ? 'rtl' : 'ltr'; + + $textArea.attr('dir', newDir).focus(); + }, + paste(e) { if (!$(".d-editor-input").is(":focus")) { return; @@ -724,6 +744,7 @@ export default Ember.Component.extend({ addText: text => this._addText(selected, text), replaceText: text => this._addText({pre: '', post: ''}, text), getText: () => this.get('value'), + toggleDirection: () => this._toggleDirection(), }; if (button.sendAction) { diff --git a/app/assets/javascripts/discourse/components/text-field.js.es6 b/app/assets/javascripts/discourse/components/text-field.js.es6 index a9246efa7d..c2bb0eaf51 100644 --- a/app/assets/javascripts/discourse/components/text-field.js.es6 +++ b/app/assets/javascripts/discourse/components/text-field.js.es6 @@ -1,7 +1,35 @@ import computed from "ember-addons/ember-computed-decorators"; +import { siteDir } from "discourse/lib/text-direction"; +import { isRTL } from "discourse/lib/text-direction"; +import { isLTR } from "discourse/lib/text-direction"; export default Ember.TextField.extend({ - attributeBindings: ['autocorrect', 'autocapitalize', 'autofocus', 'maxLength'], + attributeBindings: ['autocorrect', 'autocapitalize', 'autofocus', 'maxLength', 'dir'], + + @computed + dir() { + if (Discourse.SiteSettings.support_mixed_text_direction) { + let val = this.value; + if (val) { + return isRTL(val) ? 'rtl' : 'ltr'; + } else { + return siteDir(); + } + } + }, + + keyUp() { + if (Discourse.SiteSettings.support_mixed_text_direction) { + let val = this.value; + if (isRTL(val)) { + this.set('dir', 'rtl'); + } else if (isLTR(val)) { + this.set('dir', 'ltr'); + } else { + this.set('dir', siteDir()); + } + } + }, @computed("placeholderKey") placeholder(placeholderKey) { diff --git a/app/assets/javascripts/discourse/helpers/category-link.js.es6 b/app/assets/javascripts/discourse/helpers/category-link.js.es6 index 336e25314e..de7f73f579 100644 --- a/app/assets/javascripts/discourse/helpers/category-link.js.es6 +++ b/app/assets/javascripts/discourse/helpers/category-link.js.es6 @@ -1,4 +1,5 @@ import { registerUnbound } from 'discourse-common/lib/helpers'; +import { isRTL } from "discourse/lib/text-direction"; import { iconHTML } from 'discourse-common/lib/icon-library'; var get = Em.get, @@ -38,6 +39,7 @@ export function categoryBadgeHTML(category, opts) { let color = get(category, 'color'); let html = ""; let parentCat = null; + let categoryDir = ""; if (!opts.hideParent) { parentCat = Discourse.Category.findById(get(category, 'parent_category_id')); @@ -66,10 +68,14 @@ export function categoryBadgeHTML(category, opts) { let categoryName = escapeExpression(get(category, 'name')); + if (Discourse.SiteSettings.support_mixed_text_direction) { + categoryDir = isRTL(categoryName) ? 'dir="rtl"' : 'dir="ltr"'; + } + if (restricted) { - html += `${iconHTML('lock')}${categoryName}`; + html += `${iconHTML('lock')}${categoryName}`; } else { - html += `${categoryName}`; + html += `${categoryName}`; } html += ""; diff --git a/app/assets/javascripts/discourse/helpers/dir-span.js.es6 b/app/assets/javascripts/discourse/helpers/dir-span.js.es6 new file mode 100644 index 0000000000..6ce29e8f59 --- /dev/null +++ b/app/assets/javascripts/discourse/helpers/dir-span.js.es6 @@ -0,0 +1,14 @@ +import { registerUnbound } from "discourse-common/lib/helpers"; +import { isRTL } from 'discourse/lib/text-direction'; + +function setDir(text) { + if (Discourse.SiteSettings.support_mixed_text_direction) { + let textDir = isRTL(text) ? 'rtl' : 'ltr'; + return `${text}`; + } + return text; +} + +export default registerUnbound('dir-span', function(str) { + return new Handlebars.SafeString(setDir(str)); +}); diff --git a/app/assets/javascripts/discourse/initializers/post-decorations.js.es6 b/app/assets/javascripts/discourse/initializers/post-decorations.js.es6 index d17b2087ac..0cab9b554d 100644 --- a/app/assets/javascripts/discourse/initializers/post-decorations.js.es6 +++ b/app/assets/javascripts/discourse/initializers/post-decorations.js.es6 @@ -1,13 +1,18 @@ import highlightSyntax from 'discourse/lib/highlight-syntax'; import lightbox from 'discourse/lib/lightbox'; +import { setTextDirections } from "discourse/lib/text-direction"; import { withPluginApi } from 'discourse/lib/plugin-api'; export default { name: "post-decorations", - initialize() { + initialize(container) { withPluginApi('0.1', api => { + const siteSettings = container.lookup('site-settings:main'); api.decorateCooked(highlightSyntax); api.decorateCooked(lightbox); + if (siteSettings.support_mixed_text_direction) { + api.decorateCooked(setTextDirections); + } api.decorateCooked($elem => { const players = $('audio', $elem); diff --git a/app/assets/javascripts/discourse/lib/text-direction.js.es6 b/app/assets/javascripts/discourse/lib/text-direction.js.es6 new file mode 100644 index 0000000000..2a4b9da2c3 --- /dev/null +++ b/app/assets/javascripts/discourse/lib/text-direction.js.es6 @@ -0,0 +1,28 @@ +const ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF'; +const rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC'; + +export function isRTL(text) { + const rtlDirCheck = new RegExp('^[^'+ltrChars+']*['+rtlChars+']'); + + return rtlDirCheck.test(text); +} + +export function isLTR(text) { + const ltrDirCheck = new RegExp('^[^'+rtlChars+']*['+ltrChars+']'); + + return ltrDirCheck.test(text); +} + +export function setTextDirections($elem) { + $elem.find('*').each((i, e) => { + let $e = $(e), + textContent = $e.text(); + if (textContent) { + isRTL(textContent) ? $e.attr('dir', 'rtl') : $e.attr('dir', 'ltr'); + } + }); +} + +export function siteDir() { + return $('html').hasClass('rtl') ? 'rtl' : 'ltr'; +} diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6 index 69a4f9c27f..4239398f05 100644 --- a/app/assets/javascripts/discourse/models/topic.js.es6 +++ b/app/assets/javascripts/discourse/models/topic.js.es6 @@ -3,6 +3,7 @@ import { flushMap } from 'discourse/models/store'; import RestModel from 'discourse/models/rest'; import { propertyEqual } from 'discourse/lib/computed'; import { longDate } from 'discourse/lib/formatter'; +import { isRTL } from 'discourse/lib/text-direction'; import computed from 'ember-addons/ember-computed-decorators'; import ActionSummary from 'discourse/models/action-summary'; import { popupAjaxError } from 'discourse/lib/ajax-error'; @@ -58,7 +59,13 @@ const Topic = RestModel.extend({ @computed('fancy_title') fancyTitle(title) { - return censor(emojiUnescape(title || ""), Discourse.Site.currentProp('censored_words')); + let fancyTitle = censor(emojiUnescape(title || ""), Discourse.Site.currentProp('censored_words')); + + if (Discourse.SiteSettings.support_mixed_text_direction) { + let titleDir = isRTL(title) ? 'rtl' : 'ltr'; + return '' + fancyTitle + ''; + } + return fancyTitle; }, // returns createdAt if there's no bumped date diff --git a/app/assets/javascripts/discourse/templates/components/categories-only.hbs b/app/assets/javascripts/discourse/templates/components/categories-only.hbs index f10712bb61..2fa528e3a9 100644 --- a/app/assets/javascripts/discourse/templates/components/categories-only.hbs +++ b/app/assets/javascripts/discourse/templates/components/categories-only.hbs @@ -16,7 +16,7 @@
{{category-title-link category=c}}
- {{{c.description_excerpt}}} + {{{dir-span c.description_excerpt}}}
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 fcb0bdeb07..3403e70d49 100644 --- a/app/assets/javascripts/discourse/templates/components/category-title-link.hbs +++ b/app/assets/javascripts/discourse/templates/components/category-title-link.hbs @@ -3,7 +3,7 @@ {{d-icon 'lock'}} {{/if}} - {{category.name}} + {{dir-span category.name}} {{#if category.uploaded_logo.url}}
{{cdn-img src=category.uploaded_logo.url class="category-logo"}}
diff --git a/app/assets/javascripts/discourse/templates/list/topic-excerpt.raw.hbs b/app/assets/javascripts/discourse/templates/list/topic-excerpt.raw.hbs index 0a42a6a0c5..1c7b5eead0 100644 --- a/app/assets/javascripts/discourse/templates/list/topic-excerpt.raw.hbs +++ b/app/assets/javascripts/discourse/templates/list/topic-excerpt.raw.hbs @@ -1,6 +1,6 @@ {{#if topic.hasExcerpt}}
- {{{topic.escapedExcerpt}}} + {{{dir-span topic.escapedExcerpt}}} {{#if topic.excerptTruncated}} {{i18n 'read_more'}} {{/if}} diff --git a/app/assets/javascripts/discourse/templates/navigation/category.hbs b/app/assets/javascripts/discourse/templates/navigation/category.hbs index d8dcac7040..55c7e7ef84 100644 --- a/app/assets/javascripts/discourse/templates/navigation/category.hbs +++ b/app/assets/javascripts/discourse/templates/navigation/category.hbs @@ -5,7 +5,7 @@ {{#if category.uploaded_logo.url}} {{cdn-img src=category.uploaded_logo.url class="category-logo"}} {{#if category.description}} -

{{{category.description}}}

+

{{{dir-span category.description}}}

{{/if}} {{/if}} diff --git a/app/assets/javascripts/select-kit/templates/components/category-row.hbs b/app/assets/javascripts/select-kit/templates/components/category-row.hbs index 985fe9cd74..85cdfd8f15 100644 --- a/app/assets/javascripts/select-kit/templates/components/category-row.hbs +++ b/app/assets/javascripts/select-kit/templates/components/category-row.hbs @@ -15,7 +15,7 @@ {{/if}} {{#if shouldDisplayDescription}} -
{{{description}}}
+
{{{dir-span description}}}
{{/if}} {{else}} {{{label}}} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index dc948cbbdb..0edc451ef1 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1278,6 +1278,7 @@ en: olist_title: "Numbered List" ulist_title: "Bulleted List" list_item: "List item" + toggle_direction: "Toggle Direction" help: "Markdown Editing Help" collapse: "minimize the composer panel" abandon: "close composer and discard draft" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index d698d82fba..a328a54a71 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -966,6 +966,7 @@ en: default_locale: "The default language of this Discourse instance" allow_user_locale: "Allow users to choose their own language interface preference" set_locale_from_accept_language_header: "set interface language for anonymous users from their web browser's language headers. (EXPERIMENTAL, does not work with anonymous cache)" + support_mixed_text_direction: "Support mixed left-to-right and right-to-left text directions." min_post_length: "Minimum allowed post length in characters" min_first_post_length: "Minimum allowed first post (topic body) length in characters" min_private_message_post_length: "Minimum allowed post length in characters for messages" diff --git a/config/site_settings.yml b/config/site_settings.yml index e02399e151..3fddeee679 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -79,6 +79,9 @@ basic: set_locale_from_accept_language_header: default: false validator: "AllowUserLocaleEnabledValidator" + support_mixed_text_direction: + client: true + default: false categories_topics: default: 20 min: 5 From 42970e758e80b1dccf7aaeee637e36a4c7f2cf83 Mon Sep 17 00:00:00 2001 From: scossar Date: Sun, 28 Jan 2018 18:45:11 -0800 Subject: [PATCH 002/202] Use consistent style for string concatenation --- app/assets/javascripts/discourse/models/topic.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6 index 4239398f05..b8e6f89725 100644 --- a/app/assets/javascripts/discourse/models/topic.js.es6 +++ b/app/assets/javascripts/discourse/models/topic.js.es6 @@ -63,7 +63,7 @@ const Topic = RestModel.extend({ if (Discourse.SiteSettings.support_mixed_text_direction) { let titleDir = isRTL(title) ? 'rtl' : 'ltr'; - return '' + fancyTitle + ''; + return `${fancyTitle}`; } return fancyTitle; }, From 41f1e7430c2c01d158247a470f81a92158aafb09 Mon Sep 17 00:00:00 2001 From: scossar Date: Mon, 29 Jan 2018 17:42:19 -0800 Subject: [PATCH 003/202] Refactor code; add tests --- .../discourse/components/d-editor.js.es6 | 2 +- .../discourse/components/text-field.js.es6 | 8 +++--- .../discourse/lib/text-direction.js.es6 | 12 +++++---- .../components/d-editor-test.js.es6 | 20 +++++++++++++++ .../components/text-field-test.js.es6 | 24 ++++++++++++++++++ .../lib/category-badge-test.js.es6 | 25 +++++++++++++++++++ test/javascripts/models/topic-test.js.es6 | 9 +++++++ 7 files changed, 89 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6 index 10d1a4fa53..010f11bd2b 100644 --- a/app/assets/javascripts/discourse/components/d-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/d-editor.js.es6 @@ -112,7 +112,7 @@ class Toolbar { this.addButton({ id: 'toggle-direction', group: 'extras', - icon: 'arrows-h', + icon: 'exchange', shortcut: 'Shift+6', title: 'composer.toggle_direction', perform: e => e.toggleDirection(), diff --git a/app/assets/javascripts/discourse/components/text-field.js.es6 b/app/assets/javascripts/discourse/components/text-field.js.es6 index c2bb0eaf51..ae26f6e99f 100644 --- a/app/assets/javascripts/discourse/components/text-field.js.es6 +++ b/app/assets/javascripts/discourse/components/text-field.js.es6 @@ -1,14 +1,12 @@ import computed from "ember-addons/ember-computed-decorators"; -import { siteDir } from "discourse/lib/text-direction"; -import { isRTL } from "discourse/lib/text-direction"; -import { isLTR } from "discourse/lib/text-direction"; +import { siteDir, isRTL, isLTR } from "discourse/lib/text-direction"; export default Ember.TextField.extend({ attributeBindings: ['autocorrect', 'autocapitalize', 'autofocus', 'maxLength', 'dir'], @computed dir() { - if (Discourse.SiteSettings.support_mixed_text_direction) { + if (this.siteSettings.support_mixed_text_direction) { let val = this.value; if (val) { return isRTL(val) ? 'rtl' : 'ltr'; @@ -19,7 +17,7 @@ export default Ember.TextField.extend({ }, keyUp() { - if (Discourse.SiteSettings.support_mixed_text_direction) { + if (this.siteSettings.support_mixed_text_direction) { let val = this.value; if (isRTL(val)) { this.set('dir', 'rtl'); diff --git a/app/assets/javascripts/discourse/lib/text-direction.js.es6 b/app/assets/javascripts/discourse/lib/text-direction.js.es6 index 2a4b9da2c3..928b147ed7 100644 --- a/app/assets/javascripts/discourse/lib/text-direction.js.es6 +++ b/app/assets/javascripts/discourse/lib/text-direction.js.es6 @@ -1,15 +1,14 @@ const ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF'; const rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC'; +const rtlDirCheck = new RegExp('^[^'+ltrChars+']*['+rtlChars+']'); +const ltrDirCheck = new RegExp('^[^'+rtlChars+']*['+ltrChars+']'); +let _siteDir; export function isRTL(text) { - const rtlDirCheck = new RegExp('^[^'+ltrChars+']*['+rtlChars+']'); - return rtlDirCheck.test(text); } export function isLTR(text) { - const ltrDirCheck = new RegExp('^[^'+rtlChars+']*['+ltrChars+']'); - return ltrDirCheck.test(text); } @@ -24,5 +23,8 @@ export function setTextDirections($elem) { } export function siteDir() { - return $('html').hasClass('rtl') ? 'rtl' : 'ltr'; + if (!_siteDir) { + _siteDir = $('html').hasClass('rtl') ? 'rtl' : 'ltr'; + } + return _siteDir; } diff --git a/test/javascripts/components/d-editor-test.js.es6 b/test/javascripts/components/d-editor-test.js.es6 index faf4f30b3b..e0409e4957 100644 --- a/test/javascripts/components/d-editor-test.js.es6 +++ b/test/javascripts/components/d-editor-test.js.es6 @@ -715,6 +715,26 @@ testCase(`list button with line sequence`, function(assert, textarea) { }); }); +componentTest('clicking the toggle-direction button toggles the direction', { + template: '{{d-editor value=value}}', + beforeEach() { + this.siteSettings.support_mixed_text_direction = true; + this.siteSettings.default_locale = "en"; + }, + + test(assert) { + const textarea = this.$('textarea.d-editor-input'); + click('button.toggle-direction'); + andThen(() => { + assert.equal(textarea.attr('dir'), 'rtl'); + }); + click('button.toggle-direction'); + andThen(() => { + assert.equal(textarea.attr('dir'), 'ltr'); + }); + } +}); + testCase(`doesn't jump to bottom with long text`, function(assert, textarea) { let longText = 'hello world.'; diff --git a/test/javascripts/components/text-field-test.js.es6 b/test/javascripts/components/text-field-test.js.es6 index e18662c2ab..7a2de69a5a 100644 --- a/test/javascripts/components/text-field-test.js.es6 +++ b/test/javascripts/components/text-field-test.js.es6 @@ -22,3 +22,27 @@ componentTest("support a placeholder", { assert.equal(this.$('input').prop('placeholder'), 'placeholder.i18n.key'); } }); + +componentTest("sets the dir attribute to ltr for Hebrew text", { + template: `{{text-field value='זהו שם עברי עם מקום עברי'}}`, + beforeEach() { + this.siteSettings.support_mixed_text_direction = true; + }, + + test(assert) { + assert.equal(this.$('input').attr('dir'), 'rtl'); + } +}); + +componentTest("sets the dir attribute to ltr for English text", { + template: `{{text-field value='This is a ltr title'}}`, + beforeEach() { + this.siteSettings.support_mixed_text_direction = true; + }, + + test(assert) { + assert.equal(this.$('input').attr('dir'), 'ltr'); + } +}); + + diff --git a/test/javascripts/lib/category-badge-test.js.es6 b/test/javascripts/lib/category-badge-test.js.es6 index 17322d4619..5fdc807dec 100644 --- a/test/javascripts/lib/category-badge-test.js.es6 +++ b/test/javascripts/lib/category-badge-test.js.es6 @@ -45,3 +45,28 @@ QUnit.test("allowUncategorized", assert => { assert.blank(categoryBadgeHTML(uncategorized), "it doesn't return HTML for uncategorized by default"); assert.present(categoryBadgeHTML(uncategorized, {allowUncategorized: true}), "it returns HTML"); }); + +QUnit.test("category names are wrapped in dir-spans", assert => { + Discourse.SiteSettings.support_mixed_text_direction = true; + const store = createStore(); + const rtlCategory = store.createRecord('category', { + name: 'תכנות עם Ruby', + id: 123, + description_text: 'cool description', + color: 'ff0', + text_color: 'f00' + }); + + const ltrCategory = store.createRecord('category', { + name: 'Programming in Ruby', + id: 234 + }); + + let tag = parseHTML(categoryBadgeHTML(rtlCategory))[0]; + let dirSpan = tag.children[1].children[0]; + assert.equal(dirSpan.attributes.dir, 'rtl'); + + tag = parseHTML(categoryBadgeHTML(ltrCategory))[0]; + dirSpan = tag.children[1].children[0]; + assert.equal(dirSpan.attributes.dir, 'ltr'); +}); diff --git a/test/javascripts/models/topic-test.js.es6 b/test/javascripts/models/topic-test.js.es6 index 2e9ce44436..7c33bceff0 100644 --- a/test/javascripts/models/topic-test.js.es6 +++ b/test/javascripts/models/topic-test.js.es6 @@ -98,6 +98,15 @@ QUnit.test('fancyTitle', assert => { "supports emojis"); }); +QUnit.test('fancyTitle direction', assert => { + const rtlTopic = Topic.create({ fancy_title: "هذا اختبار" }); + const ltrTopic = Topic.create({ fancy_title: "This is a test"}); + + Discourse.SiteSettings.support_mixed_text_direction = true; + assert.equal(rtlTopic.get('fancyTitle'), `هذا اختبار`, "sets the dir-span to rtl"); + assert.equal(ltrTopic.get('fancyTitle'), `This is a test`, "sets the dir-span to ltr"); +}); + QUnit.test('excerpt', assert => { const topic = Topic.create({ excerpt: "This is a test topic :smile:", pinned: true }); From 5d18f72b8c17d09917a52c7f0ab4bbdbae41a7d9 Mon Sep 17 00:00:00 2001 From: scossar Date: Mon, 29 Jan 2018 19:16:35 -0800 Subject: [PATCH 004/202] Add tests for text-direction functions --- .../lib/text-direction-test.js.es6 | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 test/javascripts/lib/text-direction-test.js.es6 diff --git a/test/javascripts/lib/text-direction-test.js.es6 b/test/javascripts/lib/text-direction-test.js.es6 new file mode 100644 index 0000000000..c12c03cb1e --- /dev/null +++ b/test/javascripts/lib/text-direction-test.js.es6 @@ -0,0 +1,23 @@ +import { isRTL, isLTR, setTextDirections, siteDir } from 'discourse/lib/text-direction'; + +QUnit.module('lib:text-direction'); + +QUnit.test("isRTL", assert => { + // Hebrew + assert.equal(isRTL('זה מבחן'), true); + + // Arabic + assert.equal(isRTL('هذا اختبار'), true); + + // Persian + assert.equal(isRTL('این یک امتحان است'), true); + + assert.equal(isRTL('This is a test'), false); + assert.equal(isRTL(''), false); +}); + +QUnit.test("isLTR", assert => { + assert.equal(isLTR('This is a test'), true); + assert.equal(isLTR('זה מבחן'), false); +}); + From 843d88c5d0a3b937ef48f4ffa0df3e73572629e3 Mon Sep 17 00:00:00 2001 From: scossar Date: Mon, 29 Jan 2018 20:11:12 -0800 Subject: [PATCH 005/202] Remove unused imports --- test/javascripts/lib/text-direction-test.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/javascripts/lib/text-direction-test.js.es6 b/test/javascripts/lib/text-direction-test.js.es6 index c12c03cb1e..c8ddee262b 100644 --- a/test/javascripts/lib/text-direction-test.js.es6 +++ b/test/javascripts/lib/text-direction-test.js.es6 @@ -1,4 +1,4 @@ -import { isRTL, isLTR, setTextDirections, siteDir } from 'discourse/lib/text-direction'; +import { isRTL, isLTR } from 'discourse/lib/text-direction'; QUnit.module('lib:text-direction'); From 8c91d418ddc240cf6f1faf3561c140b1bdbce130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 31 Jan 2018 20:09:59 +0100 Subject: [PATCH 006/202] UX: improve semantic of login button for better password managers compatibility --- .../discourse/templates/modal/login.hbs | 29 +++++-------------- app/assets/stylesheets/desktop/login.scss | 3 ++ 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/discourse/templates/modal/login.hbs b/app/assets/javascripts/discourse/templates/modal/login.hbs index 81b3fb611d..9b47c12958 100644 --- a/app/assets/javascripts/discourse/templates/modal/login.hbs +++ b/app/assets/javascripts/discourse/templates/modal/login.hbs @@ -6,24 +6,14 @@
- - + + - - - + + + @@ -40,14 +30,11 @@
- - - {{text-field value=loginName placeholderKey="login.email_placeholder" id="login-account-name" autocorrect="off" autocapitalize="off" autofocus="autofocus"}} - {{text-field value=loginName placeholderKey="login.email_placeholder" id="login-account-name" autocorrect="off" autocapitalize="off" autofocus="autofocus"}}
- - - {{password-field value=loginPassword type="password" id="login-account-password" maxlength="200" capsLockOn=capsLockOn}}   - - {{i18n 'forgot_password.action'}} - {{password-field value=loginPassword type="password" id="login-account-password" maxlength="200" capsLockOn=capsLockOn}}{{i18n 'forgot_password.action'}}
diff --git a/app/assets/javascripts/admin/templates/groups-type.hbs b/app/assets/javascripts/admin/templates/groups-type.hbs index a1fa829897..47bbb1c51e 100644 --- a/app/assets/javascripts/admin/templates/groups-type.hbs +++ b/app/assets/javascripts/admin/templates/groups-type.hbs @@ -1,6 +1,6 @@
{{#if sortedGroups}} -
+

{{i18n 'admin.groups.edit'}}

    {{#each sortedGroups as |group|}} @@ -25,7 +25,7 @@
{{/if}} -
+
{{outlet}}
diff --git a/app/assets/javascripts/admin/templates/user-badges.hbs b/app/assets/javascripts/admin/templates/user-badges.hbs index 167f4ffa1d..52faf9b433 100644 --- a/app/assets/javascripts/admin/templates/user-badges.hbs +++ b/app/assets/javascripts/admin/templates/user-badges.hbs @@ -1,9 +1,9 @@
-
+
+
{{#conditional-loading-spinner condition=loading}} diff --git a/app/assets/javascripts/admin/templates/user-tl3-requirements.hbs b/app/assets/javascripts/admin/templates/user-tl3-requirements.hbs index 1561a95670..1a848710a3 100644 --- a/app/assets/javascripts/admin/templates/user-tl3-requirements.hbs +++ b/app/assets/javascripts/admin/templates/user-tl3-requirements.hbs @@ -1,10 +1,10 @@
-
+
+
diff --git a/app/assets/javascripts/admin/templates/users-list.hbs b/app/assets/javascripts/admin/templates/users-list.hbs index 398732685b..645fc83c1f 100644 --- a/app/assets/javascripts/admin/templates/users-list.hbs +++ b/app/assets/javascripts/admin/templates/users-list.hbs @@ -1,5 +1,5 @@
-
+
+
{{#unless siteSettings.enable_sso}} {{d-button action="sendInvites" title="admin.invite.button_title" icon="user-plus" label="admin.invite.button_text"}} diff --git a/app/assets/javascripts/discourse/templates/modal/keyboard-shortcuts-help.hbs b/app/assets/javascripts/discourse/templates/modal/keyboard-shortcuts-help.hbs index 539101bdaf..a7b6c0705b 100644 --- a/app/assets/javascripts/discourse/templates/modal/keyboard-shortcuts-help.hbs +++ b/app/assets/javascripts/discourse/templates/modal/keyboard-shortcuts-help.hbs @@ -1,6 +1,6 @@ {{#d-modal-body id="keyboard-shortcuts-help"}}
-
+

{{i18n 'keyboard_shortcuts_help.jump_to.title'}}

  • {{{i18n 'keyboard_shortcuts_help.jump_to.home'}}}
  • @@ -24,7 +24,7 @@
  • {{{i18n 'keyboard_shortcuts_help.navigation.next_prev'}}}
-
+

{{i18n 'keyboard_shortcuts_help.application.title'}}

  • {{{i18n 'keyboard_shortcuts_help.application.hamburger_menu'}}}
  • @@ -46,7 +46,7 @@
  • {{{i18n 'keyboard_shortcuts_help.actions.quote_post'}}}
-
+

{{i18n 'keyboard_shortcuts_help.actions.title'}}

  • {{{i18n 'keyboard_shortcuts_help.actions.bookmark_topic'}}}
  • diff --git a/app/assets/javascripts/discourse/templates/user-invited-show.hbs b/app/assets/javascripts/discourse/templates/user-invited-show.hbs index 5f8dc6d18a..c02499e76b 100644 --- a/app/assets/javascripts/discourse/templates/user-invited-show.hbs +++ b/app/assets/javascripts/discourse/templates/user-invited-show.hbs @@ -5,13 +5,13 @@

    {{i18n 'user.invited.title'}}

    {{#if model.can_see_invite_details}} -
    -
    +
    +
    +
    {{d-button icon="plus" action="showInvite" label="user.invited.create" class="btn"}} diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss index 2786a6e61a..670d93d894 100644 --- a/app/assets/stylesheets/common/admin/admin_base.scss +++ b/app/assets/stylesheets/common/admin/admin_base.scss @@ -130,14 +130,23 @@ $mobile-breakpoint: 700px; } } -.content-list li a span.count { - font-size: $font-down-1; - float: right; - margin-right: 10px; - background-color: $primary-low; - padding: 2px 5px; - border-radius: 5px; - color: $primary; +.content-list { + width: 27%; + float: left; + li a span.count { + font-size: $font-down-1; + float: right; + margin-right: 10px; + background-color: $primary-low; + padding: 2px 5px; + border-radius: 5px; + color: $primary; + } +} + +.content-body { + float: left; + width: 60%; } .admin-content { @@ -259,6 +268,10 @@ $mobile-breakpoint: 700px; background-color: $primary-low; padding: 10px 10px 3px 0; @include clearfix; + nav { + float: left; + margin-left: 12px; + } .nav.nav-pills { li.active { a { diff --git a/app/assets/stylesheets/common/admin/customize.scss b/app/assets/stylesheets/common/admin/customize.scss index 9a23fb7d73..97a589ca74 100644 --- a/app/assets/stylesheets/common/admin/customize.scss +++ b/app/assets/stylesheets/common/admin/customize.scss @@ -220,3 +220,7 @@ margin-top: 20px; } } + +.custom_emoji { + width: 27%; +} diff --git a/app/assets/stylesheets/common/components/keyboard_shortcuts.scss b/app/assets/stylesheets/common/components/keyboard_shortcuts.scss index 04045f843f..13010953fe 100644 --- a/app/assets/stylesheets/common/components/keyboard_shortcuts.scss +++ b/app/assets/stylesheets/common/components/keyboard_shortcuts.scss @@ -15,8 +15,12 @@ } #keyboard-shortcuts-help { - .span6 { - width:32%; + div.row { + width: 100%; + div { + float: left; + width:32%; + } } ul { list-style: none; diff --git a/app/assets/stylesheets/desktop/discourse.scss b/app/assets/stylesheets/desktop/discourse.scss index 5f718e9502..8dce9693b5 100644 --- a/app/assets/stylesheets/desktop/discourse.scss +++ b/app/assets/stylesheets/desktop/discourse.scss @@ -351,46 +351,6 @@ input { @include clearfix; } -.span { - &4 { - width: 196px; - margin-right: 12px; - float: left; - } - - &6 { - width: 27.027%; - float: left; - } - - &8 { - width: 404px; - float: left; - } - - &10 { - width: 508px; - float: left; - } - - &13 { - width: 59.8198%; - float: left; - } - - &15 { - /* intentionally no width set here, do not add one */ - margin-left: 12px; - float: left; - } - - &24 { - width: 1236px; - float: left; - color: amarillo; - } -} - .offset { &2 { margin-left: 116px; diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss index d342a3a83e..0fb3e102f9 100644 --- a/app/assets/stylesheets/desktop/user.scss +++ b/app/assets/stylesheets/desktop/user.scss @@ -109,12 +109,6 @@ } } -.user-invite-controls { - background-color: $primary-low; - padding: 5px 10px 0 0; - height: 35px; -} - .user-invite-search { clear: both; margin: 15px 0px -15px 0px; From dffdff91a6a82dcc38f0ea2141976e1e51a4e320 Mon Sep 17 00:00:00 2001 From: Kris Date: Wed, 14 Feb 2018 13:39:30 -0500 Subject: [PATCH 169/202] Fix avatar flair alignment on mobile user profiles --- app/assets/stylesheets/common/base/user.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index 1d0343cb56..8c39904e93 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -124,6 +124,7 @@ .user-profile-avatar { position: relative; float: left; + height: 100%; .avatar-flair { bottom: 8px; right: 16px; From c3606fdef6689604fa660b87563b50169b1fd0a6 Mon Sep 17 00:00:00 2001 From: Rafael dos Santos Silva Date: Wed, 14 Feb 2018 17:43:13 -0200 Subject: [PATCH 170/202] Update onebox --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 3fa66d5d30..ced4bbd2dc 100644 --- a/Gemfile +++ b/Gemfile @@ -36,7 +36,7 @@ gem 'redis-namespace' gem 'active_model_serializers', '~> 0.8.3' -gem 'onebox', '1.8.36' +gem 'onebox', '1.8.38' gem 'http_accept_language', '~>2.0.5', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 08bec02878..36dde5f8f6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -232,7 +232,7 @@ GEM omniauth-twitter (1.3.0) omniauth-oauth (~> 1.1) rack - onebox (1.8.36) + onebox (1.8.38) fast_blank (>= 1.0.0) htmlentities (~> 4.3) moneta (~> 1.0) @@ -469,7 +469,7 @@ DEPENDENCIES omniauth-oauth2 omniauth-openid omniauth-twitter - onebox (= 1.8.36) + onebox (= 1.8.38) openid-redis-store pg (~> 0.21.0) pry-nav From a3e5a316740dea89836fe66c6b32c0a0a1692426 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 14 Feb 2018 15:29:01 -0500 Subject: [PATCH 171/202] FIX: Allow 404 pages to use the current theme --- app/controllers/application_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2d468017c2..653ba952fc 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -192,7 +192,9 @@ class ApplicationController < ActionController::Base render_json_error message, type: type, status: status_code else begin + # 404 pages won't have the session and theme_keys without these: current_user + handle_theme rescue Discourse::InvalidAccess return render plain: message, status: status_code end From b4aa0b096ee0ceb596db7904ce508edb01f27885 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 14 Feb 2018 15:46:04 -0500 Subject: [PATCH 172/202] FIX: Couldn't like staff when `allow_flagging_staff` was set --- lib/guardian/post_guardian.rb | 4 +++- spec/components/guardian_spec.rb | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/guardian/post_guardian.rb b/lib/guardian/post_guardian.rb index aa92423b57..4344e5fd34 100644 --- a/lib/guardian/post_guardian.rb +++ b/lib/guardian/post_guardian.rb @@ -22,7 +22,9 @@ module PostGuardian result = if authenticated? && post && !@user.anonymous? # post made by staff, but we don't allow staff flags - return false if !SiteSetting.allow_flagging_staff? && post.user.staff? + return false if is_flag && + (!SiteSetting.allow_flagging_staff?) && + post.user.staff? return false if [:notify_user, :notify_moderators].include?(action_key) && !SiteSetting.enable_personal_messages? diff --git a/spec/components/guardian_spec.rb b/spec/components/guardian_spec.rb index 77667c4e01..0fc91d7b79 100644 --- a/spec/components/guardian_spec.rb +++ b/spec/components/guardian_spec.rb @@ -83,6 +83,12 @@ describe Guardian do expect(Guardian.new(user).post_can_act?(staff_post, :spam)).to eq(false) end + it "allows liking of staff when allow_flagging_staff is false" do + SiteSetting.allow_flagging_staff = false + staff_post = Fabricate(:post, user: Fabricate(:moderator)) + expect(Guardian.new(user).post_can_act?(staff_post, :like)).to eq(true) + end + it "returns false when liking yourself" do expect(Guardian.new(post.user).post_can_act?(post, :like)).to be_falsey end From 96e5a7da46b75827ecc989483ced6f262b0d3bf7 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Thu, 15 Feb 2018 07:46:33 +0800 Subject: [PATCH 173/202] Prefer `success_Json` over custom success JSON payload. --- app/controllers/session_controller.rb | 2 +- app/controllers/users_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb index 079ba724ef..8b0e0f243c 100644 --- a/app/controllers/session_controller.rb +++ b/app/controllers/session_controller.rb @@ -261,7 +261,7 @@ class SessionController < ApplicationController Jobs.enqueue(:critical_user_email, type: :forgot_password, user_id: user.id, email_token: email_token.token) end - json = { result: "ok" } + json = success_json unless SiteSetting.hide_email_address_taken json[:user_found] = user_presence end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 613215df52..4d0726d865 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -609,7 +609,7 @@ class UsersController < ApplicationController end end - json = { result: "ok" } + json = success_json json[:user_found] = user_presence unless SiteSetting.hide_email_address_taken render json: json rescue RateLimiter::LimitExceeded From a03e7cee634eea35cbd6e83aae25fa2d708861af Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 14 Feb 2018 20:33:13 -0500 Subject: [PATCH 174/202] FIX: Group posts had the wrong paths in subfolders --- .../javascripts/discourse/components/group-post.js.es6 | 6 ++++++ .../discourse/templates/components/group-post.hbs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/discourse/components/group-post.js.es6 diff --git a/app/assets/javascripts/discourse/components/group-post.js.es6 b/app/assets/javascripts/discourse/components/group-post.js.es6 new file mode 100644 index 0000000000..51e67b8490 --- /dev/null +++ b/app/assets/javascripts/discourse/components/group-post.js.es6 @@ -0,0 +1,6 @@ +import computed from 'ember-addons/ember-computed-decorators'; + +export default Ember.Component.extend({ + @computed('post.url') + postUrl: Discourse.getURL +}); diff --git a/app/assets/javascripts/discourse/templates/components/group-post.hbs b/app/assets/javascripts/discourse/templates/components/group-post.hbs index 9f51a1584f..a6d3d6b101 100644 --- a/app/assets/javascripts/discourse/templates/components/group-post.hbs +++ b/app/assets/javascripts/discourse/templates/components/group-post.hbs @@ -7,7 +7,7 @@