Change License to GNU Affero General Public License
[webgit] / src / actions.c
1 /*
2     cehtehs git web frontend
3
4   Copyright (C)
5     2007, 2008,         Christian Thaeter <ct@pipapo.org>
6
7   This program is free software: you can redistribute it and/or modify
8   it under the terms of the GNU Affero General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU Affero General Public License for more details.
16
17   You should have received a copy of the GNU Affero General Public License
18   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "actions.h"
22 #include "summary.h"
23 #include "object.h"
24 #include "log.h"
25 #include "repo.h"
26 #include "age.h"
27 #include "branch.h"
28 #include "tag.h"
29
30 #include "llist.h"
31 #include <cwa.h>
32
33 /* todo configure this */
34 #define SHA1_HEADER <openssl/sha.h>
35 #include "git/cache.h"
36 #include "git/refs.h"
37
38 #include <stdio.h>
39
40
41 /*
42   Main (repository list) page
43 */
44 static Html
45 webgit_main_menu_action (struct webgit_query* query)
46 {
47   (void) query;
48   return html ("TODO: main menu");
49 }
50
51 static Html
52 webgit_header_action (struct webgit_query* query)
53 {
54   (void) query;
55   return html (
56                "webgit header action"
57                );
58 }
59
60 static Html
61 webgit_main_content_action (struct webgit_query* query)
62 {
63   Html table = html_list ();
64
65   html_list_append (table, html(
66                           html(html_tag ("tr"),
67                                html(html_tag ("th"),"Name"),
68                                html(html_tag ("th"),"Git URL"),
69                                html(html_tag ("th"),"Description"),
70                                html(html_tag ("th"),"Owner"),
71                                html(html_tag ("th"),"Last Change"),
72                                html(html_tag ("th"),"Author"),
73                                html(html_tag ("th"),"Committer"),
74                                html(html_tag ("th"),"Action")
75                                ),
76                           html_nl()
77                           )
78               );
79
80   LLIST_FOREACH (&query->repos, node)
81     {
82       struct webgit_repo_info* n = (struct webgit_repo_info*)node;
83
84       webgit_repoinfo_find_last (n);
85
86       if (n->age < n->maxage)
87         {
88           html_list_append (table, html (
89                                          /*
90                                            TODO:
91                                            It would be great to have alternate rows with class="odd" or class="even"
92                                            html_tag ("tr", html_attr ("class", "odd")),
93                                          */
94                                          html_tag ("tr"),
95                                          html(html_tag ("td"),
96                                               webgit_summary_link (query, n, html (n->name))),
97                                          html(html_tag ("td"), n->url ?
98                                               html (
99                                                     html_tag ("a", html_attr ("href", n->url)),
100                                                     n->url)
101                                               : html("N/A")
102                                               ),
103                                          html(html_tag ("td"), n->description),
104                                          html(html_tag ("td"), n->owner),
105                                          html(html_tag ("td"),
106                                               webgit_object_link (query,
107                                                                   n->name, strlen(n->name),
108                                                                   n->last_commit, 40,
109                                                                   NULL,
110                                                                   NULL,
111                                                                   webgit_pretty_age (n->age))
112                                               ),
113                                          html(html_tag ("td"),
114                                               webgit_email_link (
115                                                                  html (n->last_author_name),
116                                                                  html (n->last_author_email)
117                                                                  )
118                                               ),
119                                          html(html_tag ("td"),
120                                               webgit_email_link (
121                                                                  html (n->last_committer_name),
122                                                                  html (n->last_committer_email)
123                                                                  )
124                                               ),
125                                          html(html_tag ("td"),
126                                               html(
127                                                    webgit_log_link (query,
128                                                                     n->name,
129                                                                     n->last_head,
130                                                                     NULL, 0,
131                                                                     query->count,
132                                                                     html("Log")
133                                                                     ),
134                                                    " ",
135                                                    "Diff ",
136                                                    //webgit_diff_link (query, n, n->last_head, html("Diff")),
137                                                    //" ",
138                                                    webgit_object_link (query,
139                                                                        n->name, strlen(n->name),
140                                                                        n->last_tree, 40,
141                                                                        NULL,
142                                                                        NULL,
143                                                                        html("Tree"))
144                                                    )
145                                               )
146                                          ),
147                             html_nl()
148                             );
149         }
150     }
151
152   return html (html_tag ("table", html_attr ("id", "summary")), table);
153 }
154
155 static Html
156 webgit_main_action (struct webgit_query* query)
157 {
158   return html(
159               html(html_tag("div", html_attr("id", "sub-menu")), webgit_header_action (query)), html_nl (),
160               /*
161                 TODO:
162                 I'm not sure if we need this? Is the sub-menu sufficient?
163                 html(html_tag("div"), webgit_main_menu_action (query)), html_nl (), 
164               */
165               html(html_tag("div", html_attr("id", "content")), webgit_main_content_action (query)), html_nl ()
166               );
167 }
168
169
170 /*
171   Summary of a single repository
172 */
173 static Html
174 webgit_summary_menu_action (struct webgit_repo_info* repo)
175 {
176   (void) repo;
177   return html(html_tag("div", html_attr("id", "sub-menu")), "TODO: summary");
178 }
179
180
181 static Html
182 webgit_summary_content_action (struct webgit_repo_info* repo)
183 {
184   Html content = html_list ();
185
186   /* Title */
187   html_list_append (content, html (html_tag ("h1"), repo->description));
188
189   /* Readme */
190   if (repo->readme)
191     html_list_append (content, html_include (repo->readme));
192
193   /* Info */
194   html_list_append (content,
195                     html (
196                           html (html_tag ("h2"), "Repository information"),
197                           repo->url ?
198                           html (
199                                 html_tag ("div"),
200                                 "Public URL: ",
201                                 html (
202                                       html_tag ("a", html_attr ("href", repo->url)),
203                                       repo->url
204                                       )
205                                 ) : html(),
206                           html (
207                                 html_tag ("div"),
208                                 "Owner: ", repo->owner
209                                 ),
210                           html(
211                                html_tag ("div"),
212                                "Last change: ",
213                                webgit_object_link (repo->query,
214                                                    repo->name, strlen(repo->name),
215                                                    repo->last_commit, 40,
216                                                    NULL,
217                                                    NULL,
218                                                    webgit_pretty_age (repo->age)),
219                                " in branch ",
220                                 webgit_log_link (repo->query,
221                                                  repo->name,
222                                                  repo->last_head,
223                                                  NULL, 0,
224                                                  repo->query->count,
225                                                  html ("'", repo->last_head, "'")
226                                                  )
227                                )
228                           )
229                     );
230
231   /* log */
232   html_list_append (content,
233                     html (
234                           html (html_tag ("h2"), "Log of branch '", repo->last_head, "'"),
235                           webgit_log_table (repo->query, repo->last_head, 5 /*TODO: config this*/, 0))
236                     );
237
238
239   /* branches */
240   html_list_append (content,
241                     html (
242                           html (html_tag ("h2"), "Branches"),
243                           webgit_branch_table (repo, NULL, 5 /*TODO: config this*/)
244                           )
245                     );
246
247   /* tags */
248   html_list_append (content,
249                     html (
250                           html (html_tag ("h2"), "Tags"),
251                           webgit_tag_table (repo, NULL, 5 /*TODO: config this*/)
252                           )
253                     );
254
255
256   return content;
257 }
258
259
260 static Html
261 webgit_summary_action (struct webgit_query* query)
262 {
263   struct webgit_repo_info* repo = webgit_repo_enter (query);
264   webgit_repoinfo_find_last (repo);
265
266   return html(
267               html(html_tag("div"), webgit_summary_menu_action (repo)), html_nl (),
268               html(html_tag("div", html_attr("id", "content")), webgit_summary_content_action (repo)), html_nl ()
269               );
270 }
271
272
273 /*
274   Log page
275 */
276 static Html
277 webgit_log_action (struct webgit_query* query)
278 {
279   webgit_repo_enter (query);
280
281   return html(
282               html(html_tag("div"), webgit_log_menu_action (query)), html_nl (),
283               html(html_tag("div", html_attr("id", "content")), webgit_log_content_action (query)), html_nl ()
284               );
285 }
286
287
288 /*
289   Diff page
290 */
291 static Html
292 webgit_diff_action (struct webgit_query* query)
293 {
294   (void) query;
295   return html("diff");
296 }
297
298
299 /*
300   pretty printed objects
301 */
302 static Html
303 webgit_object_action (struct webgit_query* query)
304 {
305   struct webgit_repo_info* repo = webgit_repo_enter (query);
306
307   unsigned char sha1[20];
308   if (get_sha1 (query->object, sha1))
309     return html("error: unknown object");
310
311   switch (sha1_object_info(sha1, NULL))
312     {
313     case OBJ_COMMIT:
314       if (!query->deref_to_tree)
315         return webgit_object_commit_action (repo, sha1);
316       /* else fallthrough */
317     case OBJ_TREE:
318       return webgit_object_tree_action (repo, sha1);
319     case OBJ_BLOB:
320       return webgit_object_blob_action (repo, sha1);
321     case OBJ_TAG:
322       return webgit_object_tag_action (repo, sha1);
323
324       break;
325     default:
326       return html ("error: unknown object type");
327       break;
328     }
329 }
330
331
332 /*
333   treeish dereferenced to tree
334 */
335 static Html
336 webgit_tree_action (struct webgit_query* query)
337 {
338   query->deref_to_tree = 1;
339   return webgit_object_action (query);
340 }
341
342
343 /*
344   spew out raw data
345 */
346 static Html
347 webgit_raw_action (struct webgit_query* query)
348 {
349   webgit_repo_enter (query);
350
351   unsigned char sha1[20];
352   if (get_sha1 (query->object, sha1))
353     return html("error: unknown object");
354
355   void* buf;
356   unsigned long size;
357
358   buf = read_object_with_reference (sha1, "blob", &size, NULL);
359
360   free ((char*)query->content_type);
361   query->content_type = webgit_mimetype (query->path);
362
363   return html_binary (buf, size);
364 }
365
366
367 /*
368   show branch
369 */
370 static Html
371 webgit_branch_menu_action (struct webgit_repo_info* repo)
372 {
373   (void) repo;
374   return html ("TODO: branch sidebar");
375 }
376
377
378 static Html
379 webgit_branch_action (struct webgit_query* query)
380 {
381   struct webgit_repo_info* repo = webgit_repo_enter (query);
382   webgit_repoinfo_find_last (repo);
383
384   return html(
385               html(html_tag("div", html_attr("id", "header")), webgit_header_action (query)), html_nl (),
386               html(html_tag("div", html_attr("id", "sub-menu")), webgit_branch_menu_action (repo)), html_nl (),
387               html(html_tag("div", html_attr("id", "content")), webgit_branch_table (repo, query->head, query->count), html_nl ())
388               );
389 }
390
391
392
393 /*
394   show tag
395 */
396 static Html
397 webgit_tag_action (struct webgit_query* query)
398 {
399   (void) query;
400   return html("tag");
401 }
402
403
404 /*
405   show and edit config
406 */
407 static Html
408 webgit_config_menu_action (struct webgit_repo_info* repo)
409 {
410   (void) repo;
411   return html ("TODO: config menu");
412 }
413
414 static Html
415 webgit_config_content_action (struct webgit_repo_info* repo)
416 {
417   int readonly = !!access (git_path("config"), W_OK);
418
419   return html (
420                html_tag ("form",
421                          html_attr ("name", "config-change"),
422                          html_attr ("enctype", "multipart/form-data"),
423                          html_attr ("method", "post"),
424                          html_attr ("action",
425                                     html_fmt ("%s",
426                                               repo->query->request->script_name
427                                               )
428                                     )
429                          ),
430
431                html_hidden ("repo", repo->name),
432                html_hidden ("action", "config"),
433                html (
434                      html_tag ("textarea",
435                                html_attr ("name", "blob"),
436                                html_attr ("rows", "40"),
437                                html_attr ("cols", "80"),
438                                readonly ? "readonly" : ""
439                                ),
440                      html_include_escaped (git_path("config"))
441                      ),
442                readonly ? html ()
443                : html (
444                        html_tag ("input",
445                                  html_attr ("type", "submit"),
446                                  html_attr ("value", "Save")
447                                  )
448                        )
449                );
450 }
451
452 static Html
453 webgit_config_action (struct webgit_query* query)
454 {
455   struct webgit_repo_info* repo = webgit_repo_enter (query);
456   webgit_repoinfo_find_last (repo);
457
458   if (query->blob)
459     {
460       FILE* cfg = fopen (git_path ("config"), "w");
461
462       /*TODO rxpd check */
463       if (!cfg)
464         webgit_warn ("could not open %s for writing", git_path("config"));
465       else
466         {
467           if (fwrite (query->blob, query->blob_size, 1, cfg) < 1)
468             webgit_warn ("could not write config");
469           fclose (cfg);
470         }
471     }
472
473   return html(
474               html (html_tag ("div"), webgit_config_menu_action (repo)), html_nl (),
475               html (html_tag ("div", html_attr("id", "content")), webgit_config_content_action (repo)), html_nl ()
476               );
477 }
478
479
480 /*
481   edit an object
482 */
483 static Html
484 webgit_edit_action (struct webgit_query* query)
485 {
486   (void) query;
487   return html("edit");
488 }
489
490 /*
491   commit changes
492 */
493 static Html
494 webgit_commit_action (struct webgit_query* query)
495 {
496   (void) query;
497   return html("commit");
498 }
499
500
501 Html
502 webgit_action_dispatch (struct webgit_query* query)
503 {
504 #define WEBGIT_ACTION(name, _)                  \
505 {                                               \
506  #name,                                         \
507  webgit_##name##_action,                        \
508 },
509   struct action_table{
510     char* name;
511     Html (*cb)(struct webgit_query* query);
512   } cmds[] = {WEBGIT_ACTIONS {"", NULL}};
513 #undef WEBGIT_ACTION
514
515   for (struct action_table* j = cmds; j->cb; ++j)
516     {
517       if (!strcmp (j->name, query->action))
518         return j->cb (query);
519     }
520   return html("error, no such action");
521 }
522
523
524
525 /*
526 //      Local Variables:
527 //      mode: C
528 //      c-file-style: "gnu"
529 //      indent-tabs-mode: nil
530 //      End:
531 */