b27c457013448684f6ebcbeb81e16ee0a1830fe6
[nit.git] / contrib / nitrpg / src / web.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014-2015 Alexandre Terrasa <alexandre@moz-code.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Display `nitrpg` data as a website.
18 module web
19
20 import nitcorn
21 import templates
22
23 # A custom action forn `nitrpg`.
24 class RpgAction
25 super Action
26
27 # Root URL is used as a prefix for all URL generated by the actions.
28 var root_url: String
29
30 # Github oauth token used for GithubAPI.
31 var auth: String is lazy do return get_github_oauth
32
33 # API client used to import data from Github.
34 var api: GithubAPI is lazy do
35 var api = new GithubAPI(auth)
36 return api
37 end
38
39 init do
40 super
41 if auth.is_empty then
42 print "Error: Invalid Github oauth token!"
43 exit 1
44 end
45 end
46
47 # Return an Error reponse page.
48 fun bad_request(msg: String): HttpResponse do
49 var rsp = new HttpResponse(400)
50 var page = new NitRpgPage(root_url)
51 var error = new ErrorPanel(msg)
52 page.flow_panels.add error
53 rsp.body = page.write_to_string
54 return rsp
55 end
56 end
57
58 # An action that require a game.
59 class GameAction
60 super RpgAction
61
62 # Response page stub.
63 var page: NitRpgPage is noinit
64
65 # Target game.
66 var game: Game is noinit
67
68 redef fun answer(request, url) is abstract
69
70 # Check errors and prepare response.
71 private fun prepare_response(request: HttpRequest, url: String): HttpResponse do
72 var owner = request.param("owner")
73 var repo_name = request.param("repo")
74 if owner == null or repo_name == null then
75 var msg = "Bad request: should look like /repos/:owner/:repo."
76 return bad_request(msg)
77 end
78 var repo = new Repo(api, "{owner}/{repo_name}")
79 game = new Game(api, repo)
80 game.root_url = root_url
81 if api.was_error then
82 var msg = api.last_error.message
83 return bad_request("Repo Error: {msg}")
84 end
85 var response = new HttpResponse(200)
86 page = new NitRpgPage(root_url)
87 page.side_panels.add new GameStatusPanel(game)
88 page.breadcrumbs = new Breadcrumbs
89 page.breadcrumbs.add_link(game.url, game.name)
90 prepare_pagination(request)
91 return response
92 end
93
94 # Parse pagination related parameters.
95 private fun prepare_pagination(request: HttpRequest) do
96 var args = request.get_args
97 list_from = args.get_or_default("pfrom", "0").to_i
98 list_limit = args.get_or_default("plimit", "10").to_i
99 end
100
101 # Limit of events to display in lists.
102 var list_limit = 10
103
104 # From where to start the display of events related lists.
105 var list_from = 0
106 end
107
108 # Repo overview page.
109 class RepoHome
110 super GameAction
111
112 redef fun answer(request, url) do
113 var rsp = prepare_response(request, url)
114 page.side_panels.add new ShortListPlayersPanel(game)
115 page.flow_panels.add new PodiumPanel(game)
116 page.flow_panels.add new EventListPanel(game, list_limit, list_from)
117 page.flow_panels.add new AchievementsListPanel(game)
118 rsp.body = page.write_to_string
119 return rsp
120 end
121 end
122
123 # Repo players list.
124 class ListPlayers
125 super GameAction
126
127 redef fun answer(request, url) do
128 var rsp = prepare_response(request, url)
129 page.breadcrumbs.add_link(game.url / "players", "players")
130 page.flow_panels.add new ListPlayersPanel(game)
131 rsp.body = page.write_to_string
132 return rsp
133 end
134 end
135
136 # Player details page.
137 class PlayerHome
138 super GameAction
139
140 redef fun answer(request, url) do
141 var rsp = prepare_response(request, url)
142 var name = request.param("player")
143 if name == null then
144 var msg = "Bad request: should look like /:owner/:repo/:players/:name."
145 return bad_request(msg)
146 end
147 var player = game.load_player(name)
148 if player == null then
149 return bad_request("Request Error: unknown player {name}.")
150 end
151 page.breadcrumbs.add_link(game.url / "players", "players")
152 page.breadcrumbs.add_link(player.url, name)
153 page.side_panels.clear
154 page.side_panels.add new PlayerStatusPanel(game, player)
155 page.flow_panels.add new PlayerReviewsPanel(game, player)
156 page.flow_panels.add new AchievementsListPanel(player)
157 page.flow_panels.add new EventListPanel(player, list_limit, list_from)
158 rsp.body = page.write_to_string
159 return rsp
160 end
161 end
162
163 # Display the list of achievements unlocked for this game.
164 class ListAchievements
165 super GameAction
166
167 redef fun answer(request, url) do
168 var rsp = prepare_response(request, url)
169 page.breadcrumbs.add_link(game.url / "achievements", "achievements")
170 page.flow_panels.add new AchievementsListPanel(game)
171 rsp.body = page.write_to_string
172 return rsp
173 end
174 end
175
176 # Player details page.
177 class AchievementHome
178 super GameAction
179
180 redef fun answer(request, url) do
181 var rsp = prepare_response(request, url)
182 var name = request.param("achievement")
183 if name == null then
184 var msg = "Bad request: should look like /:owner/:repo/achievements/:achievement."
185 return bad_request(msg)
186 end
187 var achievement = game.load_achievement(name)
188 if achievement == null then
189 return bad_request("Request Error: unknown achievement {name}.")
190 end
191 page.breadcrumbs.add_link(game.url / "achievements", "achievements")
192 page.breadcrumbs.add_link(achievement.url, achievement.name)
193 page.flow_panels.add new AchievementPanel(achievement)
194 page.flow_panels.add new EventListPanel(achievement, list_limit, list_from)
195 rsp.body = page.write_to_string
196 return rsp
197 end
198 end
199
200 if args.length != 3 then
201 print "Error: missing argument"
202 print ""
203 print "Usage:"
204 print "web <host> <port> <root_url>"
205 exit 1
206 end
207
208 var host = args[0]
209 var port = args[1]
210 var root = args[2]
211
212 var iface = "{host}:{port}"
213 var vh = new VirtualHost(iface)
214 vh.routes.add new Route("/styles/", new FileServer("www/styles"))
215 vh.routes.add new Route("/games/:owner/:repo/players/:player", new PlayerHome(root))
216 vh.routes.add new Route("/games/:owner/:repo/players", new ListPlayers(root))
217 vh.routes.add new Route("/games/:owner/:repo/achievements/:achievement", new AchievementHome(root))
218 vh.routes.add new Route("/games/:owner/:repo/achievements", new ListAchievements(root))
219 vh.routes.add new Route("/games/:owner/:repo", new RepoHome(root))
220
221 var fac = new HttpFactory.and_libevent
222 fac.config.virtual_hosts.add vh
223
224 print "Launching server on http://{iface}/"
225 fac.run