0c7d286cf2c659815b916a87443cf3a962588d15
[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
57 # Returns the game with `name` or null if no game exists with this name.
58 fun load_game(name: String): nullable Game do
59 var repo = new Repo(api, name)
60 if api.was_error then return null
61 var game = new Game(api, repo)
62 game.root_url = root_url
63 return game
64 end
65 end
66
67 # An action that require a game.
68 class GameAction
69 super RpgAction
70
71 # Response page stub.
72 var page: NitRpgPage is noinit
73
74 # Target game.
75 var game: Game is noinit
76
77 redef fun answer(request, url) is abstract
78
79 # Check errors and prepare response.
80 private fun prepare_response(request: HttpRequest, url: String): HttpResponse do
81 var owner = request.param("owner")
82 var repo_name = request.param("repo")
83 if owner == null or repo_name == null then
84 var msg = "Bad request: should look like /games/:owner/:repo."
85 return bad_request(msg)
86 end
87 var game = load_game("{owner}/{repo_name}")
88 if game == null then
89 var msg = api.last_error.message
90 return bad_request("Repo Error: {msg}")
91 end
92 self.game = game
93 var response = new HttpResponse(200)
94 page = new NitRpgPage(root_url)
95 page.side_panels.add new GameStatusPanel(game)
96 page.breadcrumbs = new Breadcrumbs
97 page.breadcrumbs.add_link(game.url, game.name)
98 prepare_pagination(request)
99 return response
100 end
101
102 # Parse pagination related parameters.
103 private fun prepare_pagination(request: HttpRequest) do
104 var args = request.get_args
105 list_from = args.get_or_default("pfrom", "0").to_i
106 list_limit = args.get_or_default("plimit", "10").to_i
107 end
108
109 # Limit of events to display in lists.
110 var list_limit = 10
111
112 # From where to start the display of events related lists.
113 var list_from = 0
114 end
115
116 # Repo overview page.
117 class RepoHome
118 super GameAction
119
120 redef fun answer(request, url) do
121 var rsp = prepare_response(request, url)
122 page.side_panels.add new ShortListPlayersPanel(game)
123 page.flow_panels.add new PodiumPanel(game)
124 page.flow_panels.add new EventListPanel(game, list_limit, list_from)
125 page.flow_panels.add new AchievementsListPanel(game)
126 rsp.body = page.write_to_string
127 return rsp
128 end
129 end
130
131 # Repo players list.
132 class ListPlayers
133 super GameAction
134
135 redef fun answer(request, url) do
136 var rsp = prepare_response(request, url)
137 page.breadcrumbs.add_link(game.url / "players", "players")
138 page.flow_panels.add new ListPlayersPanel(game)
139 rsp.body = page.write_to_string
140 return rsp
141 end
142 end
143
144 # Player details page.
145 class PlayerHome
146 super GameAction
147
148 redef fun answer(request, url) do
149 var rsp = prepare_response(request, url)
150 var name = request.param("player")
151 if name == null then
152 var msg = "Bad request: should look like /:owner/:repo/:players/:name."
153 return bad_request(msg)
154 end
155 var player = game.load_player(name)
156 if player == null then
157 return bad_request("Request Error: unknown player {name}.")
158 end
159 page.breadcrumbs.add_link(game.url / "players", "players")
160 page.breadcrumbs.add_link(player.url, name)
161 page.side_panels.clear
162 page.side_panels.add new PlayerStatusPanel(game, player)
163 page.flow_panels.add new PlayerReviewsPanel(game, player)
164 page.flow_panels.add new AchievementsListPanel(player)
165 page.flow_panels.add new EventListPanel(player, list_limit, list_from)
166 rsp.body = page.write_to_string
167 return rsp
168 end
169 end
170
171 # Display the list of achievements unlocked for this game.
172 class ListAchievements
173 super GameAction
174
175 redef fun answer(request, url) do
176 var rsp = prepare_response(request, url)
177 page.breadcrumbs.add_link(game.url / "achievements", "achievements")
178 page.flow_panels.add new AchievementsListPanel(game)
179 rsp.body = page.write_to_string
180 return rsp
181 end
182 end
183
184 # Player details page.
185 class AchievementHome
186 super GameAction
187
188 redef fun answer(request, url) do
189 var rsp = prepare_response(request, url)
190 var name = request.param("achievement")
191 if name == null then
192 var msg = "Bad request: should look like /:owner/:repo/achievements/:achievement."
193 return bad_request(msg)
194 end
195 var achievement = game.load_achievement(name)
196 if achievement == null then
197 return bad_request("Request Error: unknown achievement {name}.")
198 end
199 page.breadcrumbs.add_link(game.url / "achievements", "achievements")
200 page.breadcrumbs.add_link(achievement.url, achievement.name)
201 page.flow_panels.add new AchievementPanel(achievement)
202 page.flow_panels.add new EventListPanel(achievement, list_limit, list_from)
203 rsp.body = page.write_to_string
204 return rsp
205 end
206 end
207
208 if args.length != 3 then
209 print "Error: missing argument"
210 print ""
211 print "Usage:"
212 print "web <host> <port> <root_url>"
213 exit 1
214 end
215
216 var host = args[0]
217 var port = args[1]
218 var root = args[2]
219
220 var iface = "{host}:{port}"
221 var vh = new VirtualHost(iface)
222 vh.routes.add new Route("/styles/", new FileServer("www/styles"))
223 vh.routes.add new Route("/games/:owner/:repo/players/:player", new PlayerHome(root))
224 vh.routes.add new Route("/games/:owner/:repo/players", new ListPlayers(root))
225 vh.routes.add new Route("/games/:owner/:repo/achievements/:achievement", new AchievementHome(root))
226 vh.routes.add new Route("/games/:owner/:repo/achievements", new ListAchievements(root))
227 vh.routes.add new Route("/games/:owner/:repo", new RepoHome(root))
228
229 var fac = new HttpFactory.and_libevent
230 fac.config.virtual_hosts.add vh
231
232 print "Launching server on http://{iface}/"
233 fac.run