diff --git a/app/assets/javascripts/discourse/components/composer-editor.js.es6 b/app/assets/javascripts/discourse/components/composer-editor.js.es6 index 86a9b02e501..43d33da68fa 100644 --- a/app/assets/javascripts/discourse/components/composer-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/composer-editor.js.es6 @@ -37,10 +37,19 @@ export default Ember.Component.extend({ return `[${I18n.t('uploading')}]() `; }, - @computed() - replyPlaceholder() { - const key = authorizesOneOrMoreImageExtensions() ? "reply_placeholder" : "reply_placeholder_no_images"; - return `composer.${key}`; + @computed('composer.requiredCategoryMissing') + replyPlaceholder(requiredCategoryMissing) { + if (requiredCategoryMissing) { + return 'composer.reply_placeholder_choose_category'; + } else { + const key = authorizesOneOrMoreImageExtensions() ? "reply_placeholder" : "reply_placeholder_no_images"; + return `composer.${key}`; + } + }, + + @computed('composer.requiredCategoryMissing', 'composer.replyLength') + disableTextarea(requiredCategoryMissing, replyLength) { + return requiredCategoryMissing && replyLength === 0; }, @observes('composer.uploadCancelled') diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6 index e59ac9d78f1..c2811e5e5a3 100644 --- a/app/assets/javascripts/discourse/components/d-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/d-editor.js.es6 @@ -750,6 +750,8 @@ export default Ember.Component.extend({ }, toolbarButton(button) { + if (this.get('disabled')) { return; } + const selected = this._getSelected(button.trimLeading); const toolbarEvent = { selected, @@ -770,6 +772,8 @@ export default Ember.Component.extend({ }, showLinkModal() { + if (this.get('disabled')) { return; } + this._lastSel = this._getSelected(); if (this._lastSel) { @@ -780,6 +784,8 @@ export default Ember.Component.extend({ }, formatCode() { + if (this.get('disabled')) { return; } + const sel = this._getSelected('', { lineVal: true }); const selValue = sel.value; const hasNewLine = selValue.indexOf("\n") !== -1; @@ -833,6 +839,7 @@ export default Ember.Component.extend({ }, emoji() { + if (this.get('disabled')) { return; } this.set('emojiPickerIsActive', !this.get('emojiPickerIsActive')); } } diff --git a/app/assets/javascripts/discourse/models/composer.js.es6 b/app/assets/javascripts/discourse/models/composer.js.es6 index 52411269e6d..94a3e328001 100644 --- a/app/assets/javascripts/discourse/models/composer.js.es6 +++ b/app/assets/javascripts/discourse/models/composer.js.es6 @@ -265,13 +265,17 @@ const Composer = RestModel.extend({ return this.get('targetUsernames') && (this.get('targetUsernames').trim() + ',').indexOf(',') === 0; } else { // has a category? (when needed) - return this.get('canCategorize') && - !this.siteSettings.allow_uncategorized_topics && - !this.get('categoryId') && - !this.user.get('admin'); + return this.get('requiredCategoryMissing'); } }.property('loading', 'canEditTitle', 'titleLength', 'targetUsernames', 'replyLength', 'categoryId', 'missingReplyCharacters'), + @computed('canCategorize', 'categoryId') + requiredCategoryMissing(canCategorize, categoryId) { + return canCategorize && !categoryId && + !this.siteSettings.allow_uncategorized_topics && + !this.user.get('admin'); + }, + titleLengthValid: function() { if (this.user.get('admin') && this.get('post.static_doc') && this.get('titleLength') > 0) return true; if (this.get('titleLength') < this.get('minimumTitleLength')) return false; diff --git a/app/assets/javascripts/discourse/templates/components/composer-editor.hbs b/app/assets/javascripts/discourse/templates/components/composer-editor.hbs index 511961d8f14..ebf9a3d856f 100644 --- a/app/assets/javascripts/discourse/templates/components/composer-editor.hbs +++ b/app/assets/javascripts/discourse/templates/components/composer-editor.hbs @@ -16,6 +16,7 @@ onExpandPopupMenuOptions="onExpandPopupMenuOptions" onPopupMenuAction=onPopupMenuAction popupMenuOptions=popupMenuOptions + disabled=disableTextarea outletArgs=(hash composer=composer editorType="composer")}} {{#if site.mobileView}} diff --git a/app/assets/javascripts/discourse/templates/components/d-editor.hbs b/app/assets/javascripts/discourse/templates/components/d-editor.hbs index 06c636c2b7c..571285df208 100644 --- a/app/assets/javascripts/discourse/templates/components/d-editor.hbs +++ b/app/assets/javascripts/discourse/templates/components/d-editor.hbs @@ -10,7 +10,7 @@
-
+
{{#each toolbar.groups as |group|}} {{#each group.buttons as |b|}} {{#if b.popupMenu}} @@ -39,7 +39,7 @@
{{conditional-loading-spinner condition=loading}} - {{textarea tabindex=tabindex value=value class="d-editor-input" placeholder=placeholderTranslated}} + {{textarea tabindex=tabindex value=value class="d-editor-input" placeholder=placeholderTranslated disabled=disabled}} {{popup-input-tip validation=validation}} {{plugin-outlet name="after-d-editor" tagName="" args=outletArgs}}
diff --git a/app/assets/stylesheets/common/d-editor.scss b/app/assets/stylesheets/common/d-editor.scss index f5a32ca1ece..7bf2b433f57 100644 --- a/app/assets/stylesheets/common/d-editor.scss +++ b/app/assets/stylesheets/common/d-editor.scss @@ -90,6 +90,11 @@ .btn.italic { font-style: italic; } + + &.disabled { + visibility: hidden; + cursor: not-allowed; + } } .d-editor-spacer { diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index d65cc600fe8..a3c56dd2594 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -1325,6 +1325,7 @@ en: remove_featured_link: "Remove link from topic." reply_placeholder: "Type here. Use Markdown, BBCode, or HTML to format. Drag or paste images." reply_placeholder_no_images: "Type here. Use Markdown, BBCode, or HTML to format." + reply_placeholder_choose_category: "Please select a category." view_new_post: "View your new post." saving: "Saving" saved: "Saved!" diff --git a/test/javascripts/acceptance/composer-test.js.es6 b/test/javascripts/acceptance/composer-test.js.es6 index e35505eca67..c159485336b 100644 --- a/test/javascripts/acceptance/composer-test.js.es6 +++ b/test/javascripts/acceptance/composer-test.js.es6 @@ -1,4 +1,4 @@ -import { acceptance } from "helpers/qunit-helpers"; +import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers"; acceptance("Composer", { loggedIn: true, @@ -335,3 +335,42 @@ QUnit.test("Composer draft with dirty reply can toggle to edit", assert => { assert.equal(find('.d-editor-input').val().indexOf('This is the first post.'), 0, 'it populates the input with the post text'); }); }); + +acceptance("Composer and uncategorized is not allowed", { + loggedIn: true, + settings: { + enable_whispers: true, + allow_uncategorized_topics: false + } +}); + +QUnit.test("Disable body until category is selected", assert => { + replaceCurrentUser({ admin: false, staff: false, trust_level: 1 }); + + visit("/"); + click('#create-topic'); + andThen(() => { + assert.ok(exists('.d-editor-input'), 'the composer input is visible'); + assert.ok(exists('.title-input .popup-tip.bad.hide'), 'title errors are hidden by default'); + assert.ok(exists('.d-editor-textarea-wrapper .popup-tip.bad.hide'), 'body errors are hidden by default'); + assert.ok(exists('.d-editor-container .d-editor-button-bar.disabled'), 'toolbar is disabled'); + assert.ok(find('.d-editor-container .d-editor-input:disabled').length, 'textarea is disabled'); + }); + + const categoryChooser = selectKit('.category-chooser'); + + categoryChooser.expand().selectRowByValue(2); + + andThen(() => { + assert.ok(!exists('.d-editor-container .d-editor-button-bar.disabled'), 'toolbar is enabled'); + assert.ok(find('.d-editor-container .d-editor-input:disabled').length === 0, 'textarea is enabled'); + }); + + fillIn('.d-editor-input', 'Now I can type stuff'); + categoryChooser.expand().selectRowByValue('__none__'); + + andThen(() => { + assert.ok(!exists('.d-editor-container .d-editor-button-bar.disabled'), 'toolbar is still enabled'); + assert.ok(find('.d-editor-container .d-editor-input:disabled').length === 0, 'textarea is still enabled'); + }); +}); diff --git a/test/javascripts/acceptance/composer-topic-links-test.js.es6 b/test/javascripts/acceptance/composer-topic-links-test.js.es6 index d8083623f26..c57464d11ef 100644 --- a/test/javascripts/acceptance/composer-topic-links-test.js.es6 +++ b/test/javascripts/acceptance/composer-topic-links-test.js.es6 @@ -1,4 +1,4 @@ -import { acceptance } from "helpers/qunit-helpers"; +import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers"; acceptance("Composer topic featured links", { loggedIn: true, @@ -76,3 +76,32 @@ QUnit.test("onebox with title but extra words in title field", assert => { assert.equal(find('.title-input input').val(), "http://www.example.com/has-title.html test", "title is unchanged"); }); }); + +acceptance("Composer topic featured links when uncategorized is not allowed", { + loggedIn: true, + settings: { + topic_featured_link_enabled: true, + max_topic_title_length: 80, + enable_markdown_linkify: true, + allow_uncategorized_topics: false + } +}); + +QUnit.test("Pasting a link enables the text input area", assert => { + replaceCurrentUser({ admin: false, staff: false, trust_level: 1 }); + + visit("/"); + click('#create-topic'); + andThen(() => { + assert.ok(exists('.d-editor-container .d-editor-button-bar.disabled'), 'toolbar is disabled'); + assert.ok(find('.d-editor-container .d-editor-input:disabled').length, 'textarea is disabled'); + }); + fillIn('#reply-title', "http://www.example.com/has-title.html"); + andThen(() => { + assert.ok(find('.d-editor-preview').html().trim().indexOf('onebox') > 0, "it pastes the link into the body and previews it"); + assert.ok(exists('.d-editor-textarea-wrapper .popup-tip.good'), 'the body is now good'); + assert.equal(find('.title-input input').val(), "An interesting article", "title is from the oneboxed article"); + assert.ok(!exists('.d-editor-container .d-editor-button-bar.disabled'), 'toolbar is enabled'); + assert.ok(find('.d-editor-container .d-editor-input:disabled').length === 0, 'textarea is enabled'); + }); +});