mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
parent
83c761b71a
commit
4eb2fec67c
8 changed files with 75 additions and 40 deletions
|
@ -82,7 +82,7 @@ func executeArcheTypeAsTemplate(s *hugolib.Site, kind, targetPath, archetypeFile
|
||||||
)
|
)
|
||||||
|
|
||||||
sp := source.NewSourceSpec(s.Deps.Cfg, s.Deps.Fs)
|
sp := source.NewSourceSpec(s.Deps.Cfg, s.Deps.Fs)
|
||||||
f := sp.NewFileInfo("", targetPath, nil)
|
f := sp.NewFileInfo("", targetPath, false, nil)
|
||||||
|
|
||||||
data := ArchetypeFileData{
|
data := ArchetypeFileData{
|
||||||
Type: kind,
|
Type: kind,
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (fi *fileInfo) isContentFile() bool {
|
||||||
|
|
||||||
func newFileInfo(sp *source.SourceSpec, baseDir, filename string, fi os.FileInfo, tp bundleDirType) *fileInfo {
|
func newFileInfo(sp *source.SourceSpec, baseDir, filename string, fi os.FileInfo, tp bundleDirType) *fileInfo {
|
||||||
|
|
||||||
baseFi := sp.NewFileInfo(baseDir, filename, fi)
|
baseFi := sp.NewFileInfo(baseDir, filename, tp == bundleLeaf, fi)
|
||||||
f := &fileInfo{
|
f := &fileInfo{
|
||||||
bundleTp: tp,
|
bundleTp: tp,
|
||||||
ReadableFile: baseFi,
|
ReadableFile: baseFi,
|
||||||
|
|
|
@ -772,7 +772,7 @@ func (s *Site) newPageFromFile(fi *fileInfo) *Page {
|
||||||
Keywords: []string{}, Sitemap: Sitemap{Priority: -1},
|
Keywords: []string{}, Sitemap: Sitemap{Priority: -1},
|
||||||
params: make(map[string]interface{}),
|
params: make(map[string]interface{}),
|
||||||
translations: make(Pages, 0),
|
translations: make(Pages, 0),
|
||||||
sections: sectionsFromDir(fi.Dir()),
|
sections: sectionsFromFile(fi),
|
||||||
Site: &s.Info,
|
Site: &s.Info,
|
||||||
s: s,
|
s: s,
|
||||||
}
|
}
|
||||||
|
@ -2000,12 +2000,20 @@ func (p *Page) addLangPathPrefixIfFlagSet(outfile string, should bool) string {
|
||||||
return outfile
|
return outfile
|
||||||
}
|
}
|
||||||
|
|
||||||
func sectionsFromDir(dirname string) []string {
|
func sectionsFromFile(fi *fileInfo) []string {
|
||||||
|
dirname := fi.Dir()
|
||||||
dirname = strings.Trim(dirname, helpers.FilePathSeparator)
|
dirname = strings.Trim(dirname, helpers.FilePathSeparator)
|
||||||
if dirname == "" {
|
if dirname == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return strings.Split(dirname, helpers.FilePathSeparator)
|
parts := strings.Split(dirname, helpers.FilePathSeparator)
|
||||||
|
|
||||||
|
if fi.bundleTp == bundleLeaf && len(parts) > 0 {
|
||||||
|
// my-section/mybundle/index.md => my-section
|
||||||
|
return parts[:len(parts)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts
|
||||||
}
|
}
|
||||||
|
|
||||||
func kindFromFileInfo(fi *fileInfo) string {
|
func kindFromFileInfo(fi *fileInfo) string {
|
||||||
|
|
|
@ -143,8 +143,9 @@ F:
|
||||||
D:
|
D:
|
||||||
__bundle/en/work/base/_index.md/resources/en/work/base/_1.png
|
__bundle/en/work/base/_index.md/resources/en/work/base/_1.png
|
||||||
__bundle/en/work/base/a/b/index.md/resources/en/work/base/a/b/ab1.md
|
__bundle/en/work/base/a/b/index.md/resources/en/work/base/a/b/ab1.md
|
||||||
__bundle/en/work/base/b/index.md/resources/en/work/base/b/1.md|en/work/base/b/2.md|en/work/base/b/c/logo.png|en/work/base/b/custom-mime.bep|en/work/base/b/sunset1.jpg|en/work/base/b/sunset2.jpg
|
__bundle/en/work/base/b/my-bundle/index.md/resources/en/work/base/b/my-bundle/1.md|en/work/base/b/my-bundle/2.md|en/work/base/b/my-bundle/c/logo.png|en/work/base/b/my-bundle/custom-mime.bep|en/work/base/b/my-bundle/sunset1.jpg|en/work/base/b/my-bundle/sunset2.jpg
|
||||||
__bundle/en/work/base/c/index.md/resources/en/work/base/c/logo-은행.png
|
__bundle/en/work/base/c/bundle/index.md/resources/en/work/base/c/bundle/logo-은행.png
|
||||||
|
__bundle/en/work/base/root/index.md/resources/en/work/base/root/1.md|en/work/base/root/c/logo.png
|
||||||
C:
|
C:
|
||||||
/work/base/assets/pic1.png
|
/work/base/assets/pic1.png
|
||||||
/work/base/assets/pic2.png
|
/work/base/assets/pic2.png
|
||||||
|
|
|
@ -52,6 +52,7 @@ func TestPageBundlerSite(t *testing.T) {
|
||||||
"a": ":sections/:filename",
|
"a": ":sections/:filename",
|
||||||
"b": ":year/:slug/",
|
"b": ":year/:slug/",
|
||||||
"c": ":sections/:slug",
|
"c": ":sections/:slug",
|
||||||
|
"": ":filename/",
|
||||||
})
|
})
|
||||||
|
|
||||||
cfg.Set("outputFormats", map[string]interface{}{
|
cfg.Set("outputFormats", map[string]interface{}{
|
||||||
|
@ -74,8 +75,7 @@ func TestPageBundlerSite(t *testing.T) {
|
||||||
|
|
||||||
th := testHelper{s.Cfg, s.Fs, t}
|
th := testHelper{s.Cfg, s.Fs, t}
|
||||||
|
|
||||||
// Singles (2), Below home (1), Bundle (1)
|
assert.Len(s.RegularPages, 8)
|
||||||
assert.Len(s.RegularPages, 7)
|
|
||||||
|
|
||||||
singlePage := s.getPage(KindPage, "a/1.md")
|
singlePage := s.getPage(KindPage, "a/1.md")
|
||||||
|
|
||||||
|
@ -99,33 +99,47 @@ func TestPageBundlerSite(t *testing.T) {
|
||||||
// This should be just copied to destination.
|
// This should be just copied to destination.
|
||||||
th.assertFileContent(filepath.FromSlash("/work/public/assets/pic1.png"), "content")
|
th.assertFileContent(filepath.FromSlash("/work/public/assets/pic1.png"), "content")
|
||||||
|
|
||||||
leafBundle1 := s.getPage(KindPage, "b/index.md")
|
leafBundle1 := s.getPage(KindPage, "b/my-bundle/index.md")
|
||||||
assert.NotNil(leafBundle1)
|
assert.NotNil(leafBundle1)
|
||||||
|
assert.Equal("b", leafBundle1.Section())
|
||||||
|
assert.NotNil(s.getPage(KindSection, "b"))
|
||||||
|
|
||||||
|
// This is a root bundle and should live in the "home section"
|
||||||
|
// See https://github.com/gohugoio/hugo/issues/4332
|
||||||
|
rootBundle := s.getPage(KindPage, "root")
|
||||||
|
assert.NotNil(rootBundle)
|
||||||
|
assert.True(rootBundle.Parent().IsHome())
|
||||||
|
if ugly {
|
||||||
|
assert.Equal("/root.html", rootBundle.RelPermalink())
|
||||||
|
} else {
|
||||||
|
assert.Equal("/root/", rootBundle.RelPermalink())
|
||||||
|
}
|
||||||
|
|
||||||
leafBundle2 := s.getPage(KindPage, "a/b/index.md")
|
leafBundle2 := s.getPage(KindPage, "a/b/index.md")
|
||||||
assert.NotNil(leafBundle2)
|
assert.NotNil(leafBundle2)
|
||||||
unicodeBundle := s.getPage(KindPage, "c/index.md")
|
unicodeBundle := s.getPage(KindPage, "c/bundle/index.md")
|
||||||
assert.NotNil(unicodeBundle)
|
assert.NotNil(unicodeBundle)
|
||||||
|
|
||||||
pageResources := leafBundle1.Resources.ByType(pageResourceType)
|
pageResources := leafBundle1.Resources.ByType(pageResourceType)
|
||||||
assert.Len(pageResources, 2)
|
assert.Len(pageResources, 2)
|
||||||
firstPage := pageResources[0].(*Page)
|
firstPage := pageResources[0].(*Page)
|
||||||
secondPage := pageResources[1].(*Page)
|
secondPage := pageResources[1].(*Page)
|
||||||
assert.Equal(filepath.FromSlash("b/1.md"), firstPage.pathOrTitle(), secondPage.pathOrTitle())
|
assert.Equal(filepath.FromSlash("b/my-bundle/1.md"), firstPage.pathOrTitle(), secondPage.pathOrTitle())
|
||||||
assert.Contains(firstPage.Content, "TheContent")
|
assert.Contains(firstPage.Content, "TheContent")
|
||||||
assert.Len(leafBundle1.Resources, 6) // 2 pages 3 images 1 custom mime type
|
assert.Equal(6, len(leafBundle1.Resources))
|
||||||
|
|
||||||
assert.Equal(firstPage, pageResources.GetByPrefix("1"))
|
assert.Equal(firstPage, pageResources.GetByPrefix("1"))
|
||||||
assert.Equal(secondPage, pageResources.GetByPrefix("2"))
|
assert.Equal(secondPage, pageResources.GetByPrefix("2"))
|
||||||
assert.Nil(pageResources.GetByPrefix("doesnotexist"))
|
assert.Nil(pageResources.GetByPrefix("doesnotexist"))
|
||||||
|
|
||||||
imageResources := leafBundle1.Resources.ByType("image")
|
imageResources := leafBundle1.Resources.ByType("image")
|
||||||
assert.Len(imageResources, 3)
|
assert.Equal(3, len(imageResources))
|
||||||
image := imageResources[0]
|
image := imageResources[0]
|
||||||
|
|
||||||
altFormat := leafBundle1.OutputFormats().Get("CUSTOMO")
|
altFormat := leafBundle1.OutputFormats().Get("CUSTOMO")
|
||||||
assert.NotNil(altFormat)
|
assert.NotNil(altFormat)
|
||||||
|
|
||||||
assert.Equal(filepath.FromSlash("/work/base/b/c/logo.png"), image.(resource.Source).AbsSourceFilename())
|
assert.Equal(filepath.FromSlash("/work/base/b/my-bundle/c/logo.png"), image.(resource.Source).AbsSourceFilename())
|
||||||
assert.Equal("https://example.com/2017/pageslug/c/logo.png", image.Permalink())
|
assert.Equal("https://example.com/2017/pageslug/c/logo.png", image.Permalink())
|
||||||
th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/c/logo.png"), "content")
|
th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/c/logo.png"), "content")
|
||||||
th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/c/logo.png"), "content")
|
th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/c/logo.png"), "content")
|
||||||
|
@ -161,6 +175,8 @@ func TestPageBundlerSite(t *testing.T) {
|
||||||
assert.Equal("/2017/pageslug/", leafBundle1.RelPermalink())
|
assert.Equal("/2017/pageslug/", leafBundle1.RelPermalink())
|
||||||
th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/index.html"), "TheContent")
|
th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/index.html"), "TheContent")
|
||||||
th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/cindex.html"), "TheContent")
|
th.assertFileContent(filepath.FromSlash("/work/public/cpath/2017/pageslug/cindex.html"), "TheContent")
|
||||||
|
th.assertFileContent(filepath.FromSlash("/work/public/2017/pageslug/index.html"), "Single Title")
|
||||||
|
th.assertFileContent(filepath.FromSlash("/work/public/root/index.html"), "Single Title")
|
||||||
|
|
||||||
assert.Equal("/a/b/", leafBundle2.RelPermalink())
|
assert.Equal("/a/b/", leafBundle2.RelPermalink())
|
||||||
|
|
||||||
|
@ -193,8 +209,8 @@ func TestPageBundlerSiteMultilingual(t *testing.T) {
|
||||||
s := sites.Sites[0]
|
s := sites.Sites[0]
|
||||||
|
|
||||||
assert.Equal(8, len(s.RegularPages))
|
assert.Equal(8, len(s.RegularPages))
|
||||||
assert.Equal(18, len(s.Pages))
|
assert.Equal(16, len(s.Pages))
|
||||||
assert.Equal(35, len(s.AllPages))
|
assert.Equal(31, len(s.AllPages))
|
||||||
|
|
||||||
bundleWithSubPath := s.getPage(KindPage, "lb/index")
|
bundleWithSubPath := s.getPage(KindPage, "lb/index")
|
||||||
assert.NotNil(bundleWithSubPath)
|
assert.NotNil(bundleWithSubPath)
|
||||||
|
@ -269,9 +285,9 @@ func TestMultilingualDisableLanguage(t *testing.T) {
|
||||||
s := sites.Sites[0]
|
s := sites.Sites[0]
|
||||||
|
|
||||||
assert.Equal(8, len(s.RegularPages))
|
assert.Equal(8, len(s.RegularPages))
|
||||||
assert.Equal(18, len(s.Pages))
|
assert.Equal(16, len(s.Pages))
|
||||||
// No nn pages
|
// No nn pages
|
||||||
assert.Equal(18, len(s.AllPages))
|
assert.Equal(16, len(s.AllPages))
|
||||||
for _, p := range s.rawAllPages {
|
for _, p := range s.rawAllPages {
|
||||||
assert.True(p.Lang() != "nn")
|
assert.True(p.Lang() != "nn")
|
||||||
}
|
}
|
||||||
|
@ -431,7 +447,7 @@ TheContent.
|
||||||
`
|
`
|
||||||
|
|
||||||
singleLayout := `
|
singleLayout := `
|
||||||
Title: {{ .Title }}
|
Single Title: {{ .Title }}
|
||||||
Content: {{ .Content }}
|
Content: {{ .Content }}
|
||||||
{{ $sunset := .Resources.GetByPrefix "my-sunset-1" }}
|
{{ $sunset := .Resources.GetByPrefix "my-sunset-1" }}
|
||||||
{{ with $sunset }}
|
{{ with $sunset }}
|
||||||
|
@ -482,15 +498,15 @@ Short Thumb Width: {{ $thumb.Width }}
|
||||||
writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pages", "mypage.md"), pageContent)
|
writeSource(t, fs, filepath.Join(workDir, "base", "assets", "pages", "mypage.md"), pageContent)
|
||||||
|
|
||||||
// Bundle
|
// Bundle
|
||||||
writeSource(t, fs, filepath.Join(workDir, "base", "b", "index.md"), pageWithImageShortcodeAndResourceMetadataContent)
|
writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "index.md"), pageWithImageShortcodeAndResourceMetadataContent)
|
||||||
writeSource(t, fs, filepath.Join(workDir, "base", "b", "1.md"), pageContent)
|
writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "1.md"), pageContent)
|
||||||
writeSource(t, fs, filepath.Join(workDir, "base", "b", "2.md"), pageContent)
|
writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "2.md"), pageContent)
|
||||||
writeSource(t, fs, filepath.Join(workDir, "base", "b", "custom-mime.bep"), "bepsays")
|
writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "custom-mime.bep"), "bepsays")
|
||||||
writeSource(t, fs, filepath.Join(workDir, "base", "b", "c", "logo.png"), "content")
|
writeSource(t, fs, filepath.Join(workDir, "base", "b", "my-bundle", "c", "logo.png"), "content")
|
||||||
|
|
||||||
// Bundle with 은행 slug
|
// Bundle with 은행 slug
|
||||||
// See https://github.com/gohugoio/hugo/issues/4241
|
// See https://github.com/gohugoio/hugo/issues/4241
|
||||||
writeSource(t, fs, filepath.Join(workDir, "base", "c", "index.md"), `---
|
writeSource(t, fs, filepath.Join(workDir, "base", "c", "bundle", "index.md"), `---
|
||||||
title: "은행 은행"
|
title: "은행 은행"
|
||||||
slug: 은행
|
slug: 은행
|
||||||
date: 2017-10-09
|
date: 2017-10-09
|
||||||
|
@ -498,16 +514,22 @@ date: 2017-10-09
|
||||||
|
|
||||||
Content for 은행.
|
Content for 은행.
|
||||||
`)
|
`)
|
||||||
writeSource(t, fs, filepath.Join(workDir, "base", "c", "logo-은행.png"), "은행 PNG")
|
|
||||||
|
// Bundle in root
|
||||||
|
writeSource(t, fs, filepath.Join(workDir, "base", "root", "index.md"), pageWithImageShortcodeAndResourceMetadataContent)
|
||||||
|
writeSource(t, fs, filepath.Join(workDir, "base", "root", "1.md"), pageContent)
|
||||||
|
writeSource(t, fs, filepath.Join(workDir, "base", "root", "c", "logo.png"), "content")
|
||||||
|
|
||||||
|
writeSource(t, fs, filepath.Join(workDir, "base", "c", "bundle", "logo-은행.png"), "은행 PNG")
|
||||||
|
|
||||||
// Write a real image into one of the bundle above.
|
// Write a real image into one of the bundle above.
|
||||||
src, err := os.Open("testdata/sunset.jpg")
|
src, err := os.Open("testdata/sunset.jpg")
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
|
||||||
// We need 2 to test https://github.com/gohugoio/hugo/issues/4202
|
// We need 2 to test https://github.com/gohugoio/hugo/issues/4202
|
||||||
out, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "sunset1.jpg"))
|
out, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "my-bundle", "sunset1.jpg"))
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
out2, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "sunset2.jpg"))
|
out2, err := fs.Source.Create(filepath.Join(workDir, "base", "b", "my-bundle", "sunset2.jpg"))
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
|
|
||||||
_, err = io.Copy(out, src)
|
_, err = io.Copy(out, src)
|
||||||
|
|
|
@ -54,6 +54,7 @@ type File interface {
|
||||||
LogicalName() string
|
LogicalName() string
|
||||||
|
|
||||||
// Section is first directory below the content root.
|
// Section is first directory below the content root.
|
||||||
|
// For page bundles in root, the Section will be empty.
|
||||||
Section() string
|
Section() string
|
||||||
|
|
||||||
// BaseFileName is a filename without extension.
|
// BaseFileName is a filename without extension.
|
||||||
|
@ -99,6 +100,7 @@ type FileInfo struct {
|
||||||
baseName string
|
baseName string
|
||||||
translationBaseName string
|
translationBaseName string
|
||||||
section string
|
section string
|
||||||
|
isLeafBundle bool
|
||||||
|
|
||||||
uniqueID string
|
uniqueID string
|
||||||
|
|
||||||
|
@ -142,16 +144,12 @@ func (fi *FileInfo) String() string { return fi.BaseFileName() }
|
||||||
// in some cases that is slightly expensive to construct.
|
// in some cases that is slightly expensive to construct.
|
||||||
func (fi *FileInfo) init() {
|
func (fi *FileInfo) init() {
|
||||||
fi.lazyInit.Do(func() {
|
fi.lazyInit.Do(func() {
|
||||||
parts := strings.Split(fi.relDir, helpers.FilePathSeparator)
|
relDir := strings.Trim(fi.relDir, helpers.FilePathSeparator)
|
||||||
|
parts := strings.Split(relDir, helpers.FilePathSeparator)
|
||||||
|
|
||||||
var section string
|
var section string
|
||||||
if len(parts) == 1 {
|
if (!fi.isLeafBundle && len(parts) == 1) || len(parts) > 1 {
|
||||||
section = parts[0]
|
section = parts[0]
|
||||||
} else if len(parts) > 1 {
|
|
||||||
if parts[0] == "" {
|
|
||||||
section = parts[1]
|
|
||||||
} else {
|
|
||||||
section = parts[0]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fi.section = section
|
fi.section = section
|
||||||
|
@ -161,7 +159,7 @@ func (fi *FileInfo) init() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *SourceSpec) NewFileInfo(baseDir, filename string, fi os.FileInfo) *FileInfo {
|
func (sp *SourceSpec) NewFileInfo(baseDir, filename string, isLeafBundle bool, fi os.FileInfo) *FileInfo {
|
||||||
|
|
||||||
dir, name := filepath.Split(filename)
|
dir, name := filepath.Split(filename)
|
||||||
if !strings.HasSuffix(dir, helpers.FilePathSeparator) {
|
if !strings.HasSuffix(dir, helpers.FilePathSeparator) {
|
||||||
|
@ -204,6 +202,7 @@ func (sp *SourceSpec) NewFileInfo(baseDir, filename string, fi os.FileInfo) *Fil
|
||||||
name: name,
|
name: name,
|
||||||
baseName: baseName,
|
baseName: baseName,
|
||||||
translationBaseName: translationBaseName,
|
translationBaseName: translationBaseName,
|
||||||
|
isLeafBundle: isLeafBundle,
|
||||||
}
|
}
|
||||||
|
|
||||||
return f
|
return f
|
||||||
|
|
|
@ -34,10 +34,15 @@ func TestFileInfo(t *testing.T) {
|
||||||
assert.Equal(filepath.FromSlash("/a/b/page.md"), f.Filename())
|
assert.Equal(filepath.FromSlash("/a/b/page.md"), f.Filename())
|
||||||
assert.Equal(filepath.FromSlash("b/"), f.Dir())
|
assert.Equal(filepath.FromSlash("b/"), f.Dir())
|
||||||
assert.Equal(filepath.FromSlash("b/page.md"), f.Path())
|
assert.Equal(filepath.FromSlash("b/page.md"), f.Path())
|
||||||
|
assert.Equal("b", f.Section())
|
||||||
|
|
||||||
|
}},
|
||||||
|
{filepath.FromSlash("/a/"), filepath.FromSlash("/a/b/c/d/page.md"), func(f *FileInfo) {
|
||||||
|
assert.Equal("b", f.Section())
|
||||||
|
|
||||||
}},
|
}},
|
||||||
} {
|
} {
|
||||||
f := s.NewFileInfo(this.base, this.filename, nil)
|
f := s.NewFileInfo(this.base, this.filename, false, nil)
|
||||||
this.assert(f)
|
this.assert(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (f *Filesystem) add(name string, fi os.FileInfo) (err error) {
|
||||||
name = norm.NFC.String(name)
|
name = norm.NFC.String(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
file = f.SourceSpec.NewFileInfo(f.Base, name, fi)
|
file = f.SourceSpec.NewFileInfo(f.Base, name, false, fi)
|
||||||
f.files = append(f.files, file)
|
f.files = append(f.files, file)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in a new issue