This commit is contained in:
lealife
2017-06-22 13:18:16 +08:00
parent 2654b684df
commit b140cd538f
549 changed files with 185885 additions and 1 deletions

View File

@@ -0,0 +1,16 @@
package pongo2
import (
p2 "github.com/flosch/pongo2"
"github.com/revel/revel"
)
func init() {
p2.RegisterFilter("field", func(in *p2.Value, param *p2.Value) (out *p2.Value, err *p2.Error) {
if nil == in.Interface() || in.String() == "" {
return nil, &p2.Error{Sender: "field argument must is string"}
}
newField := revel.NewField(in.String(), getContext())
return p2.AsValue(newField), nil
})
}

View File

@@ -0,0 +1,184 @@
package pongo2
import (
"io"
"strings"
p2 "github.com/flosch/pongo2"
"github.com/revel/revel"
"github.com/tylerb/gls"
)
// Adapter for HAML Templates.
type PongoTemplate struct {
template *p2.Template
engine *PongoEngine
*revel.TemplateView
}
type Pongo2BaseTag struct {
field string
}
func (node *Pongo2BaseTag) GetField(ctx *p2.ExecutionContext) (value interface{}, found bool) {
value, found = ctx.Public[node.field]
if !found {
value, found = ctx.Private[node.field]
}
if found {
if wrapped, ok := value.(*p2.Value); ok {
value = wrapped.Interface()
}
}
return
}
type INodeImplied struct {
Exec func(*p2.ExecutionContext, p2.TemplateWriter) *p2.Error
}
func (i *INodeImplied) Execute(ctx *p2.ExecutionContext, w p2.TemplateWriter) *p2.Error {
return i.Exec(ctx, w)
}
func (tmpl PongoTemplate) Name() string {
return tmpl.TemplateName
}
func getContext() map[string]interface{} {
return gls.Get("data").(map[string]interface{})
}
// return a 'revel.Template' from HAML's template.
func (tmpl PongoTemplate) Render(wr io.Writer, arg interface{}) (err error) {
gls.With(gls.Values(map[interface{}]interface{}{"data": arg}), func() {
err = tmpl.template.ExecuteWriter(p2.Context(arg.(map[string]interface{})), wr)
if nil != err {
if e, ok := err.(*p2.Error); ok {
rerr := &revel.Error{
Title: "Template Execution Error",
Path: tmpl.TemplateName,
Description: e.Error(),
Line: e.Line,
}
if revel.DevMode {
rerr.SourceLines = tmpl.Content()
}
err = rerr
}
}
})
return err
}
// There is only a single instance of the PongoEngine initialized
type PongoEngine struct {
loader *revel.TemplateLoader
templateSetBybasePath map[string]*p2.TemplateSet
templates map[string]*PongoTemplate
CaseInsensitiveMode bool
}
func (i *PongoEngine) ConvertPath(path string) string {
if i.CaseInsensitiveMode {
return strings.ToLower(path)
}
return path
}
func (i *PongoEngine) Handles(templateView *revel.TemplateView) bool {
return revel.EngineHandles(i, templateView)
}
func (engine *PongoEngine) ParseAndAdd(baseTemplate *revel.TemplateView) error {
templateSet := engine.templateSetBybasePath[baseTemplate.BasePath]
if nil == templateSet {
templateSet = p2.NewSet(baseTemplate.BasePath, p2.MustNewLocalFileSystemLoader(baseTemplate.BasePath))
engine.templateSetBybasePath[baseTemplate.BasePath] = templateSet
}
tpl, err := templateSet.FromBytes(baseTemplate.FileBytes)
if nil != err {
_, line, description := parsePongo2Error(err)
return &revel.Error{
Title: "Template Compilation Error",
Path: baseTemplate.FilePath,
Description: description,
Line: line,
SourceLines: strings.Split(string(baseTemplate.FileBytes), "\n"),
}
}
baseTemplate.TemplateName = engine.ConvertPath(baseTemplate.TemplateName)
engine.templates[baseTemplate.TemplateName] = &PongoTemplate{
template: tpl,
engine: engine,
TemplateView: baseTemplate}
return nil
}
func (engine *PongoEngine) Name() string {
return "pongo2"
}
func parsePongo2Error(err error) (templateName string, line int, description string) {
pongoError := err.(*p2.Error)
if nil != pongoError {
return pongoError.Filename, pongoError.Line, pongoError.Error()
}
return "Unknown error", 0, err.Error()
}
func (engine *PongoEngine) Lookup(templateName string) revel.Template {
tpl, found := engine.templates[engine.ConvertPath(templateName)]
if !found {
return nil
}
return tpl
}
func (engine *PongoEngine) Event(action int, i interface{}) {
if action == revel.TEMPLATE_REFRESH_REQUESTED {
// At this point all the templates have been passed into the
engine.templateSetBybasePath = map[string]*p2.TemplateSet{}
engine.templates = map[string]*PongoTemplate{}
engine.CaseInsensitiveMode = revel.Config.StringDefault("pongo2.template.path", "lower") != "case"
}
}
func init() {
revel.RegisterTemplateLoader("pongo2", func(loader *revel.TemplateLoader) (revel.TemplateEngine, error) {
return &PongoEngine{
loader: loader,
templateSetBybasePath: map[string]*p2.TemplateSet{},
templates: map[string]*PongoTemplate{},
}, nil
})
/*
// TODO Dynamically call all the built in functions, PR welcome
for key,templateFunction := range revel.TemplateFuncs {
p2.RegisterTag(key, func(doc *p2.Parser, start *p2.Token, arguments *p2.Parser) (p2.INodeTag, *p2.Error) {
evals := []p2.IEvaluator{}
for arguments.Remaining() > 0 {
expr, err := arguments.ParseExpression()
evals = append(evals, expr)
if err != nil {
return nil, err
}
}
return &INodeImplied{Exec: func(ctx *p2.ExecutionContext,w p2.TemplateWriter) *p2.Error {
args := make([]interface{}, len(evals))
for i, ev := range evals {
obj, err := ev.Evaluate(ctx)
if err != nil {
return err
}
args[i] = obj
}
v:= &tagURLForNode{evals}
reflect.MakeFunc ....
return v.Execute(ctx,w)
}}, nil
})
}
*/
}

View File

@@ -0,0 +1,87 @@
package pongo2
import (
p2 "github.com/flosch/pongo2"
"reflect"
)
type tagAppendNode struct {
isPublic bool
name string
objectEvaluators []p2.IEvaluator
}
func (node *tagAppendNode) Execute(ctx *p2.ExecutionContext, writer p2.TemplateWriter) *p2.Error {
var values []interface{}
var reflectValues reflect.Value
found := false
if o, found := ctx.Public[node.name]; found && nil != o {
values, _ = o.([]interface{})
if nil == values {
reflectValues = reflect.ValueOf(o)
if reflectValues.Kind() == reflect.Ptr {
reflectValues = reflectValues.Elem()
}
if reflectValues.Kind() != reflect.Slice {
return &p2.Error{Sender: "'" + node.name + "' isn't a slice."}
}
}
}
for _, ev := range node.objectEvaluators {
obj, err := ev.Evaluate(ctx)
if err != nil {
return err
}
if !found || reflectValues.IsNil() {
values = append(values, obj)
} else {
reflectValues = reflect.AppendSlice(reflectValues, reflect.ValueOf(obj))
}
}
if !found || reflectValues.IsNil() {
ctx.Public[node.name] = values
} else {
ctx.Public[node.name] = reflectValues.Interface()
}
return nil
}
// tagURLForParser implements a {% urlfor %} tag.
//
// urlfor takes one argument for the controller, as well as any number of key/value pairs for additional URL data.
// Example: {% urlfor "UserController.View" ":slug" "oal" %}
func tagAppendParser(doc *p2.Parser, start *p2.Token, arguments *p2.Parser) (p2.INodeTag, *p2.Error) {
var name string
var isPublic bool
// Parse variable name
typeToken := arguments.MatchType(p2.TokenIdentifier)
if typeToken != nil {
name = typeToken.Val
} else if sToken := arguments.MatchType(p2.TokenString); nil != sToken {
name = sToken.Val
} else {
return nil, arguments.Error("Expected an identifier or string.", nil)
}
evals := []p2.IEvaluator{}
for arguments.Remaining() > 0 {
expr, err := arguments.ParseExpression()
if err != nil {
return nil, err
}
evals = append(evals, expr)
}
return &INodeImplied{Exec: func(ctx *p2.ExecutionContext, w p2.TemplateWriter) *p2.Error {
node := &tagAppendNode{isPublic, name, evals}
return node.Execute(ctx, w)
}}, nil
}
func init() {
p2.RegisterTag("append", tagAppendParser)
}

View File

@@ -0,0 +1,70 @@
package pongo2
import (
"fmt"
"html"
p2 "github.com/flosch/pongo2"
"github.com/revel/revel"
)
type tagCheckboxNode struct {
value p2.IEvaluator
Pongo2BaseTag
}
func (node *tagCheckboxNode) Execute(ctx *p2.ExecutionContext, writer p2.TemplateWriter) *p2.Error {
fieldObj, _ := node.GetField(ctx)
if nil == fieldObj {
return ctx.Error("field '"+node.field+"' tagCheckboxNode is missing.", nil)
}
field, _ := fieldObj.(*revel.Field)
if nil == field {
return ctx.Error(fmt.Sprintf("field '"+node.field+"' isn't Field - %T.", fieldObj), nil)
}
val, err := node.value.Evaluate(ctx)
if err != nil {
return err
}
val_str := val.String()
checked := ""
if field.Flash() == val_str {
checked = " checked"
}
fmt.Fprintf(writer, `<input type="checkbox" name="%s" value="%s"%s>`,
html.EscapeString(field.Name), html.EscapeString(val_str), checked)
return nil
}
// tagURLForParser implements a {% urlfor %} tag.
//
// urlfor takes one argument for the controller, as well as any number of key/value pairs for additional URL data.
// Example: {% urlfor "UserController.View" ":slug" "oal" %}
func tagCheckboxParser(doc *p2.Parser, start *p2.Token, arguments *p2.Parser) (p2.INodeTag, *p2.Error) {
var field string
typeToken := arguments.MatchType(p2.TokenIdentifier)
if typeToken != nil {
field = typeToken.Val
} else if sToken := arguments.MatchType(p2.TokenString); nil != sToken {
field = sToken.Val
} else {
return nil, arguments.Error("Expected an identifier or string.", nil)
}
expr, err := arguments.ParseExpression()
if err != nil {
return nil, err
}
return &INodeImplied{Exec: func(ctx *p2.ExecutionContext, w p2.TemplateWriter) *p2.Error {
v := tagCheckboxNode{Pongo2BaseTag: Pongo2BaseTag{field: field},
value: expr}
return v.Execute(ctx, w)
}}, nil
}
func init() {
p2.RegisterTag("checkbox", tagCheckboxParser)
}

View File

