Merge pull request #22 from csoutherland/exactattach

Add ExactAttachment header
This commit is contained in:
Egor Kovetskiy 2020-07-19 17:35:58 +03:00 committed by GitHub
commit f39aeb10d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 18 deletions

View File

@ -29,6 +29,8 @@ File in extended format should follow specification
<!-- Parent: <parent 1> --> <!-- Parent: <parent 1> -->
<!-- Parent: <parent 2> --> <!-- Parent: <parent 2> -->
<!-- Title: <title> --> <!-- Title: <title> -->
<!-- ExactAttachment: <local path> -->
<!-- Attachment: <local path> -->
<page contents> <page contents>
``` ```
@ -61,6 +63,33 @@ follows include tag:
<yaml-data> --> <yaml-data> -->
``` ```
Mark also supports attachments. The standard way involves declaring an
`Attachment` along with the other items in the header, then have any links
start with an `attachment://` pseudo-URL:
```markdown
<!-- Attachment: <path> -->
<beginning of page content>
An attached link is [here](attachment://<path>)
```
The standard attachment mechanism may not work in some circumstances (e.g.
keeping content as close to identical as possible between GitHub and
Confluence), so `ExactAttachment` has been introduced:
```markdown
<!-- ExactAttachment: <path> -->
<beginning of page content>
An attached link is [here](<path>)
```
**NOTE**: Be careful with `ExactAttachment`! If your path string is a subset of
another longer string or referenced in text, you may get undesired behavior.
Mark also supports macro definitions, which are defined as regexps which will Mark also supports macro definitions, which are defined as regexps which will
be replaced with specified template: be replaced with specified template:

View File

@ -28,20 +28,22 @@ type Attachment struct {
Path string Path string
Checksum string Checksum string
Link string Link string
Replace string
} }
func ResolveAttachments( func ResolveAttachments(
api *confluence.API, api *confluence.API,
page *confluence.PageInfo, page *confluence.PageInfo,
base string, base string,
names []string, replacements map[string]string,
) ([]Attachment, error) { ) ([]Attachment, error) {
attaches := []Attachment{} attaches := []Attachment{}
for _, name := range names { for replace, name := range replacements {
attach := Attachment{ attach := Attachment{
Name: name, Name: name,
Filename: strings.ReplaceAll(name, "/", "_"), Filename: strings.ReplaceAll(name, "/", "_"),
Path: filepath.Join(base, name), Path: filepath.Join(base, name),
Replace: replace,
} }
checksum, err := getChecksum(attach.Path) checksum, err := getChecksum(attach.Path)
@ -160,18 +162,18 @@ func ResolveAttachments(
func CompileAttachmentLinks(markdown []byte, attaches []Attachment) []byte { func CompileAttachmentLinks(markdown []byte, attaches []Attachment) []byte {
links := map[string]string{} links := map[string]string{}
names := []string{} replaces := []string{}
for _, attach := range attaches { for _, attach := range attaches {
uri, err := url.ParseRequestURI(attach.Link) uri, err := url.ParseRequestURI(attach.Link)
if err != nil { if err != nil {
links[attach.Name] = strings.ReplaceAll("&", "&amp;", attach.Link) links[attach.Replace] = strings.ReplaceAll("&", "&amp;", attach.Link)
} else { } else {
links[attach.Name] = uri.Path + links[attach.Replace] = uri.Path +
"?" + url.QueryEscape(uri.Query().Encode()) "?" + url.QueryEscape(uri.Query().Encode())
} }
names = append(names, attach.Name) replaces = append(replaces, attach.Replace)
} }
// sort by length so first items will have bigger length // sort by length so first items will have bigger length
@ -179,13 +181,13 @@ func CompileAttachmentLinks(markdown []byte, attaches []Attachment) []byte {
// attachments/a.jpg // attachments/a.jpg
// attachments/a.jpg.jpg // attachments/a.jpg.jpg
// so we replace longer and then shorter // so we replace longer and then shorter
sort.SliceStable(names, func(i, j int) bool { sort.SliceStable(replaces, func(i, j int) bool {
return len(names[i]) > len(names[j]) return len(replaces[i]) > len(replaces[j])
}) })
for _, name := range names { for _, replace := range replaces {
from := `attachment://` + name from := replace
to := links[name] to := links[replace]
log.Debugf(nil, "replacing: %q -> %q", from, to) log.Debugf(nil, "replacing: %q -> %q", from, to)

View File

@ -11,11 +11,12 @@ import (
) )
const ( const (
HeaderParent = `Parent` HeaderParent = `Parent`
HeaderSpace = `Space` HeaderSpace = `Space`
HeaderTitle = `Title` HeaderTitle = `Title`
HeaderLayout = `Layout` HeaderLayout = `Layout`
HeaderAttachment = `Attachment` HeaderAttachment = `Attachment`
HeaderExactAttachment = `ExactAttachment`
) )
type Meta struct { type Meta struct {
@ -23,7 +24,7 @@ type Meta struct {
Space string Space string
Title string Title string
Layout string Layout string
Attachments []string Attachments map[string]string
} }
var ( var (
@ -64,6 +65,7 @@ func ExtractMeta(data []byte) (*Meta, []byte, error) {
if meta == nil { if meta == nil {
meta = &Meta{} meta = &Meta{}
meta.Attachments = make(map[string]string)
} }
header := strings.Title(matches[1]) header := strings.Title(matches[1])
@ -87,7 +89,10 @@ func ExtractMeta(data []byte) (*Meta, []byte, error) {
meta.Layout = strings.TrimSpace(value) meta.Layout = strings.TrimSpace(value)
case HeaderAttachment: case HeaderAttachment:
meta.Attachments = append(meta.Attachments, value) meta.Attachments["attachment://"+value] = value
case HeaderExactAttachment:
meta.Attachments[value] = value
default: default:
log.Errorf( log.Errorf(