diff --git a/app/helpers/posts_helper.rb b/app/helpers/posts_helper.rb index 2c9e19ab61dd5932eb06a33f755709bc1798b72f..d11b5db1d8005a953a061ddbbccf0a133fd3d05f 100644 --- a/app/helpers/posts_helper.rb +++ b/app/helpers/posts_helper.rb @@ -15,6 +15,30 @@ module PostsHelper end end + # @param category [Category, Nil] + # @return [Integer] the minimum length for post bodies + def min_body_length(category) + category&.min_body_length || 30 + end + + # @param _category [Category, Nil] + # @return [Integer] the maximum length for post bodies + def max_body_length(_category) + 30_000 + end + + # @param category [Category, Nil] + # @return [Integer] the minimum length for post titles + def min_title_length(category) + category&.min_title_length || 15 + end + + # @param _category [Category, Nil] + # @return [Integer] the maximum length for post titles + def max_title_length(_category) + [SiteSetting['MaxTitleLength'] || 255, 255].min + end + class PostScrubber < Rails::Html::PermitScrubber def initialize super diff --git a/app/models/concerns/post_validations.rb b/app/models/concerns/post_validations.rb index 288d8d4eef0d1774227c4657bacc1e726f706f3c..687fa85f7746714dc1510d92c5c76ec003646b56 100644 --- a/app/models/concerns/post_validations.rb +++ b/app/models/concerns/post_validations.rb @@ -41,14 +41,14 @@ module PostValidations def stripped_minimum_body min_body = category.nil? ? 30 : category.min_body_length if (body_markdown&.gsub(/(?:^[\s\t\u2000-\u200F]+|[\s\t\u2000-\u200F]+$)/, '')&.length || 0) < min_body - errors.add(:body, 'must be more than 30 non-whitespace characters long') + errors.add(:body, "must be more than #{min_body} non-whitespace characters long") end end def stripped_minimum_title min_title = category.nil? ? 15 : category.min_title_length if (title&.gsub(/(?:^[\s\t\u2000-\u200F]+|[\s\t\u2000-\u200F]+$)/, '')&.length || 0) < min_title - errors.add(:title, 'must be more than 15 non-whitespace characters long') + errors.add(:title, "must be more than #{min_title} non-whitespace characters long") end end diff --git a/app/models/post.rb b/app/models/post.rb index 9bedb6c7cb88c0fc787fe247a695c708d27ffb90..c7c2df8759f631149eebc9f376882027d85a6210 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -28,7 +28,7 @@ class Post < ApplicationRecord serialize :tags_cache, Array - validates :body, presence: true, length: { minimum: 30, maximum: 30_000 } + validates :body, presence: true, length: { maximum: 30_000 } validates :doc_slug, uniqueness: { scope: [:community_id], case_sensitive: false }, if: -> { doc_slug.present? } validates :title, presence: true, if: -> { post_type.is_top_level? } validates :tags_cache, presence: true, if: -> { post_type.has_tags } diff --git a/app/views/posts/_form.html.erb b/app/views/posts/_form.html.erb index f6de1d3f6a145154506d830ce20c63b0394d5022..47a8eaf77f229315a3119d90bbe9a8829456a68d 100644 --- a/app/views/posts/_form.html.erb +++ b/app/views/posts/_form.html.erb @@ -54,7 +54,8 @@ </p> <% end %> - <%= render 'shared/body_field', f: f, field_name: :body_markdown, field_label: t('posts.body_label'), post: post, category: category %> + <%= render 'shared/body_field', f: f, field_name: :body_markdown, field_label: t('posts.body_label'), post: post, + min_length: min_body_length(category), max_length: max_body_length(category) %> <div class="post-preview"></div> @@ -65,11 +66,11 @@ </div> <div> <span class="has-float-right has-font-size-caption js-character-count-post-title hide" - data-min="<%= category&.min_title_length %>" - data-max="255" - data-display-at="0.75"> + data-min="<%= min_title_length(category) %>" + data-max="<%= max_title_length(category) %>" + data-display-at="0.75"> <i class="fas fa-ellipsis-h js-character-count__icon"></i> - <span class="js-character-count__count">0 / 255</span> + <span class="js-character-count__count">0 / <%= max_title_length(category) %></span> </span> </div> <% end %> diff --git a/app/views/posts/_mdhint.html.erb b/app/views/posts/_mdhint.html.erb index c4cdb07cfb4328b4a681febc88c7eb035bad61f4..b8e5ed06741ecdc6cb0858128fdcd3a0230a3082 100644 --- a/app/views/posts/_mdhint.html.erb +++ b/app/views/posts/_mdhint.html.erb @@ -1,16 +1,26 @@ +<%# + Widget displaying that we support markdown, linking to the help article. This also applies the min/max length for the + post body. + + Variables: + min_length : [Integer, Nil] optional, the minimum allowed length (default 30) + max_length : [Integer, Nil] optional, the maximum allowed length (default 30_000) +%> + <% - # defaults - category ||= defined?(category) ? category : nil - %> + # Defaults + min_length = (defined?(min_length) ? min_length : nil) || 30 + max_length = (defined?(max_length) ? max_length : nil) || 30_000 +%> <div class="form-caption widget--footer js-post-field-footer"> We <a href="/help/formatting">support Markdown</a> for posts: <strong>**bold**</strong>, <em>*italics*</em>, <code>`code`</code>, two newlines for paragraphs <span class="has-float-right has-font-size-caption js-character-count-post-body hide" - data-min="<%= category&.min_body_length %>" - data-max="30000" - data-display-at="0.75"> + data-min="<%= min_length %>" + data-max="<%= max_length %>" + data-display-at="0.75"> <i class="fas fa-ellipsis-h js-character-count__icon"></i> - <span class="js-character-count__count">0 / 30000</span> + <span class="js-character-count__count">0 / <%= max_length %></span> </span> </div> diff --git a/app/views/shared/_body_field.html.erb b/app/views/shared/_body_field.html.erb index 9d92ec7ce40b1eb48190ec9839b5eea0c117eeb6..2ab596799399b4de70fee5d67b84adef1b1fbaac 100644 --- a/app/views/shared/_body_field.html.erb +++ b/app/views/shared/_body_field.html.erb @@ -1,7 +1,18 @@ +<%# + Adds a markdown body form textarea. + Variables: + post : [ApplicationRecord, Nil] the entity to which this body field belongs (a post, a tag, a user, ...) + field_name : [Symbol] the name of the body field (for the given entity) + field_label : [String] the label for the field + min_length : [Integer, Nil] optional, the minimum allowed length + max_length : [Integer, Nil] optional, the maximum allowed length +%> + <% - # defaults - category ||= defined?(category) ? category : nil - %> + # Defaults + min_length = defined?(min_length) ? min_length : nil + max_length = defined?(max_length) ? max_length : nil +%> <div class="form-group"> <%= f.label field_name, field_label, class: "form-element" %> @@ -27,7 +38,7 @@ <%= render 'shared/markdown_tools' %> <%= f.text_area field_name, **({ class: classes, rows: 15, placeholder: 'Start typing your post...' }).merge(value), data: { character_count: ".js-character-count-post-body" } %> - <%= render 'posts/mdhint', category: category %> + <%= render 'posts/mdhint', min_length: min_length, max_length: max_length %> </div> <%= hidden_field_tag "__html", nil, class: 'js-post-html' %> </div> diff --git a/app/views/users/edit_profile.html.erb b/app/views/users/edit_profile.html.erb index 376665ed209f94c4b2c860a8d7f13961525e2319..25f8c6a0c34301ea1bec9c8d58d12b943e6d93ad 100644 --- a/app/views/users/edit_profile.html.erb +++ b/app/views/users/edit_profile.html.erb @@ -37,7 +37,8 @@ <%= f.text_field :username, class: 'form-element', autocomplete: 'off' %> </div> - <%= render 'shared/body_field', f: f, field_name: :profile_markdown, field_label: 'Profile', post: current_user %> + <%= render 'shared/body_field', f: f, field_name: :profile_markdown, field_label: 'Profile', post: current_user, + min_length: 0 %> <div class="post-preview"></div> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 4900e8052a1a980c33a50b8d7a42533830d202d2..925437b4249a030b3e43b001ee6d8427d567ba9b 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -44,10 +44,11 @@ <% end %> </p> - <% if @user.profile.nil? || @user.profile.blank? %> + <% effective_profile = raw(sanitize(@user.profile&.strip || '', scrubber: scrubber)) %> + <% if effective_profile.blank? %> <p class="is-lead">A quiet enigma. We don't know anything about <span dir="ltr"><%= rtl_safe_username(@user) %></span> yet.</p> <% else %> - <%= raw(sanitize(@user.profile, scrubber: scrubber)) %> + <%= effective_profile %> <% end %> </div>