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