@@ -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,71 @@ func timeEstimateString(timeSec any) string {
293294 return util .TimeEstimateString (v )
294295}
295296
297+ type QueryString string
298+
299+ func queryBuild (a ... any ) QueryString {
300+ var s string
301+ if len (a )% 2 == 1 {
302+ if v , ok := a [0 ].(string ); ok {
303+ if v == "" || (v [0 ] != '?' && v [0 ] != '&' ) {
304+ panic ("queryBuild: invalid argument" )
305+ }
306+ s = v
307+ } else if v , ok := a [0 ].(QueryString ); ok {
308+ s = string (v )
309+ } else {
310+ panic ("queryBuild: invalid argument" )
311+ }
312+ }
313+ for i := len (a ) % 2 ; i < len (a ); i += 2 {
314+ k , ok := a [i ].(string )
315+ if ! ok {
316+ panic ("queryBuild: invalid argument" )
317+ }
318+ var v string
319+ if va , ok := a [i + 1 ].(string ); ok {
320+ v = va
321+ } else if a [i + 1 ] != nil {
322+ v = fmt .Sprint (a [i + 1 ])
323+ }
324+ // pos1 to pos2 is the "k=v&" part, "&" is optional
325+ pos1 := strings .Index (s , "&" + k + "=" )
326+ if pos1 != - 1 {
327+ pos1 ++
328+ } else {
329+ pos1 = strings .Index (s , "?" + k + "=" )
330+ if pos1 != - 1 {
331+ pos1 ++
332+ } else if strings .HasPrefix (s , k + "=" ) {
333+ pos1 = 0
334+ }
335+ }
336+ pos2 := len (s )
337+ if pos1 == - 1 {
338+ pos1 = len (s )
339+ } else {
340+ pos2 = pos1 + 1
341+ for pos2 < len (s ) && s [pos2 - 1 ] != '&' {
342+ pos2 ++
343+ }
344+ }
345+ if v != "" {
346+ sep := ""
347+ hasPrefixSep := pos1 == 0 || (pos1 <= len (s ) && (s [pos1 - 1 ] == '?' || s [pos1 - 1 ] == '&' ))
348+ if ! hasPrefixSep {
349+ sep = "&"
350+ }
351+ s = s [:pos1 ] + sep + k + "=" + url .QueryEscape (v ) + "&" + s [pos2 :]
352+ } else {
353+ s = s [:pos1 ] + s [pos2 :]
354+ }
355+ }
356+ if s != "" && s != "&" && s [len (s )- 1 ] == '&' {
357+ s = s [:len (s )- 1 ]
358+ }
359+ return QueryString (s )
360+ }
361+
296362func panicIfDevOrTesting () {
297363 if ! setting .IsProd || setting .IsInTesting {
298364 panic ("legacy template functions are for backward compatibility only, do not use them in new code" )
0 commit comments