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