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