@@ -42,6 +42,7 @@ func NewFuncMap() template.FuncMap {
4242 "HTMLFormat" : htmlutil .HTMLFormat ,
4343 "HTMLEscape" : htmlEscape ,
4444 "QueryEscape" : queryEscape ,
45+ "QueryBuild" : queryBuild ,
4546 "JSEscape" : jsEscapeSafe ,
4647 "SanitizeHTML" : SanitizeHTML ,
4748 "URLJoin" : util .URLJoin ,
@@ -293,6 +294,69 @@ func timeEstimateString(timeSec any) string {
293294 return util .TimeEstimateString (v )
294295}
295296
297+ func queryBuild (a ... any ) template.URL {
298+ var s string
299+ if len (a )% 2 == 1 {
300+ if v , ok := a [0 ].(string ); ok {
301+ if v == "" || (v [0 ] != '?' && v [0 ] != '&' ) {
302+ panic ("queryBuild: invalid argument" )
303+ }
304+ s = v
305+ } else if v , ok := a [0 ].(template.URL ); ok {
306+ s = string (v )
307+ } else {
308+ panic ("queryBuild: invalid argument" )
309+ }
310+ }
311+ for i := len (a ) % 2 ; i < len (a ); i += 2 {
312+ k , ok := a [i ].(string )
313+ if ! ok {
314+ panic ("queryBuild: invalid argument" )
315+ }
316+ var v string
317+ if va , ok := a [i + 1 ].(string ); ok {
318+ v = va
319+ } else if a [i + 1 ] != nil {
320+ v = fmt .Sprint (a [i + 1 ])
321+ }
322+ // pos1 to pos2 is the "k=v&" part, "&" is optional
323+ pos1 := strings .Index (s , "&" + k + "=" )
324+ if pos1 != - 1 {
325+ pos1 ++
326+ } else {
327+ pos1 = strings .Index (s , "?" + k + "=" )
328+ if pos1 != - 1 {
329+ pos1 ++
330+ } else if strings .HasPrefix (s , k + "=" ) {
331+ pos1 = 0
332+ }
333+ }
334+ pos2 := len (s )
335+ if pos1 == - 1 {
336+ pos1 = len (s )
337+ } else {
338+ pos2 = pos1 + 1
339+ for pos2 < len (s ) && s [pos2 - 1 ] != '&' {
340+ pos2 ++
341+ }
342+ }
343+ if v != "" {
344+ sep := ""
345+ hasPrefixSep := pos1 == 0 || (pos1 <= len (s ) && (s [pos1 - 1 ] == '?' || s [pos1 - 1 ] == '&' ))
346+ if ! hasPrefixSep {
347+ sep = "&"
348+ }
349+ s = s [:pos1 ] + sep + k + "=" + url .QueryEscape (v ) + "&" + s [pos2 :]
350+ } else {
351+ s = s [:pos1 ] + s [pos2 :]
352+ }
353+ }
354+ if s != "" && s != "&" && s [len (s )- 1 ] == '&' {
355+ s = s [:len (s )- 1 ]
356+ }
357+ return template .URL (s )
358+ }
359+
296360func panicIfDevOrTesting () {
297361 if ! setting .IsProd || setting .IsInTesting {
298362 panic ("legacy template functions are for backward compatibility only, do not use them in new code" )
0 commit comments