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