Merge branch 'master' of git://git.pipapo.org/webgit
[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 "webgit.h"
22 #include "actions.h"
23 #include "summary.h"
24 #include "object.h"
25 #include "log.h"
26 #include "repo.h"
27 #include "age.h"
28 #include "branch.h"
29 #include "tag.h"
30
31 #include "llist.h"
32 #include <cwa.h>
33
34 /* todo configure this */
35 #define SHA1_HEADER <openssl/sha.h>
36 #include "git/cache.h"
37 #include "git/refs.h"
38
39 #include <stdio.h>
40
41
42 Html
43 webgit_main_link (struct webgit_query* query, Html text)
44 {
45   return html (
46                html_tag ("a",
47                          html_attr ("href", html_str (query->request->script_name))
48                          ),
49                text
50                );
51 }
52
53
54 /*
55   Main (repository list) page
56 */
57 static Html
58 webgit_main_menu_action (struct webgit_query* query)
59 {
60   return html (html_tag ("div"),
61                html (
62                      html_tag ("img",
63                                html_attr ("src", webgit_webskinpath (query, "icons/webgit_logo.png")),
64                                html_attr ("alt", "Webgit-Logo")
65                                )
66                      ), "<br />",
67                html_include (webgit_skinpath (query, "inc/site.inc")), "<br />",
68                // TODO: webgit_user_config_link (query), "<br />",
69                html (
70                      html_tag ("a",
71                                html_attr ("href", webgit_webskinpath (query, "inc/about.html"))
72                                ),
73                      "Powered by webgit"
74                      )
75                );
76 }
77
78 static Html
79 webgit_main_content_action (struct webgit_query* query)
80 {
81   Html table = html_list ();
82
83   html_list_append (table, html(
84                           html(html_tag ("tr"),
85                                html(html_tag ("th"),"Name"),
86                                html(html_tag ("th"),"Git URL"),
87                                html(html_tag ("th"),"Description"),
88                                html(html_tag ("th"),"Owner"),
89                                html(html_tag ("th"),"Last Change"),
90                                html(html_tag ("th"),"Author"),
91                                html(html_tag ("th"),"Committer"),
92                                html(html_tag ("th"),"Action")
93                                ),
94                           html_nl()
95                           )
96               );
97
98   int row = 0;
99
100   LLIST_FOREACH (&query->repos, node)
101     {
102       struct webgit_repo_info* n = (struct webgit_repo_info*)node;
103
104       webgit_repoinfo_find_last (n);
105
106       if (n->age < n->maxage)
107         {
108           html_list_append (table, html (
109                                          html_tag ("tr",
110                                                    html_attr ("class", (++row & 1) ? "odd" : "even")
111                                                    ),
112                                          html(html_tag ("td"),
113                                               webgit_summary_link (query, n, html (n->name))),
114                                          html(html_tag ("td"), n->url ?
115                                               html (
116                                                     html_tag ("a", html_attr ("href", n->url)),
117                                                     n->url)
118                                               : html("N/A")
119                                               ),
120                                          html(html_tag ("td"), n->description),
121                                          html(html_tag ("td"), n->owner),
122                                          html(html_tag ("td"),
123                                               webgit_object_link (query,
124                                                                   n->name, strlen(n->name),
125                                                                   n->last_commit, 40,
126                                                                   NULL,
127                                                                   NULL,
128                                                                   webgit_pretty_age (n->age))
129                                               ),
130                                          html(html_tag ("td"),
131                                               webgit_email_link (
132                                                                  html (n->last_author_name),
133                                                                  html (n->last_author_email)
134                                                                  )
135                                               ),
136                                          html(html_tag ("td"),
137                                               webgit_email_link (
138                                                                  html (n->last_committer_name),
139                                                                  html (n->last_committer_email)
140                                                                  )
141                                               ),
142                                          html(html_tag ("td"),
143                                               html(
144                                                    webgit_log_link (query,
145                                                                     n->name,
146                                                                     n->last_head,
147                                                                     NULL, 0,
148                                                                     query->count,
149                                                                     html("Log")
150                                                                     ),
151                                                    " ",
152                                                    "Diff ",
153                                                    //webgit_diff_link (query, n, n->last_head, html("Diff")),
154                                                    //" ",
155                                                    webgit_object_link (query,
156                                                                        n->name, strlen(n->name),
157                                                                        n->last_tree, 40,
158                                                                        NULL,
159                                                                        NULL,
160                                                                        html("Tree"))
161                                                    )
162                                               )
163                                          ),
164                             html_nl()
165                             );
166         }
167     }
168
169   return html (html_tag ("table", html_attr ("id", "summary")), table);
170 }
171
172 static Html
173 webgit_main_action (struct webgit_query* query)
174 {
175   return html(
176               html(html_tag("div", html_attr ("id", "sub-menu")), webgit_main_menu_action (query)), html_nl (),
177               html(html_tag("div", html_attr ("id", "content")), webgit_main_content_action (query)), html_nl ()
178               );
179 }
180
181
182 /*
183   Summary of a single repository
184 */
185 static Html
186 webgit_summary_menu_action (struct webgit_repo_info* repo)
187 {
188   return html (html_tag ("div"),
189                webgit_repo_logo (repo), "<br />",
190                repo->readme ? html_include (repo->readme) : html (), "<br />",
191                html (
192                      html_tag ("a",
193                                html_attr ("href", html_fmt ("%s?repo=%s&action=config",
194                                                             repo->query->request->script_name,
195                                                             repo->name)
196                                           )
197                                ),
198                      "Configure"
199                      ), "<br />",
200                webgit_main_link (repo->query, html ("Main")), "<br />"
201                );
202 }
203
204
205 static Html
206 webgit_summary_content_action (struct webgit_repo_info* repo)
207 {
208   Html content = html_list ();
209
210   /* Title */
211   html_list_append (content, html (html_tag ("h1"), repo->description));
212
213   /* Readme */
214   if (repo->readme)
215     html_list_append (content, html_include (repo->readme));
216
217   /* Info */
218   html_list_append (content,
219                     html (
220                           html (html_tag ("h2"), "Repository information"),
221                           repo->url ?
222                           html (
223                                 html_tag ("div"),
224                                 "Public URL: ",
225                                 html (
226                                       html_tag ("a", html_attr ("href", repo->url)),
227                                       repo->url
228                                       )
229                                 ) : html(),
230                           html (
231                                 html_tag ("div"),
232                                 "Owner: ", repo->owner
233                                 ),
234                           html(
235                                html_tag ("div"),
236                                "Last change: ",
237                                webgit_object_link (repo->query,
238                                                    repo->name, strlen(repo->name),
239                                                    repo->last_commit, 40,
240                                                    NULL,
241                                                    NULL,
242                                                    webgit_pretty_age (repo->age)),
243                                " in branch ",
244                                 webgit_log_link (repo->query,
245                                                  repo->name,
246                                                  repo->last_head,
247                                                  NULL, 0,
248                                                  repo->query->count,
249                                                  html ("'", repo->last_head, "'")
250                                                  )
251                                )
252                           )
253                     );
254
255   /* log */
256   html_list_append (content,
257                     html (
258                           html (html_tag ("h2"),
259                                 webgit_log_link (repo->query, repo->name, repo->last_head,
260                                                  NULL, 0, repo->query->count,
261                                                  html ("Log of branch '", repo->last_head, "'")
262                                                  )
263                                 ),
264                           webgit_log_table (repo->query, repo->last_head, 5 /*TODO: config this*/, 0))
265                     );
266
267
268   /* branches */
269   html_list_append (content,
270                     html (
271                           html (html_tag ("h2"),
272                                 webgit_branch_link (repo, NULL, repo->query->count,
273                                                     html ("Branches")
274                                                     )
275                                 ),
276                           webgit_branch_table (repo, NULL, 5 /*TODO: config this*/)
277                           )
278                     );
279
280   /* tags */
281   html_list_append (content,
282                     html (
283                           html (html_tag ("h2"),
284                                 webgit_tag_link (repo, NULL, repo->query->count,
285                                                  html ("Tags")
286                                                  )
287                                 ),
288                           webgit_tag_table (repo, NULL, 5 /*TODO: config this*/)
289                           )
290                     );
291
292
293   return content;
294 }
295
296
297 static Html
298 webgit_summary_action (struct webgit_query* query)
299 {
300   struct webgit_repo_info* repo = webgit_repo_enter (query);
301   webgit_repoinfo_find_last (repo);
302
303   return html(
304               html(html_tag("div", html_attr ("id", "sub-menu")), webgit_summary_menu_action (repo)), html_nl (),
305               html(html_tag("div", html_attr ("id", "content")), webgit_summary_content_action (repo)), html_nl ()
306               );
307 }
308
309
310 /*
311   Log page
312 */
313 static Html
314 webgit_log_action (struct webgit_query* query)
315 {
316   struct webgit_repo_info* repo = webgit_repo_enter (query);
317
318   return html(
319               html(html_tag("div", html_attr ("id", "sub-menu")), webgit_log_menu_action (repo)), html_nl (),
320               html(html_tag("div", html_attr ("id", "content")), webgit_log_content_action (repo)), html_nl ()
321               );
322 }
323
324
325 /*
326   Diff page
327 */
328 static Html
329 webgit_diff_action (struct webgit_query* query)
330 {
331   (void) query;
332 #if 0
333   return html (html_tag ("div"),
334                "Webgit logo<br />",
335                "Readme.html <br />",
336                "User (cookie) config <br />",
337                "About webgit (info/admin) <br />"
338                );
339 #endif
340   return html("diff");
341 }
342
343
344 /*
345   pretty printed objects
346 */
347 static Html
348 webgit_object_action (struct webgit_query* query)
349 {
350   struct webgit_repo_info* repo = webgit_repo_enter (query);
351
352   unsigned char sha1[20];
353   if (get_sha1 (query->object, sha1))
354     return html("error: unknown object");
355
356   switch (sha1_object_info(sha1, NULL))
357     {
358     case OBJ_COMMIT:
359       if (!query->deref_to_tree)
360         return webgit_object_commit_action (repo, sha1);
361       /* else fallthrough */
362     case OBJ_TREE:
363       return webgit_object_tree_action (repo, sha1);
364     case OBJ_BLOB:
365       return webgit_object_blob_action (repo, sha1);
366     case OBJ_TAG:
367       return webgit_object_tag_action (repo, sha1);
368
369       break;
370     default:
371       return html ("error: unknown object type");
372       break;
373     }
374 }
375
376
377 /*
378   treeish dereferenced to tree
379 */
380 static Html
381 webgit_tree_action (struct webgit_query* query)
382 {
383   query->deref_to_tree = 1;
384   return webgit_object_action (query);
385 }
386
387
388 /*
389   spew out raw data
390 */
391 static Html
392 webgit_raw_action (struct webgit_query* query)
393 {
394   webgit_repo_enter (query);
395
396   unsigned char sha1[20];
397   if (get_sha1 (query->object, sha1))
398     return html("error: unknown object");
399
400   void* buf;
401   unsigned long size;
402
403   buf = read_object_with_reference (sha1, "blob", &size, NULL);
404
405   free ((char*)query->content_type);
406   query->content_type = webgit_mimetype (query->path);
407
408   return html_binary (buf, size);
409 }
410
411
412 /*
413   show branch
414 */
415 static Html
416 webgit_branch_menu_action (struct webgit_repo_info* repo)
417 {
418   return html (html_tag ("div"),
419                webgit_repo_logo (repo), "<br />",
420                // TODO: "switch-branch-dropdown <br />",
421                // TODO: "push/fetch/merge <br />",
422                // TODO: "branch administation <br />",
423                webgit_summary_link (repo->query, repo, html("Summary")), "<br />",
424                webgit_main_link (repo->query, html ("Main"))
425                );
426 }
427
428
429 static Html
430 webgit_branch_action (struct webgit_query* query)
431 {
432   struct webgit_repo_info* repo = webgit_repo_enter (query);
433   webgit_repoinfo_find_last (repo);
434
435   return html(
436               html(html_tag("div", html_attr ("id", "sub-menu")), webgit_branch_menu_action (repo)), html_nl (),
437               html(html_tag("div", html_attr ("id", "content")),
438                    webgit_branch_table (repo, query->head, query->count), html_nl ())
439               );
440 }
441
442
443 /*
444   show tag
445 */
446 static Html
447 webgit_tag_menu_action (struct webgit_repo_info* repo)
448 {
449   (void) repo;
450   return html (html_tag ("div"),
451                webgit_repo_logo (repo), "<br />",
452                // TODO: "tag administation <br />",
453                webgit_summary_link (repo->query, repo, html("Summary")), "<br />",
454                webgit_main_link (repo->query, html ("Main"))
455                );
456 }
457
458
459 static Html
460 webgit_tag_action (struct webgit_query* query)
461 {
462   struct webgit_repo_info* repo = webgit_repo_enter (query);
463   webgit_repoinfo_find_last (repo);
464
465   return html(
466               html(html_tag("div", html_attr ("id", "sub-menu")), webgit_tag_menu_action (repo)), html_nl (),
467               html(html_tag("div", html_attr ("id", "content")),
468                    webgit_tag_table (repo, query->head, query->count), html_nl ())
469               );
470 }
471
472
473 /*
474   show and edit config
475 */
476 static Html
477 webgit_config_menu_action (struct webgit_repo_info* repo)
478 {
479   (void) repo;
480   return html (html_tag ("div"),
481                webgit_repo_logo (repo), "<br />",
482                // TODO: "manage remotes? <br />",
483                // TODO: "manage subprojects? <br />",
484                webgit_summary_link (repo->query, repo, html("Summary")), "<br />",
485                webgit_main_link (repo->query, html ("Main"))
486                );
487 }
488
489 static Html
490 webgit_config_content_action (struct webgit_repo_info* repo)
491 {
492   int readonly = !!access (git_path("config"), W_OK);
493
494   return html (
495                html_tag ("form",
496                          html_attr ("name", "config-change"),
497                          html_attr ("enctype", "multipart/form-data"),
498                          html_attr ("method", "post"),
499                          html_attr ("action",
500                                     html_fmt ("%s",
501                                               repo->query->request->script_name
502                                               )
503                                     )
504                          ),
505
506                html_hidden ("repo", repo->name),
507                html_hidden ("action", "config"),
508                html (
509                      html_tag ("textarea",
510                                html_attr ("name", "blob"),
511                                html_attr ("rows", "40"),
512                                html_attr ("cols", "80"),
513                                readonly ? "readonly" : ""
514                                ),
515                      html_include_escaped (git_path("config"))
516                      ),
517                readonly ? html ()
518                : html (
519                        html_tag ("input",
520                                  html_attr ("type", "submit"),
521                                  html_attr ("value", "Save")
522                                  )
523                        )
524                );
525 }
526
527 static Html
528 webgit_config_action (struct webgit_query* query)
529 {
530   struct webgit_repo_info* repo = webgit_repo_enter (query);
531   webgit_repoinfo_find_last (repo);
532
533   if (query->blob)
534     {
535       FILE* cfg = fopen (git_path ("config"), "w");
536
537       /*TODO rxpd check */
538       if (!cfg)
539         webgit_warn ("could not open %s for writing", git_path("config"));
540       else
541         {
542           if (fwrite (query->blob, query->blob_size, 1, cfg) < 1)
543             webgit_warn ("could not write config");
544           fclose (cfg);
545         }
546     }
547
548   return html(
549               html (html_tag ("div", html_attr("id", "sub-menu")), webgit_config_menu_action (repo)), html_nl (),
550               html (html_tag ("div", html_attr("id", "content")), webgit_config_content_action (repo)), html_nl ()
551               );
552 }
553
554
555 /*
556   edit an object
557 */
558 static Html
559 webgit_edit_action (struct webgit_query* query)
560 {
561   (void) query;
562   return html("edit");
563 }
564
565 /*
566   commit changes
567 */
568 static Html
569 webgit_commit_action (struct webgit_query* query)
570 {
571   (void) query;
572   return html("commit");
573 }
574
575
576 Html
577 webgit_action_dispatch (struct webgit_query* query)
578 {
579 #define WEBGIT_ACTION(name, _)                  \
580 {                                               \
581  #name,                                         \
582  webgit_##name##_action,                        \
583 },
584   struct action_table{
585     char* name;
586     Html (*cb)(struct webgit_query* query);
587   } cmds[] = {WEBGIT_ACTIONS {"", NULL}};
588 #undef WEBGIT_ACTION
589
590   for (struct action_table* j = cmds; j->cb; ++j)
591     {
592       if (!strcmp (j->name, query->action))
593         return j->cb (query);
594     }
595   return html("error, no such action");
596 }
597
598
599
600 /*
601 //      Local Variables:
602 //      mode: C
603 //      c-file-style: "gnu"
604 //      indent-tabs-mode: nil
605 //      End:
606 */