Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
933fe8e1
Commit
933fe8e1
authored
Aug 19, 2021
by
Lee Tickett
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Render audio in content editor
Changelog: added
parent
a19ecc2f
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
150 additions
and
60 deletions
+150
-60
app/assets/javascripts/behaviors/markdown/nodes/playable.js
app/assets/javascripts/behaviors/markdown/nodes/playable.js
+20
-23
app/assets/javascripts/content_editor/extensions/audio.js
app/assets/javascripts/content_editor/extensions/audio.js
+8
-0
app/assets/javascripts/content_editor/extensions/playable.js
app/assets/javascripts/content_editor/extensions/playable.js
+71
-0
app/assets/javascripts/content_editor/services/create_content_editor.js
...ascripts/content_editor/services/create_content_editor.js
+2
-0
app/assets/javascripts/content_editor/services/markdown_serializer.js
...avascripts/content_editor/services/markdown_serializer.js
+5
-6
app/assets/javascripts/content_editor/services/serialization_helpers.js
...ascripts/content_editor/services/serialization_helpers.js
+11
-0
app/assets/stylesheets/framework/typography.scss
app/assets/stylesheets/framework/typography.scss
+6
-0
lib/banzai/filter/playable_link_filter.rb
lib/banzai/filter/playable_link_filter.rb
+5
-9
spec/frontend/fixtures/api_markdown.yml
spec/frontend/fixtures/api_markdown.yml
+12
-0
spec/lib/banzai/filter/audio_link_filter_spec.rb
spec/lib/banzai/filter/audio_link_filter_spec.rb
+5
-11
spec/lib/banzai/filter/video_link_filter_spec.rb
spec/lib/banzai/filter/video_link_filter_spec.rb
+5
-11
No files found.
app/assets/javascripts/behaviors/markdown/nodes/playable.js
View file @
933fe8e1
/* eslint-disable class-methods-use-this */
/* eslint-disable @gitlab/require-i18n-strings */
import
{
defaultMarkdownSerializer
}
from
'
prosemirror-markdown
'
;
import
{
Node
}
from
'
tiptap
'
;
import
{
HIGHER_PARSE_RULE_PRIORITY
}
from
'
../constants
'
;
/**
* Abstract base class for playable media, like video and audio.
...
...
@@ -32,34 +30,34 @@ export default class Playable extends Node {
const
parseDOM
=
[
{
tag
:
`.
${
this
.
mediaType
}
-container`
,
skip
:
true
,
},
{
tag
:
`.
${
this
.
mediaType
}
-container p`
,
priority
:
HIGHER_PARSE_RULE_PRIORITY
,
ignore
:
true
,
},
{
tag
:
`
${
this
.
mediaType
}
[src]`
,
getAttrs
:
(
el
)
=>
({
src
:
el
.
src
,
alt
:
el
.
dataset
.
title
}),
tag
:
`.media-container`
,
getAttrs
:
(
el
)
=>
({
src
:
el
.
querySelector
(
'
audio,video
'
).
src
,
alt
:
el
.
querySelector
(
'
audio,video
'
).
dataset
.
title
,
}),
},
];
const
toDOM
=
(
node
)
=>
[
this
.
mediaType
,
{
src
:
node
.
attrs
.
src
,
controls
:
true
,
'
data-setup
'
:
'
{}
'
,
'
data-title
'
:
node
.
attrs
.
alt
,
...
this
.
extraElementAttrs
,
},
'
span
'
,
{
class
:
'
media-container
'
},
[
this
.
options
.
mediaType
,
{
src
:
node
.
attrs
.
src
,
controls
:
true
,
'
data-setup
'
:
'
{}
'
,
'
data-title
'
:
node
.
attrs
.
alt
,
...
this
.
extraElementAttrs
,
},
],
[
'
a
'
,
{
href
:
node
.
attrs
.
src
},
node
.
attrs
.
alt
],
];
return
{
attrs
,
group
:
'
block
'
,
group
:
'
inline
'
,
inline
:
true
,
draggable
:
true
,
parseDOM
,
toDOM
,
...
...
@@ -68,6 +66,5 @@ export default class Playable extends Node {
toMarkdown
(
state
,
node
)
{
defaultMarkdownSerializer
.
nodes
.
image
(
state
,
node
);
state
.
closeBlock
(
node
);
}
}
app/assets/javascripts/content_editor/extensions/audio.js
0 → 100644
View file @
933fe8e1
import
Playable
from
'
./playable
'
;
export
default
Playable
.
extend
({
defaultOptions
:
{
...
Playable
.
options
,
mediaType
:
'
audio
'
,
},
});
app/assets/javascripts/content_editor/extensions/playable.js
0 → 100644
View file @
933fe8e1
import
{
Node
}
from
'
@tiptap/core
'
;
const
queryPlayableElement
=
(
element
,
mediaType
)
=>
element
.
querySelector
(
mediaType
);
export
default
Node
.
create
({
name
:
'
playable
'
,
group
:
'
inline
'
,
inline
:
true
,
draggable
:
true
,
addAttributes
()
{
return
{
src
:
{
default
:
null
,
parseHTML
:
(
element
)
=>
{
const
playable
=
queryPlayableElement
(
element
,
this
.
options
.
mediaType
);
return
{
src
:
playable
.
src
,
};
},
},
canonicalSrc
:
{
default
:
null
,
parseHTML
:
(
element
)
=>
{
const
playable
=
queryPlayableElement
(
element
,
this
.
options
.
mediaType
);
return
{
canonicalSrc
:
playable
.
dataset
.
canonicalSrc
,
};
},
},
alt
:
{
default
:
null
,
parseHTML
:
(
element
)
=>
{
const
playable
=
queryPlayableElement
(
element
,
this
.
options
.
mediaType
);
return
{
alt
:
playable
.
dataset
.
title
,
};
},
},
};
},
parseHTML
()
{
return
[
{
tag
:
'
.media-container
'
,
},
];
},
renderHTML
({
node
})
{
return
[
'
span
'
,
{
class
:
'
media-container
'
},
[
this
.
options
.
mediaType
,
{
src
:
node
.
attrs
.
src
,
controls
:
true
,
'
data-setup
'
:
'
{}
'
,
'
data-title
'
:
node
.
attrs
.
alt
,
...
this
.
extraElementAttrs
,
},
],
[
'
a
'
,
{
href
:
node
.
attrs
.
src
},
node
.
attrs
.
alt
],
];
},
});
app/assets/javascripts/content_editor/services/create_content_editor.js
View file @
933fe8e1
...
...
@@ -2,6 +2,7 @@ import { Editor } from '@tiptap/vue-2';
import
{
isFunction
}
from
'
lodash
'
;
import
{
PROVIDE_SERIALIZER_OR_RENDERER_ERROR
}
from
'
../constants
'
;
import
Attachment
from
'
../extensions/attachment
'
;
import
Audio
from
'
../extensions/audio
'
;
import
Blockquote
from
'
../extensions/blockquote
'
;
import
Bold
from
'
../extensions/bold
'
;
import
BulletList
from
'
../extensions/bullet_list
'
;
...
...
@@ -63,6 +64,7 @@ export const createContentEditor = ({
const
builtInContentEditorExtensions
=
[
Attachment
.
configure
({
uploadsPath
,
renderMarkdown
}),
Audio
,
Blockquote
,
Bold
,
BulletList
,
...
...
app/assets/javascripts/content_editor/services/markdown_serializer.js
View file @
933fe8e1
...
...
@@ -3,6 +3,7 @@ import {
defaultMarkdownSerializer
,
}
from
'
prosemirror-markdown/src/to_markdown
'
;
import
{
DOMParser
as
ProseMirrorDOMParser
}
from
'
prosemirror-model
'
;
import
Audio
from
'
../extensions/audio
'
;
import
Blockquote
from
'
../extensions/blockquote
'
;
import
Bold
from
'
../extensions/bold
'
;
import
BulletList
from
'
../extensions/bullet_list
'
;
...
...
@@ -40,6 +41,8 @@ import {
openTag
,
closeTag
,
renderOrderedList
,
renderImage
,
renderPlayable
,
}
from
'
./serialization_helpers
'
;
const
defaultSerializerConfig
=
{
...
...
@@ -92,6 +95,7 @@ const defaultSerializerConfig = {
},
nodes
:
{
[
Audio
.
name
]:
renderPlayable
,
[
Blockquote
.
name
]:
(
state
,
node
)
=>
{
if
(
node
.
attrs
.
multiline
)
{
state
.
write
(
'
>>>
'
);
...
...
@@ -120,12 +124,7 @@ const defaultSerializerConfig = {
[
HardBreak
.
name
]:
renderHardBreak
,
[
Heading
.
name
]:
defaultMarkdownSerializer
.
nodes
.
heading
,
[
HorizontalRule
.
name
]:
defaultMarkdownSerializer
.
nodes
.
horizontal_rule
,
[
Image
.
name
]:
(
state
,
node
)
=>
{
const
{
alt
,
canonicalSrc
,
src
,
title
}
=
node
.
attrs
;
const
quotedTitle
=
title
?
`
${
state
.
quote
(
title
)}
`
:
''
;
state
.
write
(
`![
${
state
.
esc
(
alt
||
''
)}
](
${
state
.
esc
(
canonicalSrc
||
src
)}${
quotedTitle
}
)`
);
},
[
Image
.
name
]:
renderImage
,
[
ListItem
.
name
]:
defaultMarkdownSerializer
.
nodes
.
list_item
,
[
OrderedList
.
name
]:
renderOrderedList
,
[
Paragraph
.
name
]:
defaultMarkdownSerializer
.
nodes
.
paragraph
,
...
...
app/assets/javascripts/content_editor/services/serialization_helpers.js
View file @
933fe8e1
...
...
@@ -286,3 +286,14 @@ export function renderHardBreak(state, node, parent, index) {
}
}
}
export function renderImage(state, node) {
const { alt, canonicalSrc, src, title } = node.attrs;
const quotedTitle = title ? ` ${state.quote(title)}` : '';
state.write(`![${state.esc(alt || '')}](${state.esc(canonicalSrc || src)}${quotedTitle})`);
}
export function renderPlayable(state, node) {
renderImage(state, node);
}
app/assets/stylesheets/framework/typography.scss
View file @
933fe8e1
...
...
@@ -41,6 +41,12 @@
}
}
.media-container
{
display
:
inline-flex
;
flex-direction
:
column
;
margin-bottom
:
$gl-spacing-scale-2
;
}
img
:not
(
.emoji
)
{
margin
:
0
0
8px
;
}
...
...
lib/banzai/filter/playable_link_filter.rb
View file @
933fe8e1
...
...
@@ -52,7 +52,7 @@ module Banzai
doc
.
document
.
create_element
(
media_type
,
media_element_attrs
)
end
def
download_
paragraph
(
doc
,
element
)
def
download_
link
(
doc
,
element
)
link_content
=
element
[
'title'
]
||
element
[
'alt'
]
link_element_attrs
=
{
...
...
@@ -67,19 +67,15 @@ module Banzai
link_element_attrs
[
'data-canonical-src'
]
=
element
[
'data-canonical-src'
]
end
link
=
doc
.
document
.
create_element
(
'a'
,
link_content
,
link_element_attrs
)
doc
.
document
.
create_element
(
'p'
).
tap
do
|
paragraph
|
paragraph
.
children
=
link
end
doc
.
document
.
create_element
(
'a'
,
link_content
,
link_element_attrs
)
end
def
media_node
(
doc
,
element
)
container_element_attrs
=
{
class:
"
#{
media_type
}
-container"
}
container_element_attrs
=
{
class:
"
media-container
#{
media_type
}
-container"
}
doc
.
document
.
create_element
(
"div"
,
container_element_attrs
).
tap
do
|
container
|
doc
.
document
.
create_element
(
'span'
,
container_element_attrs
).
tap
do
|
container
|
container
.
add_child
(
media_element
(
doc
,
element
))
container
.
add_child
(
download_
paragraph
(
doc
,
element
))
container
.
add_child
(
download_
link
(
doc
,
element
))
end
end
end
...
...
spec/frontend/fixtures/api_markdown.yml
View file @
933fe8e1
...
...
@@ -145,3 +145,15 @@
context
:
project_wiki
markdown
:
|-
Hi @gitlab - thank you for reporting this ~bug (#1) we hope to fix it in %1.1 as part of !1
-
name
:
audio
markdown
:
'
![Sample
Audio](https://gitlab.com/gitlab.mp3)'
-
name
:
audio_in_lists
markdown
:
|-
* ![Sample Audio](https://gitlab.com/1.mp3)
* ![Sample Audio](https://gitlab.com/2.mp3)
1. ![Sample Audio](https://gitlab.com/1.mp3)
2. ![Sample Audio](https://gitlab.com/2.mp3)
* [x] ![Sample Audio](https://gitlab.com/1.mp3)
* [x] ![Sample Audio](https://gitlab.com/2.mp3)
spec/lib/banzai/filter/audio_link_filter_spec.rb
View file @
933fe8e1
...
...
@@ -25,18 +25,14 @@ RSpec.describe Banzai::Filter::AudioLinkFilter do
it
'replaces the image tag with an audio tag'
do
container
=
filter
(
image
).
children
.
first
expect
(
container
.
name
).
to
eq
'
div
'
expect
(
container
[
'class'
]).
to
eq
'audio-container'
expect
(
container
.
name
).
to
eq
'
span
'
expect
(
container
[
'class'
]).
to
eq
'
media-container
audio-container'
audio
,
paragraph
=
container
.
children
audio
,
link
=
container
.
children
expect
(
audio
.
name
).
to
eq
'audio'
expect
(
audio
[
'src'
]).
to
eq
src
expect
(
paragraph
.
name
).
to
eq
'p'
link
=
paragraph
.
children
.
first
expect
(
link
.
name
).
to
eq
'a'
expect
(
link
[
'href'
]).
to
eq
src
expect
(
link
[
'target'
]).
to
eq
'_blank'
...
...
@@ -105,15 +101,13 @@ RSpec.describe Banzai::Filter::AudioLinkFilter do
image
=
%(<img src="#{proxy_src}" data-canonical-src="#{canonical_src}"/>)
container
=
filter
(
image
).
children
.
first
expect
(
container
[
'class'
]).
to
eq
'audio-container'
expect
(
container
[
'class'
]).
to
eq
'
media-container
audio-container'
audio
,
paragraph
=
container
.
children
audio
,
link
=
container
.
children
expect
(
audio
[
'src'
]).
to
eq
proxy_src
expect
(
audio
[
'data-canonical-src'
]).
to
eq
canonical_src
link
=
paragraph
.
children
.
first
expect
(
link
[
'href'
]).
to
eq
proxy_src
end
end
...
...
spec/lib/banzai/filter/video_link_filter_spec.rb
View file @
933fe8e1
...
...
@@ -25,20 +25,16 @@ RSpec.describe Banzai::Filter::VideoLinkFilter do
it
'replaces the image tag with a video tag'
do
container
=
filter
(
image
).
children
.
first
expect
(
container
.
name
).
to
eq
'
div
'
expect
(
container
[
'class'
]).
to
eq
'video-container'
expect
(
container
.
name
).
to
eq
'
span
'
expect
(
container
[
'class'
]).
to
eq
'
media-container
video-container'
video
,
paragraph
=
container
.
children
video
,
link
=
container
.
children
expect
(
video
.
name
).
to
eq
'video'
expect
(
video
[
'src'
]).
to
eq
src
expect
(
video
[
'width'
]).
to
eq
"400"
expect
(
video
[
'preload'
]).
to
eq
'metadata'
expect
(
paragraph
.
name
).
to
eq
'p'
link
=
paragraph
.
children
.
first
expect
(
link
.
name
).
to
eq
'a'
expect
(
link
[
'href'
]).
to
eq
src
expect
(
link
[
'target'
]).
to
eq
'_blank'
...
...
@@ -107,15 +103,13 @@ RSpec.describe Banzai::Filter::VideoLinkFilter do
image
=
%(<img src="#{proxy_src}" data-canonical-src="#{canonical_src}"/>)
container
=
filter
(
image
).
children
.
first
expect
(
container
[
'class'
]).
to
eq
'video-container'
expect
(
container
[
'class'
]).
to
eq
'
media-container
video-container'
video
,
paragraph
=
container
.
children
video
,
link
=
container
.
children
expect
(
video
[
'src'
]).
to
eq
proxy_src
expect
(
video
[
'data-canonical-src'
]).
to
eq
canonical_src
link
=
paragraph
.
children
.
first
expect
(
link
[
'href'
]).
to
eq
proxy_src
end
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment