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
6543c30e
Commit
6543c30e
authored
Nov 09, 2020
by
Jacques Erasmus
Committed by
Simon Knox
Nov 09, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Resolve image relative paths
Added the ability to resolve image relative paths
parent
8a3e6935
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
151 additions
and
31 deletions
+151
-31
app/assets/javascripts/static_site_editor/components/edit_area.vue
...s/javascripts/static_site_editor/components/edit_area.vue
+9
-1
app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql
...static_site_editor/graphql/queries/app_data.query.graphql
+2
-0
app/assets/javascripts/static_site_editor/graphql/typedefs.graphql
...s/javascripts/static_site_editor/graphql/typedefs.graphql
+2
-0
app/assets/javascripts/static_site_editor/index.js
app/assets/javascripts/static_site_editor/index.js
+3
-0
app/assets/javascripts/static_site_editor/pages/home.vue
app/assets/javascripts/static_site_editor/pages/home.vue
+2
-0
app/assets/javascripts/static_site_editor/services/renderers/render_image.js
...pts/static_site_editor/services/renderers/render_image.js
+62
-11
app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js
...ontent_editor/services/build_html_to_markdown_renderer.js
+4
-0
changelogs/unreleased/218531-determine-image-relative-paths.yml
...logs/unreleased/218531-determine-image-relative-paths.yml
+5
-0
spec/frontend/static_site_editor/components/edit_area_spec.js
.../frontend/static_site_editor/components/edit_area_spec.js
+4
-0
spec/frontend/static_site_editor/mock_data.js
spec/frontend/static_site_editor/mock_data.js
+9
-1
spec/frontend/static_site_editor/pages/home_spec.js
spec/frontend/static_site_editor/pages/home_spec.js
+4
-0
spec/frontend/static_site_editor/services/renderers/render_image_spec.js
...tatic_site_editor/services/renderers/render_image_spec.js
+19
-18
spec/frontend/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer_spec.js
...t_editor/services/build_html_to_markdown_renderer_spec.js
+26
-0
No files found.
app/assets/javascripts/static_site_editor/components/edit_area.vue
View file @
6543c30e
...
@@ -37,6 +37,14 @@ export default {
...
@@ -37,6 +37,14 @@ export default {
required
:
false
,
required
:
false
,
default
:
''
,
default
:
''
,
},
},
branch
:
{
type
:
String
,
required
:
true
,
},
baseUrl
:
{
type
:
String
,
required
:
true
,
},
mounts
:
{
mounts
:
{
type
:
Array
,
type
:
Array
,
required
:
true
,
required
:
true
,
...
@@ -75,7 +83,7 @@ export default {
...
@@ -75,7 +83,7 @@ export default {
return
this
.
editorMode
===
EDITOR_TYPES
.
wysiwyg
;
return
this
.
editorMode
===
EDITOR_TYPES
.
wysiwyg
;
},
},
customRenderers
()
{
customRenderers
()
{
const
imageRenderer
=
renderImage
.
build
(
this
.
mounts
,
this
.
project
);
const
imageRenderer
=
renderImage
.
build
(
this
.
mounts
,
this
.
project
,
this
.
branch
,
this
.
baseUrl
);
return
{
return
{
image
:
[
imageRenderer
],
image
:
[
imageRenderer
],
};
};
...
...
app/assets/javascripts/static_site_editor/graphql/queries/app_data.query.graphql
View file @
6543c30e
...
@@ -6,6 +6,8 @@ query appData {
...
@@ -6,6 +6,8 @@ query appData {
sourcePath
sourcePath
username
username
returnUrl
returnUrl
branch
baseUrl
mounts
{
mounts
{
source
source
target
target
...
...
app/assets/javascripts/static_site_editor/graphql/typedefs.graphql
View file @
6543c30e
...
@@ -26,6 +26,8 @@ type AppData {
...
@@ -26,6 +26,8 @@ type AppData {
returnUrl
:
String
returnUrl
:
String
sourcePath
:
String
!
sourcePath
:
String
!
username
:
String
!
username
:
String
!
branch
:
String
!
baseUrl
:
String
!
mounts
:
[
Mount
]!
mounts
:
[
Mount
]!
imageUploadPath
:
String
!
imageUploadPath
:
String
!
}
}
...
...
app/assets/javascripts/static_site_editor/index.js
View file @
6543c30e
...
@@ -9,6 +9,7 @@ const initStaticSiteEditor = el => {
...
@@ -9,6 +9,7 @@ const initStaticSiteEditor = el => {
isSupportedContent
,
isSupportedContent
,
path
:
sourcePath
,
path
:
sourcePath
,
baseUrl
,
baseUrl
,
branch
,
namespace
,
namespace
,
project
,
project
,
mergeRequestsIllustrationPath
,
mergeRequestsIllustrationPath
,
...
@@ -27,6 +28,8 @@ const initStaticSiteEditor = el => {
...
@@ -27,6 +28,8 @@ const initStaticSiteEditor = el => {
hasSubmittedChanges
:
false
,
hasSubmittedChanges
:
false
,
project
:
`
${
namespace
}
/
${
project
}
`
,
project
:
`
${
namespace
}
/
${
project
}
`
,
mounts
:
JSON
.
parse
(
mounts
),
// NOTE that the object in 'mounts' is a JSON string from the data attribute, so it must be parsed into an object.
mounts
:
JSON
.
parse
(
mounts
),
// NOTE that the object in 'mounts' is a JSON string from the data attribute, so it must be parsed into an object.
branch
,
baseUrl
,
returnUrl
,
returnUrl
,
sourcePath
,
sourcePath
,
username
,
username
,
...
...
app/assets/javascripts/static_site_editor/pages/home.vue
View file @
6543c30e
...
@@ -139,6 +139,8 @@ export default {
...
@@ -139,6 +139,8 @@ export default {
:saving-changes=
"isSavingChanges"
:saving-changes=
"isSavingChanges"
:return-url=
"appData.returnUrl"
:return-url=
"appData.returnUrl"
:mounts=
"appData.mounts"
:mounts=
"appData.mounts"
:branch=
"appData.branch"
:base-url=
"appData.baseUrl"
:project=
"appData.project"
:project=
"appData.project"
:image-root=
"appData.imageUploadPath"
:image-root=
"appData.imageUploadPath"
@
submit=
"onPrepareSubmit"
@
submit=
"onPrepareSubmit"
...
...
app/assets/javascripts/static_site_editor/services/renderers/render_image.js
View file @
6543c30e
import
{
isAbsolute
,
getBaseURL
,
joinPaths
}
from
'
~/lib/utils/url_utility
'
;
const
canRender
=
({
type
})
=>
type
===
'
image
'
;
const
canRender
=
({
type
})
=>
type
===
'
image
'
;
// NOTE: the `metadata` is not used yet, but will be used in a follow-up iteration
// To be removed with the next iteration of https://gitlab.com/gitlab-org/gitlab/-/issues/218531
// eslint-disable-next-line no-unused-vars
let
metadata
;
let
metadata
;
const
render
=
(
node
,
{
skipChildren
})
=>
{
const
isRelativeToCurrentDirectory
=
basePath
=>
!
basePath
.
startsWith
(
'
/
'
);
skipChildren
();
const
extractSourceDirectory
=
url
=>
{
const
sourceDir
=
/^
(
.+
)\/([^/]
+
)
$/
.
exec
(
url
);
// Extracts the base path and fileName from an image path
return
sourceDir
||
[
null
,
null
,
url
];
// If no source directory was extracted it means only a fileName was specified (e.g. url='file.png')
};
const
parseCurrentDirectory
=
basePath
=>
{
const
baseUrl
=
decodeURIComponent
(
metadata
.
baseUrl
);
const
sourceDirectory
=
extractSourceDirectory
(
baseUrl
)[
1
];
const
currentDirectory
=
sourceDirectory
.
split
(
`/-/sse/
${
metadata
.
branch
}
`
)[
1
];
return
joinPaths
(
currentDirectory
,
basePath
);
};
// For more context around this logic, please see the following comment:
// https://gitlab.com/gitlab-org/gitlab/-/issues/241166#note_409413500
const
generateSourceDirectory
=
basePath
=>
{
let
sourceDir
=
''
;
let
defaultSourceDir
=
''
;
if
(
!
basePath
||
isRelativeToCurrentDirectory
(
basePath
))
{
return
parseCurrentDirectory
(
basePath
);
}
if
(
!
metadata
.
mounts
.
length
)
{
return
basePath
;
}
// To be removed with the next iteration of https://gitlab.com/gitlab-org/gitlab/-/issues/218531
metadata
.
mounts
.
forEach
(({
source
,
target
})
=>
{
// TODO resolve relative paths
const
hasTarget
=
target
!==
''
;
if
(
hasTarget
&&
basePath
.
includes
(
target
))
{
sourceDir
=
source
;
}
else
if
(
!
hasTarget
)
{
defaultSourceDir
=
joinPaths
(
source
,
basePath
);
}
});
return
sourceDir
||
defaultSourceDir
;
};
const
resolveFullPath
=
originalSrc
=>
{
if
(
isAbsolute
(
originalSrc
))
{
return
originalSrc
;
}
const
sourceDirectory
=
extractSourceDirectory
(
originalSrc
);
const
[,
basePath
,
fileName
]
=
sourceDirectory
;
const
sourceDir
=
generateSourceDirectory
(
basePath
);
return
joinPaths
(
getBaseURL
(),
metadata
.
project
,
'
/-/raw/
'
,
metadata
.
branch
,
sourceDir
,
fileName
);
};
const
render
=
({
destination
:
originalSrc
,
firstChild
},
{
skipChildren
})
=>
{
skipChildren
();
return
{
return
{
type
:
'
openTag
'
,
type
:
'
openTag
'
,
tagName
:
'
img
'
,
tagName
:
'
img
'
,
selfClose
:
true
,
selfClose
:
true
,
attributes
:
{
attributes
:
{
src
:
node
.
destination
,
'
data-original-src
'
:
!
isAbsolute
(
originalSrc
)
?
originalSrc
:
''
,
alt
:
node
.
firstChild
.
literal
,
src
:
resolveFullPath
(
originalSrc
),
alt
:
firstChild
.
literal
,
},
},
};
};
};
};
const
build
=
(
mounts
,
project
)
=>
{
const
build
=
(
mounts
=
[],
project
,
branch
,
baseUrl
)
=>
{
metadata
=
{
mounts
,
project
};
metadata
=
{
mounts
,
project
,
branch
,
baseUrl
};
return
{
canRender
,
render
};
return
{
canRender
,
render
};
};
};
...
...
app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js
View file @
6543c30e
...
@@ -99,6 +99,10 @@ const buildHTMLToMarkdownRender = (baseRenderer, formattingPreferences = {}) =>
...
@@ -99,6 +99,10 @@ const buildHTMLToMarkdownRender = (baseRenderer, formattingPreferences = {}) =>
?
`\n\n
${
node
.
innerText
}
\n\n`
?
`\n\n
${
node
.
innerText
}
\n\n`
:
baseRenderer
.
convert
(
node
,
subContent
);
:
baseRenderer
.
convert
(
node
,
subContent
);
},
},
IMG
(
node
)
{
const
{
originalSrc
}
=
node
.
dataset
;
return
`![
${
node
.
alt
}
](
${
originalSrc
||
node
.
src
}
)`
;
},
};
};
};
};
...
...
changelogs/unreleased/218531-determine-image-relative-paths.yml
0 → 100644
View file @
6543c30e
---
title
:
Determine image relative paths
merge_request
:
46208
author
:
type
:
added
spec/frontend/static_site_editor/components/edit_area_spec.js
View file @
6543c30e
...
@@ -17,6 +17,8 @@ import {
...
@@ -17,6 +17,8 @@ import {
returnUrl
,
returnUrl
,
mounts
,
mounts
,
project
,
project
,
branch
,
baseUrl
,
imageRoot
,
imageRoot
,
}
from
'
../mock_data
'
;
}
from
'
../mock_data
'
;
...
@@ -36,6 +38,8 @@ describe('~/static_site_editor/components/edit_area.vue', () => {
...
@@ -36,6 +38,8 @@ describe('~/static_site_editor/components/edit_area.vue', () => {
returnUrl
,
returnUrl
,
mounts
,
mounts
,
project
,
project
,
branch
,
baseUrl
,
imageRoot
,
imageRoot
,
savingChanges
,
savingChanges
,
...
propsData
,
...
propsData
,
...
...
spec/frontend/static_site_editor/mock_data.js
View file @
6543c30e
...
@@ -75,9 +75,17 @@ export const images = new Map([
...
@@ -75,9 +75,17 @@ export const images = new Map([
export
const
mounts
=
[
export
const
mounts
=
[
{
{
source
:
'
some
/source/
'
,
source
:
'
default
/source/
'
,
target
:
''
,
target
:
''
,
},
},
{
source
:
'
source/with/target
'
,
target
:
'
target
'
,
},
];
];
export
const
branch
=
'
master
'
;
export
const
baseUrl
=
'
/user1/project1/-/sse/master%2Ftest.md
'
;
export
const
imageRoot
=
'
source/images/
'
;
export
const
imageRoot
=
'
source/images/
'
;
spec/frontend/static_site_editor/pages/home_spec.js
View file @
6543c30e
...
@@ -24,6 +24,8 @@ import {
...
@@ -24,6 +24,8 @@ import {
trackingCategory
,
trackingCategory
,
images
,
images
,
mounts
,
mounts
,
branch
,
baseUrl
,
imageRoot
,
imageRoot
,
}
from
'
../mock_data
'
;
}
from
'
../mock_data
'
;
...
@@ -44,6 +46,8 @@ describe('static_site_editor/pages/home', () => {
...
@@ -44,6 +46,8 @@ describe('static_site_editor/pages/home', () => {
username
,
username
,
sourcePath
,
sourcePath
,
mounts
,
mounts
,
branch
,
baseUrl
,
imageUploadPath
:
imageRoot
,
imageUploadPath
:
imageRoot
,
};
};
const
hasSubmittedChangesMutationPayload
=
{
const
hasSubmittedChangesMutationPayload
=
{
...
...
spec/frontend/static_site_editor/services/renderers/render_image_spec.js
View file @
6543c30e
import
imageRenderer
from
'
~/static_site_editor/services/renderers/render_image
'
;
import
imageRenderer
from
'
~/static_site_editor/services/renderers/render_image
'
;
import
{
mounts
,
project
}
from
'
../../mock_data
'
;
import
{
mounts
,
project
,
branch
,
baseUrl
}
from
'
../../mock_data
'
;
describe
(
'
rich_content_editor/renderers/render_image
'
,
()
=>
{
describe
(
'
rich_content_editor/renderers/render_image
'
,
()
=>
{
let
renderer
;
let
renderer
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
renderer
=
imageRenderer
.
build
(
mounts
,
project
);
renderer
=
imageRenderer
.
build
(
mounts
,
project
,
branch
,
baseUrl
);
});
});
describe
(
'
build
'
,
()
=>
{
describe
(
'
build
'
,
()
=>
{
...
@@ -27,37 +27,38 @@ describe('rich_content_editor/renderers/render_image', () => {
...
@@ -27,37 +27,38 @@ describe('rich_content_editor/renderers/render_image', () => {
});
});
describe
(
'
render
'
,
()
=>
{
describe
(
'
render
'
,
()
=>
{
let
context
;
it
.
each
`
let
result
;
destination | isAbsolute | src
const
skipChildren
=
jest
.
fn
();
${
'
http://test.host/absolute/path/to/image.png
'
}
|
${
true
}
|
${
'
http://test.host/absolute/path/to/image.png
'
}
${
'
/relative/path/to/image.png
'
}
|
${
false
}
|
${
'
http://test.host/user1/project1/-/raw/master/default/source/relative/path/to/image.png
'
}
beforeEach
(()
=>
{
${
'
/target/image.png
'
}
|
${
false
}
|
${
'
http://test.host/user1/project1/-/raw/master/source/with/target/image.png
'
}
${
'
relative/to/current/image.png
'
}
|
${
false
}
|
${
'
http://test.host/user1/project1/-/raw/master/relative/to/current/image.png
'
}
${
'
./relative/to/current/image.png
'
}
|
${
false
}
|
${
'
http://test.host/user1/project1/-/raw/master/./relative/to/current/image.png
'
}
${
'
../relative/to/current/image.png
'
}
|
${
false
}
|
${
'
http://test.host/user1/project1/-/raw/master/../relative/to/current/image.png
'
}
`
(
'
returns an image with the correct attributes
'
,
({
destination
,
isAbsolute
,
src
})
=>
{
const
skipChildren
=
jest
.
fn
();
const
context
=
{
skipChildren
};
const
node
=
{
const
node
=
{
destination
:
'
/some/path/image.png
'
,
destination
,
firstChild
:
{
firstChild
:
{
type
:
'
img
'
,
type
:
'
img
'
,
literal
:
'
Some Image
'
,
literal
:
'
Some Image
'
,
},
},
};
};
const
result
=
renderer
.
render
(
node
,
context
);
context
=
{
skipChildren
};
result
=
renderer
.
render
(
node
,
context
);
});
it
(
'
invokes `skipChildren`
'
,
()
=>
{
expect
(
skipChildren
).
toHaveBeenCalled
();
});
it
(
'
returns an image
'
,
()
=>
{
expect
(
result
).
toEqual
({
expect
(
result
).
toEqual
({
type
:
'
openTag
'
,
type
:
'
openTag
'
,
tagName
:
'
img
'
,
tagName
:
'
img
'
,
selfClose
:
true
,
selfClose
:
true
,
attributes
:
{
attributes
:
{
src
:
'
/some/path/image.png
'
,
'
data-original-src
'
:
!
isAbsolute
?
destination
:
''
,
src
,
alt
:
'
Some Image
'
,
alt
:
'
Some Image
'
,
},
},
});
});
expect
(
skipChildren
).
toHaveBeenCalled
();
});
});
});
});
});
});
spec/frontend/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer_spec.js
View file @
6543c30e
...
@@ -189,4 +189,30 @@ describe('rich_content_editor/services/html_to_markdown_renderer', () => {
...
@@ -189,4 +189,30 @@ describe('rich_content_editor/services/html_to_markdown_renderer', () => {
expect
(
htmlToMarkdownRenderer
[
'
PRE CODE
'
](
node
,
subContent
)).
toBe
(
originalConverterResult
);
expect
(
htmlToMarkdownRenderer
[
'
PRE CODE
'
](
node
,
subContent
)).
toBe
(
originalConverterResult
);
});
});
});
});
describe
(
'
IMG
'
,
()
=>
{
const
originalSrc
=
'
path/to/image.png
'
;
const
alt
=
'
alt text
'
;
let
node
;
beforeEach
(()
=>
{
node
=
document
.
createElement
(
'
img
'
);
node
.
alt
=
alt
;
node
.
src
=
originalSrc
;
});
it
(
'
returns an image with its original src of the `original-src` attribute is preset
'
,
()
=>
{
node
.
dataset
.
originalSrc
=
originalSrc
;
node
.
src
=
'
modified/path/to/image.png
'
;
htmlToMarkdownRenderer
=
buildHTMLToMarkdownRenderer
(
baseRenderer
);
expect
(
htmlToMarkdownRenderer
.
IMG
(
node
)).
toBe
(
`![
${
alt
}
](
${
originalSrc
}
)`
);
});
it
(
'
fallback to `src` if no `original-src` is specified on the image
'
,
()
=>
{
htmlToMarkdownRenderer
=
buildHTMLToMarkdownRenderer
(
baseRenderer
);
expect
(
htmlToMarkdownRenderer
.
IMG
(
node
)).
toBe
(
`![
${
alt
}
](
${
originalSrc
}
)`
);
});
});
});
});
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