Repository: caddyserver/caddy (language: go)

Issue:
include template function with arguments
The documentation for the include template function states:

      Includes the contents of another file and renders in-place.
      Optionally can pass key-value pairs as arguments to be accessed by the included file.

     {{include "path/to/file.html"}}  // no arguments
     {{include "path/to/file.html" "arg1" 2 "value 3"}}  // with arguments

How does one access these arguments? 


Discussion / hints:
You access "arg1" as ".Args 0", which wasn't what i expected when the example starts with 1.

Could you please change the example to start with "arg0", and add a link to .Args?


Fix patch:
diff --git a/modules/caddyhttp/templates/templates.go b/modules/caddyhttp/templates/templates.go
index 65359d9e9aa..4da02b580ca 100644
--- a/modules/caddyhttp/templates/templates.go
+++ b/modules/caddyhttp/templates/templates.go
@@ -46,7 +46,8 @@ func init() {
 //
 // ##### `.Args`
 //
-// A slice of arguments passed to this page/context, for example as the result of a `include`.
+// A slice of arguments passed to this page/context, for example
+// as the result of a [`include`](#include).
 //
 // ```
 // {{index .Args 0}} // first argument
@@ -103,8 +104,8 @@ func init() {
 // Reads and returns the contents of another file, and parses it
 // as a template, adding any template definitions to the template
 // stack. If there are no definitions, the filepath will be the
-// definition name. Any {{ define }} blocks will be accessible by
-// {{ template }} or {{ block }}. Imports must happen before the
+// definition name. Any `{{ define }}` blocks will be accessible by
+// `{{ template }}` or `{{ block }}`. Imports must happen before the
 // template or block action is called. Note that the contents are
 // NOT escaped, so you should only import trusted template files.
 //
@@ -125,12 +126,13 @@ func init() {
 //
 // Includes the contents of another file, rendering it in-place.
 // Optionally can pass key-value pairs as arguments to be accessed
-// by the included file. Note that the contents are NOT escaped,
-// so you should only include trusted template files.
+// by the included file. Use [`.Args N`](#args) to access the N-th
+// argument, 0-indexed. Note that the contents are NOT escaped, so
+// you should only include trusted template files.
 //
 // ```
 // {{include "path/to/file.html"}}  // no arguments
-// {{include "path/to/file.html" "arg1" 2 "value 3"}}  // with arguments
+// {{include "path/to/file.html" "arg0" 1 "value 2"}}  // with arguments
 // ```
 //
 // ##### `readFile`
@@ -145,7 +147,8 @@ func init() {
 //
 // ##### `listFiles`
 //
-// Returns a list of the files in the given directory, which is relative to the template context's file root.
+// Returns a list of the files in the given directory, which is relative
+// to the template context's file root.
 //
 // ```
 // {{listFiles "/mydir"}}
@@ -165,12 +168,21 @@ func init() {
 //
 // ##### `.RemoteIP`
 //
-// Returns the client's IP address.
+// Returns the connection's IP address.
 //
 // ```
 // {{.RemoteIP}}
 // ```
 //
+// ##### `.ClientIP`
+//
+// Returns the real client's IP address, if `trusted_proxies` was configured,
+// otherwise returns the connection's IP address.
+//
+// ```
+// {{.ClientIP}}
+// ```
+//
 // ##### `.Req`
 //
 // Accesses the current HTTP request, which has various fields, including:
@@ -186,7 +198,8 @@ func init() {
 //
 // ##### `.OriginalReq`
 //
-// Like .Req, except it accesses the original HTTP request before rewrites or other internal modifications.
+// Like [`.Req`](#req), except it accesses the original HTTP
+// request before rewrites or other internal modifications.
 //
 // ##### `.RespHeader.Add`
 //
@@ -222,11 +235,13 @@ func init() {
 //
 // ##### `splitFrontMatter`
 //
-// Splits front matter out from the body. Front matter is metadata that appears at the very beginning of a file or string. Front matter can be in YAML, TOML, or JSON formats:
+// Splits front matter out from the body. Front matter is metadata that
+// appears at the very beginning of a file or string. Front matter can
+// be in YAML, TOML, or JSON formats:
 //
 // **TOML** front matter starts and ends with `+++`:
 //
-// ```
+// ```toml
 // +++
 // template = "blog"
 // title = "Blog Homepage"
@@ -236,7 +251,7 @@ func init() {
 //
 // **YAML** is surrounded by `---`:
 //
-// ```
+// ```yaml
 // ---
 // template: blog
 // title: Blog Homepage
@@ -246,14 +261,12 @@ func init() {
 //
 // **JSON** is simply `{` and `}`:
 //
-// ```
-//
-//	{
-//		"template": "blog",
-//		"title": "Blog Homepage",
-//		"sitename": "A Caddy site"
-//	}
-//
+// ```json
+// {
+// "template": "blog",
+// "title": "Blog Homepage",
+// "sitename": "A Caddy site"
+// }
 // ```
 //
 // The resulting front matter will be made available like so:
diff --git a/modules/caddyhttp/templates/tplcontext.go b/modules/caddyhttp/templates/tplcontext.go
index a7d53148208..63e645d37f8 100644
--- a/modules/caddyhttp/templates/tplcontext.go
+++ b/modules/caddyhttp/templates/tplcontext.go
@@ -265,7 +265,7 @@ func (c TemplateContext) Cookie(name string) string {
 	return ""
 }
 
-// RemoteIP gets the IP address of the client making the request.
+// RemoteIP gets the IP address of the connection's remote IP.
 func (c TemplateContext) RemoteIP() string {
 	ip, _, err := net.SplitHostPort(c.Req.RemoteAddr)
 	if err != nil {
@@ -274,6 +274,18 @@ func (c TemplateContext) RemoteIP() string {
 	return ip
 }
 
+// ClientIP gets the IP address of the real client making the request
+// if the request is trusted (see trusted_proxies), otherwise returns
+// the connection's remote IP.
+func (c TemplateContext) ClientIP() string {
+	address := caddyhttp.GetVar(c.Req.Context(), caddyhttp.ClientIPVarKey).(string)
+	clientIP, _, err := net.SplitHostPort(address)
+	if err != nil {
+		clientIP = address // no port
+	}
+	return clientIP
+}
+
 // Host returns the hostname portion of the Host header
 // from the HTTP request.
 func (c TemplateContext) Host() (string, error) {


Test patch: