35 | {{if .flash.error}}
36 |
37 | {{.flash.error}}
38 |
39 | {{end}}
40 | {{if .flash.success}}
41 |
42 | {{.flash.success}}
43 |
44 | {{end}}
45 |
46 |
--------------------------------------------------------------------------------
/samples/booking/app/views/hotels/confirmbooking.html:
--------------------------------------------------------------------------------
1 | {{template "header.html" .}}
2 |
3 |
Confirm hotel booking
4 |
5 |
57 |
58 | {{template "footer.html" .}}
59 |
--------------------------------------------------------------------------------
/samples/booking/app/views/hotels/index.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Search"}}
2 | {{template "header.html" .}}
3 |
4 |
Search Hotels
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Maximum results:
13 |
14 | 5
15 | 10
16 | 20
17 |
18 |
19 |
20 |
21 |
22 |
23 |
71 |
72 |
Current Hotel Bookings
73 |
74 | {{if not .bookings}}
75 |
76 | No Bookings Found
77 |
78 | {{else}}
79 |
80 |
81 |
82 | Name
83 | Address
84 | City, State
85 | Check in
86 | Check out
87 | Confirmation number
88 | Action
89 |
90 |
91 |
92 | {{range .bookings}}
93 |
94 | {{.Hotel.Name}}
95 | {{.Hotel.Address}}
96 | {{.Hotel.City}}, {{.Hotel.State}}, {{.Hotel.Country}}
97 | {{.CheckInDate.Format "2006-01-02"}}
98 | {{.CheckOutDate.Format "2006-01-02"}}
99 | {{.BookingId}}
100 |
101 |
104 |
105 |
106 | {{end}}
107 |
108 |
109 | {{end}}
110 |
111 | {{template "footer.html" .}}
112 |
--------------------------------------------------------------------------------
/samples/booking/app/views/hotels/list.html:
--------------------------------------------------------------------------------
1 | {{if .hotels}}
2 |
3 |
4 |
5 | Name
6 | Address
7 | City, State
8 | Zip
9 | Action
10 |
11 |
12 |
13 | {{range .hotels}}
14 |
15 | {{.Name}}
16 | {{.Address}}
17 | {{.City}}, {{.State}}, {{.Country}}
18 | {{.Zip}}
19 |
20 | View Hotel
21 |
22 |
23 | {{end}}
24 |
25 |
26 |
27 | More results
28 |
29 |
30 | {{else}}
31 |
32 | No more results
33 |
34 | {{end}}
35 |
--------------------------------------------------------------------------------
/samples/booking/app/views/hotels/settings.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Settings"}}
2 | {{template "header.html" .}}
3 |
4 |
Change your password
5 |
6 |
26 |
27 | {{template "footer.html" .}}
28 |
--------------------------------------------------------------------------------
/samples/booking/app/views/hotels/show.html:
--------------------------------------------------------------------------------
1 | {{template "header.html" .}}
2 |
3 |
View hotel
4 |
5 | {{with .hotel}}
6 |
35 | {{end}}
36 |
37 | {{template "footer.html" .}}
38 |
--------------------------------------------------------------------------------
/samples/booking/conf/app.conf:
--------------------------------------------------------------------------------
1 | # Application
2 | app.name=Booking example
3 | app.secret=secret
4 |
5 | # Server
6 | http.addr=
7 | http.port=9000
8 | http.ssl=false
9 | http.sslcert=
10 | http.sslkey=
11 |
12 | # Logging
13 | log.trace.output = stderr
14 | log.info.output = stderr
15 | log.warn.output = stderr
16 | log.error.output = stderr
17 |
18 | log.trace.prefix = "TRACE "
19 | log.info.prefix = "INFO "
20 | log.warn.prefix = "WARN "
21 | log.error.prefix = "ERROR "
22 |
23 | db.import = github.com/mattn/go-sqlite3
24 | db.driver = sqlite3
25 | db.spec = :memory:
26 |
27 | build.tags=gorp
28 |
29 | module.jobs=github.com/robfig/revel/modules/jobs
30 | module.static=github.com/robfig/revel/modules/static
31 |
32 | [dev]
33 | mode.dev=true
34 | watch=true
35 | module.testrunner=github.com/robfig/revel/modules/testrunner
36 |
37 | [prod]
38 | watch=false
39 | module.testrunner=
40 |
41 | log.trace.output = off
42 | log.info.output = off
43 | log.warn.output = stderr
44 | log.error.output = stderr
45 |
--------------------------------------------------------------------------------
/samples/booking/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | module:testrunner
6 | module:jobs
7 |
8 | GET / Application.Index
9 | GET /hotels Hotels.Index
10 | GET /hotels/list Hotels.List
11 | GET /hotels/:id Hotels.Show
12 | GET /hotels/:id/booking Hotels.Book
13 | POST /hotels/:id/booking Hotels.ConfirmBooking
14 | POST /bookings/:id/cancel Hotels.CancelBooking
15 | GET /register Application.Register
16 | POST /register Application.SaveUser
17 | GET /settings Hotels.Settings
18 | POST /settings Hotels.SaveSettings
19 | POST /login Application.Login
20 | GET /logout Application.Logout
21 |
22 | # Map static resources from the /app/public folder to the /public path
23 | GET /public/*filepath Static.Serve("public")
24 | GET /favicon.ico Static.Serve("public/img","favicon.png")
25 |
26 | # Catch all
27 | * /:controller/:action :controller.:action
28 |
--------------------------------------------------------------------------------
/samples/booking/public/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/img/favicon.png
--------------------------------------------------------------------------------
/samples/booking/public/img/hotel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/img/hotel.jpg
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-bg_flat_10_000000_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-bg_flat_10_000000_40x100.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-icons_222222_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-icons_222222_256x240.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-icons_228ef1_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-icons_228ef1_256x240.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-icons_ef8c08_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-icons_ef8c08_256x240.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-icons_ffd27a_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-icons_ffd27a_256x240.png
--------------------------------------------------------------------------------
/samples/booking/public/ui-lightness/images/ui-icons_ffffff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/booking/public/ui-lightness/images/ui-icons_ffffff_256x240.png
--------------------------------------------------------------------------------
/samples/booking/tests/apptest.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type ApplicationTest struct {
6 | revel.TestSuite
7 | }
8 |
9 | func (t *ApplicationTest) Before() {
10 | println("Set up")
11 | }
12 |
13 | func (t *ApplicationTest) TestThatIndexPageWorks() {
14 | t.Get("/")
15 | t.AssertOk()
16 | t.AssertContentType("text/html; charset=utf-8")
17 | }
18 |
19 | func (t *ApplicationTest) After() {
20 | println("Tear down")
21 | }
22 |
--------------------------------------------------------------------------------
/samples/chat/app/chatroom/chatroom.go:
--------------------------------------------------------------------------------
1 | package chatroom
2 |
3 | import (
4 | "container/list"
5 | "time"
6 | )
7 |
8 | type Event struct {
9 | Type string // "join", "leave", or "message"
10 | User string
11 | Timestamp int // Unix timestamp (secs)
12 | Text string // What the user said (if Type == "message")
13 | }
14 |
15 | type Subscription struct {
16 | Archive []Event // All the events from the archive.
17 | New <-chan Event // New events coming in.
18 | }
19 |
20 | // Owner of a subscription must cancel it when they stop listening to events.
21 | func (s Subscription) Cancel() {
22 | unsubscribe <- s.New // Unsubscribe the channel.
23 | drain(s.New) // Drain it, just in case there was a pending publish.
24 | }
25 |
26 | func newEvent(typ, user, msg string) Event {
27 | return Event{typ, user, int(time.Now().Unix()), msg}
28 | }
29 |
30 | func Subscribe() Subscription {
31 | resp := make(chan Subscription)
32 | subscribe <- resp
33 | return <-resp
34 | }
35 |
36 | func Join(user string) {
37 | publish <- newEvent("join", user, "")
38 | }
39 |
40 | func Say(user, message string) {
41 | publish <- newEvent("message", user, message)
42 | }
43 |
44 | func Leave(user string) {
45 | publish <- newEvent("leave", user, "")
46 | }
47 |
48 | const archiveSize = 10
49 |
50 | var (
51 | // Send a channel here to get room events back. It will send the entire
52 | // archive initially, and then new messages as they come in.
53 | subscribe = make(chan (chan<- Subscription), 10)
54 | // Send a channel here to unsubscribe.
55 | unsubscribe = make(chan (<-chan Event), 10)
56 | // Send events here to publish them.
57 | publish = make(chan Event, 10)
58 | )
59 |
60 | // This function loops forever, handling the chat room pubsub
61 | func chatroom() {
62 | archive := list.New()
63 | subscribers := list.New()
64 |
65 | for {
66 | select {
67 | case ch := <-subscribe:
68 | var events []Event
69 | for e := archive.Front(); e != nil; e = e.Next() {
70 | events = append(events, e.Value.(Event))
71 | }
72 | subscriber := make(chan Event, 10)
73 | subscribers.PushBack(subscriber)
74 | ch <- Subscription{events, subscriber}
75 |
76 | case event := <-publish:
77 | for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
78 | ch.Value.(chan Event) <- event
79 | }
80 | if archive.Len() >= archiveSize {
81 | archive.Remove(archive.Front())
82 | }
83 | archive.PushBack(event)
84 |
85 | case unsub := <-unsubscribe:
86 | for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
87 | if ch.Value.(chan Event) == unsub {
88 | subscribers.Remove(ch)
89 | break
90 | }
91 | }
92 | }
93 | }
94 | }
95 |
96 | func init() {
97 | go chatroom()
98 | }
99 |
100 | // Helpers
101 |
102 | // Drains a given channel of any messages.
103 | func drain(ch <-chan Event) {
104 | for {
105 | select {
106 | case _, ok := <-ch:
107 | if !ok {
108 | return
109 | }
110 | default:
111 | return
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/samples/chat/app/controllers/app.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/robfig/revel"
5 | )
6 |
7 | type Application struct {
8 | *revel.Controller
9 | }
10 |
11 | func (c Application) Index() revel.Result {
12 | return c.Render()
13 | }
14 |
15 | func (c Application) EnterDemo(user, demo string) revel.Result {
16 | c.Validation.Required(user)
17 | c.Validation.Required(demo)
18 |
19 | if c.Validation.HasErrors() {
20 | c.Flash.Error("Please choose a nick name and the demonstration type.")
21 | return c.Redirect(Application.Index)
22 | }
23 |
24 | switch demo {
25 | case "refresh":
26 | return c.Redirect("/refresh?user=%s", user)
27 | case "longpolling":
28 | return c.Redirect("/longpolling/room?user=%s", user)
29 | case "websocket":
30 | return c.Redirect("/websocket/room?user=%s", user)
31 | }
32 | return nil
33 | }
34 |
--------------------------------------------------------------------------------
/samples/chat/app/controllers/longpolling.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/robfig/revel"
5 | "github.com/robfig/revel/samples/chat/app/chatroom"
6 | )
7 |
8 | type LongPolling struct {
9 | *revel.Controller
10 | }
11 |
12 | func (c LongPolling) Room(user string) revel.Result {
13 | chatroom.Join(user)
14 | return c.Render(user)
15 | }
16 |
17 | func (c LongPolling) Say(user, message string) revel.Result {
18 | chatroom.Say(user, message)
19 | return nil
20 | }
21 |
22 | func (c LongPolling) WaitMessages(lastReceived int) revel.Result {
23 | subscription := chatroom.Subscribe()
24 | defer subscription.Cancel()
25 |
26 | // See if anything is new in the archive.
27 | var events []chatroom.Event
28 | for _, event := range subscription.Archive {
29 | if event.Timestamp > lastReceived {
30 | events = append(events, event)
31 | }
32 | }
33 |
34 | // If we found one, grand.
35 | if len(events) > 0 {
36 | return c.RenderJson(events)
37 | }
38 |
39 | // Else, wait for something new.
40 | event := <-subscription.New
41 | return c.RenderJson([]chatroom.Event{event})
42 | }
43 |
44 | func (c LongPolling) Leave(user string) revel.Result {
45 | chatroom.Leave(user)
46 | return c.Redirect(Application.Index)
47 | }
48 |
--------------------------------------------------------------------------------
/samples/chat/app/controllers/refresh.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/robfig/revel"
5 | "github.com/robfig/revel/samples/chat/app/chatroom"
6 | "github.com/robfig/revel/samples/chat/app/routes"
7 | )
8 |
9 | type Refresh struct {
10 | *revel.Controller
11 | }
12 |
13 | func (c Refresh) Index(user string) revel.Result {
14 | chatroom.Join(user)
15 | return c.Redirect(routes.Refresh.Room(user))
16 | }
17 |
18 | func (c Refresh) Room(user string) revel.Result {
19 | subscription := chatroom.Subscribe()
20 | defer subscription.Cancel()
21 | events := subscription.Archive
22 | for i, _ := range events {
23 | if events[i].User == user {
24 | events[i].User = "you"
25 | }
26 | }
27 | return c.Render(user, events)
28 | }
29 |
30 | func (c Refresh) Say(user, message string) revel.Result {
31 | chatroom.Say(user, message)
32 | return c.Redirect(routes.Refresh.Room(user))
33 | }
34 |
35 | func (c Refresh) Leave(user string) revel.Result {
36 | chatroom.Leave(user)
37 | return c.Redirect(Application.Index)
38 | }
39 |
--------------------------------------------------------------------------------
/samples/chat/app/controllers/websocket.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "code.google.com/p/go.net/websocket"
5 | "github.com/robfig/revel"
6 | "github.com/robfig/revel/samples/chat/app/chatroom"
7 | )
8 |
9 | type WebSocket struct {
10 | *revel.Controller
11 | }
12 |
13 | func (c WebSocket) Room(user string) revel.Result {
14 | return c.Render(user)
15 | }
16 |
17 | func (c WebSocket) RoomSocket(user string, ws *websocket.Conn) revel.Result {
18 | // Join the room.
19 | subscription := chatroom.Subscribe()
20 | defer subscription.Cancel()
21 |
22 | chatroom.Join(user)
23 | defer chatroom.Leave(user)
24 |
25 | // Send down the archive.
26 | for _, event := range subscription.Archive {
27 | if websocket.JSON.Send(ws, &event) != nil {
28 | // They disconnected
29 | return nil
30 | }
31 | }
32 |
33 | // In order to select between websocket messages and subscription events, we
34 | // need to stuff websocket events into a channel.
35 | newMessages := make(chan string)
36 | go func() {
37 | var msg string
38 | for {
39 | err := websocket.Message.Receive(ws, &msg)
40 | if err != nil {
41 | close(newMessages)
42 | return
43 | }
44 | newMessages <- msg
45 | }
46 | }()
47 |
48 | // Now listen for new events from either the websocket or the chatroom.
49 | for {
50 | select {
51 | case event := <-subscription.New:
52 | if websocket.JSON.Send(ws, &event) != nil {
53 | // They disconnected.
54 | return nil
55 | }
56 | case msg, ok := <-newMessages:
57 | // If the channel is closed, they disconnected.
58 | if !ok {
59 | return nil
60 | }
61 |
62 | // Otherwise, say something.
63 | chatroom.Say(user, msg)
64 | }
65 | }
66 | return nil
67 | }
68 |
--------------------------------------------------------------------------------
/samples/chat/app/views/Application/Index.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Sign in"}}
2 | {{template "header.html" .}}
3 |
4 |
5 |
6 |
The Chat demonstration (from Play!)
7 |
8 |
34 |
35 |
36 | {{template "footer.html" .}}
37 |
--------------------------------------------------------------------------------
/samples/chat/app/views/LongPolling/Room.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Chat room"}}
2 | {{template "header.html" .}}
3 |
4 |
Ajax, long polling — You are now chatting as {{.user}}
5 | Leave the chat room
6 |
7 |
8 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
83 | {{template "footer.html" .}}
84 |
--------------------------------------------------------------------------------
/samples/chat/app/views/Refresh/Room.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Basic Chat room"}}
2 | {{template "header.html" .}}
3 |
4 |
Ajax, using active refresh — You are now chatting as {{.user}}
5 | Leave the chat room
6 |
7 |
8 | {{range .events}}
9 | {{if eq .Type "message"}}
10 |
11 |
{{.User}}
12 |
13 | {{.Text}}
14 |
15 |
16 | {{end}}
17 | {{if eq .Type "join"}}
18 |
19 |
20 |
21 | {{.User}} joined the room
22 |
23 |
24 | {{end}}
25 | {{if eq .Type "leave"}}
26 |
27 |
28 |
29 | {{.User}} left the room
30 |
31 |
32 | {{end}}
33 | {{end}}
34 |
35 |
36 |
37 |
41 |
42 |
43 |
63 | {{template "footer.html" .}}
64 |
--------------------------------------------------------------------------------
/samples/chat/app/views/WebSocket/Room.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Chat room"}}
2 | {{template "header.html" .}}
3 |
4 |
WebSocket — You are now chatting as {{.user}}
5 | Leave the chat room
6 |
7 |
8 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
79 |
--------------------------------------------------------------------------------
/samples/chat/app/views/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/samples/chat/app/views/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{.title}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/chat/conf/app.conf:
--------------------------------------------------------------------------------
1 | app.name=chat
2 | app.secret=pJLzyoiDe17L36mytqC912j81PfTiolHm1veQK6Grn1En3YFdB5lvEHVTwFEaWvj
3 | http.addr=
4 | http.port=9000
5 |
6 | module.static=github.com/robfig/revel/modules/static
7 |
8 | [dev]
9 | mode.dev=true
10 | results.pretty=true
11 | watch=true
12 |
13 | log.trace.output = off
14 | log.info.output = stderr
15 | log.warn.output = stderr
16 | log.error.output = stderr
17 |
18 | module.testrunner=github.com/robfig/revel/modules/testrunner
19 |
20 | [prod]
21 | mode.dev=false
22 | results.pretty=false
23 | watch=false
24 |
25 | log.trace.output = off
26 | log.info.output = off
27 | log.warn.output = %(app.name)s.log
28 | log.error.output = %(app.name)s.log
29 |
--------------------------------------------------------------------------------
/samples/chat/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | module:testrunner
6 |
7 | # Login
8 | GET / Application.Index
9 | GET /demo Application.EnterDemo
10 |
11 | # Refresh demo
12 | GET /refresh Refresh.Index
13 | GET /refresh/room Refresh.Room
14 | POST /refresh/room Refresh.Say
15 | GET /refresh/room/leave Refresh.Leave
16 |
17 | # Long polling demo
18 | GET /longpolling/room LongPolling.Room
19 | GET /longpolling/room/messages LongPolling.WaitMessages
20 | POST /longpolling/room/messages LongPolling.Say
21 | GET /longpolling/room/leave LongPolling.Leave
22 |
23 | # WebSocket demo
24 | GET /websocket/room WebSocket.Room
25 | WS /websocket/room/socket WebSocket.RoomSocket
26 |
27 | # Map static resources from the /app/public folder to the /public path
28 | GET /public/*filepath Static.Serve("public")
29 |
30 |
--------------------------------------------------------------------------------
/samples/chat/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/chat/public/images/favicon.png
--------------------------------------------------------------------------------
/samples/chat/public/javascripts/jquery.scrollTo-min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * jQuery.ScrollTo - Easy element scrolling using jQuery.
3 | * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
4 | * Dual licensed under MIT and GPL.
5 | * Date: 5/25/2009
6 | * @author Ariel Flesler
7 | * @version 1.4.2
8 | *
9 | * http://flesler.blogspot.com/2007/10/jqueryscrollto.html
10 | */
11 | ;(function(d){var k=d.scrollTo=function(a,i,e){d(window).scrollTo(a,i,e)};k.defaults={axis:'xy',duration:parseFloat(d.fn.jquery)>=1.3?0:1};k.window=function(a){return d(window)._scrollable()};d.fn._scrollable=function(){return this.map(function(){var a=this,i=!a.nodeName||d.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!i)return a;var e=(a.contentWindow||a).document||a.ownerDocument||a;return d.browser.safari||e.compatMode=='BackCompat'?e.body:e.documentElement})};d.fn.scrollTo=function(n,j,b){if(typeof j=='object'){b=j;j=0}if(typeof b=='function')b={onAfter:b};if(n=='max')n=9e9;b=d.extend({},k.defaults,b);j=j||b.speed||b.duration;b.queue=b.queue&&b.axis.length>1;if(b.queue)j/=2;b.offset=p(b.offset);b.over=p(b.over);return this._scrollable().each(function(){var q=this,r=d(q),f=n,s,g={},u=r.is('html,body');switch(typeof f){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(f)){f=p(f);break}f=d(f,this);case'object':if(f.is||f.style)s=(f=d(f)).offset()}d.each(b.axis.split(''),function(a,i){var e=i=='x'?'Left':'Top',h=e.toLowerCase(),c='scroll'+e,l=q[c],m=k.max(q,i);if(s){g[c]=s[h]+(u?0:l-r.offset()[h]);if(b.margin){g[c]-=parseInt(f.css('margin'+e))||0;g[c]-=parseInt(f.css('border'+e+'Width'))||0}g[c]+=b.offset[h]||0;if(b.over[h])g[c]+=f[i=='x'?'width':'height']()*b.over[h]}else{var o=f[h];g[c]=o.slice&&o.slice(-1)=='%'?parseFloat(o)/100*m:o}if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],m);if(!a&&b.queue){if(l!=g[c])t(b.onAfterFirst);delete g[c]}});t(b.onAfter);function t(a){r.animate(g,j,b.easing,a&&function(){a.call(this,n,b)})}}).end()};k.max=function(a,i){var e=i=='x'?'Width':'Height',h='scroll'+e;if(!d(a).is('html,body'))return a[h]-d(a)[e.toLowerCase()]();var c='client'+e,l=a.ownerDocument.documentElement,m=a.ownerDocument.body;return Math.max(l[h],m[h])-Math.min(l[c],m[c])};function p(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery);
--------------------------------------------------------------------------------
/samples/chat/public/javascripts/templating.js:
--------------------------------------------------------------------------------
1 |
2 | // Simple JavaScript Templating
3 | // John Resig - http://ejohn.org/ - MIT Licensed
4 | (function(){
5 | var cache = {};
6 |
7 | this.tmpl = function tmpl(str, data){
8 | // Figure out if we're getting a template, or if we need to
9 | // load the template - and be sure to cache the result.
10 | var fn = !/\W/.test(str) ?
11 | cache[str] = cache[str] ||
12 | tmpl(document.getElementById(str).innerHTML) :
13 |
14 | // Generate a reusable function that will serve as a template
15 | // generator (and which will be cached).
16 | new Function("obj",
17 | "var p=[],print=function(){p.push.apply(p,arguments);};" +
18 |
19 | // Introduce the data as local variables using with(){}
20 | "with(obj){p.push('" +
21 |
22 | // Convert the template into pure JavaScript
23 | str
24 | .replace(/[\r\t\n]/g, " ")
25 | .split("<%").join("\t")
26 | .replace(/((^|%>)[^\t]*)'/g, "$1\r")
27 | .replace(/\t=(.*?)%>/g, "',$1,'")
28 | .split("\t").join("');")
29 | .split("%>").join("p.push('")
30 | .split("\r").join("\\'")
31 | + "');}return p.join('');");
32 |
33 | // Provide some basic currying to the user
34 | return data ? fn( data ) : fn;
35 | };
36 | })();
37 |
--------------------------------------------------------------------------------
/samples/chat/public/stylesheets/main.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | background: #ddd;
3 | padding: 0;
4 | margin: 0;
5 | font-family: Helvetica, Arial, Sans;
6 | }
7 |
8 | #home {
9 | margin: 100px auto;
10 | text-align: center;
11 | }
12 |
13 | #signin {
14 | margin: 10px auto;
15 | width: 400px;
16 | border: 8px solid #333;
17 | padding: 20px;
18 | background: #fff;
19 | text-align: left;
20 | }
21 |
22 | #signin p {
23 | margin: 5px 0;
24 | }
25 |
26 | #signin label {
27 | font-size: 14px;
28 | display: block;
29 | width: 150px;
30 | text-align: right;
31 | float: left;
32 | margin-right: 5px;
33 | padding-top: 2px;
34 | }
35 |
36 | #signin #nick {
37 | margin-left: 10px;
38 | width: 200px;
39 | }
40 |
41 | #signin #enter {
42 | display: block;
43 | margin: 10px 0 0 160px;
44 | }
45 |
46 | #signin .error {
47 | background: #c00;
48 | margin: 0 0 10px 0;
49 | padding: 5px;
50 | text-align: center;
51 | color: #fff;
52 | font-size: 14px;
53 | }
54 |
55 | h1 {
56 | background: #111;
57 | margin: 0;
58 | color: gold;
59 | padding: 7px 14px;
60 | font-size: 14px;
61 | font-weight: normal;
62 | }
63 |
64 | h1 a {
65 | position: absolute;
66 | right: 1em;
67 | color: lightblue;
68 | }
69 |
70 | #thread {
71 | position: fixed;
72 | top: 50px;
73 | left: 14px;
74 | right: 14px;
75 | bottom: 100px;
76 | background: white;
77 | border-bottom: 3px solid #666;
78 | padding: 10px;
79 | overflow-y: scroll;
80 | }
81 |
82 | .message {
83 | border-bottom: 1px solid #efefef;
84 | position: relative;
85 | }
86 |
87 | .message h2 {
88 | background: #efefef;
89 | color: #000;
90 | font-size: 12px;
91 | font-weight: bold;
92 | margin: 0;
93 | padding: 5px;
94 | position: absolute;
95 | top: 0;
96 | left: 0;
97 | bottom: 0;
98 | width: 100px;
99 | text-align: right;
100 | border-right: 1px solid #ccc;
101 | }
102 |
103 | .message p {
104 | font-size: 12px;
105 | padding: 5px;
106 | margin: 0 0 0 110px;
107 | }
108 |
109 | .message.you * {
110 | background: #FFFFCC;
111 | }
112 |
113 | .message.notice * {
114 | background: #D9E7FB;
115 | }
116 |
117 | .message.important * {
118 | background: #c00;
119 | color: #fff;
120 | }
121 |
122 | .message.notice h2 {
123 | visibility: hidden;
124 | }
125 |
126 | .message.notice p {
127 | margin: 0;
128 | padding-left: 115px;
129 | }
130 |
131 | #newMessage {
132 | position: fixed;
133 | left: 14px;
134 | right: 14px;
135 | bottom: 30px;
136 | height: 60px;
137 | }
138 |
139 | #newMessage #message {
140 | width: 70%;
141 | padding: 5px;
142 | }
143 |
--------------------------------------------------------------------------------
/samples/chat/tests/apptest.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type ApplicationTest struct {
6 | revel.TestSuite
7 | }
8 |
9 | func (t *ApplicationTest) Before() {
10 | println("Set up")
11 | }
12 |
13 | func (t *ApplicationTest) TestThatIndexPageWorks() {
14 | t.Get("/")
15 | t.AssertOk()
16 | t.AssertContentType("text/html; charset=utf-8")
17 | }
18 |
19 | func (t *ApplicationTest) After() {
20 | println("Tear down")
21 | }
22 |
--------------------------------------------------------------------------------
/samples/facebook-oauth2/app/controllers/app.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "code.google.com/p/goauth2/oauth"
7 | "github.com/robfig/revel"
8 | "github.com/robfig/revel/samples/facebook-oauth2/app/models"
9 | "net/http"
10 | "net/url"
11 | "strconv"
12 | )
13 |
14 | type Application struct {
15 | *revel.Controller
16 | }
17 |
18 | // The following keys correspond to a test application
19 | // registered on Facebook, and associated with the loisant.org domain.
20 | // You need to bind loisant.org to your machine with /etc/hosts to
21 | // test the application locally.
22 |
23 | var FACEBOOK = &oauth.Config{
24 | ClientId: "95341411595",
25 | ClientSecret: "8eff1b488da7fe3426f9ecaf8de1ba54",
26 | AuthURL: "https://graph.facebook.com/oauth/authorize",
27 | TokenURL: "https://graph.facebook.com/oauth/access_token",
28 | RedirectURL: "http://loisant.org:9000/Application/Auth",
29 | }
30 |
31 | func (c Application) Index() revel.Result {
32 | u := c.connected()
33 | me := map[string]interface{}{}
34 | if u != nil && u.AccessToken != "" {
35 | resp, _ := http.Get("https://graph.facebook.com/me?access_token=" +
36 | url.QueryEscape(u.AccessToken))
37 | defer resp.Body.Close()
38 | if err := json.NewDecoder(resp.Body).Decode(&me); err != nil {
39 | revel.ERROR.Println(err)
40 | }
41 | revel.INFO.Println(me)
42 | }
43 |
44 | authUrl := FACEBOOK.AuthCodeURL("foo")
45 | return c.Render(me, authUrl)
46 | }
47 |
48 | func (c Application) Auth(code string) revel.Result {
49 | t := &oauth.Transport{Config: FACEBOOK}
50 | tok, err := t.Exchange(code)
51 | if err != nil {
52 | revel.ERROR.Println(err)
53 | return c.Redirect(Application.Index)
54 | }
55 |
56 | user := c.connected()
57 | user.AccessToken = tok.AccessToken
58 | return c.Redirect(Application.Index)
59 | }
60 |
61 | func setuser(c *revel.Controller) revel.Result {
62 | var user *models.User
63 | if _, ok := c.Session["uid"]; ok {
64 | uid, _ := strconv.ParseInt(c.Session["uid"], 10, 0)
65 | user = models.GetUser(int(uid))
66 | }
67 | if user == nil {
68 | user = models.NewUser()
69 | c.Session["uid"] = fmt.Sprintf("%d", user.Uid)
70 | }
71 | c.RenderArgs["user"] = user
72 | return nil
73 | }
74 |
75 | func init() {
76 | revel.InterceptFunc(setuser, revel.BEFORE, &Application{})
77 | }
78 |
79 | func (c Application) connected() *models.User {
80 | return c.RenderArgs["user"].(*models.User)
81 | }
82 |
--------------------------------------------------------------------------------
/samples/facebook-oauth2/app/models/user.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "math/rand"
4 |
5 | type User struct {
6 | Uid int
7 | AccessToken string
8 | }
9 |
10 | var db = make(map[int]*User)
11 |
12 | func GetUser(id int) *User {
13 | return db[id]
14 | }
15 |
16 | func NewUser() *User {
17 | user := &User{Uid: rand.Intn(10000)}
18 | db[user.Uid] = user
19 | return user
20 | }
21 |
--------------------------------------------------------------------------------
/samples/facebook-oauth2/app/views/Application/Index.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Home"}}
2 | {{template "header.html" .}}
3 |
4 |
Facebook!
5 |
6 |
The example is configured with a key corresponding to the loisant.org domain.
7 | Facebook will no accept redirection to other domains (such as localhost)
8 | To test it, you need to either map 127.0.0.1 to loisant.org in your /etc/hosts,
9 | either replace the configuration to an application that you created on Facebook.
10 |
11 | {{if .me}}
12 |
You're {{.me.name}} on Facebook
13 |
14 |
15 | {{else}}
16 |
login
17 | {{end}}
18 |
19 | {{template "footer.html" .}}
20 |
--------------------------------------------------------------------------------
/samples/facebook-oauth2/app/views/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/samples/facebook-oauth2/app/views/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{.title}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/samples/facebook-oauth2/conf/app.conf:
--------------------------------------------------------------------------------
1 | app.name=Facebook OAuth2
2 | app.secret=ly2bgKNgQ7BSoWW8BZ33Tos9Z9hy2Ck6bFXotLxwSaXk60iXPqlZUKnJpGVoIHhs
3 | module.static=github.com/robfig/revel/modules/static
4 | module.testrunner=github.com/robfig/revel/modules/testrunner
5 | mode.dev=true
6 | [dev]
7 | [prod]
8 |
--------------------------------------------------------------------------------
/samples/facebook-oauth2/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | module:testrunner
6 |
7 | # Home page
8 | GET / Application.Index
9 |
10 | # Map static resources from the /app/public folder to the /public path
11 | GET /public/*filepath Static.Serve("public")
12 |
13 | # Catch all
14 | * /:controller/:action :controller.:action
15 |
--------------------------------------------------------------------------------
/samples/facebook-oauth2/public/css/main.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/facebook-oauth2/public/css/main.css
--------------------------------------------------------------------------------
/samples/facebook-oauth2/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/facebook-oauth2/public/images/favicon.png
--------------------------------------------------------------------------------
/samples/facebook-oauth2/tests/apptest.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type ApplicationTest struct {
6 | revel.TestSuite
7 | }
8 |
9 | func (t *ApplicationTest) Before() {
10 | println("Set up")
11 | }
12 |
13 | func (t *ApplicationTest) TestThatIndexPageWorks() {
14 | t.Get("/")
15 | t.AssertOk()
16 | t.AssertContentType("text/html; charset=utf-8")
17 | }
18 |
19 | func (t *ApplicationTest) After() {
20 | println("Tear down")
21 | }
22 |
--------------------------------------------------------------------------------
/samples/i18n/app/controllers/app.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type Application struct {
6 | *revel.Controller
7 | }
8 |
9 | func (c Application) Index() revel.Result {
10 | // Localization information
11 | c.RenderArgs["acceptLanguageHeader"] = c.Request.Header.Get("Accept-Language")
12 | c.RenderArgs["acceptLanguageHeaderParsed"] = c.Request.AcceptLanguages.String()
13 | c.RenderArgs["acceptLanguageHeaderMostQualified"] = c.Request.AcceptLanguages[0]
14 | c.RenderArgs["controllerCurrentLocale"] = c.Request.Locale
15 |
16 | // Controller-resolves messages
17 | c.RenderArgs["controllerGreeting"] = c.Message("greeting")
18 | c.RenderArgs["controllerGreetingName"] = c.Message("greeting.name")
19 | c.RenderArgs["controllerGreetingSuffix"] = c.Message("greeting.suffix")
20 | c.RenderArgs["controllerGreetingFull"] = c.Message("greeting.full")
21 | c.RenderArgs["controllerGreetingWithArgument"] = c.Message("greeting.full.name", "Steve Buscemi")
22 |
23 | return c.Render()
24 | }
25 |
--------------------------------------------------------------------------------
/samples/i18n/app/views/Application/Index.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Home"}}
2 | {{template "header.html" .}}
3 |
4 |
Internationalization overview/test page
5 |
6 |
Client localization information
7 |
8 |
9 | Accept-Language HTTP header: {{.acceptLanguageHeader}}
10 | Parsed Accept-Language HTTP header: {{.acceptLanguageHeaderParsed}}
11 | Current preferred locale as determined by the framework: {{.currentLocale}}
12 |
13 |
14 |
15 |
Messages from the sample message file as resolved using Controller.Message(...)
16 |
17 |
18 | Greeting: {{.controllerGreeting}}
19 | Greeting name: {{.controllerGreetingName}}
20 | Greeting suffix: {{.controllerGreetingSuffix}}
21 | Full greeting: {{.controllerGreetingFull}}
22 | Full greeting with argument: {{.controllerGreetingWithArgument}}
23 |
24 |
25 |
26 |
Messages from the sample message file as resolved using the {{msg . "message" "arg" "arg"}}
template function
27 |
28 |
29 | Greeting: {{msg . "greeting"}}
30 | Greeting name: {{msg . "greeting.name"}}
31 | Greeting suffix: {{msg . "greeting.suffix"}}
32 | Full greeting: {{msg . "greeting.full"}}
33 | Full greeting with argument: {{msg . "greeting.full.name" "Tommy Lee Jones"}}
34 | Cross-file referencing: {{msg . "alpha"}}
35 | Message not found: {{msg . "doesnt exist"}}
36 |
37 |
38 |
39 | {{template "footer.html" .}}
40 |
--------------------------------------------------------------------------------
/samples/i18n/app/views/errors/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Not found
5 |
6 |
7 | {{if eq .RunMode "dev"}}
8 | {{template "errors/404-dev.html" .}}
9 | {{else}}
10 | {{with .Error}}
11 |
12 | {{.Title}}
13 |
14 |
15 | {{.Description}}
16 |
17 | {{end}}
18 | {{end}}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/samples/i18n/app/views/errors/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Application error
5 |
6 |
7 | {{if eq .RunMode "dev"}}
8 | {{template "errors/500-dev.html" .}}
9 | {{else}}
10 |
Oops, an error occured
11 |
12 | This exception has been logged.
13 |
14 | {{end}}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/samples/i18n/app/views/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/samples/i18n/app/views/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{.title}}
6 |
7 |
8 |
9 |
10 | {{range .moreStyles}}
11 |
12 | {{end}}
13 | {{range .moreScripts}}
14 |
15 | {{end}}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/samples/i18n/conf/app.conf:
--------------------------------------------------------------------------------
1 | app.name=i18n
2 | app.secret=bPlNFGdSC2wd8f2QnFhk5A84JJjKWZdKH9H2FHFuvUs9Jz8UvBHv3Vc5awx39ivu
3 | http.addr=
4 | http.port=9000
5 | cookie.prefix=REVEL
6 |
7 | # The language cookie name used to store the current language.
8 | i18n.cookie=%(cookie.prefix)s_LANG
9 |
10 | # The default language of this application.
11 | i18n.default_language=en
12 |
13 | [dev]
14 | mode.dev=true
15 | results.pretty=true
16 | results.staging=true
17 | watch=true
18 |
19 | #module.testrunner = github.com/robfig/revel/modules/testrunner
20 | module.static=github.com/robfig/revel/modules/static
21 |
22 | log.trace.output = stdout
23 | log.info.output = stderr
24 | log.warn.output = stderr
25 | log.error.output = stderr
26 |
27 | [prod]
28 | mode.dev=false
29 | results.pretty=false
30 | results.staging=false
31 | watch=false
32 |
33 | module.testrunner =
34 |
35 | log.trace.output = off
36 | log.info.output = off
37 | log.warn.output = %(app.name)s.log
38 | log.error.output = %(app.name)s.log
39 |
--------------------------------------------------------------------------------
/samples/i18n/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | GET / Application.Index
6 |
7 | # Ignore favicon requests
8 | GET /favicon.ico 404
9 |
10 | # Map static resources from the /app/public folder to the /public path
11 | GET /public/*filepath Static.Serve("public")
12 |
13 | # Catch all
14 | * /:controller/:action :controller.:action
15 |
--------------------------------------------------------------------------------
/samples/i18n/messages/sample.en:
--------------------------------------------------------------------------------
1 | # Sample messages file for the English language (en)
2 | # Message file extensions should be ISO 639-1 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
3 | # Sections within each message file can optionally override the defaults using ISO 3166-1 alpha-2 codes (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
4 | # See also:
5 | # - http://www.rfc-editor.org/rfc/bcp/bcp47.txt
6 | # - http://www.w3.org/International/questions/qa-accept-lang-locales
7 |
8 | # Default values for English
9 | greeting=Hello
10 | greeting.name=Rob
11 | greeting.suffix=, welcome to Revel!
12 |
13 | greeting.full=%(greeting)s %(greeting.name)s%(greeting.suffix)s
14 |
15 | greeting.full.name=%(greeting)s %s%(greeting.suffix)s
16 |
17 | # beta is defined in sample2.en
18 | alpha=alpha %(beta)s
19 |
20 | # Specify region-specific overrides
21 | [AU]
22 | greeting=G'day
23 |
24 | [US]
25 | greeting=Howdy
26 |
27 | [GB]
28 | greeting=All right
29 |
--------------------------------------------------------------------------------
/samples/i18n/messages/sample2.en:
--------------------------------------------------------------------------------
1 | beta=beta
2 |
--------------------------------------------------------------------------------
/samples/i18n/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/i18n/public/images/favicon.png
--------------------------------------------------------------------------------
/samples/i18n/public/js/jquery-1.5.2.min.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/i18n/public/js/jquery-1.5.2.min.js
--------------------------------------------------------------------------------
/samples/i18n/tests/apptest.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type ApplicationTest struct {
6 | revel.TestSuite
7 | }
8 |
9 | func (t ApplicationTest) Before() {
10 | println("Set up")
11 | }
12 |
13 | func (t ApplicationTest) TestThatIndexPageWorks() {
14 | t.Get("/")
15 | t.AssertOk()
16 | t.AssertContentType("text/html; charset=utf-8")
17 | }
18 |
19 | func (t ApplicationTest) After() {
20 | println("Tear down")
21 | }
22 |
--------------------------------------------------------------------------------
/samples/persona/.gitignore:
--------------------------------------------------------------------------------
1 | test-results/
2 | tmp/
3 | routes/
4 |
--------------------------------------------------------------------------------
/samples/persona/app/controllers/app.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "net/http"
8 | "net/url"
9 | "strings"
10 |
11 | "github.com/robfig/revel"
12 | )
13 |
14 | const host = "" // set this to your host
15 |
16 | type App struct {
17 | *revel.Controller
18 | }
19 |
20 | type PersonaResponse struct {
21 | Status string `json:"status"`
22 | Email string `json:"email"`
23 | Audience string `json:"audience"`
24 | Expires int64 `json:"expires"`
25 | Issuer string `json:"issuer"`
26 | }
27 |
28 | type LoginResult struct {
29 | StatusCode int
30 | Message string
31 | }
32 |
33 | func (r LoginResult) Apply(req *revel.Request, resp *revel.Response) {
34 | resp.WriteHeader(r.StatusCode, "text/html")
35 | resp.Out.Write([]byte(r.Message))
36 | }
37 |
38 | func (c App) Index() revel.Result {
39 | email := c.Session["email"]
40 | return c.Render(email)
41 | }
42 |
43 | func (c App) Login(assertion string) revel.Result {
44 | assertion = strings.TrimSpace(assertion)
45 | if assertion == "" {
46 | return &LoginResult{
47 | StatusCode: http.StatusBadRequest,
48 | Message: "Assertion required.",
49 | }
50 | }
51 |
52 | values := url.Values{"assertion": {assertion}, "audience": {host}}
53 | resp, err := http.PostForm("https://verifier.login.persona.org/verify", values)
54 | if err != nil {
55 | return &LoginResult{
56 | StatusCode: http.StatusBadRequest,
57 | Message: "Authentication failed.",
58 | }
59 | }
60 |
61 | body, err := ioutil.ReadAll(resp.Body)
62 | if err != nil {
63 | return &LoginResult{
64 | StatusCode: http.StatusBadRequest,
65 | Message: "Authentication failed.",
66 | }
67 | }
68 |
69 | p := &PersonaResponse{}
70 | err = json.Unmarshal(body, p)
71 | if err != nil {
72 | return &LoginResult{
73 | StatusCode: http.StatusBadRequest,
74 | Message: "Authentication failed.",
75 | }
76 | }
77 |
78 | c.Session["email"] = p.Email
79 | fmt.Println("Login successful: ", p.Email)
80 |
81 | return &LoginResult{
82 | StatusCode: http.StatusOK,
83 | Message: "Login successful.",
84 | }
85 | }
86 |
87 | func (c App) Logout() revel.Result {
88 | delete(c.Session, "email")
89 | return c.Redirect("/")
90 | }
91 |
--------------------------------------------------------------------------------
/samples/persona/app/views/App/Index.html:
--------------------------------------------------------------------------------
1 | {{append . "moreStyles" "css/common.css"}}
2 |
3 | {{set . "title" "Home"}}
4 | {{template "header.html" .}}
5 |
6 |
19 |
20 |
21 |
22 |
23 | {{template "flash.html" .}}
24 |
25 |
26 |
27 |
28 | {{template "footer.html" .}}
29 |
--------------------------------------------------------------------------------
/samples/persona/app/views/debug.html:
--------------------------------------------------------------------------------
1 |
20 |
44 |
45 |
46 |
63 |
--------------------------------------------------------------------------------
/samples/persona/app/views/flash.html:
--------------------------------------------------------------------------------
1 | {{if .flash.success}}
2 |
3 | {{.flash.success}}
4 |
5 | {{end}}
6 |
7 | {{if or .errors .flash.error}}
8 |
9 | {{if .flash.error}}
10 | {{.flash.error}}
11 | {{end}}
12 |
13 | {{range .errors}}
14 | {{.}}
15 | {{end}}
16 |
17 |
18 | {{end}}
19 |
--------------------------------------------------------------------------------
/samples/persona/app/views/footer.html:
--------------------------------------------------------------------------------
1 | {{if eq .RunMode "dev"}}
2 | {{template "debug.html" .}}
3 | {{end}}
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/samples/persona/app/views/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{.title}}
6 |
7 |
8 |
9 |
10 |
11 |
42 | {{range .moreStyles}}
43 |
44 | {{end}}
45 | {{range .moreScripts}}
46 |
47 | {{end}}
48 |
49 |
50 |
--------------------------------------------------------------------------------
/samples/persona/conf/app.conf:
--------------------------------------------------------------------------------
1 | app.name=persona
2 | app.secret=bPlNFGdSC2wd8f2QnFhk5A84JJjKWZdKH9H2FHFuvUs9Jz8UvBHv3Vc5awx39ivu
3 | http.addr=
4 | http.port=9000
5 | http.ssl=false
6 | http.sslcert=
7 | http.sslkey=
8 | cookie.prefix=REVEL
9 | format.date=01/02/2006
10 | format.datetime=01/02/2006 15:04
11 | results.chunked=false
12 |
13 | log.trace.prefix = "TRACE "
14 | log.info.prefix = "INFO "
15 | log.warn.prefix = "WARN "
16 | log.error.prefix = "ERROR "
17 |
18 | # The default language of this application.
19 | i18n.default_language=en
20 |
21 | module.static=github.com/robfig/revel/modules/static
22 |
23 | [dev]
24 | mode.dev=true
25 | results.pretty=true
26 | watch=true
27 |
28 | module.testrunner = github.com/robfig/revel/modules/testrunner
29 |
30 | log.trace.output = off
31 | log.info.output = stderr
32 | log.warn.output = stderr
33 | log.error.output = stderr
34 |
35 | [prod]
36 | mode.dev=false
37 | results.pretty=false
38 | watch=false
39 |
40 | module.testrunner =
41 |
42 | log.trace.output = off
43 | log.info.output = off
44 | log.warn.output = %(app.name)s.log
45 | log.error.output = %(app.name)s.log
46 |
--------------------------------------------------------------------------------
/samples/persona/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | module:testrunner
6 |
7 | GET / App.Index
8 | GET /login App.Login
9 | POST /login App.Login
10 | GET /logout App.Logout
11 |
12 | # Ignore favicon requests
13 | GET /favicon.ico 404
14 |
15 | # Map static resources from the /app/public folder to the /public path
16 | GET /public/*filepath Static.Serve("public")
17 |
18 | # Catch all
19 | * /:controller/:action :controller.:action
20 |
--------------------------------------------------------------------------------
/samples/persona/messages/sample.en:
--------------------------------------------------------------------------------
1 | # Sample messages file for the English language (en)
2 | # Message file extensions should be ISO 639-1 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
3 | # Sections within each message file can optionally override the defaults using ISO 3166-1 alpha-2 codes (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
4 | # See also:
5 | # - http://www.rfc-editor.org/rfc/bcp/bcp47.txt
6 | # - http://www.w3.org/International/questions/qa-accept-lang-locales
7 |
8 |
--------------------------------------------------------------------------------
/samples/persona/public/css/common.css:
--------------------------------------------------------------------------------
1 | #login {
2 | background: url(../img/persona-signin.png) no-repeat top left;
3 | text-indent: -99999px;
4 | width: 202px;
5 | height: 25px;
6 | display: inline-block;
7 | }
--------------------------------------------------------------------------------
/samples/persona/public/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/persona/public/img/favicon.png
--------------------------------------------------------------------------------
/samples/persona/public/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/persona/public/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/samples/persona/public/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/persona/public/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/samples/persona/public/img/persona-signin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/persona/public/img/persona-signin.png
--------------------------------------------------------------------------------
/samples/persona/tests/apptest.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type AppTest struct {
6 | revel.TestSuite
7 | }
8 |
9 | func (t *AppTest) Before() {
10 | println("Set up")
11 | }
12 |
13 | func (t AppTest) TestThatIndexPageWorks() {
14 | t.Get("/")
15 | t.AssertOk()
16 | t.AssertContentType("text/html; charset=utf-8")
17 | }
18 |
19 | func (t *AppTest) After() {
20 | println("Tear down")
21 | }
22 |
--------------------------------------------------------------------------------
/samples/twitter-oauth/app/controllers/app.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "encoding/json"
5 | "github.com/mrjones/oauth"
6 | "github.com/robfig/revel"
7 | "github.com/robfig/revel/samples/twitter-oauth/app/models"
8 | "io/ioutil"
9 | )
10 |
11 | var TWITTER = oauth.NewConsumer(
12 | "VgRjky4dTA1U2Ck16MmZw",
13 | "l8lOLyIF3peCFEvrEoTc8h4oFwieAFgPM6eeTRo30I",
14 | oauth.ServiceProvider{
15 | AuthorizeTokenUrl: "https://api.twitter.com/oauth/authorize",
16 | RequestTokenUrl: "https://api.twitter.com/oauth/request_token",
17 | AccessTokenUrl: "https://api.twitter.com/oauth/access_token",
18 | },
19 | )
20 |
21 | type Application struct {
22 | *revel.Controller
23 | }
24 |
25 | func (c Application) Index() revel.Result {
26 | user := getUser()
27 | if user.AccessToken == nil {
28 | return c.Render()
29 | }
30 |
31 | // We have a token, so look for mentions.
32 | resp, err := TWITTER.Get(
33 | "https://api.twitter.com/1.1/statuses/mentions_timeline.json",
34 | map[string]string{"count": "10"},
35 | user.AccessToken)
36 | if err != nil {
37 | revel.ERROR.Println(err)
38 | return c.Render()
39 | }
40 | defer resp.Body.Close()
41 |
42 | // Extract the mention text.
43 | mentions := []struct {
44 | Text string `json:text`
45 | }{}
46 | err = json.NewDecoder(resp.Body).Decode(&mentions)
47 | if err != nil {
48 | revel.ERROR.Println(err)
49 | }
50 | revel.INFO.Println(mentions)
51 | return c.Render(mentions)
52 | }
53 |
54 | func (c Application) SetStatus(status string) revel.Result {
55 | resp, err := TWITTER.PostForm(
56 | "http://api.twitter.com/1.1/statuses/update.json",
57 | map[string]string{"status": status},
58 | getUser().AccessToken,
59 | )
60 | if err != nil {
61 | revel.ERROR.Println(err)
62 | return c.RenderError(err)
63 | }
64 | defer resp.Body.Close()
65 | bodyBytes, _ := ioutil.ReadAll(resp.Body)
66 | revel.INFO.Println(string(bodyBytes))
67 | c.Response.ContentType = "application/json"
68 | return c.RenderText(string(bodyBytes))
69 | }
70 |
71 | // Twitter authentication
72 |
73 | func (c Application) Authenticate(oauth_verifier string) revel.Result {
74 | user := getUser()
75 | if oauth_verifier != "" {
76 | // We got the verifier; now get the access token, store it and back to index
77 | accessToken, err := TWITTER.AuthorizeToken(user.RequestToken, oauth_verifier)
78 | if err == nil {
79 | user.AccessToken = accessToken
80 | } else {
81 | revel.ERROR.Println("Error connecting to twitter:", err)
82 | }
83 | return c.Redirect(Application.Index)
84 | }
85 |
86 | requestToken, url, err := TWITTER.GetRequestTokenAndUrl("http://127.0.0.1:9000/Application/Authenticate")
87 | if err == nil {
88 | // We received the unauthorized tokens in the OAuth object - store it before we proceed
89 | user.RequestToken = requestToken
90 | return c.Redirect(url)
91 | } else {
92 | revel.ERROR.Println("Error connecting to twitter:", err)
93 | }
94 | return c.Redirect(Application.Index)
95 | }
96 |
97 | func getUser() *models.User {
98 | return models.FindOrCreate("guest")
99 | }
100 |
101 | func init() {
102 | TWITTER.Debug(true)
103 | }
104 |
--------------------------------------------------------------------------------
/samples/twitter-oauth/app/models/user.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "github.com/mrjones/oauth"
4 |
5 | type User struct {
6 | Username string
7 | RequestToken *oauth.RequestToken
8 | AccessToken *oauth.AccessToken
9 | }
10 |
11 | func FindOrCreate(username string) *User {
12 | if user, ok := db[username]; ok {
13 | return user
14 | }
15 | user := &User{Username: username}
16 | db[username] = user
17 | return user
18 | }
19 |
20 | var db = make(map[string]*User)
21 |
--------------------------------------------------------------------------------
/samples/twitter-oauth/app/views/Application/Index.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Home"}}
2 | {{template "header.html" .}}
3 |
4 |
Useless Twitter Mashup
5 |
6 |
7 |
8 | Status:
9 |
10 |
Mentions
11 |
12 |
13 | {{range .mentions}}
14 |
15 | {{.Text}}
16 |
17 | {{end}}
18 |
19 |
20 |
40 |
41 | {{template "footer.html" .}}
42 |
--------------------------------------------------------------------------------
/samples/twitter-oauth/app/views/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/samples/twitter-oauth/app/views/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{.title}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/samples/twitter-oauth/conf/app.conf:
--------------------------------------------------------------------------------
1 | app.secret=e227tafmfs0xrexah43hm34kkrcetav48nwk9x037wp87jkrp06m7n8wc8m7gbag
2 | module.static=github.com/robfig/revel/modules/static
3 | module.testrunner=github.com/robfig/revel/modules/testrunner
4 | mode.dev=true
5 | [dev]
6 | [prod]
7 |
--------------------------------------------------------------------------------
/samples/twitter-oauth/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | module:testrunner
6 |
7 | # Home page
8 | GET / Application.index
9 | GET /auth Application.authenticate
10 |
11 | POST /ws/status Application.setStatus
12 |
13 | # Map static resources from the /app/public folder to the /public path
14 | GET /public/*filepath Static.Serve("public")
15 |
16 | # Catch all
17 | * /:controller/:action :controller.:action
18 |
--------------------------------------------------------------------------------
/samples/twitter-oauth/public/css/main.css:
--------------------------------------------------------------------------------
1 | html {
2 | background-color: #1196FC;
3 | font-family: sans-serif;
4 | }
5 |
6 | body {
7 | background-color: #77D9F9;
8 | -moz-border-radius: 20px;
9 | -webkit-border-radius: 20px;
10 | padding: 20px 30px 20px 30px;
11 | }
12 |
13 | .toolbar {
14 | height: 50px;
15 | }
16 |
17 | .toolbar a {
18 | float: right;
19 | text-decoration: none;
20 | background-color: #1196FC;
21 | color: white;
22 | font-weight: bold;
23 | cursor: pointer;
24 | -moz-border-radius: 10px;
25 | -webkit-border-radius: 10px;
26 | border-radius: 10px;
27 | padding: 3px 15px 3px 15px;
28 | }
29 |
30 | h1, h2, h3 {
31 | color: #154E84;
32 | }
33 |
--------------------------------------------------------------------------------
/samples/twitter-oauth/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/twitter-oauth/public/images/favicon.png
--------------------------------------------------------------------------------
/samples/twitter-oauth/tests/apptest.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type ApplicationTest struct {
6 | revel.TestSuite
7 | }
8 |
9 | func (t *ApplicationTest) Before() {
10 | println("Set up")
11 | }
12 |
13 | func (t *ApplicationTest) TestThatIndexPageWorks() {
14 | t.Get("/")
15 | t.AssertOk()
16 | t.AssertContentType("text/html; charset=utf-8")
17 | }
18 |
19 | func (t *ApplicationTest) After() {
20 | println("Tear down")
21 | }
22 |
--------------------------------------------------------------------------------
/samples/validation/app/controllers/app.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type Application struct {
6 | *revel.Controller
7 | }
8 |
9 | func (c Application) Index() revel.Result {
10 | return c.Render()
11 | }
12 |
--------------------------------------------------------------------------------
/samples/validation/app/controllers/sample1.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/robfig/revel"
5 | )
6 |
7 | type Sample1 struct {
8 | *revel.Controller
9 | }
10 |
11 | func (c Sample1) Index() revel.Result {
12 | return c.Render()
13 | }
14 |
15 | func (c Sample1) HandleSubmit(
16 | username, firstname, lastname string,
17 | age int,
18 | password, passwordConfirm, email, emailConfirm string,
19 | termsOfUse bool) revel.Result {
20 |
21 | // Validation rules
22 | c.Validation.Required(username).Message("Username is required.")
23 | c.Validation.MinSize(username, 6).Message("Username must be at least 6 characters.")
24 | c.Validation.Required(firstname).Message("First name is required.")
25 | c.Validation.Required(lastname).Message("Last name is required.")
26 | c.Validation.Required(age).Message("Age is required.")
27 | c.Validation.Range(age, 16, 120).Message("Age must be between 16 and 120.")
28 | c.Validation.Required(password).Message("Password is required.")
29 | c.Validation.MinSize(password, 6).Message("Password must be greater than 6 characters.")
30 | c.Validation.Required(passwordConfirm).Message("Please confirm your password.")
31 | c.Validation.Required(passwordConfirm == password).Message("Your passwords do not match.")
32 | c.Validation.Required(email).Message("Email is required.")
33 | c.Validation.Email(email).Message("A valid email is required.")
34 | c.Validation.Required(emailConfirm).Message("Please confirm your email address.")
35 | c.Validation.Required(emailConfirm == email).Message("Your email addresses do not match.")
36 | c.Validation.Required(termsOfUse == true).Message("Please agree to the terms.")
37 |
38 | // Handle errors
39 | if c.Validation.HasErrors() {
40 | c.Validation.Keep()
41 | c.FlashParams()
42 | return c.Redirect(Sample1.Index)
43 | }
44 |
45 | // Ok, display the created user
46 | return c.Render(username, firstname, lastname, age, password, email)
47 | }
48 |
--------------------------------------------------------------------------------
/samples/validation/app/controllers/sample2.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/robfig/revel"
5 | )
6 |
7 | type Sample2 struct {
8 | *revel.Controller
9 | }
10 |
11 | func (c Sample2) Index() revel.Result {
12 | return c.Render()
13 | }
14 |
15 | func (c Sample2) HandleSubmit(
16 | username, firstname, lastname string,
17 | age int,
18 | password, passwordConfirm, email, emailConfirm string,
19 | termsOfUse bool) revel.Result {
20 |
21 | // Validation rules
22 | c.Validation.Required(username)
23 | c.Validation.MinSize(username, 6)
24 | c.Validation.Required(firstname)
25 | c.Validation.Required(lastname)
26 | c.Validation.Required(age)
27 | c.Validation.Range(age, 16, 120)
28 | c.Validation.Required(password)
29 | c.Validation.MinSize(password, 6)
30 | c.Validation.Required(passwordConfirm)
31 | c.Validation.Required(passwordConfirm == password).Message("Your passwords do not match.")
32 | c.Validation.Required(email)
33 | c.Validation.Email(email)
34 | c.Validation.Required(emailConfirm)
35 | c.Validation.Required(emailConfirm == email).Message("Your email addresses do not match.")
36 | c.Validation.Required(termsOfUse == true).Message("Please agree to the terms.")
37 |
38 | // Handle errors
39 | if c.Validation.HasErrors() {
40 | c.Validation.Keep()
41 | c.FlashParams()
42 | return c.Redirect(Sample2.Index)
43 | }
44 |
45 | // Ok, display the created user
46 | return c.Render(username, firstname, lastname, age, password, email)
47 | }
48 |
--------------------------------------------------------------------------------
/samples/validation/app/controllers/sample3.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/robfig/revel"
5 | "github.com/robfig/revel/samples/validation/app/models"
6 | )
7 |
8 | type Sample3 struct {
9 | *revel.Controller
10 | }
11 |
12 | func (c Sample3) Index() revel.Result {
13 | return c.Render()
14 | }
15 |
16 | func (c Sample3) HandleSubmit(user *models.User) revel.Result {
17 | user.Validate(c.Validation)
18 |
19 | // Handle errors
20 | if c.Validation.HasErrors() {
21 | c.Validation.Keep()
22 | c.FlashParams()
23 | return c.Redirect(Sample3.Index)
24 | }
25 |
26 | // Ok, display the created user
27 | return c.Render(user)
28 | }
29 |
--------------------------------------------------------------------------------
/samples/validation/app/controllers/sample4.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import (
4 | "github.com/robfig/revel"
5 | "github.com/robfig/revel/samples/validation/app/models"
6 | )
7 |
8 | type Sample4 struct {
9 | *revel.Controller
10 | }
11 |
12 | func (c Sample4) Index() revel.Result {
13 | return c.Render()
14 | }
15 |
16 | func (c Sample4) HandleSubmit(user *models.User) revel.Result {
17 | user.Validate(c.Validation)
18 |
19 | // Handle errors
20 | if c.Validation.HasErrors() {
21 | c.Validation.Keep()
22 | c.FlashParams()
23 | return c.Redirect(Sample4.Index)
24 | }
25 |
26 | // Ok, display the created user
27 | return c.Render(user)
28 | }
29 |
--------------------------------------------------------------------------------
/samples/validation/app/models/user.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type User struct {
6 | Username string
7 | FirstName string
8 | LastName string
9 | Age int
10 | Password string
11 | PasswordConfirm string
12 | Email string
13 | EmailConfirm string
14 | TermsOfUse bool
15 | }
16 |
17 | func (user *User) Validate(v *revel.Validation) {
18 | v.Required(user.Username)
19 | v.MinSize(user.Username, 6)
20 | v.Required(user.FirstName)
21 | v.Required(user.LastName)
22 | v.Required(user.Age)
23 | v.Range(user.Age, 16, 120)
24 | v.Required(user.Password)
25 | v.MinSize(user.Password, 6)
26 | v.Required(user.PasswordConfirm)
27 | v.Required(user.PasswordConfirm == user.Password).
28 | Message("The passwords do not match.")
29 | v.Required(user.Email)
30 | v.Email(user.Email)
31 | v.Required(user.EmailConfirm)
32 | v.Required(user.EmailConfirm == user.Email).
33 | Message("The email addresses do not match")
34 | v.Required(user.TermsOfUse)
35 | }
36 |
--------------------------------------------------------------------------------
/samples/validation/app/views/Application/Index.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Home"}}
2 | {{template "header.html" .}}
3 |
4 |
Validation samples
5 |
6 |
7 | Learn how to use the Revel validation framework . Each sample has it own controller and it own templates.
8 | {{/* Don't forget to take a look at the the conf/messages file. */}}
9 |
10 |
11 |
12 |
13 | Sample 1
14 |
15 | This sample shows very basic validation. All errors are displayed at the top
16 | of the form.
17 |
18 |
19 | See the sample
20 |
21 |
22 |
23 | Sample 2
24 |
25 | Same controller than for the 1st sample, but errors are displayed inline, next to each
26 | field.
27 |
28 |
29 | See the sample
30 |
31 |
32 |
33 | Sample 3
34 |
35 | This demonstrates best practice for validating a struct (bean), rather
36 | than many individual fields.
37 |
38 |
39 | See the sample
40 |
41 |
42 |
43 | Sample 4
44 |
45 | Same validation as Sample 3, but the template use the {{"{{field}}"}} tag
46 | to scope all field data (error, flash, name, ...) to a single part of the
47 | template.
48 |
49 |
50 | See the sample
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/samples/validation/app/views/Sample1/HandleSubmit.html:
--------------------------------------------------------------------------------
1 | {{template "header.html" .}}
2 |
3 |
User created !
4 |
5 |
6 | Desired username is {{.username}}
7 |
8 |
9 | First name is {{.firstname}}
10 |
11 |
12 | Last name is {{.lastname}}
13 |
14 |
15 | Age is {{.age}}
16 |
17 |
18 | Password is {{.password}}
19 |
20 |
21 | Email is {{.email}}
22 |
23 |
24 |
Back
25 |
26 | {{template "footer.html" .}}
27 |
--------------------------------------------------------------------------------
/samples/validation/app/views/Sample1/Index.html:
--------------------------------------------------------------------------------
1 | {{template "header.html" .}}
2 |
3 |
Validation sample 1
4 |
5 | {{if .errors}}
6 |
7 |
Oops, please correct these errors
8 |
9 | {{range .errors}}
10 | {{.}}
11 | {{end}}
12 |
13 |
14 | {{end}}
15 |
16 |
86 |
87 | {{template "footer.html" .}}
88 |
--------------------------------------------------------------------------------
/samples/validation/app/views/Sample2/HandleSubmit.html:
--------------------------------------------------------------------------------
1 | {{template "header.html" .}}
2 |
3 |
User created !
4 |
5 |
6 | Desired username is {{.username}}
7 |
8 |
9 | First name is {{.firstname}}
10 |
11 |
12 | Last name is {{.lastname}}
13 |
14 |
15 | Age is {{.age}}
16 |
17 |
18 | Password is {{.password}}
19 |
20 |
21 | Email is {{.email}}
22 |
23 |
24 |
Back
25 |
26 | {{template "footer.html" .}}
27 |
--------------------------------------------------------------------------------
/samples/validation/app/views/Sample2/Index.html:
--------------------------------------------------------------------------------
1 | {{template "header.html" .}}
2 |
3 |
Validation sample 2
4 |
5 | {{if .errors}}
6 |
7 |
Oops, please correct these errors
8 |
9 | {{end}}
10 |
11 |
90 |
91 | {{template "footer.html" .}}
92 |
--------------------------------------------------------------------------------
/samples/validation/app/views/Sample3/HandleSubmit.html:
--------------------------------------------------------------------------------
1 | {{template "header.html" .}}
2 |
3 |
User created !
4 |
5 |
6 | Desired username is {{.user.Username}}
7 |
8 |
9 | First name is {{.user.FirstName}}
10 |
11 |
12 | Last name is {{.user.LastName}}
13 |
14 |
15 | Age is {{.user.Age}}
16 |
17 |
18 | Password is {{.user.Password}}
19 |
20 |
21 | Email is {{.user.Email}}
22 |
23 |
24 |
Back
25 |
26 | {{template "footer.html" .}}
27 |
--------------------------------------------------------------------------------
/samples/validation/app/views/Sample4/HandleSubmit.html:
--------------------------------------------------------------------------------
1 | {{template "header.html" .}}
2 |
3 |
User created !
4 |
5 |
6 | Desired username is {{.user.Username}}
7 |
8 |
9 | First name is {{.user.FirstName}}
10 |
11 |
12 | Last name is {{.user.LastName}}
13 |
14 |
15 | Age is {{.user.Age}}
16 |
17 |
18 | Password is {{.user.Password}}
19 |
20 |
21 | Email is {{.user.Email}}
22 |
23 |
24 |
Back
25 |
26 | {{template "footer.html" .}}
27 |
--------------------------------------------------------------------------------
/samples/validation/app/views/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
Home
3 |
4 | This sample is based on the Validation sample app provided
5 | with
Play! Framework .
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/samples/validation/app/views/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{.title}}
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/samples/validation/conf/app.conf:
--------------------------------------------------------------------------------
1 | app.name=Sample validation
2 | app.secret=774a66ffb3d8113a2f730bbb33192251dc521ab0bee1bb0d88290299fe05c618
3 | module.static=github.com/robfig/revel/modules/static
4 | module.testrunner=github.com/robfig/revel/modules/testrunner
5 | mode.dev=true
6 | [dev]
7 | [prod]
8 |
--------------------------------------------------------------------------------
/samples/validation/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | module:testrunner
6 |
7 | GET / Application.Index
8 |
9 | # Map static resources from the app/public folder to /public
10 | GET /public/*filepath Static.Serve("public")
11 |
12 | # Catch all
13 | * /:controller/:action :controller.:action
14 |
--------------------------------------------------------------------------------
/samples/validation/public/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Arial, Sans;
3 | font-size: 90%;
4 | width: 800px;
5 | margin: 1em auto;
6 | }
7 |
8 | fieldset {
9 | border: 1px solid #888;
10 | margin-bottom: 1em;
11 | }
12 |
13 | legend {
14 | font-weight: bold;
15 | }
16 |
17 | label {
18 | display: block;
19 | width: 200px;
20 | float: left;
21 | cursor: pointer;
22 | }
23 |
24 | input[type=text], input[type=password] {
25 | border: 1px solid #aaa;
26 | float: left;
27 | margin-right: 5px;
28 | }
29 |
30 | .field {
31 | clear: both;
32 | margin-bottom: 2px;
33 | }
34 |
35 | .infos {
36 | color: #666;
37 | margin-bottom: 5px;
38 | }
39 |
40 | .error {
41 | color: #c00;
42 | }
43 |
44 | .hasError {
45 | background: pink;
46 | }
47 |
48 | .has-error {
49 | background: #ffb6c1;
50 | }
51 |
--------------------------------------------------------------------------------
/samples/validation/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/samples/validation/public/images/favicon.png
--------------------------------------------------------------------------------
/samples/validation/tests/apptest.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type ApplicationTest struct {
6 | revel.TestSuite
7 | }
8 |
9 | func (t *ApplicationTest) Before() {
10 | println("Set up")
11 | }
12 |
13 | func (t *ApplicationTest) TestThatIndexPageWorks() {
14 | t.Get("/")
15 | t.AssertOk()
16 | t.AssertContentType("text/html; charset=utf-8")
17 | }
18 |
19 | func (t *ApplicationTest) After() {
20 | println("Tear down")
21 | }
22 |
--------------------------------------------------------------------------------
/server_test.go:
--------------------------------------------------------------------------------
1 | package revel
2 |
3 | import (
4 | "net/http"
5 | "net/http/httptest"
6 | "os"
7 | "path"
8 | "strings"
9 | "testing"
10 | )
11 |
12 | // This tries to benchmark the usual request-serving pipeline to get an overall
13 | // performance metric.
14 | //
15 | // Each iteration runs one mock request to display a hotel's detail page by id.
16 | //
17 | // Contributing parts:
18 | // - Routing
19 | // - Controller lookup / invocation
20 | // - Parameter binding
21 | // - Session, flash, i18n cookies
22 | // - Render() call magic
23 | // - Template rendering
24 | func BenchmarkServeAction(b *testing.B) {
25 | benchmarkRequest(b, showRequest)
26 | }
27 |
28 | func BenchmarkServeJson(b *testing.B) {
29 | benchmarkRequest(b, jsonRequest)
30 | }
31 |
32 | func BenchmarkServePlaintext(b *testing.B) {
33 | benchmarkRequest(b, plaintextRequest)
34 | }
35 |
36 | // This tries to benchmark the static serving overhead when serving an "average
37 | // size" 7k file.
38 | func BenchmarkServeStatic(b *testing.B) {
39 | benchmarkRequest(b, staticRequest)
40 | }
41 |
42 | func benchmarkRequest(b *testing.B, req *http.Request) {
43 | startFakeBookingApp()
44 | b.ResetTimer()
45 | resp := httptest.NewRecorder()
46 | for i := 0; i < b.N; i++ {
47 | handle(resp, req)
48 | }
49 | }
50 |
51 | // Test that the booking app can be successfully run for a test.
52 | func TestFakeServer(t *testing.T) {
53 | startFakeBookingApp()
54 |
55 | resp := httptest.NewRecorder()
56 |
57 | // First, test that the expected responses are actually generated
58 | handle(resp, showRequest)
59 | if !strings.Contains(resp.Body.String(), "300 Main St.") {
60 | t.Errorf("Failed to find hotel address in action response:\n%s", resp.Body)
61 | t.FailNow()
62 | }
63 | resp.Body.Reset()
64 |
65 | handle(resp, staticRequest)
66 | sessvarsSize := getFileSize(t, path.Join(BasePath, "public", "js", "sessvars.js"))
67 | if int64(resp.Body.Len()) != sessvarsSize {
68 | t.Errorf("Expected sessvars.js to have %d bytes, got %d:\n%s", sessvarsSize, resp.Body.Len(), resp.Body)
69 | t.FailNow()
70 | }
71 | resp.Body.Reset()
72 |
73 | handle(resp, jsonRequest)
74 | if !strings.Contains(resp.Body.String(), `"Address":"300 Main St."`) {
75 | t.Errorf("Failed to find hotel address in JSON response:\n%s", resp.Body)
76 | t.FailNow()
77 | }
78 | resp.Body.Reset()
79 |
80 | handle(resp, plaintextRequest)
81 | if resp.Body.String() != "Hello, World!" {
82 | t.Errorf("Failed to find greeting in plaintext response:\n%s", resp.Body)
83 | t.FailNow()
84 | }
85 |
86 | resp.Body = nil
87 | }
88 |
89 | func getFileSize(t *testing.T, name string) int64 {
90 | fi, err := os.Stat(name)
91 | if err != nil {
92 | t.Errorf("Unable to stat file:\n%s", name)
93 | t.FailNow()
94 | }
95 | return fi.Size()
96 | }
97 |
98 | var (
99 | showRequest, _ = http.NewRequest("GET", "/hotels/3", nil)
100 | staticRequest, _ = http.NewRequest("GET", "/public/js/sessvars.js", nil)
101 | jsonRequest, _ = http.NewRequest("GET", "/hotels/3/booking", nil)
102 | plaintextRequest, _ = http.NewRequest("GET", "/hotels", nil)
103 | )
104 |
--------------------------------------------------------------------------------
/session_test.go:
--------------------------------------------------------------------------------
1 | package revel
2 |
3 | import (
4 | "net/http"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestSessionRestore(t *testing.T) {
10 | expireAfterDuration = 0
11 | originSession := make(Session)
12 | originSession["foo"] = "foo"
13 | originSession["bar"] = "bar"
14 | cookie := originSession.cookie()
15 | if !cookie.Expires.IsZero() {
16 | t.Error("incorrect cookie expire", cookie.Expires)
17 | }
18 |
19 | restoredSession := getSessionFromCookie(cookie)
20 | for k, v := range originSession {
21 | if restoredSession[k] != v {
22 | t.Errorf("session restore failed session[%s] != %s", k, v)
23 | }
24 | }
25 | }
26 |
27 | func TestSessionExpire(t *testing.T) {
28 | expireAfterDuration = time.Hour
29 | session := make(Session)
30 | session["user"] = "Tom"
31 | var cookie *http.Cookie
32 | for i := 0; i < 3; i++ {
33 | cookie = session.cookie()
34 | time.Sleep(time.Second)
35 | session = getSessionFromCookie(cookie)
36 | }
37 | expectExpire := time.Now().Add(expireAfterDuration)
38 | if cookie.Expires.Unix() < expectExpire.Add(-time.Second).Unix() {
39 | t.Error("expect expires", cookie.Expires, "after", expectExpire.Add(-time.Second))
40 | }
41 | if cookie.Expires.Unix() > expectExpire.Unix() {
42 | t.Error("expect expires", cookie.Expires, "before", expectExpire)
43 | }
44 |
45 | session.SetNoExpiration()
46 | for i := 0; i < 3; i++ {
47 | cookie = session.cookie()
48 | session = getSessionFromCookie(cookie)
49 | }
50 | cookie = session.cookie()
51 | if !cookie.Expires.IsZero() {
52 | t.Error("expect cookie expires is zero")
53 | }
54 |
55 | session.SetDefaultExpiration()
56 | cookie = session.cookie()
57 | expectExpire = time.Now().Add(expireAfterDuration)
58 | if cookie.Expires.Unix() < expectExpire.Add(-time.Second).Unix() {
59 | t.Error("expect expires", cookie.Expires, "after", expectExpire.Add(-time.Second))
60 | }
61 | if cookie.Expires.Unix() > expectExpire.Unix() {
62 | t.Error("expect expires", cookie.Expires, "before", expectExpire)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/skeleton/.gitignore:
--------------------------------------------------------------------------------
1 | test-results/
2 | tmp/
3 | routes/
4 |
--------------------------------------------------------------------------------
/skeleton/app/controllers/app.go:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type App struct {
6 | *revel.Controller
7 | }
8 |
9 | func (c App) Index() revel.Result {
10 | return c.Render()
11 | }
12 |
--------------------------------------------------------------------------------
/skeleton/app/init.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import "github.com/robfig/revel"
4 |
5 | func init() {
6 | // Filters is the default set of global filters.
7 | revel.Filters = []revel.Filter{
8 | revel.PanicFilter, // Recover from panics and display an error page instead.
9 | revel.RouterFilter, // Use the routing table to select the right Action
10 | revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
11 | revel.ParamsFilter, // Parse parameters into Controller.Params.
12 | revel.SessionFilter, // Restore and write the session cookie.
13 | revel.FlashFilter, // Restore and write the flash cookie.
14 | revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
15 | revel.I18nFilter, // Resolve the requested language
16 | HeaderFilter, // Add some security based headers
17 | revel.InterceptorFilter, // Run interceptors around the action.
18 | revel.CompressFilter, // Compress the result.
19 | revel.ActionInvoker, // Invoke the action.
20 | }
21 |
22 | // register startup functions with OnAppStart
23 | // ( order dependent )
24 | // revel.OnAppStart(InitDB())
25 | // revel.OnAppStart(FillCache())
26 | }
27 |
28 | // TODO turn this into revel.HeaderFilter
29 | // should probably also have a filter for CSRF
30 | // not sure if it can go in the same filter or not
31 | var HeaderFilter = func(c *revel.Controller, fc []revel.Filter) {
32 | // Add some common security headers
33 | c.Response.Out.Header().Add("X-Frame-Options", "SAMEORIGIN")
34 | c.Response.Out.Header().Add("X-XSS-Protection", "1; mode=block")
35 | c.Response.Out.Header().Add("X-Content-Type-Options", "nosniff")
36 |
37 | fc[0](c, fc[1:]) // Execute the next filter stage.
38 | }
39 |
--------------------------------------------------------------------------------
/skeleton/app/views/App/Index.html:
--------------------------------------------------------------------------------
1 | {{set . "title" "Home"}}
2 | {{template "header.html" .}}
3 |
4 |
14 |
15 |
16 |
17 |
18 | {{template "flash.html" .}}
19 |
20 |
21 |
22 |
23 | {{template "footer.html" .}}
24 |
--------------------------------------------------------------------------------
/skeleton/app/views/debug.html:
--------------------------------------------------------------------------------
1 |
20 |
44 |
45 |
46 |
65 |
--------------------------------------------------------------------------------
/skeleton/app/views/errors/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Not found
5 |
6 |
7 | {{if eq .RunMode "dev"}}
8 | {{template "errors/404-dev.html" .}}
9 | {{else}}
10 | {{with .Error}}
11 |
12 | {{.Title}}
13 |
14 |
15 | {{.Description}}
16 |
17 | {{end}}
18 | {{end}}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/skeleton/app/views/errors/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Application error
5 |
6 |
7 | {{if eq .RunMode "dev"}}
8 | {{template "errors/500-dev.html" .}}
9 | {{else}}
10 |
Oops, an error occured
11 |
12 | This exception has been logged.
13 |
14 | {{end}}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/skeleton/app/views/flash.html:
--------------------------------------------------------------------------------
1 | {{if .flash.success}}
2 |
3 | {{.flash.success}}
4 |
5 | {{end}}
6 |
7 | {{if or .errors .flash.error}}
8 |
9 | {{if .flash.error}}
10 | {{.flash.error}}
11 | {{end}}
12 |
13 | {{range .errors}}
14 | {{.}}
15 | {{end}}
16 |
17 |
18 | {{end}}
19 |
--------------------------------------------------------------------------------
/skeleton/app/views/footer.html:
--------------------------------------------------------------------------------
1 | {{if eq .RunMode "dev"}}
2 | {{template "debug.html" .}}
3 | {{end}}
4 |
5 |
6 |
--------------------------------------------------------------------------------
/skeleton/app/views/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{.title}}
6 |
7 |
8 |
9 |
10 | {{range .moreStyles}}
11 |
12 | {{end}}
13 | {{range .moreScripts}}
14 |
15 | {{end}}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/skeleton/conf/app.conf.template:
--------------------------------------------------------------------------------
1 | app.name={{ .AppName }}
2 | app.secret={{ .Secret }}
3 | http.addr=
4 | http.port=9000
5 | http.ssl=false
6 | http.sslcert=
7 | http.sslkey=
8 | cookie.httponly=false
9 | cookie.prefix=REVEL
10 | cookie.secure=false
11 | format.date=01/02/2006
12 | format.datetime=01/02/2006 15:04
13 | results.chunked=false
14 |
15 | log.trace.prefix = "TRACE "
16 | log.info.prefix = "INFO "
17 | log.warn.prefix = "WARN "
18 | log.error.prefix = "ERROR "
19 |
20 | # The default language of this application.
21 | i18n.default_language=en
22 |
23 | module.static=github.com/robfig/revel/modules/static
24 |
25 | [dev]
26 | mode.dev=true
27 | results.pretty=true
28 | watch=true
29 |
30 | module.testrunner = github.com/robfig/revel/modules/testrunner
31 |
32 | log.trace.output = off
33 | log.info.output = stderr
34 | log.warn.output = stderr
35 | log.error.output = stderr
36 |
37 | [prod]
38 | mode.dev=false
39 | results.pretty=false
40 | watch=false
41 |
42 | module.testrunner =
43 |
44 | log.trace.output = off
45 | log.info.output = off
46 | log.warn.output = %(app.name)s.log
47 | log.error.output = %(app.name)s.log
48 |
--------------------------------------------------------------------------------
/skeleton/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | module:testrunner
6 |
7 | GET / App.Index
8 |
9 | # Ignore favicon requests
10 | GET /favicon.ico 404
11 |
12 | # Map static resources from the /app/public folder to the /public path
13 | GET /public/*filepath Static.Serve("public")
14 |
15 | # Catch all
16 | * /:controller/:action :controller.:action
17 |
--------------------------------------------------------------------------------
/skeleton/messages/sample.en:
--------------------------------------------------------------------------------
1 | # Sample messages file for the English language (en)
2 | # Message file extensions should be ISO 639-1 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
3 | # Sections within each message file can optionally override the defaults using ISO 3166-1 alpha-2 codes (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
4 | # See also:
5 | # - http://www.rfc-editor.org/rfc/bcp/bcp47.txt
6 | # - http://www.w3.org/International/questions/qa-accept-lang-locales
7 |
8 |
--------------------------------------------------------------------------------
/skeleton/public/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/skeleton/public/img/favicon.png
--------------------------------------------------------------------------------
/skeleton/public/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/skeleton/public/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/skeleton/public/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/skeleton/public/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/skeleton/tests/apptest.go:
--------------------------------------------------------------------------------
1 | package tests
2 |
3 | import "github.com/robfig/revel"
4 |
5 | type AppTest struct {
6 | revel.TestSuite
7 | }
8 |
9 | func (t *AppTest) Before() {
10 | println("Set up")
11 | }
12 |
13 | func (t AppTest) TestThatIndexPageWorks() {
14 | t.Get("/")
15 | t.AssertOk()
16 | t.AssertContentType("text/html; charset=utf-8")
17 | }
18 |
19 | func (t *AppTest) After() {
20 | println("Tear down")
21 | }
22 |
--------------------------------------------------------------------------------
/templates/errors/403.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Forbidden
5 |
6 |
7 | {{with .Error}}
8 |
9 | {{.Title}}
10 |
11 |
12 | {{.Description}}
13 |
14 | {{end}}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/templates/errors/403.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "{{js .Error.Title}}",
3 | "description": "{{js .Error.Description}}"
4 | }
5 |
--------------------------------------------------------------------------------
/templates/errors/403.txt:
--------------------------------------------------------------------------------
1 | {{.Error.Title}}
2 |
3 | {{.Error.Description}}
4 |
--------------------------------------------------------------------------------
/templates/errors/403.xml:
--------------------------------------------------------------------------------
1 |
{{.Error.Description}}
2 |
--------------------------------------------------------------------------------
/templates/errors/404-dev.html:
--------------------------------------------------------------------------------
1 |
45 |
46 |
56 |
57 |
These routes have been tried, in this order :
58 |
59 | {{range .Router.Routes}}
60 | {{pad .Method 10}}{{pad .Path 50}}{{.Action}}
61 | {{end}}
62 |
63 |
64 |
--------------------------------------------------------------------------------
/templates/errors/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Not found
5 |
6 |
7 |
8 | {{if .DevMode}}
9 |
10 | {{template "errors/404-dev.html" .}}
11 |
12 | {{else}}
13 |
14 | {{with .Error}}
15 |
16 | {{.Title}}
17 |
18 |
19 | {{.Description}}
20 |
21 | {{end}}
22 |
23 | {{end}}
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/templates/errors/404.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "{{js .Error.Title}}",
3 | "description": "{{js .Error.Description}}"
4 | }
5 |
--------------------------------------------------------------------------------
/templates/errors/404.txt:
--------------------------------------------------------------------------------
1 | {{.Error.Title}}
2 |
3 | {{.Error.Description}}
4 |
--------------------------------------------------------------------------------
/templates/errors/404.xml:
--------------------------------------------------------------------------------
1 |
{{.Error.Description}}
2 |
--------------------------------------------------------------------------------
/templates/errors/500-dev.html:
--------------------------------------------------------------------------------
1 |
82 | {{with .Error}}
83 |
95 | {{if .Path}}
96 |
97 |
In {{.Path}}
98 | {{if .Line}}
99 | (around {{if .Line}}line {{.Line}}{{end}}{{if .Column}} column {{.Column}}{{end}})
100 | {{end}}
101 |
102 | {{range .ContextSource}}
103 |
104 |
{{.Line}}:
105 |
{{.Source}}
106 |
107 | {{end}}
108 |
109 | {{end}}
110 | {{if .MetaError}}
111 |
112 |
Additionally, an error occurred while handling this error.
113 |
114 | {{.MetaError}}
115 |
116 |
117 | {{end}}
118 | {{end}}
119 |
--------------------------------------------------------------------------------
/templates/errors/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Application error
5 |
6 |
7 | {{if .DevMode}}
8 | {{template "errors/500-dev.html" .}}
9 | {{else}}
10 |
Oops, an error occured
11 |
12 | This exception has been logged.
13 |
14 | {{end}}
15 |
16 |
17 |
--------------------------------------------------------------------------------
/templates/errors/500.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "{{js .Error.Title}}",
3 | "description": "{{js .Error.Description}}"
4 | }
5 |
--------------------------------------------------------------------------------
/templates/errors/500.txt:
--------------------------------------------------------------------------------
1 | {{.Error.Title}}
2 | {{.Error.Description}}
3 |
4 | {{if eq .RunMode "dev"}}
5 | {{with .Error}}
6 | {{if .Path}}
7 | ----------
8 | In {{.Path}} {{if .Line}}(around line {{.Line}}){{end}}
9 |
10 | {{range .ContextSource}}
11 | {{if .IsError}}>{{else}} {{end}} {{.Line}}: {{.Source}}{{end}}
12 |
13 | {{end}}
14 | {{end}}
15 | {{end}}
16 |
--------------------------------------------------------------------------------
/templates/errors/500.xml:
--------------------------------------------------------------------------------
1 |
2 | {{.Error.Title}}
3 | {{.Error.Description}}
4 |
5 |
--------------------------------------------------------------------------------
/testdata/i18n/config/test_app.conf:
--------------------------------------------------------------------------------
1 | app.name={{ .AppName }}
2 | app.secret={{ .Secret }}
3 | http.addr=
4 | http.port=9000
5 | cookie.prefix=REVEL
6 |
7 | i18n.default_language=en
8 | i18n.cookie=APP_LANG
9 |
10 | [dev]
11 | results.pretty=true
12 | results.staging=true
13 | watch=true
14 |
15 | module.testrunner = github.com/robfig/revel/modules/testrunner
16 | module.static=github.com/robfig/revel/modules/static
17 |
18 | log.trace.output = off
19 | log.info.output = stderr
20 | log.warn.output = stderr
21 | log.error.output = stderr
22 |
23 | [prod]
24 | results.pretty=false
25 | results.staging=false
26 | watch=false
27 |
28 | module.testrunner =
29 |
30 | log.trace.output = off
31 | log.info.output = off
32 | log.warn.output = %(app.name)s.log
33 | log.error.output = %(app.name)s.log
34 |
--------------------------------------------------------------------------------
/testdata/i18n/messages/dutch_messages.nl:
--------------------------------------------------------------------------------
1 | greeting=Hallo
2 | greeting.name=Rob
3 | greeting.suffix=, welkom bij Revel!
4 |
5 | [NL]
6 | greeting=Goeiedag
7 |
8 | [BE]
9 | greeting=Hallokes
10 |
--------------------------------------------------------------------------------
/testdata/i18n/messages/english_messages.en:
--------------------------------------------------------------------------------
1 | greeting=Hello
2 | greeting.name=Rob
3 | greeting.suffix=, welcome to Revel!
4 |
5 | folded=Greeting is '%(greeting)s'
6 | folded.arguments=%(greeting.name)s is %d years old
7 |
8 | arguments.string=My name is %s
9 | arguments.hex=The number %d in hexadecimal notation would be %x
10 | arguments.none=No arguments here son
11 |
12 | only_exists_in_default=Default
13 |
14 | [AU]
15 | greeting=G'day
16 |
17 | [US]
18 | greeting=Howdy
19 |
20 | [GB]
21 | greeting=All right
--------------------------------------------------------------------------------
/testdata/i18n/messages/english_messages2.en:
--------------------------------------------------------------------------------
1 | greeting2=Yo!
2 |
--------------------------------------------------------------------------------
/testdata/i18n/messages/invalid_message_file_name.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robfig/revel/e8aac16df9e93b17d309595b4ff57405dc567fdf/testdata/i18n/messages/invalid_message_file_name.txt
--------------------------------------------------------------------------------
/util_test.go:
--------------------------------------------------------------------------------
1 | package revel
2 |
3 | import (
4 | "path"
5 | "path/filepath"
6 | "reflect"
7 | "testing"
8 | )
9 |
10 | func TestContentTypeByFilename(t *testing.T) {
11 | testCases := map[string]string{
12 | "xyz.jpg": "image/jpeg",
13 | "helloworld.c": "text/x-c; charset=utf-8",
14 | "helloworld.": "application/octet-stream",
15 | "helloworld": "application/octet-stream",
16 | "hello.world.c": "text/x-c; charset=utf-8",
17 | }
18 | srcPath, _ := findSrcPaths(REVEL_IMPORT_PATH)
19 | ConfPaths = []string{path.Join(
20 | srcPath,
21 | filepath.FromSlash(REVEL_IMPORT_PATH),
22 | "conf"),
23 | }
24 | LoadMimeConfig()
25 | for filename, expected := range testCases {
26 | actual := ContentTypeByFilename(filename)
27 | if actual != expected {
28 | t.Errorf("%s: %s, Expected %s", filename, actual, expected)
29 | }
30 | }
31 | }
32 |
33 | func TestEqual(t *testing.T) {
34 | type testStruct struct{}
35 | type testStruct2 struct{}
36 | i, i2 := 8, 9
37 | s, s2 := "@朕µ\n\tüöäß", "@朕µ\n\tüöäss"
38 | slice, slice2 := []int{1, 2, 3, 4, 5}, []int{1, 2, 3, 4, 5}
39 | slice3, slice4 := []int{5, 4, 3, 2, 1}, []int{5, 4, 3, 2, 1}
40 |
41 | tm := map[string][]interface{}{
42 | "slices": {slice, slice2},
43 | "slices2": {slice3, slice4},
44 | "types": {new(testStruct), new(testStruct)},
45 | "types2": {new(testStruct2), new(testStruct2)},
46 | "ints": {int(i), int8(i), int16(i), int32(i), int64(i)},
47 | "ints2": {int(i2), int8(i2), int16(i2), int32(i2), int64(i2)},
48 | "uints": {uint(i), uint8(i), uint16(i), uint32(i), uint64(i)},
49 | "uints2": {uint(i2), uint8(i2), uint16(i2), uint32(i2), uint64(i2)},
50 | "floats": {float32(i), float64(i)},
51 | "floats2": {float32(i2), float64(i2)},
52 | "strings": {[]byte(s), s},
53 | "strings2": {[]byte(s2), s2},
54 | }
55 |
56 | testRow := func(row, row2 string, expected bool) {
57 | for _, a := range tm[row] {
58 | for _, b := range tm[row2] {
59 | ok := Equal(a, b)
60 | if ok != expected {
61 | ak := reflect.TypeOf(a).Kind()
62 | bk := reflect.TypeOf(b).Kind()
63 | t.Errorf("eq(%s=%v,%s=%v) want %t got %t", ak, a, bk, b, expected, ok)
64 | }
65 | }
66 | }
67 | }
68 |
69 | testRow("slices", "slices", true)
70 | testRow("slices", "slices2", false)
71 | testRow("slices2", "slices", false)
72 |
73 | testRow("types", "types", true)
74 | testRow("types2", "types", false)
75 | testRow("types", "types2", false)
76 |
77 | testRow("ints", "ints", true)
78 | testRow("ints", "ints2", false)
79 | testRow("ints2", "ints", false)
80 |
81 | testRow("uints", "uints", true)
82 | testRow("uints2", "uints", false)
83 | testRow("uints", "uints2", false)
84 |
85 | testRow("floats", "floats", true)
86 | testRow("floats2", "floats", false)
87 | testRow("floats", "floats2", false)
88 |
89 | testRow("strings", "strings", true)
90 | testRow("strings2", "strings", false)
91 | testRow("strings", "strings2", false)
92 | }
93 |
--------------------------------------------------------------------------------
/validation_test.go:
--------------------------------------------------------------------------------
1 | package revel
2 |
3 | import (
4 | "net/http"
5 | "net/http/httptest"
6 | "testing"
7 | )
8 |
9 | // getRecordedCookie returns the recorded cookie from a ResponseRecorder with
10 | // the given name. It utilizes the cookie reader found in the standard library.
11 | func getRecordedCookie(recorder *httptest.ResponseRecorder, name string) (*http.Cookie, error) {
12 | r := &http.Response{Header: recorder.HeaderMap}
13 | for _, cookie := range r.Cookies() {
14 | if cookie.Name == name {
15 | return cookie, nil
16 | }
17 | }
18 | return nil, http.ErrNoCookie
19 | }
20 |
21 | func validationTester(req *Request, fn func(c *Controller)) *httptest.ResponseRecorder {
22 | recorder := httptest.NewRecorder()
23 | c := NewController(req, NewResponse(recorder))
24 | ValidationFilter(c, []Filter{func(c *Controller, _ []Filter) {
25 | fn(c)
26 | }})
27 | return recorder
28 | }
29 |
30 | // Test that errors are encoded into the _ERRORS cookie.
31 | func TestValidationWithError(t *testing.T) {
32 | recorder := validationTester(buildEmptyRequest(), func(c *Controller) {
33 | c.Validation.Required("")
34 | if !c.Validation.HasErrors() {
35 | t.Fatal("errors should be present")
36 | }
37 | c.Validation.Keep()
38 | })
39 |
40 | if cookie, err := getRecordedCookie(recorder, "REVEL_ERRORS"); err != nil {
41 | t.Fatal(err)
42 | } else if cookie.MaxAge < 0 {
43 | t.Fatalf("cookie should not expire")
44 | }
45 | }
46 |
47 | // Test that no cookie is sent if errors are found, but Keep() is not called.
48 | func TestValidationNoKeep(t *testing.T) {
49 | recorder := validationTester(buildEmptyRequest(), func(c *Controller) {
50 | c.Validation.Required("")
51 | if !c.Validation.HasErrors() {
52 | t.Fatal("errors should not be present")
53 | }
54 | })
55 |
56 | if _, err := getRecordedCookie(recorder, "REVEL_ERRORS"); err != http.ErrNoCookie {
57 | t.Fatal(err)
58 | }
59 | }
60 |
61 | // Test that a previously set _ERRORS cookie is deleted if no errors are found.
62 | func TestValidationNoKeepCookiePreviouslySet(t *testing.T) {
63 | req := buildRequestWithCookie("REVEL_ERRORS", "invalid")
64 | recorder := validationTester(req, func(c *Controller) {
65 | c.Validation.Required("success")
66 | if c.Validation.HasErrors() {
67 | t.Fatal("errors should not be present")
68 | }
69 | })
70 |
71 | if cookie, err := getRecordedCookie(recorder, "REVEL_ERRORS"); err != nil {
72 | t.Fatal(err)
73 | } else if cookie.MaxAge >= 0 {
74 | t.Fatalf("cookie should be deleted")
75 | }
76 | }
77 |
--------------------------------------------------------------------------------