@@ -0,0 +1,79 @@
package pongo2
import (
"fmt"
"html"
p2 "github.com/flosch/pongo2"
"github.com/revel/revel"
)
type tagOptionNode struct {
value p2.IEvaluator
label string
Pongo2BaseTag
}
func (node *tagOptionNode) Execute(ctx *p2.ExecutionContext, writer p2.TemplateWriter) *p2.Error {
fieldObj, _ := node.GetField(ctx)
if nil == fieldObj {
return ctx.Error("field '"+node.field+"' tagOptionNode is missing.", nil)
}
field, _ := fieldObj.(*revel.Field)
if nil == field {
return ctx.Error(fmt.Sprintf("field '"+node.field+"' isn't Field - %T.", fieldObj), nil)
}
val, err := node.value.Evaluate(ctx)
if err != nil {
return err
}
val_str := val.String()
selected := ""
if field.Flash() == val_str || (field.Flash() == "" && field.Value() == val_str) {
selected = " selected"
}
fmt.Fprintf(writer, `<option value="%s"%s>%s</option>`,
html.EscapeString(val_str), selected, html.EscapeString(node.label))
return nil
}
// tagURLForParser implements a {% urlfor %} tag.
//
// urlfor takes one argument for the controller, as well as any number of key/value pairs for additional URL data.
// Example: {% urlfor "UserController.View" ":slug" "oal" %}
func tagOptionParser(doc *p2.Parser, start *p2.Token, arguments *p2.Parser) (p2.INodeTag, *p2.Error) {
var field string
typeToken := arguments.MatchType(p2.TokenIdentifier)
if typeToken != nil {
field = typeToken.Val
} else if sToken := arguments.MatchType(p2.TokenString); nil != sToken {
field = sToken.Val
} else {
return nil, arguments.Error("Expected an identifier or string.", nil)
}
expr, err := arguments.ParseExpression()
if err != nil {
return nil, err
}
var v *tagOptionNode
if sToken := arguments.MatchType(p2.TokenString); nil != sToken {
v = &tagOptionNode{Pongo2BaseTag: Pongo2BaseTag{field: field},
value: expr,
label: sToken.Val}
} else {
return nil, arguments.Error("Expected an string.", nil)
}
return &INodeImplied{Exec: func(ctx *p2.ExecutionContext, w p2.TemplateWriter) *p2.Error {
return v.Execute(ctx, w)
}}, nil
}
func init() {
p2.RegisterTag("option", tagOptionParser)
}

View File

@@ -0,0 +1,69 @@
package pongo2
import (
"fmt"
"html"
p2 "github.com/flosch/pongo2"
"github.com/revel/revel"
)
type tagRadioNode struct {
value p2.IEvaluator
Pongo2BaseTag
}
func (node *tagRadioNode) Execute(ctx *p2.ExecutionContext, writer p2.TemplateWriter) *p2.Error {
fieldObj, _ := node.GetField(ctx)
if nil == fieldObj {
return ctx.Error("field '"+node.field+"' tagRadioNode is missing.", nil)
}
field, _ := fieldObj.(*revel.Field)
if nil == field {
return ctx.Error(fmt.Sprintf("field '"+node.field+"' isn't Field - %T.", fieldObj), nil)
}
val, err := node.value.Evaluate(ctx)
if err != nil {
return err
}
val_str := val.String()
checked := ""
if field.Flash() == val_str {
checked = " checked"
}
fmt.Fprintf(writer, `<input type="radio" name="%s" value="%s"%s>`,
html.EscapeString(field.Name), html.EscapeString(val_str), checked)
return nil
}
// tagURLForParser implements a {% urlfor %} tag.
//
// urlfor takes one argument for the controller, as well as any number of key/value pairs for additional URL data.
// Example: {% urlfor "UserController.View" ":slug" "oal" %}
func tagRadioParser(doc *p2.Parser, start *p2.Token, arguments *p2.Parser) (p2.INodeTag, *p2.Error) {
var field string
typeToken := arguments.MatchType(p2.TokenIdentifier)
if typeToken != nil {
field = typeToken.Val
} else if sToken := arguments.MatchType(p2.TokenString); nil != sToken {
field = sToken.Val
} else {
return nil, arguments.Error("Expected an identifier or string.", nil)
}
expr, err := arguments.ParseExpression()
if err != nil {
return nil, err
}
return &INodeImplied{Exec: func(ctx *p2.ExecutionContext, w p2.TemplateWriter) *p2.Error {
v := &tagRadioNode{Pongo2BaseTag: Pongo2BaseTag{field: field},
value: expr}
return v.Execute(ctx, w)
}}, nil
}
func init() {
p2.RegisterTag("radio", tagRadioParser)
}

View File

