]> gitweb.ps.run Git - ps-cgit/blob - ui-repolist.c
Add support for sorting by Age in the repolist
[ps-cgit] / ui-repolist.c
1 /* ui-repolist.c: functions for generating the repolist page
2  *
3  * Copyright (C) 2006 Lars Hjemli
4  *
5  * Licensed under GNU General Public License v2
6  *   (see COPYING for full license text)
7  */
8
9 #include <time.h>
10
11 #include "cgit.h"
12 #include "html.h"
13 #include "ui-shared.h"
14
15 time_t read_agefile(char *path)
16 {
17         FILE *f;
18         static char buf[64], buf2[64];
19
20         if (!(f = fopen(path, "r")))
21                 return -1;
22         if (fgets(buf, sizeof(buf), f) == NULL)
23                 return -1;
24         fclose(f);
25         if (parse_date(buf, buf2, sizeof(buf2)))
26                 return strtoul(buf2, NULL, 10);
27         else
28                 return 0;
29 }
30
31 static void print_modtime(struct cgit_repo *repo)
32 {
33         char *path;
34         struct stat s;
35
36         path = fmt("%s/%s", repo->path, ctx.cfg.agefile);
37         if (stat(path, &s) == 0) {
38                 cgit_print_age(read_agefile(path), -1, NULL);
39                 return;
40         }
41
42         path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch);
43         if (stat(path, &s) != 0)
44                 return;
45         cgit_print_age(s.st_mtime, -1, NULL);
46 }
47
48 int is_match(struct cgit_repo *repo)
49 {
50         if (!ctx.qry.search)
51                 return 1;
52         if (repo->url && strcasestr(repo->url, ctx.qry.search))
53                 return 1;
54         if (repo->name && strcasestr(repo->name, ctx.qry.search))
55                 return 1;
56         if (repo->desc && strcasestr(repo->desc, ctx.qry.search))
57                 return 1;
58         if (repo->owner && strcasestr(repo->owner, ctx.qry.search))
59                 return 1;
60         return 0;
61 }
62
63 int is_in_url(struct cgit_repo *repo)
64 {
65         if (!ctx.qry.url)
66                 return 1;
67         if (repo->url && !prefixcmp(repo->url, ctx.qry.url))
68                 return 1;
69         return 0;
70 }
71
72 void print_header(int columns)
73 {
74         html("<tr class='nohover'>"
75              "<th class='left'>Name</th>"
76              "<th class='left'>Description</th>"
77              "<th class='left'>Owner</th>"
78              "<th class='left'><a href=\"?s=1\">Idle</a></th>");
79         if (ctx.cfg.enable_index_links)
80                 html("<th class='left'>Links</th>");
81         html("</tr>\n");
82 }
83
84
85 void print_pager(int items, int pagelen, char *search)
86 {
87         int i;
88         html("<div class='pager'>");
89         for(i = 0; i * pagelen < items; i++)
90                 cgit_index_link(fmt("[%d]", i+1), fmt("Page %d", i+1), NULL,
91                                 search, i * pagelen);
92         html("</div>");
93 }
94
95 static int cgit_reposort_modtime(const void *a, const void *b)
96 {
97         const struct cgit_repo *r1 = a;
98         const struct cgit_repo *r2 = b;
99         char *path;
100         struct stat s;
101         time_t t1, t2;
102         path = fmt("%s/%s", r1->path, ctx.cfg.agefile);
103         if (stat(path, &s) == 0) {
104                 t1 = read_agefile(path);
105         } else {
106                 path = fmt("%s/refs/heads/%s", r1->path, r1->defbranch);
107                 if (stat(path, &s) != 0)
108                         return 0;
109                 t1 =s.st_mtime;
110         }
111
112         path = fmt("%s/%s", r2->path, ctx.cfg.agefile);
113         if (stat(path, &s) == 0) {
114                 t2 = read_agefile(path);
115         } else {
116                 path = fmt("%s/refs/heads/%s", r2->path, r2->defbranch);
117                 if (stat(path, &s) != 0)
118                         return 0;
119                 t2 =s.st_mtime;
120         }
121         return t2-t1;
122 }
123
124 void cgit_print_repolist()
125 {
126         int i, columns = 4, hits = 0, header = 0;
127         char *last_group = NULL;
128
129         if (ctx.cfg.enable_index_links)
130                 columns++;
131
132         ctx.page.title = ctx.cfg.root_title;
133         cgit_print_http_headers(&ctx);
134         cgit_print_docstart(&ctx);
135         cgit_print_pageheader(&ctx);
136
137         if (ctx.cfg.index_header)
138                 html_include(ctx.cfg.index_header);
139
140         if(ctx.qry.sort)
141                 qsort(cgit_repolist.repos,cgit_repolist.count,sizeof(struct cgit_repo),cgit_reposort_modtime);
142
143         html("<table summary='repository list' class='list nowrap'>");
144         for (i=0; i<cgit_repolist.count; i++) {
145                 ctx.repo = &cgit_repolist.repos[i];
146                 if (!(is_match(ctx.repo) && is_in_url(ctx.repo)))
147                         continue;
148                 hits++;
149                 if (hits <= ctx.qry.ofs)
150                         continue;
151                 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
152                         continue;
153                 if (!header++)
154                         print_header(columns);
155                 if (!ctx.qry.sort &&
156                     ((last_group == NULL && ctx.repo->group != NULL) ||
157                     (last_group != NULL && ctx.repo->group == NULL) ||
158                     (last_group != NULL && ctx.repo->group != NULL &&
159                      strcmp(ctx.repo->group, last_group)))) {
160                         htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
161                               columns);
162                         html_txt(ctx.repo->group);
163                         html("</td></tr>");
164                         last_group = ctx.repo->group;
165                 }
166                 htmlf("<tr><td class='%s'>",
167                       ctx.repo->group ? "sublevel-repo" : "toplevel-repo");
168                 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
169                 html("</td><td>");
170                 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
171                 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
172                 html_link_close();
173                 html("</td><td>");
174                 html_txt(ctx.repo->owner);
175                 html("</td><td>");
176                 print_modtime(ctx.repo);
177                 html("</td>");
178                 if (ctx.cfg.enable_index_links) {
179                         html("<td>");
180                         cgit_summary_link("summary", NULL, "button", NULL);
181                         cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
182                                       0, NULL, NULL);
183                         cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
184                         html("</td>");
185                 }
186                 html("</tr>\n");
187         }
188         html("</table>");
189         if (!hits)
190                 cgit_print_error("No repositories found");
191         else if (hits > ctx.cfg.max_repo_count)
192                 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search);
193         cgit_print_docend();
194 }
195
196 void cgit_print_site_readme()
197 {
198         if (ctx.cfg.root_readme)
199                 html_include(ctx.cfg.root_readme);
200 }