1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014-2015 Alexandre Terrasa <alexandre@moz-code.org>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Display `nitrpg` data as a website.
23 # A custom action forn `nitrpg`.
27 # Root URL is used as a prefix for all URL generated by the actions.
30 # Github oauth token used for GithubAPI.
31 var auth
: String is lazy
do return get_github_oauth
33 # API client used to import data from Github.
34 var api
: GithubAPI is lazy
do
35 var api
= new GithubAPI(auth
)
42 print
"Error: Invalid Github oauth token!"
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
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
= api
.load_repo
(name
)
60 if repo
== null then return null
61 var game
= new Game.from_mongo
(api
, repo
)
62 game
.root_url
= root_url
66 # Returns the list of saved games from NitRPG data.
67 fun load_games
: Array[Game] do
68 var res
= new Array[Game]
69 # TODO should be option
70 var mongo
= new MongoClient("mongodb://localhost:27017")
71 var db
= mongo
.database
("nitrpg")
72 for obj
in db
.collection
("games").find_all
(new JsonObject) do
73 var repo
= api
.load_repo
(obj
["name"].to_s
)
75 var game
= new Game(api
, repo
)
77 game
.root_url
= root_url
89 var page
: NitRpgPage is noinit
91 redef fun answer
(request
, url
) do
92 var readme
= load_readme
93 var games
= load_games
94 var response
= new HttpResponse(200)
95 page
= new NitRpgPage(root_url
)
96 page
.side_panels
.add
new GamesShortListPanel(root_url
, games
)
97 page
.flow_panels
.add
new MDPanel(readme
)
98 response
.body
= page
.write_to_string
102 # Load the string content of the nitrpg readme file.
103 private fun load_readme
: String do
104 var readme
= "README.md"
105 if not readme
.file_exists
then
106 return "Unable to locate README file."
108 var file
= new FileReader.open
(readme
)
109 var text
= file
.read_all
115 # Display the list of active game.
119 # Response page stub.
120 var page
: NitRpgPage is noinit
122 redef fun answer
(request
, url
) do
123 var games
= load_games
124 var response
= new HttpResponse(200)
125 page
= new NitRpgPage(root_url
)
126 page
.breadcrumbs
= new Breadcrumbs
127 page
.breadcrumbs
.add_link
(root_url
/ "games", "games")
128 page
.flow_panels
.add
new GamesListPanel(root_url
, games
)
129 response
.body
= page
.write_to_string
134 # An action that require a game.
138 # Response page stub.
139 var page
: NitRpgPage is noinit
142 var game
: Game is noinit
144 redef fun answer
(request
, url
) is abstract
146 # Check errors and prepare response.
147 private fun prepare_response
(request
: HttpRequest, url
: String): HttpResponse do
148 var owner
= request
.param
("owner")
149 var repo_name
= request
.param
("repo")
150 if owner
== null or repo_name
== null then
151 var msg
= "Bad request: should look like /games/:owner/:repo."
152 return bad_request
(msg
)
154 var game
= load_game
("{owner}/{repo_name}")
156 var msg
= api
.last_error
.message
157 return bad_request
("Repo Error: {msg}")
160 var response
= new HttpResponse(200)
161 page
= new NitRpgPage(root_url
)
162 page
.side_panels
.add
new GameStatusPanel(game
)
163 page
.breadcrumbs
= new Breadcrumbs
164 page
.breadcrumbs
.add_link
(game
.url
, game
.name
)
165 prepare_pagination
(request
)
169 # Parse pagination related parameters.
170 private fun prepare_pagination
(request
: HttpRequest) do
171 var args
= request
.get_args
172 list_from
= args
.get_or_default
("pfrom", "0").to_i
173 list_limit
= args
.get_or_default
("plimit", "10").to_i
176 # Limit of events to display in lists.
179 # From where to start the display of events related lists.
182 # TODO should also check 201, 203 ...
183 private fun is_response_error
(response
: HttpResponse): Bool do
184 return response
.status_code
!= 200
188 # Repo overview page.
192 redef fun answer
(request
, url
) do
193 var rsp
= prepare_response
(request
, url
)
194 if is_response_error
(rsp
) then return rsp
195 page
.side_panels
.add
new ShortListPlayersPanel(game
)
196 page
.flow_panels
.add
new PodiumPanel(game
)
197 page
.flow_panels
.add
new EventListPanel(game
, list_limit
, list_from
)
198 page
.flow_panels
.add
new AchievementsListPanel(game
)
199 rsp
.body
= page
.write_to_string
208 redef fun answer
(request
, url
) do
209 var rsp
= prepare_response
(request
, url
)
210 if is_response_error
(rsp
) then return rsp
211 page
.breadcrumbs
.add_link
(game
.url
/ "players", "players")
212 page
.flow_panels
.add
new ListPlayersPanel(game
)
213 rsp
.body
= page
.write_to_string
218 # Player details page.
222 redef fun answer
(request
, url
) do
223 var rsp
= prepare_response
(request
, url
)
224 if is_response_error
(rsp
) then return rsp
225 var name
= request
.param
("player")
227 var msg
= "Bad request: should look like /:owner/:repo/:players/:name."
228 return bad_request
(msg
)
230 var player
= game
.load_player
(name
)
231 if player
== null then
232 return bad_request
("Request Error: unknown player {name}.")
234 page
.breadcrumbs
.add_link
(game
.url
/ "players", "players")
235 page
.breadcrumbs
.add_link
(player
.url
, name
)
236 page
.side_panels
.clear
237 page
.side_panels
.add
new PlayerStatusPanel(game
, player
)
238 page
.flow_panels
.add
new PlayerReviewsPanel(game
, player
)
239 page
.flow_panels
.add
new PlayerWorkPanel(game
, player
)
240 page
.flow_panels
.add
new AchievementsListPanel(player
)
241 page
.flow_panels
.add
new EventListPanel(player
, list_limit
, list_from
)
242 rsp
.body
= page
.write_to_string
247 # Display the list of achievements unlocked for this game.
248 class ListAchievements
251 redef fun answer
(request
, url
) do
252 var rsp
= prepare_response
(request
, url
)
253 if is_response_error
(rsp
) then return rsp
254 page
.breadcrumbs
.add_link
(game
.url
/ "achievements", "achievements")
255 page
.flow_panels
.add
new AchievementsListPanel(game
)
256 rsp
.body
= page
.write_to_string
261 # Player details page.
262 class AchievementHome
265 redef fun answer
(request
, url
) do
266 var rsp
= prepare_response
(request
, url
)
267 if is_response_error
(rsp
) then return rsp
268 var name
= request
.param
("achievement")
270 var msg
= "Bad request: should look like /:owner/:repo/achievements/:achievement."
271 return bad_request
(msg
)
273 var achievement
= game
.load_achievement
(name
)
274 if achievement
== null then
275 return bad_request
("Request Error: unknown achievement {name}.")
277 page
.breadcrumbs
.add_link
(game
.url
/ "achievements", "achievements")
278 page
.breadcrumbs
.add_link
(achievement
.url
, achievement
.name
)
279 page
.flow_panels
.add
new AchievementPanel(achievement
)
280 page
.flow_panels
.add
new EventListPanel(achievement
, list_limit
, list_from
)
281 rsp
.body
= page
.write_to_string
286 if args
.length
!= 3 then
287 print
"Error: missing argument"
290 print
"web <host> <port> <root_url>"
298 var iface
= "{host}:{port}"
299 var vh
= new VirtualHost(iface
)
300 vh
.routes
.add
new Route("/styles/", new FileServer("www/styles"))
301 vh
.routes
.add
new Route("/games/:owner/:repo/players/:player", new PlayerHome(root
))
302 vh
.routes
.add
new Route("/games/:owner/:repo/players", new ListPlayers(root
))
303 vh
.routes
.add
new Route("/games/:owner/:repo/achievements/:achievement", new AchievementHome(root
))
304 vh
.routes
.add
new Route("/games/:owner/:repo/achievements", new ListAchievements(root
))
305 vh
.routes
.add
new Route("/games/:owner/:repo", new RepoHome(root
))
306 vh
.routes
.add
new Route("/games", new ListGames(root
))
307 vh
.routes
.add
new Route("/", new RpgHome(root
))
309 var fac
= new HttpFactory.and_libevent
310 fac
.config
.virtual_hosts
.add vh
312 print
"Launching server on http://{iface}/"