@@ -0,0 +1,64 @@
package pongo2
import (
"github.com/revel/revel"
p2 "github.com/flosch/pongo2"
)
type tagURLForNode struct {
objectEvaluators []p2.IEvaluator
}
func (node *tagURLForNode) Execute(ctx *p2.ExecutionContext, writer p2.TemplateWriter) *p2.Error {
args := make([]string, len(node.objectEvaluators))
for i, ev := range node.objectEvaluators {
obj, err := ev.Evaluate(ctx)
if err != nil {
return err
}
args[i] = obj.String()
}
params := make([]interface{}, len(args))
params[0] = args[0]
for i := range params[1:] {
params[i+1] = args[i+1]
}
url, err := revel.ReverseURL(params...)
if nil != err {
return ctx.Error(err.Error(), nil)
}
writer.WriteString(string(url))
return nil
}
// tagURLForParser implements a {% urlfor %} tag.
//
// urlfor takes one argument for the controller, as well as any number of key/value pairs for additional URL data.
// Example: {% urlfor "UserController.View" ":slug" "oal" %}
func tagURLForParser(doc *p2.Parser, start *p2.Token, arguments *p2.Parser) (p2.INodeTag, *p2.Error) {
evals := []p2.IEvaluator{}
for arguments.Remaining() > 0 {
expr, err := arguments.ParseExpression()
evals = append(evals, expr)
if err != nil {
return nil, err
}
}
if len(evals) <= 0 {
return nil, arguments.Error("URL takes one argument for the controller and any number of optional value.", nil)
}
return &INodeImplied{Exec: func(ctx *p2.ExecutionContext, w p2.TemplateWriter) *p2.Error {
v := &tagURLForNode{evals}
return v.Execute(ctx, w)
}}, nil
}
func init() {
p2.RegisterTag("url", tagURLForParser)
}

View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Forbidden</title>
</head>
<body>
<h1>
{{Error.Title}}
</h1>
<p>
{{Error.Description}}
</p>
{{end}}
</body>
</html>

View File

@@ -0,0 +1,4 @@
{
"title": "{{Error.Title|escapejs}}",
"description": "{{Error.Description|escapejs}}"
}

View File

@@ -0,0 +1,3 @@
{{Error.Title}}
{{Error.Description}}

View File

@@ -0,0 +1 @@
<forbidden>{{Error.Description}}</forbidden>

View File

@@ -0,0 +1,61 @@
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, Sans;
background: #EEEEEE;
}
.block {
padding: 20px;
border-bottom: 1px solid #aaa;
}
#header h1 {
font-weight: normal;
font-size: 28px;
margin: 0;
}
#more {
color: #666;
font-size: 80%;
border: none;
}
#header {
background: #FFFFCC;
}
#header p {
color: #333;
}
#routes {
background: #f6f6f6;
}
#routes h2 {
font-weight: normal;
font-size: 18px;
margin: 0 0 10px 0;
}
#routes ol {
}
#routes li {
font-size: 14px;
font-family: monospace;
color: #333;
}
</style>
<div id="header" class="block">
<h1>
{{Error.Title}}
</h1>
<p>
{{Error.Description}}
</p>
</div>
<div id="routes" class="block">
<h2>These routes have been tried, in this order :</h2>
<ol>
{% for route in Router.Routes %}
<li>{{route.Method}} {{route.Path}}{{route.Action}}</li>
{% endfor %}
</ol>
</div>

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Not found</title>
</head>
<body>
{% if DevMode %}
{% include "errors/404-dev.html" %}
{% else %}
<h1>
{{ Error.Title }}
</h1>
<p>
{{ Error.Description}}
</p>
{% endif %}
</body>
</html>

View File

@@ -0,0 +1,4 @@
{
"title": "{{Error.Title|escapejs}}",
"description": "{{Error.Description|escapejs}}"
}

View File

@@ -0,0 +1,3 @@
{{Error.Title}}
{{Error.Description}}

View File

@@ -0,0 +1 @@
<notfound>{{Error.Description}}</notfound>

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Method not allowed</title>
</head>
<body>
<h1>
{{Error.Title}}
</h1>
<p>
{{Error.Description}}
</p>
</body>
</html>

View File

@@ -0,0 +1,4 @@
{
"title": "{{Error.Title|escapejs}}",
"description": "{{Error.Description|escapejs}}"
}

