Use proper markdown rendering for previews
This commit is contained in:
parent
9790fe58e5
commit
750af9fd32
|
@ -108,6 +108,11 @@
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
markdownVersion: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
projectPath: {
|
projectPath: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -282,6 +287,7 @@
|
||||||
:issuable-templates="issuableTemplates"
|
:issuable-templates="issuableTemplates"
|
||||||
:markdown-docs-path="markdownDocsPath"
|
:markdown-docs-path="markdownDocsPath"
|
||||||
:markdown-preview-path="markdownPreviewPath"
|
:markdown-preview-path="markdownPreviewPath"
|
||||||
|
:markdown-version="markdownVersion"
|
||||||
:project-path="projectPath"
|
:project-path="projectPath"
|
||||||
:project-namespace="projectNamespace"
|
:project-namespace="projectNamespace"
|
||||||
:show-delete-button="showDeleteButton"
|
:show-delete-button="showDeleteButton"
|
||||||
|
|
|
@ -20,6 +20,11 @@
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
markdownVersion: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
canAttachFile: {
|
canAttachFile: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -47,6 +52,7 @@
|
||||||
<markdown-field
|
<markdown-field
|
||||||
:markdown-preview-path="markdownPreviewPath"
|
:markdown-preview-path="markdownPreviewPath"
|
||||||
:markdown-docs-path="markdownDocsPath"
|
:markdown-docs-path="markdownDocsPath"
|
||||||
|
:markdown-version="markdownVersion"
|
||||||
:can-attach-file="canAttachFile"
|
:can-attach-file="canAttachFile"
|
||||||
:enable-autocomplete="enableAutocomplete"
|
:enable-autocomplete="enableAutocomplete"
|
||||||
>
|
>
|
||||||
|
|
|
@ -35,6 +35,11 @@
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
markdownVersion: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
projectPath: {
|
projectPath: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -97,6 +102,7 @@
|
||||||
:form-state="formState"
|
:form-state="formState"
|
||||||
:markdown-preview-path="markdownPreviewPath"
|
:markdown-preview-path="markdownPreviewPath"
|
||||||
:markdown-docs-path="markdownDocsPath"
|
:markdown-docs-path="markdownDocsPath"
|
||||||
|
:markdown-version="markdownVersion"
|
||||||
:can-attach-file="canAttachFile"
|
:can-attach-file="canAttachFile"
|
||||||
:enable-autocomplete="enableAutocomplete"
|
:enable-autocomplete="enableAutocomplete"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1251,13 +1251,15 @@ export default class Notes {
|
||||||
var postUrl = $originalContentEl.data('postUrl');
|
var postUrl = $originalContentEl.data('postUrl');
|
||||||
var targetId = $originalContentEl.data('targetId');
|
var targetId = $originalContentEl.data('targetId');
|
||||||
var targetType = $originalContentEl.data('targetType');
|
var targetType = $originalContentEl.data('targetType');
|
||||||
|
var markdownVersion = $originalContentEl.data('markdownVersion');
|
||||||
|
|
||||||
this.glForm = new GLForm($editForm.find('form'), this.enableGFM);
|
this.glForm = new GLForm($editForm.find('form'), this.enableGFM);
|
||||||
|
|
||||||
$editForm
|
$editForm
|
||||||
.find('form')
|
.find('form')
|
||||||
.attr('action', `${postUrl}?html=true`)
|
.attr('action', `${postUrl}?html=true`)
|
||||||
.attr('data-remote', 'true');
|
.attr('data-remote', 'true')
|
||||||
|
.attr('data-markdown-version', markdownVersion);
|
||||||
$editForm.find('.js-form-target-id').val(targetId);
|
$editForm.find('.js-form-target-id').val(targetId);
|
||||||
$editForm.find('.js-form-target-type').val(targetType);
|
$editForm.find('.js-form-target-type').val(targetType);
|
||||||
$editForm
|
$editForm
|
||||||
|
|
|
@ -34,6 +34,11 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
markdownVersion: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -344,6 +349,7 @@ Please check your network connection and try again.`;
|
||||||
:markdown-preview-path="markdownPreviewPath"
|
:markdown-preview-path="markdownPreviewPath"
|
||||||
:markdown-docs-path="markdownDocsPath"
|
:markdown-docs-path="markdownDocsPath"
|
||||||
:quick-actions-docs-path="quickActionsDocsPath"
|
:quick-actions-docs-path="quickActionsDocsPath"
|
||||||
|
:markdown-version="markdownVersion"
|
||||||
:add-spacing-classes="false">
|
:add-spacing-classes="false">
|
||||||
<textarea
|
<textarea
|
||||||
id="note-body"
|
id="note-body"
|
||||||
|
|
|
@ -92,6 +92,7 @@ export default {
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
:note-body="noteBody"
|
:note-body="noteBody"
|
||||||
:note-id="note.id"
|
:note-id="note.id"
|
||||||
|
:markdown-version="note.cached_markdown_version"
|
||||||
@handleFormUpdate="handleFormUpdate"
|
@handleFormUpdate="handleFormUpdate"
|
||||||
@cancelForm="formCancelHandler"
|
@cancelForm="formCancelHandler"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -24,6 +24,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
|
markdownVersion: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
saveButtonTitle: {
|
saveButtonTitle: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -156,6 +161,7 @@ export default {
|
||||||
<markdown-field
|
<markdown-field
|
||||||
:markdown-preview-path="markdownPreviewPath"
|
:markdown-preview-path="markdownPreviewPath"
|
||||||
:markdown-docs-path="markdownDocsPath"
|
:markdown-docs-path="markdownDocsPath"
|
||||||
|
:markdown-version="markdownVersion"
|
||||||
:quick-actions-docs-path="quickActionsDocsPath"
|
:quick-actions-docs-path="quickActionsDocsPath"
|
||||||
:add-spacing-classes="false">
|
:add-spacing-classes="false">
|
||||||
<textarea
|
<textarea
|
||||||
|
|
|
@ -43,6 +43,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
markdownVersion: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -192,6 +197,7 @@ export default {
|
||||||
|
|
||||||
<comment-form
|
<comment-form
|
||||||
:noteable-type="noteableType"
|
:noteable-type="noteableType"
|
||||||
|
:markdown-version="markdownVersion"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -15,6 +15,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
const notesDataset = document.getElementById('js-vue-notes').dataset;
|
const notesDataset = document.getElementById('js-vue-notes').dataset;
|
||||||
const parsedUserData = JSON.parse(notesDataset.currentUserData);
|
const parsedUserData = JSON.parse(notesDataset.currentUserData);
|
||||||
const noteableData = JSON.parse(notesDataset.noteableData);
|
const noteableData = JSON.parse(notesDataset.noteableData);
|
||||||
|
const { markdownVersion } = notesDataset;
|
||||||
let currentUserData = {};
|
let currentUserData = {};
|
||||||
|
|
||||||
noteableData.noteableType = notesDataset.noteableType;
|
noteableData.noteableType = notesDataset.noteableType;
|
||||||
|
@ -33,6 +34,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
return {
|
return {
|
||||||
noteableData,
|
noteableData,
|
||||||
currentUserData,
|
currentUserData,
|
||||||
|
markdownVersion,
|
||||||
notesData: JSON.parse(notesDataset.notesData),
|
notesData: JSON.parse(notesDataset.notesData),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -42,6 +44,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
noteableData: this.noteableData,
|
noteableData: this.noteableData,
|
||||||
notesData: this.notesData,
|
notesData: this.notesData,
|
||||||
userData: this.currentUserData,
|
userData: this.currentUserData,
|
||||||
|
markdownVersion: this.markdownVersion,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -28,12 +28,16 @@ MarkdownPreview.prototype.ajaxCache = {};
|
||||||
|
|
||||||
MarkdownPreview.prototype.showPreview = function ($form) {
|
MarkdownPreview.prototype.showPreview = function ($form) {
|
||||||
var mdText;
|
var mdText;
|
||||||
|
var markdownVersion;
|
||||||
|
var url;
|
||||||
var preview = $form.find('.js-md-preview');
|
var preview = $form.find('.js-md-preview');
|
||||||
var url = preview.data('url');
|
|
||||||
if (preview.hasClass('md-preview-loading')) {
|
if (preview.hasClass('md-preview-loading')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mdText = $form.find('textarea.markdown-area').val();
|
mdText = $form.find('textarea.markdown-area').val();
|
||||||
|
markdownVersion = $form.attr('data-markdown-version');
|
||||||
|
url = this.versionedPreviewPath(preview.data('url'), markdownVersion);
|
||||||
|
|
||||||
if (mdText.trim().length === 0) {
|
if (mdText.trim().length === 0) {
|
||||||
preview.text(this.emptyMessage);
|
preview.text(this.emptyMessage);
|
||||||
|
@ -59,6 +63,14 @@ MarkdownPreview.prototype.showPreview = function ($form) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MarkdownPreview.prototype.versionedPreviewPath = function (markdownPreviewPath, markdownVersion) {
|
||||||
|
if (typeof markdownVersion === 'undefined') {
|
||||||
|
return markdownPreviewPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${markdownPreviewPath}${markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'}markdown_version=${markdownVersion}`;
|
||||||
|
};
|
||||||
|
|
||||||
MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) {
|
MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
import { s__ } from '~/locale';
|
||||||
import Flash from '../../../flash';
|
import Flash from '../../../flash';
|
||||||
import GLForm from '../../../gl_form';
|
import GLForm from '../../../gl_form';
|
||||||
import markdownHeader from './header.vue';
|
import markdownHeader from './header.vue';
|
||||||
|
@ -22,6 +23,11 @@
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
markdownVersion: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
addSpacingClasses: {
|
addSpacingClasses: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -92,10 +98,11 @@
|
||||||
|
|
||||||
if (text) {
|
if (text) {
|
||||||
this.markdownPreviewLoading = true;
|
this.markdownPreviewLoading = true;
|
||||||
this.$http.post(this.markdownPreviewPath, { text })
|
this.$http
|
||||||
.then(resp => resp.json())
|
.post(this.versionedPreviewPath(), { text })
|
||||||
.then(data => this.renderMarkdown(data))
|
.then(resp => resp.json())
|
||||||
.catch(() => new Flash('Error loading markdown preview'));
|
.then(data => this.renderMarkdown(data))
|
||||||
|
.catch(() => new Flash(s__('Error loading markdown preview')));
|
||||||
} else {
|
} else {
|
||||||
this.renderMarkdown();
|
this.renderMarkdown();
|
||||||
}
|
}
|
||||||
|
@ -119,6 +126,13 @@
|
||||||
$(this.$refs['markdown-preview']).renderGFM();
|
$(this.$refs['markdown-preview']).renderGFM();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
versionedPreviewPath() {
|
||||||
|
const { markdownPreviewPath, markdownVersion } = this;
|
||||||
|
return `${markdownPreviewPath}${
|
||||||
|
markdownPreviewPath.indexOf('?') === -1 ? '?' : '&'
|
||||||
|
}markdown_version=${markdownVersion}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -14,6 +14,8 @@ module PreviewMarkdown
|
||||||
else {}
|
else {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
markdown_params[:markdown_engine] = result[:markdown_engine]
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
body: view_context.markdown(result[:text], markdown_params),
|
body: view_context.markdown(result[:text], markdown_params),
|
||||||
references: {
|
references: {
|
||||||
|
|
|
@ -249,6 +249,7 @@ module IssuablesHelper
|
||||||
issuableRef: issuable.to_reference,
|
issuableRef: issuable.to_reference,
|
||||||
markdownPreviewPath: preview_markdown_path(parent),
|
markdownPreviewPath: preview_markdown_path(parent),
|
||||||
markdownDocsPath: help_page_path('user/markdown'),
|
markdownDocsPath: help_page_path('user/markdown'),
|
||||||
|
markdownVersion: issuable.cached_markdown_version,
|
||||||
issuableTemplates: issuable_templates(issuable),
|
issuableTemplates: issuable_templates(issuable),
|
||||||
initialTitleHtml: markdown_field(issuable, :title),
|
initialTitleHtml: markdown_field(issuable, :title),
|
||||||
initialTitleText: issuable.title,
|
initialTitleText: issuable.title,
|
||||||
|
|
|
@ -107,6 +107,7 @@ module MarkupHelper
|
||||||
|
|
||||||
def markup(file_name, text, context = {})
|
def markup(file_name, text, context = {})
|
||||||
context[:project] ||= @project
|
context[:project] ||= @project
|
||||||
|
context[:markdown_engine] ||= :redcarpet
|
||||||
html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
|
html = context.delete(:rendered) || markup_unsafe(file_name, text, context)
|
||||||
prepare_for_rendering(html, context)
|
prepare_for_rendering(html, context)
|
||||||
end
|
end
|
||||||
|
@ -120,7 +121,8 @@ module MarkupHelper
|
||||||
project: @project,
|
project: @project,
|
||||||
project_wiki: @project_wiki,
|
project_wiki: @project_wiki,
|
||||||
page_slug: wiki_page.slug,
|
page_slug: wiki_page.slug,
|
||||||
issuable_state_filter_enabled: true
|
issuable_state_filter_enabled: true,
|
||||||
|
markdown_engine: :redcarpet
|
||||||
}
|
}
|
||||||
|
|
||||||
html =
|
html =
|
||||||
|
|
|
@ -169,6 +169,7 @@ module NotesHelper
|
||||||
registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'),
|
registerPath: new_session_path(:user, redirect_to_referer: 'yes', anchor: 'register-pane'),
|
||||||
newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'),
|
newSessionPath: new_session_path(:user, redirect_to_referer: 'yes'),
|
||||||
markdownDocsPath: help_page_path('user/markdown'),
|
markdownDocsPath: help_page_path('user/markdown'),
|
||||||
|
markdownVersion: issuable.cached_markdown_version,
|
||||||
quickActionsDocsPath: help_page_path('user/project/quick_actions'),
|
quickActionsDocsPath: help_page_path('user/project/quick_actions'),
|
||||||
closePath: close_issuable_path(issuable),
|
closePath: close_issuable_path(issuable),
|
||||||
reopenPath: reopen_issuable_path(issuable),
|
reopenPath: reopen_issuable_path(issuable),
|
||||||
|
|
|
@ -40,6 +40,18 @@ module CacheMarkdownField
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class MarkdownEngine
|
||||||
|
def self.from_version(version = nil)
|
||||||
|
return :common_mark if version.nil? || version == 0
|
||||||
|
|
||||||
|
if version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
|
||||||
|
:redcarpet
|
||||||
|
else
|
||||||
|
:common_mark
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def skip_project_check?
|
def skip_project_check?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -57,7 +69,7 @@ module CacheMarkdownField
|
||||||
# Banzai is less strict about authors, so don't always have an author key
|
# Banzai is less strict about authors, so don't always have an author key
|
||||||
context[:author] = self.author if self.respond_to?(:author)
|
context[:author] = self.author if self.respond_to?(:author)
|
||||||
|
|
||||||
context[:markdown_engine] = markdown_engine
|
context[:markdown_engine] = MarkdownEngine.from_version(latest_cached_markdown_version)
|
||||||
|
|
||||||
context
|
context
|
||||||
end
|
end
|
||||||
|
@ -123,14 +135,6 @@ module CacheMarkdownField
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def markdown_engine
|
|
||||||
if latest_cached_markdown_version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
|
|
||||||
:redcarpet
|
|
||||||
else
|
|
||||||
:common_mark
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
included do
|
included do
|
||||||
cattr_reader :cached_markdown_fields do
|
cattr_reader :cached_markdown_fields do
|
||||||
FieldData.new
|
FieldData.new
|
||||||
|
|
|
@ -564,7 +564,7 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def rendered_readme
|
def rendered_readme
|
||||||
MarkupHelper.markup_unsafe(readme.name, readme.data, project: project) if readme
|
MarkupHelper.markup_unsafe(readme.name, readme.data, project: project, markdown_engine: :redcarpet) if readme
|
||||||
end
|
end
|
||||||
cache_method :rendered_readme
|
cache_method :rendered_readme
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,8 @@ class NoteEntity < API::Entities::Note
|
||||||
|
|
||||||
expose :attachment, using: NoteAttachmentEntity, if: -> (note, _) { note.attachment? }
|
expose :attachment, using: NoteAttachmentEntity, if: -> (note, _) { note.attachment? }
|
||||||
|
|
||||||
|
expose :cached_markdown_version
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def current_user
|
def current_user
|
||||||
|
|
|
@ -6,7 +6,8 @@ class PreviewMarkdownService < BaseService
|
||||||
success(
|
success(
|
||||||
text: text,
|
text: text,
|
||||||
users: users,
|
users: users,
|
||||||
commands: commands.join(' ')
|
commands: commands.join(' '),
|
||||||
|
markdown_engine: markdown_engine
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -42,4 +43,8 @@ class PreviewMarkdownService < BaseService
|
||||||
def commands_target_id
|
def commands_target_id
|
||||||
params[:quick_actions_target_id]
|
params[:quick_actions_target_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def markdown_engine
|
||||||
|
CacheMarkdownField::MarkdownEngine.from_version(params[:markdown_version].to_i)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
= form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' } do |f|
|
= form_for [@project.namespace.becomes(Namespace), @project, @issue],
|
||||||
|
html: { class: 'issue-form common-note-form js-quick-submit js-requires-input' },
|
||||||
|
data: { markdown_version: @issue.cached_markdown_version } do |f|
|
||||||
= render 'shared/issuable/form', f: f, issuable: @issue
|
= render 'shared/issuable/form', f: f, issuable: @issue
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' } do |f|
|
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request],
|
||||||
|
html: { class: 'merge-request-form common-note-form js-requires-input js-quick-submit' },
|
||||||
|
data: { markdown_version: @merge_request.cached_markdown_version } do |f|
|
||||||
= render 'shared/issuable/form', f: f, issuable: @merge_request
|
= render 'shared/issuable/form', f: f, issuable: @merge_request
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'milestone-form common-note-form js-quick-submit js-requires-input'} do |f|
|
= form_for [@project.namespace.becomes(Namespace), @project, @milestone],
|
||||||
|
html: {class: 'milestone-form common-note-form js-quick-submit js-requires-input'},
|
||||||
|
data: { markdown_version: @milestone.cached_markdown_version } do |f|
|
||||||
= form_errors(@milestone)
|
= form_errors(@milestone)
|
||||||
.row
|
.row
|
||||||
.col-md-6
|
.col-md-6
|
||||||
|
|
|
@ -11,7 +11,9 @@
|
||||||
%strong= @tag.name
|
%strong= @tag.name
|
||||||
|
|
||||||
|
|
||||||
= form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name), html: { class: 'common-note-form release-form js-quick-submit' }) do |f|
|
= form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name),
|
||||||
|
html: { class: 'common-note-form release-form js-quick-submit' },
|
||||||
|
data: { markdown_version: @release.cached_markdown_version }) do |f|
|
||||||
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
|
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
|
||||||
= render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here…"
|
= render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here…"
|
||||||
= render 'shared/notes/hints'
|
= render 'shared/notes/hints'
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
- commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}")
|
- commit_message = @page.persisted? ? s_("WikiPageEdit|Update %{page_title}") : s_("WikiPageCreate|Create %{page_title}")
|
||||||
- commit_message = commit_message % { page_title: @page.title }
|
- commit_message = commit_message % { page_title: @page.title }
|
||||||
|
|
||||||
= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' } do |f|
|
= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post,
|
||||||
|
html: { class: 'wiki-form common-note-form prepend-top-default js-quick-submit' },
|
||||||
|
data: { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION } do |f|
|
||||||
= form_errors(@page)
|
= form_errors(@page)
|
||||||
|
|
||||||
- if @page.persisted?
|
- if @page.persisted?
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
.note-text.md
|
.note-text.md
|
||||||
= markdown_field(note, :note)
|
= markdown_field(note, :note)
|
||||||
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago')
|
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago')
|
||||||
.original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore } }
|
.original-note-content.hidden{ data: { post_url: note_url(note), target_id: note.noteable.id, target_type: note.noteable.class.name.underscore, markdown_version: note.cached_markdown_version } }
|
||||||
#{note.note}
|
#{note.note}
|
||||||
- if note_editable
|
- if note_editable
|
||||||
= render 'shared/notes/edit', note: note
|
= render 'shared/notes/edit', note: note
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
= page_specific_javascript_tag('lib/ace.js')
|
= page_specific_javascript_tag('lib/ace.js')
|
||||||
|
|
||||||
.snippet-form-holder
|
.snippet-form-holder
|
||||||
= form_for @snippet, url: url, html: { class: "snippet-form js-requires-input js-quick-submit common-note-form" } do |f|
|
= form_for @snippet, url: url,
|
||||||
|
html: { class: "snippet-form js-requires-input js-quick-submit common-note-form" },
|
||||||
|
data: { markdown_version: @snippet.cached_markdown_version } do |f|
|
||||||
= form_errors(@snippet)
|
= form_errors(@snippet)
|
||||||
|
|
||||||
.form-group.row
|
.form-group.row
|
||||||
|
|
|
@ -9,6 +9,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
|
||||||
[relative link 1](../relative)
|
[relative link 1](../relative)
|
||||||
[relative link 2](./relative)
|
[relative link 2](./relative)
|
||||||
[relative link 3](./e/f/relative)
|
[relative link 3](./e/f/relative)
|
||||||
|
[spaced link](title with spaces)
|
||||||
HEREDOC
|
HEREDOC
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
|
||||||
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -64,6 +66,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
|
||||||
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -86,6 +89,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
|
||||||
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -119,6 +123,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
|
||||||
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -136,6 +141,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
|
||||||
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -153,6 +159,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
|
||||||
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
|
||||||
|
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,6 +68,26 @@ describe 'Snippet', :js do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with cached Redcarpet html' do
|
||||||
|
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION) }
|
||||||
|
let(:file_name) { 'test.md' }
|
||||||
|
let(:content) { "1. one\n - sublist\n" }
|
||||||
|
|
||||||
|
it 'renders correctly' do
|
||||||
|
expect(page).to have_xpath("//ol//li//ul")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with cached CommonMark html' do
|
||||||
|
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
|
||||||
|
let(:file_name) { 'test.md' }
|
||||||
|
let(:content) { "1. one\n - sublist\n" }
|
||||||
|
|
||||||
|
it 'renders correctly' do
|
||||||
|
expect(page).not_to have_xpath("//ol//li//ul")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'switching to the simple viewer' do
|
context 'switching to the simple viewer' do
|
||||||
before do
|
before do
|
||||||
find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
|
find('.js-blob-viewer-switch-btn[data-viewer=simple]').click
|
||||||
|
|
|
@ -184,6 +184,7 @@ describe IssuablesHelper do
|
||||||
issuableRef: "##{issue.iid}",
|
issuableRef: "##{issue.iid}",
|
||||||
markdownPreviewPath: "/#{@project.full_path}/preview_markdown",
|
markdownPreviewPath: "/#{@project.full_path}/preview_markdown",
|
||||||
markdownDocsPath: '/help/user/markdown',
|
markdownDocsPath: '/help/user/markdown',
|
||||||
|
markdownVersion: 11,
|
||||||
issuableTemplates: [],
|
issuableTemplates: [],
|
||||||
projectPath: @project.path,
|
projectPath: @project.path,
|
||||||
projectNamespace: @project.namespace.path,
|
projectNamespace: @project.namespace.path,
|
||||||
|
|
|
@ -205,7 +205,9 @@ describe MarkupHelper do
|
||||||
it "uses Wiki pipeline for markdown files" do
|
it "uses Wiki pipeline for markdown files" do
|
||||||
allow(@wiki).to receive(:format).and_return(:markdown)
|
allow(@wiki).to receive(:format).and_return(:markdown)
|
||||||
|
|
||||||
expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", issuable_state_filter_enabled: true)
|
expect(helper).to receive(:markdown_unsafe).with('wiki content',
|
||||||
|
pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page",
|
||||||
|
issuable_state_filter_enabled: true, markdown_engine: :redcarpet)
|
||||||
|
|
||||||
helper.render_wiki_content(@wiki)
|
helper.render_wiki_content(@wiki)
|
||||||
end
|
end
|
||||||
|
@ -236,19 +238,32 @@ describe MarkupHelper do
|
||||||
expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8')
|
expect(helper.markup('foo.rst', content).encoding.name).to eq('UTF-8')
|
||||||
end
|
end
|
||||||
|
|
||||||
it "delegates to #markdown_unsafe when file name corresponds to Markdown" do
|
it 'delegates to #markdown_unsafe when file name corresponds to Markdown' do
|
||||||
expect(helper).to receive(:gitlab_markdown?).with('foo.md').and_return(true)
|
expect(helper).to receive(:gitlab_markdown?).with('foo.md').and_return(true)
|
||||||
expect(helper).to receive(:markdown_unsafe).and_return('NOEL')
|
expect(helper).to receive(:markdown_unsafe).and_return('NOEL')
|
||||||
|
|
||||||
expect(helper.markup('foo.md', content)).to eq('NOEL')
|
expect(helper.markup('foo.md', content)).to eq('NOEL')
|
||||||
end
|
end
|
||||||
|
|
||||||
it "delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc" do
|
it 'delegates to #asciidoc_unsafe when file name corresponds to AsciiDoc' do
|
||||||
expect(helper).to receive(:asciidoc?).with('foo.adoc').and_return(true)
|
expect(helper).to receive(:asciidoc?).with('foo.adoc').and_return(true)
|
||||||
expect(helper).to receive(:asciidoc_unsafe).and_return('NOEL')
|
expect(helper).to receive(:asciidoc_unsafe).and_return('NOEL')
|
||||||
|
|
||||||
expect(helper.markup('foo.adoc', content)).to eq('NOEL')
|
expect(helper.markup('foo.adoc', content)).to eq('NOEL')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'uses passed in rendered content' do
|
||||||
|
expect(helper).not_to receive(:gitlab_markdown?)
|
||||||
|
expect(helper).not_to receive(:markdown_unsafe)
|
||||||
|
|
||||||
|
expect(helper.markup('foo.md', content, rendered: '<p>NOEL</p>')).to eq('<p>NOEL</p>')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'defaults to Redcarpet' do
|
||||||
|
expect(helper).to receive(:markdown_unsafe).with(content, hash_including(markdown_engine: :redcarpet)).and_return('NOEL')
|
||||||
|
|
||||||
|
expect(helper.markup('foo.md', content)).to eq('NOEL')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#first_line_in_markdown' do
|
describe '#first_line_in_markdown' do
|
||||||
|
|
|
@ -165,6 +165,7 @@ export const note = {
|
||||||
report_abuse_path:
|
report_abuse_path:
|
||||||
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1',
|
'/abuse_reports/new?ref_url=http%3A%2F%2Flocalhost%3A3000%2Fgitlab-org%2Fgitlab-ce%2Fissues%2F7%23note_546&user_id=1',
|
||||||
path: '/gitlab-org/gitlab-ce/notes/546',
|
path: '/gitlab-org/gitlab-ce/notes/546',
|
||||||
|
cached_markdown_version: 11,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const discussionMock = {
|
export const discussionMock = {
|
||||||
|
|
|
@ -3,17 +3,61 @@ require 'spec_helper'
|
||||||
describe Banzai::Filter::MarkdownFilter do
|
describe Banzai::Filter::MarkdownFilter do
|
||||||
include FilterSpecHelper
|
include FilterSpecHelper
|
||||||
|
|
||||||
context 'code block' do
|
describe 'markdown engine from context' do
|
||||||
it 'adds language to lang attribute when specified' do
|
it 'defaults to CommonMark' do
|
||||||
result = filter("```html\nsome code\n```")
|
expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
|
||||||
|
|
||||||
expect(result).to start_with("<pre><code lang=\"html\">")
|
filter('test')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not add language to lang attribute when not specified' do
|
it 'uses Redcarpet' do
|
||||||
result = filter("```\nsome code\n```")
|
expect_any_instance_of(Banzai::Filter::MarkdownEngines::Redcarpet).to receive(:render).and_return('test')
|
||||||
|
|
||||||
expect(result).to start_with("<pre><code>")
|
filter('test', { markdown_engine: :redcarpet })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses CommonMark' do
|
||||||
|
expect_any_instance_of(Banzai::Filter::MarkdownEngines::CommonMark).to receive(:render).and_return('test')
|
||||||
|
|
||||||
|
filter('test', { markdown_engine: :common_mark })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'code block' do
|
||||||
|
context 'using CommonMark' do
|
||||||
|
before do
|
||||||
|
stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :common_mark)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds language to lang attribute when specified' do
|
||||||
|
result = filter("```html\nsome code\n```")
|
||||||
|
|
||||||
|
expect(result).to start_with("<pre><code lang=\"html\">")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not add language to lang attribute when not specified' do
|
||||||
|
result = filter("```\nsome code\n```")
|
||||||
|
|
||||||
|
expect(result).to start_with("<pre><code>")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'using Redcarpet' do
|
||||||
|
before do
|
||||||
|
stub_const('Banzai::Filter::MarkdownFilter::DEFAULT_ENGINE', :redcarpet)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds language to lang attribute when specified' do
|
||||||
|
result = filter("```html\nsome code\n```")
|
||||||
|
|
||||||
|
expect(result).to start_with("\n<pre><code lang=\"html\">")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not add language to lang attribute when not specified' do
|
||||||
|
result = filter("```\nsome code\n```")
|
||||||
|
|
||||||
|
expect(result).to start_with("\n<pre><code>")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -370,4 +370,20 @@ describe CacheMarkdownField do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe CacheMarkdownField::MarkdownEngine do
|
||||||
|
subject { lambda { |version| CacheMarkdownField::MarkdownEngine.from_version(version) } }
|
||||||
|
|
||||||
|
it 'returns :common_mark as a default' do
|
||||||
|
expect(subject.call(nil)).to eq :common_mark
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns :common_mark' do
|
||||||
|
expect(subject.call(CacheMarkdownField::CACHE_COMMONMARK_VERSION)).to eq :common_mark
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns :redcarpet' do
|
||||||
|
expect(subject.call(CacheMarkdownField::CACHE_REDCARPET_VERSION)).to eq :redcarpet
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -64,4 +64,16 @@ describe PreviewMarkdownService do
|
||||||
expect(result[:commands]).to eq 'Sets time estimate to 2y.'
|
expect(result[:commands]).to eq 'Sets time estimate to 2y.'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'sets correct markdown engine' do
|
||||||
|
service = described_class.new(project, user, { markdown_version: CacheMarkdownField::CACHE_REDCARPET_VERSION })
|
||||||
|
result = service.execute
|
||||||
|
|
||||||
|
expect(result[:markdown_engine]).to eq :redcarpet
|
||||||
|
|
||||||
|
service = described_class.new(project, user, { markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION })
|
||||||
|
result = service.execute
|
||||||
|
|
||||||
|
expect(result[:markdown_engine]).to eq :common_mark
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue