diff options
Diffstat (limited to 'app')
55 files changed, 311 insertions, 80 deletions
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index 82055006ac0..762e36ee2e9 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -37,7 +37,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController end def preview - @message = broadcast_message_params[:message] + @broadcast_message = BroadcastMessage.new(broadcast_message_params) end protected diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb index de13e7a1fc2..16136d02530 100644 --- a/app/helpers/appearances_helper.rb +++ b/app/helpers/appearances_helper.rb @@ -16,7 +16,7 @@ module AppearancesHelper end def brand_text - markdown(brand_item.description) + markdown_field(brand_item, :description) end def brand_item diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 6de25bea654..6229384817b 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -11,18 +11,6 @@ module ApplicationSettingsHelper current_application_settings.signin_enabled? end - def extra_sign_in_text - current_application_settings.sign_in_text - end - - def after_sign_up_text - current_application_settings.after_sign_up_text - end - - def shared_runners_text - current_application_settings.shared_runners_text - end - def user_oauth_applications? current_application_settings.user_oauth_applications end diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb index 43a29c96bca..eb03ced67eb 100644 --- a/app/helpers/broadcast_messages_helper.rb +++ b/app/helpers/broadcast_messages_helper.rb @@ -3,7 +3,7 @@ module BroadcastMessagesHelper return unless message.present? content_tag :div, class: 'broadcast-message', style: broadcast_message_style(message) do - icon('bullhorn') << ' ' << render_broadcast_message(message.message) + icon('bullhorn') << ' ' << render_broadcast_message(message) end end @@ -32,7 +32,7 @@ module BroadcastMessagesHelper end end - def render_broadcast_message(message) - Banzai.render(message, pipeline: :broadcast_message).html_safe + def render_broadcast_message(broadcast_message) + Banzai.render_field(broadcast_message, :message).html_safe end end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 1a259656f31..0772d848289 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -13,14 +13,12 @@ module GitlabMarkdownHelper def link_to_gfm(body, url, html_options = {}) return "" if body.blank? - escaped_body = if body.start_with?('<img') - body - else - escape_once(body) - end - - user = current_user if defined?(current_user) - gfm_body = Banzai.render(escaped_body, project: @project, current_user: user, pipeline: :single_line) + context = { + project: @project, + current_user: (current_user if defined?(current_user)), + pipeline: :single_line, + } + gfm_body = Banzai.render(body, context) fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body) if fragment.children.size == 1 && fragment.children[0].name == 'a' @@ -51,17 +49,15 @@ module GitlabMarkdownHelper context[:project] ||= @project html = Banzai.render(text, context) + banzai_postprocess(html, context) + end - context.merge!( - current_user: (current_user if defined?(current_user)), - - # RelativeLinkFilter - requested_path: @path, - project_wiki: @project_wiki, - ref: @ref - ) + def markdown_field(object, field) + object = object.for_display if object.respond_to?(:for_display) + return "" unless object.present? - Banzai.post_process(html, context) + html = Banzai.render_field(object, field) + banzai_postprocess(html, object.banzai_render_context(field)) end def asciidoc(text) @@ -196,4 +192,18 @@ module GitlabMarkdownHelper icon(options[:icon]) end end + + # Calls Banzai.post_process with some common context options + def banzai_postprocess(html, context) + context.merge!( + current_user: (current_user if defined?(current_user)), + + # RelativeLinkFilter + requested_path: @path, + project_wiki: @project_wiki, + ref: @ref + ) + + Banzai.post_process(html, context) + end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 8a7446b7cc7..aba3a3f9c5d 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -153,8 +153,18 @@ module SearchHelper search_path(options) end - # Sanitize html generated after parsing markdown from issue description or comment - def search_md_sanitize(html) + # Sanitize a HTML field for search display. Most tags are stripped out and the + # maximum length is set to 200 characters. + def search_md_sanitize(object, field) + html = markdown_field(object, field) + html = Truncato.truncate( + html, + count_tags: false, + count_tail: false, + max_length: 200 + ) + + # Truncato's filtered_tags and filtered_attributes are not quite the same sanitize(html, tags: %w(a p ol ul li pre code)) end end diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb index b01a244032d..2340453831e 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -1,4 +1,8 @@ class AbuseReport < ActiveRecord::Base + include CacheMarkdownField + + cache_markdown_field :message, pipeline: :single_line + belongs_to :reporter, class_name: 'User' belongs_to :user @@ -7,6 +11,9 @@ class AbuseReport < ActiveRecord::Base validates :message, presence: true validates :user_id, uniqueness: { message: 'has already been reported' } + # For CacheMarkdownField + alias_method :author, :reporter + def remove_user(deleted_by:) user.block DeleteUserWorker.perform_async(deleted_by.id, user.id, delete_solo_owned_groups: true) diff --git a/app/models/appearance.rb b/app/models/appearance.rb index 4cf8dd9a8ce..e4106e1c2e9 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -1,4 +1,8 @@ class Appearance < ActiveRecord::Base + include CacheMarkdownField + + cache_markdown_field :description + validates :title, presence: true validates :description, presence: true validates :logo, file_size: { maximum: 1.megabyte } diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 55d2e07de08..c99aa7772bb 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -1,5 +1,7 @@ class ApplicationSetting < ActiveRecord::Base + include CacheMarkdownField include TokenAuthenticatable + add_authentication_token_field :runners_registration_token add_authentication_token_field :health_check_access_token @@ -17,6 +19,11 @@ class ApplicationSetting < ActiveRecord::Base serialize :domain_whitelist, Array serialize :domain_blacklist, Array + cache_markdown_field :sign_in_text + cache_markdown_field :help_page_text + cache_markdown_field :shared_runners_text, pipeline: :plain_markdown + cache_markdown_field :after_sign_up_text + attr_accessor :domain_whitelist_raw, :domain_blacklist_raw validates :session_expire_delay, diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index 61498140f27..cb40f33932a 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -1,6 +1,9 @@ class BroadcastMessage < ActiveRecord::Base + include CacheMarkdownField include Sortable + cache_markdown_field :message, pipeline: :broadcast_message + validates :message, presence: true validates :starts_at, presence: true validates :ends_at, presence: true diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb new file mode 100644 index 00000000000..90bd6490a02 --- /dev/null +++ b/app/models/concerns/cache_markdown_field.rb @@ -0,0 +1,131 @@ +# This module takes care of updating cache columns for Markdown-containing +# fields. Use like this in the body of your class: +# +# include CacheMarkdownField +# cache_markdown_field :foo +# cache_markdown_field :bar +# cache_markdown_field :baz, pipeline: :single_line +# +# Corresponding foo_html, bar_html and baz_html fields should exist. +module CacheMarkdownField + # Knows about the relationship between markdown and html field names, and + # stores the rendering contexts for the latter + class FieldData + extend Forwardable + + def initialize + @data = {} + end + + def_delegators :@data, :[], :[]= + def_delegator :@data, :keys, :markdown_fields + + def html_field(markdown_field) + "#{markdown_field}_html" + end + + def html_fields + markdown_fields.map {|field| html_field(field) } + end + end + + # Dynamic registries don't really work in Rails as it's not guaranteed that + # every class will be loaded, so hardcode the list. + CACHING_CLASSES = %w[ + AbuseReport + Appearance + ApplicationSetting + BroadcastMessage + Issue + Label + MergeRequest + Milestone + Namespace + Note + Project + Release + Snippet + ] + + def self.caching_classes + CACHING_CLASSES.map(&:constantize) + end + + extend ActiveSupport::Concern + + included do + cattr_reader :cached_markdown_fields do + FieldData.new + end + + # Returns the default Banzai render context for the cached markdown field. + def banzai_render_context(field) + raise ArgumentError.new("Unknown field: #{field.inspect}") unless + cached_markdown_fields.markdown_fields.include?(field) + + # Always include a project key, or Banzai complains + project = self.project if self.respond_to?(:project) + context = cached_markdown_fields[field].merge(project: project) + + # Banzai is less strict about authors, so don't always have an author key + context[:author] = self.author if self.respond_to?(:author) + + context + end + + # Allow callers to look up the cache field name, rather than hardcoding it + def markdown_cache_field_for(field) + raise ArgumentError.new("Unknown field: #{field}") unless + cached_markdown_fields.markdown_fields.include?(field) + + cached_markdown_fields.html_field(field) + end + + # Always exclude _html fields from attributes (including serialization). + # They contain unredacted HTML, which would be a security issue + alias_method :attributes_before_markdown_cache, :attributes + def attributes + attrs = attributes_before_markdown_cache + + cached_markdown_fields.html_fields.each do |field| + attrs.delete(field) + end + + attrs + end + end + + class_methods do + private + + # Specify that a field is markdown. Its rendered output will be cached in + # a corresponding _html field. Any custom rendering options may be provided + # as a context. + def cache_markdown_field(markdown_field, context = {}) + raise "Add #{self} to CacheMarkdownField::CACHING_CLASSES" unless + CacheMarkdownField::CACHING_CLASSES.include?(self.to_s) + + cached_markdown_fields[markdown_field] = context + + html_field = cached_markdown_fields.html_field(markdown_field) + cache_method = "#{markdown_field}_cache_refresh".to_sym + invalidation_method = "#{html_field}_invalidated?".to_sym + + define_method(cache_method) do + html = Banzai::Renderer.cacheless_render_field(self, markdown_field) + __send__("#{html_field}=", html) + true + end + + # The HTML becomes invalid if any dependent fields change. For now, assume + # author and project invalidate the cache in all circumstances. + define_method(invalidation_method) do + changed_fields = changed_attributes.keys + invalidations = changed_fields & [markdown_field.to_s, "author", "project"] + !invalidations.empty? + end + + before_save cache_method, if: invalidation_method + end + end +end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index ff465d2c745..c4b42ad82c7 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -6,6 +6,7 @@ # module Issuable extend ActiveSupport::Concern + include CacheMarkdownField include Participable include Mentionable include Subscribable @@ -13,6 +14,9 @@ module Issuable include Awardable included do + cache_markdown_field :title, pipeline: :single_line + cache_markdown_field :description + belongs_to :author, class_name: "User" belongs_to :assignee, class_name: "User" belongs_to :updated_by, class_name: "User" diff --git a/app/models/global_label.rb b/app/models/global_label.rb index ddd4bad5c21..698a7bbd327 100644 --- a/app/models/global_label.rb +++ b/app/models/global_label.rb @@ -4,6 +4,10 @@ class GlobalLabel delegate :color, :description, to: :@first_label + def for_display + @first_label + end + def self.build_collection(labels) labels = labels.group_by(&:title) diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index bda2b5c5d5d..cde4a568577 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -4,6 +4,10 @@ class GlobalMilestone attr_accessor :title, :milestones alias_attribute :name, :title + def for_display + @first_milestone + end + def self.build_collection(milestones) milestones = milestones.group_by(&:title) @@ -17,6 +21,7 @@ class GlobalMilestone @title = title @name = title @milestones = milestones + @first_milestone = milestones.find {|m| m.description.present? } || milestones.first end def safe_title diff --git a/app/models/label.rb b/app/models/label.rb index a23140b7d64..e8e12e2904e 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -1,4 +1,5 @@ class Label < ActiveRecord::Base + include CacheMarkdownField include Referable include Subscribable @@ -8,6 +9,8 @@ class Label < ActiveRecord::Base None = LabelStruct.new('No Label', 'No Label') Any = LabelStruct.new('Any Label', '') + cache_markdown_field :description, pipeline: :single_line + DEFAULT_COLOR = '#428BCA' default_value_for :color, DEFAULT_COLOR diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 44c3cbb2c73..23aecbfa3a6 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -6,12 +6,16 @@ class Milestone < ActiveRecord::Base Any = MilestoneStruct.new('Any Milestone', '', -1) Upcoming = MilestoneStruct.new('Upcoming', '#upcoming', -2) + include CacheMarkdownField include InternalId include Sortable include Referable include StripAttribute include Milestoneish + cache_markdown_field :title, pipeline: :single_line + cache_markdown_field :description + belongs_to :project has_many :issues has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 919b3b1f095..b7f2b2bbe61 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -1,9 +1,12 @@ class Namespace < ActiveRecord::Base acts_as_paranoid + include CacheMarkdownField include Sortable include Gitlab::ShellAdapter + cache_markdown_field :description, pipeline: :description + has_many :projects, dependent: :destroy belongs_to :owner, class_name: "User" diff --git a/app/models/note.rb b/app/models/note.rb index f2656df028b..2d644b03e4d 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -6,10 +6,13 @@ class Note < ActiveRecord::Base include Awardable include Importable include FasterCacheKeys + include CacheMarkdownField + + cache_markdown_field :note, pipeline: :note # Attribute containing rendered and redacted Markdown as generated by # Banzai::ObjectRenderer. - attr_accessor :note_html + attr_accessor :redacted_note_html # An Array containing the number of visible references as generated by # Banzai::ObjectRenderer diff --git a/app/models/project.rb b/app/models/project.rb index ecd742a17d5..88e4bd14860 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -6,6 +6,7 @@ class Project < ActiveRecord::Base include Gitlab::VisibilityLevel include Gitlab::CurrentSettings include AccessRequestable + include CacheMarkdownField include Referable include Sortable include AfterCommitQueue @@ -17,6 +18,8 @@ class Project < ActiveRecord::Base UNKNOWN_IMPORT_URL = 'http://unknown.git' + cache_markdown_field :description, pipeline: :description + delegate :feature_available?, :builds_enabled?, :wiki_enabled?, :merge_requests_enabled?, to: :project_feature, allow_nil: true default_value_for :archived, false diff --git a/app/models/release.rb b/app/models/release.rb index e196b84eb18..c936899799e 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -1,4 +1,8 @@ class Release < ActiveRecord::Base + include CacheMarkdownField + + cache_markdown_field :description + belongs_to :project validates :description, :project, :tag, presence: true diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 8a1730f3f36..2373b445009 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -1,11 +1,21 @@ class Snippet < ActiveRecord::Base include Gitlab::VisibilityLevel include Linguist::BlobHelper + include CacheMarkdownField include Participable include Referable include Sortable include Awardable + cache_markdown_field :title, pipeline: :single_line + cache_markdown_field :content + + # If file_name changes, it invalidates content + alias_method :default_content_html_invalidator, :content_html_invalidated? + def content_html_invalidated? + default_content_html_invalidator || file_name_changed? + end + default_value_for :visibility_level, Snippet::PRIVATE belongs_to :author, class_name: 'User' diff --git a/app/views/admin/abuse_reports/_abuse_report.html.haml b/app/views/admin/abuse_reports/_abuse_report.html.haml index 56bf6194914..05f3d9a3b50 100644 --- a/app/views/admin/abuse_reports/_abuse_report.html.haml +++ b/app/views/admin/abuse_reports/_abuse_report.html.haml @@ -21,7 +21,7 @@ %td %strong.subheading.visible-xs-block.visible-sm-block Message .message - = markdown(abuse_report.message.squish!, pipeline: :single_line, author: reporter) + = markdown_field(abuse_report, :message) %td - if user = link_to 'Remove user & report', admin_abuse_report_path(abuse_report, remove_user: true), diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml index f952d2e9aa1..3132d157f29 100644 --- a/app/views/admin/broadcast_messages/_form.html.haml +++ b/app/views/admin/broadcast_messages/_form.html.haml @@ -1,7 +1,10 @@ .broadcast-message-preview{ style: broadcast_message_style(@broadcast_message) } = icon('bullhorn') .js-broadcast-message-preview - = render_broadcast_message(@broadcast_message.message.presence || "Your message here") + - if @broadcast_message.message.present? + = render_broadcast_message(@broadcast_message) + - else + = "Your message here" = form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-quick-submit js-requires-input'} do |f| = form_errors(@broadcast_message) diff --git a/app/views/admin/broadcast_messages/preview.js.haml b/app/views/admin/broadcast_messages/preview.js.haml index fbc9453c72e..c72e59640d7 100644 --- a/app/views/admin/broadcast_messages/preview.js.haml +++ b/app/views/admin/broadcast_messages/preview.js.haml @@ -1 +1 @@ -$('.js-broadcast-message-preview').html("#{j(render_broadcast_message(@message))}"); +$('.js-broadcast-message-preview').html("#{j(render_broadcast_message(@broadcast_message))}"); diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml index 77a11e49e20..adfa1eaafc9 100644 --- a/app/views/admin/groups/_group.html.haml +++ b/app/views/admin/groups/_group.html.haml @@ -23,4 +23,4 @@ - if group.description.present? .description - = markdown(group.description, pipeline: :description) + = markdown_field(group, :description) diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml index f417b2e44a4..be224d66855 100644 --- a/app/views/admin/labels/_label.html.haml +++ b/app/views/admin/labels/_label.html.haml @@ -1,7 +1,7 @@ %li{id: dom_id(label)} .label-row = render_colored_label(label, tooltip: false) - = markdown(label.description, pipeline: :single_line) + = markdown_field(label, :description) .pull-right = link_to 'Edit', edit_admin_label_path(label), class: 'btn btn-sm' = link_to 'Delete', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Delete this label? Are you sure?"} diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 1e755785d90..339cfc613fe 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -87,7 +87,7 @@ - if project.description.present? .description - = markdown(project.description, pipeline: :description) + = markdown_field(project, :description) = paginate @projects, theme: 'gitlab' - else diff --git a/app/views/devise/confirmations/almost_there.haml b/app/views/devise/confirmations/almost_there.haml index 73c3a3dd2eb..20cd7b0179d 100644 --- a/app/views/devise/confirmations/almost_there.haml +++ b/app/views/devise/confirmations/almost_there.haml @@ -3,9 +3,9 @@ Almost there... %p.lead Please check your email to confirm your account -- if after_sign_up_text.present? +- if current_application_settings.after_sign_up_text.present? .well-confirmation.text-center - = markdown(after_sign_up_text) + = markdown_field(current_application_settings, :after_sign_up_text) %p.confirmation-content.text-center No confirmation email received? Please check your spam folder or .append-bottom-20.prepend-top-20.text-center diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 31db6ee0cad..fab61f447c2 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -21,7 +21,7 @@ - if @group.description.present? .cover-desc.description - = markdown(@group.description, pipeline: :description) + = markdown_field(@group, :description) %div.groups-header{ class: container_class } .top-area diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 57601ae9be0..31631887317 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -20,7 +20,7 @@ Read more about GitLab at #{link_to promo_host, promo_url, target: '_blank'}. - if current_application_settings.help_page_text.present? %hr - = markdown(current_application_settings.help_page_text) + = markdown_field(current_application_settings, :help_page_text) %hr diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 3d28eec84ef..a9a384bd5f3 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -25,8 +25,8 @@ Perform code reviews and enhance collaboration with merge requests. Each project can also have an issue tracker and a wiki. - - if extra_sign_in_text.present? - = markdown(extra_sign_in_text) + - if current_application_settings.sign_in_text.present? + = markdown_field(current_application_settings, :sign_in_text) %hr .container diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 8ef31ca3bda..5590198a20e 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -9,7 +9,7 @@ .project-home-desc - if @project.description.present? - = markdown(@project.description, pipeline: :description) + = markdown_field(@project, :description) - if forked_from_project = @project.forked_from_project %p diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 9fd87f84aaa..6c82a4e5600 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -65,10 +65,10 @@ .commit-box.content-block %h3.commit-title - = markdown escape_once(@commit.title), pipeline: :single_line, author: @commit.author + = markdown(@commit.title, pipeline: :single_line, author: @commit.author) - if @commit.description.present? %pre.commit-description - = preserve(markdown(escape_once(@commit.description), pipeline: :single_line, author: @commit.author)) + = preserve(markdown(@commit.description, pipeline: :single_line, author: @commit.author)) :javascript $(".commit-info.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}"); diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 389477d0927..fb48aef0559 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -33,7 +33,7 @@ - if commit.description? %pre.commit-row-description.js-toggle-content - = preserve(markdown(escape_once(commit.description), pipeline: :single_line, author: commit.author)) + = preserve(markdown(commit.description, pipeline: :single_line, author: commit.author)) .commit-row-info = commit_author_link(commit, avatar: false, size: 24) diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index cbdea209847..b94d6f8633c 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -55,12 +55,12 @@ .issue-details.issuable-details .detail-page-description.content-block %h2.title - = markdown escape_once(@issue.title), pipeline: :single_line, author: @issue.author + = markdown_field(@issue, :title) - if @issue.description.present? .description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' } .wiki = preserve do - = markdown(@issue.description, cache_key: [@issue, "description"], author: @issue.author) + = markdown_field(@issue, :description) %textarea.hidden.js-task-list-field = @issue.description = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue_edited_ago') diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index ebf18f6ac85..ed23d06ee5e 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -1,13 +1,13 @@ .detail-page-description.content-block %h2.title - = markdown escape_once(@merge_request.title), pipeline: :single_line, author: @merge_request.author + = markdown_field(@merge_request, :title) %div - if @merge_request.description.present? .description{class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : ''} .wiki = preserve do - = markdown(@merge_request.description, cache_key: [@merge_request, "description"], author: @merge_request.author) + = markdown_field(@merge_request, :description) %textarea.hidden.js-task-list-field = @merge_request.description diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 73772cc0e32..e62f810a521 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -30,13 +30,13 @@ .detail-page-description.milestone-detail %h2.title - = markdown escape_once(@milestone.title), pipeline: :single_line + = markdown_field(@milestone, :title) %div - if @milestone.description.present? .description .wiki = preserve do - = markdown @milestone.description + = markdown_field(@milestone, :description) - if @milestone.total_items_count(current_user).zero? .alert.alert-success.prepend-top-default diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 788be4a0047..73fe6a715fa 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -61,7 +61,7 @@ .note-body{class: note_editable ? 'js-task-list-container' : ''} .note-text.md = preserve do - = note.note_html + = note.redacted_note_html = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) - if note_editable = render 'projects/notes/edit_form', note: note diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 5800ef7de48..d288efc546f 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -33,7 +33,7 @@ - if @commit .commit-box.content-block %h3.commit-title - = markdown escape_once(@commit.title), pipeline: :single_line + = markdown(@commit.title, pipeline: :single_line) - if @commit.description.present? %pre.commit-description - = preserve(markdown(escape_once(@commit.description), pipeline: :single_line)) + = preserve(markdown(@commit.description, pipeline: :single_line)) diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml index 43a6fdfd103..d9c39fb87b7 100644 --- a/app/views/projects/repositories/_feed.html.haml +++ b/app/views/projects/repositories/_feed.html.haml @@ -12,7 +12,7 @@ = link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do %code= commit.short_id = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: '' - = markdown escape_once(truncate(commit.title, length: 40)), pipeline: :single_line, author: commit.author + = markdown(truncate(commit.title, length: 40), pipeline: :single_line, author: commit.author) %td %span.pull-right.cgray = time_ago_with_tooltip(commit.committed_date) diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml index 752b9e060d5..5afa193357e 100644 --- a/app/views/projects/runners/_shared_runners.html.haml +++ b/app/views/projects/runners/_shared_runners.html.haml @@ -1,8 +1,8 @@ %h3 Shared Runners .bs-callout.bs-callout-warning.shared-runners-description - - if shared_runners_text.present? - = markdown(shared_runners_text, pipeline: 'plain_markdown') + - if current_application_settings.shared_runners_text.present? + = markdown_field(current_application_settings, :shared_runners_text) - else GitLab Shared Runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index a156d98bab8..05fccb4f976 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -30,4 +30,4 @@ .description.prepend-top-default .wiki = preserve do - = markdown release.description + = markdown_field(release, :description) diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 4dd7439b2d0..155af755759 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -33,6 +33,6 @@ .description .wiki = preserve do - = markdown @release.description + = markdown_field(@release, :description) - else This tag has no release notes. diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml index 8f68d6d1b87..e010f21de5a 100644 --- a/app/views/search/results/_issue.html.haml +++ b/app/views/search/results/_issue.html.haml @@ -7,7 +7,7 @@ - if issue.description.present? .description.term = preserve do - = search_md_sanitize(markdown(truncate(issue.description, length: 200, separator: " "), { project: issue.project, author: issue.author })) + = search_md_sanitize(issue, :description) %span.light #{issue.project.name_with_namespace} - if issue.closed? diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml index 6331c2bd6b0..07b17bc69c0 100644 --- a/app/views/search/results/_merge_request.html.haml +++ b/app/views/search/results/_merge_request.html.haml @@ -6,7 +6,7 @@ - if merge_request.description.present? .description.term = preserve do - = search_md_sanitize(markdown(merge_request.description, { project: merge_request.project, author: merge_request.author })) + = search_md_sanitize(merge_request, :description) %span.light #{merge_request.project.name_with_namespace} .pull-right diff --git a/app/views/search/results/_milestone.html.haml b/app/views/search/results/_milestone.html.haml index b31595d8d1c..9664f65a36e 100644 --- a/app/views/search/results/_milestone.html.haml +++ b/app/views/search/results/_milestone.html.haml @@ -6,4 +6,4 @@ - if milestone.description.present? .description.term = preserve do - = search_md_sanitize(markdown(milestone.description)) + = search_md_sanitize(milestone, :description) diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml index e0400083870..f3701b89bb4 100644 --- a/app/views/search/results/_note.html.haml +++ b/app/views/search/results/_note.html.haml @@ -23,4 +23,4 @@ .note-search-result .term = preserve do - = search_md_sanitize(markdown(note.note, {no_header_anchors: true, author: note.author})) + = search_md_sanitize(note, :note) diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index 77676454b57..6f593e8dff9 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -12,4 +12,4 @@ = link_to_label(label, tooltip: false) - if label.description %span.label-description - = markdown(label.description, pipeline: :single_line) + = markdown_field(label, :description) diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index 1ad95351005..dc4ee3074d2 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -35,4 +35,4 @@ - if group.description.present? .description - = markdown(group.description, pipeline: :description) + = markdown_field(group, :description) diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml index b15e8ea73fe..33f93dccd3c 100644 --- a/app/views/shared/milestones/_labels_tab.html.haml +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -8,7 +8,7 @@ = link_to milestones_label_path(options) do - render_colored_label(label, tooltip: false) %span.prepend-description-left - = markdown(label.description, pipeline: :single_line) + = markdown_field(label, :description) .pull-info-right %span.append-right-20 diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml index 7ff947a51db..548215243db 100644 --- a/app/views/shared/milestones/_top.html.haml +++ b/app/views/shared/milestones/_top.html.haml @@ -26,7 +26,7 @@ .detail-page-description.milestone-detail %h2.title - = markdown escape_once(milestone.title), pipeline: :single_line + = markdown_field(milestone, :title) - if milestone.complete?(current_user) && milestone.active? .alert.alert-success.prepend-top-default @@ -55,4 +55,3 @@ Open %td = ms.expires_at - diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 66c309644a7..e8668048703 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -50,4 +50,4 @@ class: "commit-row-message" - elsif project.description.present? .description - = markdown(project.description, pipeline: :description) + = markdown_field(project, :description) diff --git a/app/views/shared/snippets/_blob.html.haml b/app/views/shared/snippets/_blob.html.haml index 773ce8ac240..dcdba01aee9 100644 --- a/app/views/shared/snippets/_blob.html.haml +++ b/app/views/shared/snippets/_blob.html.haml @@ -1,9 +1,12 @@ - unless @snippet.content.empty? - if markup?(@snippet.file_name) %textarea.markdown-snippet-copy.blob-content{data: {blob_id: @snippet.id}} - = @snippet.data + = @snippet.content .file-content.wiki - = render_markup(@snippet.file_name, @snippet.data) + - if gitlab_markdown?(@snippet.file_name) + = preserve(markdown_field(@snippet, :content)) + - else + = render_markup(@snippet.file_name, @snippet.content) - else = render 'shared/file_highlight', blob: @snippet - else diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 7ae4211ddfd..d7506e07ff6 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -21,4 +21,4 @@ = render "snippets/actions" %h2.snippet-title.prepend-top-0.append-bottom-0 - = markdown escape_once(@snippet.title), pipeline: :single_line, author: @snippet.author + = markdown_field(@snippet, :title) diff --git a/app/workers/clear_database_cache_worker.rb b/app/workers/clear_database_cache_worker.rb new file mode 100644 index 00000000000..c541daba50e --- /dev/null +++ b/app/workers/clear_database_cache_worker.rb @@ -0,0 +1,23 @@ +# This worker clears all cache fields in the database, working in batches. +class ClearDatabaseCacheWorker + include Sidekiq::Worker + + BATCH_SIZE = 1000 + + def perform + CacheMarkdownField.caching_classes.each do |kls| + fields = kls.cached_markdown_fields.html_fields + clear_cache_fields = fields.each_with_object({}) do |field, memo| + memo[field] = nil + end + + Rails.logger.debug("Clearing Markdown cache for #{kls}: #{fields.inspect}") + + kls.unscoped.in_batches(of: BATCH_SIZE) do |relation| + relation.update_all(clear_cache_fields) + end + end + + nil + end +end |