View File

@@ -0,0 +1,3 @@
{{Error.Title}}
{{Error.Description}}

View File

@@ -0,0 +1 @@
<method-not-allowed>{{Error.Description}}</method-not-allowed>

View File

@@ -0,0 +1,131 @@
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, Sans;
background: #EEEEEE;
}
.block {
padding: 20px;
border-bottom: 1px solid #aaa;
}
#header h1 {
font-weight: normal;
font-size: 28px;
margin: 0;
}
#more {
color: #666;
font-size: 80%;
border: none;
}
#header {
background: #fcd2da;
}
#header p {
color: #333;
}
#source {
background: #f6f6f6;
}
#source h2 {
font-weight: normal;
font-size: 18px;
margin: 0 0 10px 0;
}
#source .lineNumber {
float: left;
display: block;
width: 40px;
text-align: right;
margin-right: 10px;
font-size: 14px;
font-family: monospace;
background: #333;
color: #fff;
}
#source .line {
clear: both;
color: #333;
margin-bottom: 1px;
}
#source pre {
font-size: 14px;
margin: 0;
overflow-x: hidden;
}
#source .error {
color: #c00 !important;
}
#source .error .lineNumber {
background: #c00;
}
#source a {
text-decoration: none;
}
#source a:hover * {
cursor: pointer !important;
}
#source a:hover pre {
background: #FAFFCF !important;
}
#source em {
font-style: normal;
text-decoration: underline;
font-weight: bold;
}
#source strong {
font-style: normal;
font-weight: bold;
}
#stack {
background: #eee;
padding:0 1em 1em;
}
#stack h3 {
font-weight: normal;
}
#stack code {
font-family:monospace;
white-space: pre;
}
</style>
<div id="header" class="block">
<h1>{{Error.Title}}</h1>
<p>
{% if Error.SourceType %}
The {{Error.SourceType}} <strong>{{Error.Path}}</strong> does not compile: <strong>{{Error.Description}}</strong>
{% else %}
{{Error.Description}}
{% endif %}
</p>
</div>
{% if Error.Path %}
<div id="source" class="block">
<h2>In {{Error.Path}}
{% if Error.Line %}
(around {% if Error.Line %}line {{Error.Line}}{% endif %}{% if Error.Column %} column {{Error.Column}}{% endif %})
{% endif %}
</h2>
{% for src in Error.ContextSource %}
<div class="line{% if src.IsError %} error{% endif %}">
<span class="lineNumber">{{src.Line}}:</span>
<pre>{{src.Source}}</pre>
</div>
{% endfor %}
</div>
{% endif %}
{% if Error.Stack %}
<div id="stack">
<h3>Call Stack</h3>
<code>{{Error.Stack}}</code>
</div>
{% endif %}
{% if Error.MetaError %}
<div id="source" class="block">
<h2>Additionally, an error occurred while handling this error.</h2>
<div class="line error">
{{Error.MetaError}}
</div>
</div>
{% endif %}

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Application error</title>
</head>
<body>
{% if DevMode %}
{% include "errors/500-dev.html" %}
{% else %}
<h1>Oops, an error occured</h1>
<p>
This exception has been logged.
</p>
{% endif %}
</body>
</html>

View File

@@ -0,0 +1,4 @@
{
"title": "{{Error.Title|escapejs}}",
"description": "{{Error.Description|escapejs}}"
}

View File

@@ -0,0 +1,13 @@
{{Error.Title}}
{{Error.Description}}
{% if RunMode == "dev" %}
{% if Error.Path %}
----------
In {{Error.Path}} {% if Error.Line %}(around line {{Error.Line}}){% endif %}
{% for src in Error.ContextSource %}
{% if src.IsError %}>{% else %} {% endif %} {{src.Line}}: {{src.Source}}{% endfor %}
{% endif %}
{% endif %}

View File

@@ -0,0 +1,4 @@
<error>
<title>{{Error.Title}}</title>
<description>{{Error.Description}}</description>
</error>