Merge b82ffc1dbb
into cc9fa062e6
This commit is contained in:
commit
774abff779
|
@ -527,10 +527,13 @@ func (repo *Repository) ComposeMetas() map[string]string {
|
||||||
"format": unit.ExternalTrackerConfig().ExternalTrackerFormat,
|
"format": unit.ExternalTrackerConfig().ExternalTrackerFormat,
|
||||||
"user": repo.MustOwner().Name,
|
"user": repo.MustOwner().Name,
|
||||||
"repo": repo.Name,
|
"repo": repo.Name,
|
||||||
|
"regexp": unit.ExternalTrackerConfig().ExternalTrackerRegexpPattern,
|
||||||
}
|
}
|
||||||
switch unit.ExternalTrackerConfig().ExternalTrackerStyle {
|
switch unit.ExternalTrackerConfig().ExternalTrackerStyle {
|
||||||
case markup.IssueNameStyleAlphanumeric:
|
case markup.IssueNameStyleAlphanumeric:
|
||||||
repo.ExternalMetas["style"] = markup.IssueNameStyleAlphanumeric
|
repo.ExternalMetas["style"] = markup.IssueNameStyleAlphanumeric
|
||||||
|
case markup.IssueNameStyleRegexp:
|
||||||
|
repo.ExternalMetas["style"] = markup.IssueNameStyleRegexp
|
||||||
default:
|
default:
|
||||||
repo.ExternalMetas["style"] = markup.IssueNameStyleNumeric
|
repo.ExternalMetas["style"] = markup.IssueNameStyleNumeric
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,9 @@ func TestRepo(t *testing.T) {
|
||||||
|
|
||||||
externalTracker.ExternalTrackerConfig().ExternalTrackerStyle = markup.IssueNameStyleNumeric
|
externalTracker.ExternalTrackerConfig().ExternalTrackerStyle = markup.IssueNameStyleNumeric
|
||||||
testSuccess(markup.IssueNameStyleNumeric)
|
testSuccess(markup.IssueNameStyleNumeric)
|
||||||
|
|
||||||
|
externalTracker.ExternalTrackerConfig().ExternalTrackerStyle = markup.IssueNameStyleRegexp
|
||||||
|
testSuccess(markup.IssueNameStyleRegexp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetRepositoryCount(t *testing.T) {
|
func TestGetRepositoryCount(t *testing.T) {
|
||||||
|
|
|
@ -54,9 +54,10 @@ func (cfg *ExternalWikiConfig) ToDB() ([]byte, error) {
|
||||||
|
|
||||||
// ExternalTrackerConfig describes external tracker config
|
// ExternalTrackerConfig describes external tracker config
|
||||||
type ExternalTrackerConfig struct {
|
type ExternalTrackerConfig struct {
|
||||||
ExternalTrackerURL string
|
ExternalTrackerURL string
|
||||||
ExternalTrackerFormat string
|
ExternalTrackerFormat string
|
||||||
ExternalTrackerStyle string
|
ExternalTrackerStyle string
|
||||||
|
ExternalTrackerRegexpPattern string
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromDB fills up a ExternalTrackerConfig from serialized format.
|
// FromDB fills up a ExternalTrackerConfig from serialized format.
|
||||||
|
|
|
@ -106,6 +106,7 @@ type RepoSettingForm struct {
|
||||||
ExternalTrackerURL string
|
ExternalTrackerURL string
|
||||||
TrackerURLFormat string
|
TrackerURLFormat string
|
||||||
TrackerIssueStyle string
|
TrackerIssueStyle string
|
||||||
|
ExternalTrackerRegexpPattern string
|
||||||
EnablePulls bool
|
EnablePulls bool
|
||||||
PullsIgnoreWhitespace bool
|
PullsIgnoreWhitespace bool
|
||||||
PullsAllowMerge bool
|
PullsAllowMerge bool
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
const (
|
const (
|
||||||
IssueNameStyleNumeric = "numeric"
|
IssueNameStyleNumeric = "numeric"
|
||||||
IssueNameStyleAlphanumeric = "alphanumeric"
|
IssueNameStyleAlphanumeric = "alphanumeric"
|
||||||
|
IssueNameStyleRegexp = "regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -543,28 +544,52 @@ func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
|
||||||
|
|
||||||
// default to numeric pattern, unless alphanumeric is requested.
|
// default to numeric pattern, unless alphanumeric is requested.
|
||||||
pattern := issueNumericPattern
|
pattern := issueNumericPattern
|
||||||
if ctx.metas["style"] == IssueNameStyleAlphanumeric {
|
switch ctx.metas["style"] {
|
||||||
|
case IssueNameStyleAlphanumeric:
|
||||||
pattern = issueAlphanumericPattern
|
pattern = issueAlphanumericPattern
|
||||||
|
case IssueNameStyleRegexp:
|
||||||
|
var err error
|
||||||
|
pattern, err = regexp.Compile(ctx.metas["regexp"])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match := pattern.FindStringSubmatchIndex(node.Data)
|
match := pattern.FindStringSubmatchIndex(node.Data)
|
||||||
if match == nil {
|
if match == nil || len(match) < 4 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id := node.Data[match[2]:match[3]]
|
|
||||||
|
var index string
|
||||||
|
var content string
|
||||||
|
var start int
|
||||||
|
var end int
|
||||||
|
switch ctx.metas["style"] {
|
||||||
|
case IssueNameStyleAlphanumeric:
|
||||||
|
content = node.Data[match[2]:match[3]]
|
||||||
|
index = content
|
||||||
|
start = match[2]
|
||||||
|
end = match[3]
|
||||||
|
case IssueNameStyleRegexp:
|
||||||
|
index = node.Data[match[2]:match[3]]
|
||||||
|
content = node.Data[match[0]:match[1]]
|
||||||
|
start = match[0]
|
||||||
|
end = match[1]
|
||||||
|
default:
|
||||||
|
content = node.Data[match[2]:match[3]]
|
||||||
|
index = content[1:]
|
||||||
|
start = match[2]
|
||||||
|
end = match[3]
|
||||||
|
}
|
||||||
|
|
||||||
var link *html.Node
|
var link *html.Node
|
||||||
if ctx.metas == nil {
|
if ctx.metas == nil {
|
||||||
link = createLink(util.URLJoin(prefix, "issues", id[1:]), id)
|
link = createLink(util.URLJoin(prefix, "issues", index), content)
|
||||||
} else {
|
} else {
|
||||||
// Support for external issue tracker
|
ctx.metas["index"] = index
|
||||||
if ctx.metas["style"] == IssueNameStyleAlphanumeric {
|
link = createLink(com.Expand(ctx.metas["format"], ctx.metas), content)
|
||||||
ctx.metas["index"] = id
|
|
||||||
} else {
|
|
||||||
ctx.metas["index"] = id[1:]
|
|
||||||
}
|
|
||||||
link = createLink(com.Expand(ctx.metas["format"], ctx.metas), id)
|
|
||||||
}
|
}
|
||||||
replaceContent(node, match[2], match[3], link)
|
replaceContent(node, start, end, link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func crossReferenceIssueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
|
func crossReferenceIssueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
|
||||||
|
|
|
@ -53,6 +53,13 @@ var alphanumericMetas = map[string]string{
|
||||||
"style": IssueNameStyleAlphanumeric,
|
"style": IssueNameStyleAlphanumeric,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var regexpMetas = map[string]string{
|
||||||
|
"format": "https://someurl.com/{user}/{repo}/{index}",
|
||||||
|
"user": "someUser",
|
||||||
|
"repo": "someRepo",
|
||||||
|
"style": IssueNameStyleRegexp,
|
||||||
|
}
|
||||||
|
|
||||||
func TestRender_IssueIndexPattern(t *testing.T) {
|
func TestRender_IssueIndexPattern(t *testing.T) {
|
||||||
// numeric: render inputs without valid mentions
|
// numeric: render inputs without valid mentions
|
||||||
test := func(s string) {
|
test := func(s string) {
|
||||||
|
@ -160,6 +167,40 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
|
||||||
test("test issue ABCDEFGHIJ-1234567890", "test issue %s", "ABCDEFGHIJ-1234567890")
|
test("test issue ABCDEFGHIJ-1234567890", "test issue %s", "ABCDEFGHIJ-1234567890")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRender_IssueIndexPattern5(t *testing.T) {
|
||||||
|
test := func(s, expectedFmt string, pattern string, ids []string, names []string) {
|
||||||
|
metas := regexpMetas
|
||||||
|
metas["regexp"] = pattern
|
||||||
|
links := make([]interface{}, len(ids))
|
||||||
|
for i, id := range ids {
|
||||||
|
links[i] = link(util.URLJoin("https://someurl.com/someUser/someRepo/", id), names[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := fmt.Sprintf(expectedFmt, links...)
|
||||||
|
testRenderIssueIndexPattern(t, s, expected, &postProcessCtx{metas: metas})
|
||||||
|
}
|
||||||
|
|
||||||
|
test("abc ISSUE-123 def", "abc %s def", "ISSUE-(\\d+)",
|
||||||
|
[]string{"123"},
|
||||||
|
[]string{"ISSUE-123"},
|
||||||
|
)
|
||||||
|
|
||||||
|
test("abc (ISSUE 123) def", "abc %s def",
|
||||||
|
"\\(ISSUE (\\d+)\\)",
|
||||||
|
[]string{"123"},
|
||||||
|
[]string{"(ISSUE 123)"},
|
||||||
|
)
|
||||||
|
|
||||||
|
test("abc (ISSUE 123) def (TASK 456) ghi", "abc %s def %s ghi", "\\((?:ISSUE|TASK) (\\d+)\\)",
|
||||||
|
[]string{"123", "456"},
|
||||||
|
[]string{"(ISSUE 123)", "(TASK 456)"},
|
||||||
|
)
|
||||||
|
|
||||||
|
metas := regexpMetas
|
||||||
|
metas["regexp"] = "no matches"
|
||||||
|
testRenderIssueIndexPattern(t, "will not match", "will not match", &postProcessCtx{metas: metas})
|
||||||
|
}
|
||||||
|
|
||||||
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *postProcessCtx) {
|
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *postProcessCtx) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
ctx = new(postProcessCtx)
|
ctx = new(postProcessCtx)
|
||||||
|
|
|
@ -974,6 +974,9 @@ settings.tracker_url_format = External Issue Tracker URL Format
|
||||||
settings.tracker_issue_style = External Issue Tracker Number Format
|
settings.tracker_issue_style = External Issue Tracker Number Format
|
||||||
settings.tracker_issue_style.numeric = Numeric
|
settings.tracker_issue_style.numeric = Numeric
|
||||||
settings.tracker_issue_style.alphanumeric = Alphanumeric
|
settings.tracker_issue_style.alphanumeric = Alphanumeric
|
||||||
|
settings.tracker_issue_style.regexp = Regular Expression
|
||||||
|
settings.tracker_issue_style.regexp_pattern = Regular Expression Pattern
|
||||||
|
settings.tracker_issue_style.regexp_pattern_desc = The first captured group will be used in place of <code>{index}</code>.
|
||||||
settings.tracker_url_format_desc = Use the placeholders <code>{user}</code>, <code>{repo}</code> and <code>{index}</code> for the username, repository name and issue index.
|
settings.tracker_url_format_desc = Use the placeholders <code>{user}</code>, <code>{repo}</code> and <code>{index}</code> for the username, repository name and issue index.
|
||||||
settings.enable_timetracker = Enable Time Tracking
|
settings.enable_timetracker = Enable Time Tracking
|
||||||
settings.allow_only_contributors_to_track_time = Let Only Contributors Track Time
|
settings.allow_only_contributors_to_track_time = Let Only Contributors Track Time
|
||||||
|
|
|
@ -501,6 +501,15 @@ function initRepository() {
|
||||||
if (typeof $(this).data('context') !== 'undefined') $($(this).data('context')).addClass('disabled');
|
if (typeof $(this).data('context') !== 'undefined') $($(this).data('context')).addClass('disabled');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$('.enable-system-pick').change(function () {
|
||||||
|
if ($(this).data('context') && $(this).data('target')) {
|
||||||
|
if ($(this).data('context') === this.value) {
|
||||||
|
$($(this).data('target')).removeClass('disabled')
|
||||||
|
} else {
|
||||||
|
$($(this).data('target')).addClass('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Labels
|
// Labels
|
||||||
|
|
|
@ -190,9 +190,10 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
Type: models.UnitTypeExternalTracker,
|
Type: models.UnitTypeExternalTracker,
|
||||||
Config: &models.ExternalTrackerConfig{
|
Config: &models.ExternalTrackerConfig{
|
||||||
ExternalTrackerURL: form.ExternalTrackerURL,
|
ExternalTrackerURL: form.ExternalTrackerURL,
|
||||||
ExternalTrackerFormat: form.TrackerURLFormat,
|
ExternalTrackerFormat: form.TrackerURLFormat,
|
||||||
ExternalTrackerStyle: form.TrackerIssueStyle,
|
ExternalTrackerStyle: form.TrackerIssueStyle,
|
||||||
|
ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -183,16 +183,27 @@
|
||||||
<div class="ui radio checkbox">
|
<div class="ui radio checkbox">
|
||||||
{{$externalTracker := (.Repository.MustGetUnit $.UnitTypeExternalTracker)}}
|
{{$externalTracker := (.Repository.MustGetUnit $.UnitTypeExternalTracker)}}
|
||||||
{{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}}
|
{{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}}
|
||||||
<input class="hidden" tabindex="0" name="tracker_issue_style" type="radio" value="numeric" {{if $externalTrackerStyle}}{{if eq $externalTrackerStyle "numeric"}}checked=""{{end}}{{end}}/>
|
<input class="hidden enable-system-pick" tabindex="0" name="tracker_issue_style" type="radio" value="numeric" data-context="regexp" data-target="#tracker_regexp_pattern_box" {{if $externalTrackerStyle}}{{if eq $externalTrackerStyle "numeric"}}checked=""{{end}}{{end}}/>
|
||||||
<label>{{.i18n.Tr "repo.settings.tracker_issue_style.numeric"}} <span class="ui light grey text">(#1234)</span></label>
|
<label>{{.i18n.Tr "repo.settings.tracker_issue_style.numeric"}} <span class="ui light grey text">(#1234)</span></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui radio checkbox">
|
<div class="ui radio checkbox">
|
||||||
<input class="hidden" tabindex="0" name="tracker_issue_style" type="radio" value="alphanumeric" {{if $externalTrackerStyle}}{{if eq $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle "alphanumeric"}}checked=""{{end}}{{end}} />
|
<input class="hidden enable-system-pick" tabindex="0" name="tracker_issue_style" type="radio" value="alphanumeric" data-context="regexp" data-target="#tracker_regexp_pattern_box" {{if $externalTrackerStyle}}{{if eq $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle "alphanumeric"}}checked=""{{end}}{{end}} />
|
||||||
<label>{{.i18n.Tr "repo.settings.tracker_issue_style.alphanumeric"}} <span class="ui light grey text">(ABC-123, DEFG-234)</span></label>
|
<label>{{.i18n.Tr "repo.settings.tracker_issue_style.alphanumeric"}} <span class="ui light grey text">(ABC-123, DEFG-234)</span></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="ui radio checkbox">
|
||||||
|
<input class="hidden enable-system-pick" tabindex="0" name="tracker_issue_style" type="radio" value="regexp" data-context="regexp" data-target="#tracker_regexp_pattern_box" {{if $externalTrackerStyle}}{{if eq $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle "regexp"}}checked=""{{end}}{{end}} />
|
||||||
|
<label>{{.i18n.Tr "repo.settings.tracker_issue_style.regexp"}} <span class="ui light grey text">((?:TASK|ISSUE) (\d+))</span></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field {{if ne $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle "regexp"}}disabled{{end}}" id="tracker_regexp_pattern_box">
|
||||||
|
<label for="external_tracker_regexp_pattern">{{.i18n.Tr "repo.settings.tracker_issue_style.regexp_pattern"}}</label>
|
||||||
|
<input id="external_tracker_regexp_pattern" name="external_tracker_regexp_pattern" value="{{(.Repository.MustGetUnit $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerRegexpPattern}}">
|
||||||
|
<p class="help">{{.i18n.Tr "repo.settings.tracker_issue_style.regexp_pattern_desc" | Str2html}}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user