neocgit

a more 'modern' version of cgit
Log | Files | Refs | Submodules | README | LICENSE | git clone https://git.ne02ptzero.me/git/neocgit

ui-repolist.c (9447B)


      1 /* ui-repolist.c: functions for generating the repolist page
      2  *
      3  * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
      4  *
      5  * Licensed under GNU General Public License v2
      6  *   (see COPYING for full license text)
      7  */
      8 
      9 #include "cgit.h"
     10 #include "ui-repolist.h"
     11 #include "html.h"
     12 #include "ui-shared.h"
     13 
     14 static time_t read_agefile(char *path)
     15 {
     16 	time_t result;
     17 	size_t size;
     18 	char *buf = NULL;
     19 	struct strbuf date_buf = STRBUF_INIT;
     20 
     21 	if (readfile(path, &buf, &size)) {
     22 		free(buf);
     23 		return -1;
     24 	}
     25 
     26 	if (parse_date(buf, &date_buf) == 0)
     27 		result = strtoul(date_buf.buf, NULL, 10);
     28 	else
     29 		result = 0;
     30 	free(buf);
     31 	strbuf_release(&date_buf);
     32 	return result;
     33 }
     34 
     35 static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime)
     36 {
     37 	struct strbuf path = STRBUF_INIT;
     38 	struct stat s;
     39 	struct cgit_repo *r = (struct cgit_repo *)repo;
     40 
     41 	if (repo->mtime != -1) {
     42 		*mtime = repo->mtime;
     43 		return 1;
     44 	}
     45 	strbuf_addf(&path, "%s/%s", repo->path, ctx.cfg.agefile);
     46 	if (stat(path.buf, &s) == 0) {
     47 		*mtime = read_agefile(path.buf);
     48 		if (*mtime) {
     49 			r->mtime = *mtime;
     50 			goto end;
     51 		}
     52 	}
     53 
     54 	strbuf_reset(&path);
     55 	strbuf_addf(&path, "%s/refs/heads/%s", repo->path,
     56 		    repo->defbranch ? repo->defbranch : "master");
     57 	if (stat(path.buf, &s) == 0) {
     58 		*mtime = s.st_mtime;
     59 		r->mtime = *mtime;
     60 		goto end;
     61 	}
     62 
     63 	strbuf_reset(&path);
     64 	strbuf_addf(&path, "%s/%s", repo->path, "packed-refs");
     65 	if (stat(path.buf, &s) == 0) {
     66 		*mtime = s.st_mtime;
     67 		r->mtime = *mtime;
     68 		goto end;
     69 	}
     70 
     71 	*mtime = 0;
     72 	r->mtime = *mtime;
     73 end:
     74 	strbuf_release(&path);
     75 	return (r->mtime != 0);
     76 }
     77 
     78 static void print_modtime(struct cgit_repo *repo)
     79 {
     80 	time_t t;
     81 	if (get_repo_modtime(repo, &t))
     82 		cgit_print_age(t, 0, -1);
     83 }
     84 
     85 static int is_match(struct cgit_repo *repo)
     86 {
     87 	if (!ctx.qry.search)
     88 		return 1;
     89 	if (repo->url && strcasestr(repo->url, ctx.qry.search))
     90 		return 1;
     91 	if (repo->name && strcasestr(repo->name, ctx.qry.search))
     92 		return 1;
     93 	if (repo->desc && strcasestr(repo->desc, ctx.qry.search))
     94 		return 1;
     95 	if (repo->owner && strcasestr(repo->owner, ctx.qry.search))
     96 		return 1;
     97 	return 0;
     98 }
     99 
    100 static int is_in_url(struct cgit_repo *repo)
    101 {
    102 	if (!ctx.qry.url)
    103 		return 1;
    104 	if (repo->url && starts_with(repo->url, ctx.qry.url))
    105 		return 1;
    106 	return 0;
    107 }
    108 
    109 static int is_visible(struct cgit_repo *repo)
    110 {
    111 	if (repo->hide || repo->ignore)
    112 		return 0;
    113 	if (!(is_match(repo) && is_in_url(repo)))
    114 		return 0;
    115 	return 1;
    116 }
    117 
    118 static int any_repos_visible(void)
    119 {
    120 	int i;
    121 
    122 	for (i = 0; i < cgit_repolist.count; i++) {
    123 		if (is_visible(&cgit_repolist.repos[i]))
    124 			return 1;
    125 	}
    126 	return 0;
    127 }
    128 
    129 static void print_sort_header(const char *title, const char *sort)
    130 {
    131 	char *currenturl = cgit_currenturl();
    132 	html("<th class='left'><a href='");
    133 	html_attr(currenturl);
    134 	htmlf("?s=%s", sort);
    135 	if (ctx.qry.search) {
    136 		html("&amp;q=");
    137 		html_url_arg(ctx.qry.search);
    138 	}
    139 	htmlf("'>%s</a></th>", title);
    140 	free(currenturl);
    141 }
    142 
    143 static void print_header(void)
    144 {
    145 	html("<tr class='nohover'>");
    146 	print_sort_header("Name", "name");
    147 	print_sort_header("Description", "desc");
    148 	if (ctx.cfg.enable_index_owner)
    149 		print_sort_header("Owner", "owner");
    150 	print_sort_header("Idle", "idle");
    151 	if (ctx.cfg.enable_index_links)
    152 		html("<th class='left'>Links</th>");
    153 	html("</tr>\n");
    154 }
    155 
    156 
    157 static void print_pager(int items, int pagelen, char *search, char *sort)
    158 {
    159 	int i, ofs;
    160 	char *class = NULL;
    161 	html("<ul class='pager'>");
    162 	for (i = 0, ofs = 0; ofs < items; i++, ofs = i * pagelen) {
    163 		class = (ctx.qry.ofs == ofs) ? "current" : NULL;
    164 		html("<li>");
    165 		cgit_index_link(fmt("[%d]", i + 1), fmt("Page %d", i + 1),
    166 				class, search, sort, ofs, 0);
    167 		html("</li>");
    168 	}
    169 	html("</ul>");
    170 }
    171 
    172 static int cmp(const char *s1, const char *s2)
    173 {
    174 	if (s1 && s2) {
    175 		if (ctx.cfg.case_sensitive_sort)
    176 			return strcmp(s1, s2);
    177 		else
    178 			return strcasecmp(s1, s2);
    179 	}
    180 	if (s1 && !s2)
    181 		return -1;
    182 	if (s2 && !s1)
    183 		return 1;
    184 	return 0;
    185 }
    186 
    187 static int sort_name(const void *a, const void *b)
    188 {
    189 	const struct cgit_repo *r1 = a;
    190 	const struct cgit_repo *r2 = b;
    191 
    192 	return cmp(r1->name, r2->name);
    193 }
    194 
    195 static int sort_desc(const void *a, const void *b)
    196 {
    197 	const struct cgit_repo *r1 = a;
    198 	const struct cgit_repo *r2 = b;
    199 
    200 	return cmp(r1->desc, r2->desc);
    201 }
    202 
    203 static int sort_owner(const void *a, const void *b)
    204 {
    205 	const struct cgit_repo *r1 = a;
    206 	const struct cgit_repo *r2 = b;
    207 
    208 	return cmp(r1->owner, r2->owner);
    209 }
    210 
    211 static int sort_idle(const void *a, const void *b)
    212 {
    213 	const struct cgit_repo *r1 = a;
    214 	const struct cgit_repo *r2 = b;
    215 	time_t t1, t2;
    216 
    217 	t1 = t2 = 0;
    218 	get_repo_modtime(r1, &t1);
    219 	get_repo_modtime(r2, &t2);
    220 	return t2 - t1;
    221 }
    222 
    223 static int sort_section(const void *a, const void *b)
    224 {
    225 	const struct cgit_repo *r1 = a;
    226 	const struct cgit_repo *r2 = b;
    227 	int result;
    228 
    229 	result = cmp(r1->section, r2->section);
    230 	if (!result) {
    231 		if (!strcmp(ctx.cfg.repository_sort, "age"))
    232 			result = sort_idle(r1, r2);
    233 		if (!result)
    234 			result = cmp(r1->name, r2->name);
    235 	}
    236 	return result;
    237 }
    238 
    239 struct sortcolumn {
    240 	const char *name;
    241 	int (*fn)(const void *a, const void *b);
    242 };
    243 
    244 static const struct sortcolumn sortcolumn[] = {
    245 	{"section", sort_section},
    246 	{"name", sort_name},
    247 	{"desc", sort_desc},
    248 	{"owner", sort_owner},
    249 	{"idle", sort_idle},
    250 	{NULL, NULL}
    251 };
    252 
    253 static int sort_repolist(char *field)
    254 {
    255 	const struct sortcolumn *column;
    256 
    257 	for (column = &sortcolumn[0]; column->name; column++) {
    258 		if (strcmp(field, column->name))
    259 			continue;
    260 		qsort(cgit_repolist.repos, cgit_repolist.count,
    261 			sizeof(struct cgit_repo), column->fn);
    262 		return 1;
    263 	}
    264 	return 0;
    265 }
    266 
    267 
    268 void cgit_print_repolist(void)
    269 {
    270 	int i, columns = 3, hits = 0, header = 0;
    271 	char *last_section = NULL;
    272 	char *section;
    273 	char *repourl;
    274 	int sorted = 0;
    275 
    276 	if (!any_repos_visible()) {
    277 		cgit_print_error_page(404, "Not found", "No repositories found");
    278 		return;
    279 	}
    280 
    281 	if (ctx.cfg.enable_index_links)
    282 		++columns;
    283 	if (ctx.cfg.enable_index_owner)
    284 		++columns;
    285 
    286 	ctx.page.title = ctx.cfg.root_title;
    287 	cgit_print_http_headers();
    288 	cgit_print_docstart();
    289 	cgit_print_pageheader();
    290 
    291 	if (ctx.qry.sort)
    292 		sorted = sort_repolist(ctx.qry.sort);
    293 	else if (ctx.cfg.section_sort)
    294 		sort_repolist("section");
    295 
    296 	html("<ul class='repo-list'>");
    297 	for (i = 0; i < cgit_repolist.count; i++) {
    298             ctx.repo = &cgit_repolist.repos[i];
    299             html("<li class='repo-entry'>");
    300             if (!is_visible(ctx.repo))
    301                 continue;
    302             hits++;
    303             if (hits <= ctx.qry.ofs)
    304                 continue;
    305             if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
    306                 continue;
    307             section = ctx.repo->section;
    308             if (section && !strcmp(section, ""))
    309                 section = NULL;
    310             if (!sorted &&
    311                 ((last_section == NULL && section != NULL) ||
    312                 (last_section != NULL && section == NULL) ||
    313                 (last_section != NULL && section != NULL &&
    314                  strcmp(section, last_section))))
    315                 {
    316                     htmlf("<h5>%s</h5></li><li class='repo-entry'>", section);
    317                     last_section = section;
    318                 }
    319 
    320             _html("<ul class='repo-pad'>") {
    321 
    322                 _html("<li class='repo-logo'>") {
    323                     if (ctx.repo->logo != NULL)
    324                         htmlf("<img src='%s' />", ctx.repo->logo);
    325                     else
    326                         htmlf("<img src='%s' />", ctx.cfg.logo);
    327                 } _html("</li>");
    328 
    329                 _html("<li class='repo-infos'><ul>") {
    330                     _html("<li class='repo-title'>") {
    331                         htmlf("<a href='/%s'>%s</a>", ctx.repo->name, ctx.repo->name);
    332                     } html("</li>");
    333 
    334                     _html("<li class='repo-desc'>") {
    335                         htmlf("<a href='/%s'>", ctx.repo->name, ctx.repo->name);
    336                         if (html_ntxt(ctx.repo->desc, ctx.cfg.max_repodesc_len) < 0)
    337                             html("...");
    338                         html("</a>");
    339                     } html("</li>");
    340                 } html("</ul></li>");
    341 
    342                 _html("<li class='repo-details'><ul>") {
    343                     html("<li class='repo-links'><ul>"); {
    344                         _html("<li>") {
    345                             cgit_summary_link("summary", NULL, "button", NULL);
    346                         } html("</li>");
    347 
    348                         _html("<li>") {
    349                             cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
    350                               0, NULL, NULL, ctx.qry.showmsg, 0);
    351                         } html("</li>");
    352 
    353                         _html("<li>") {
    354                             cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
    355                         } html("</li>");
    356                     }; html("</ul></li>");
    357 
    358                     _html("<li>") {
    359                         html("updated ");
    360                         print_modtime(ctx.repo);
    361                         html(" ago");
    362                     } html("</li>");
    363                 } html("</ul></li>");
    364 
    365             } html("</ul>");
    366 
    367             html("</li>\n");
    368         }
    369         html("</ul>");
    370 	if (hits > ctx.cfg.max_repo_count)
    371 		print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search, ctx.qry.sort);
    372 	cgit_print_docend();
    373 }
    374 
    375 void cgit_print_site_readme(void)
    376 {
    377 	cgit_print_layout_start();
    378 	if (!ctx.cfg.root_readme)
    379 		goto done;
    380 	cgit_open_filter(ctx.cfg.about_filter, ctx.cfg.root_readme);
    381 	html_include(ctx.cfg.root_readme);
    382 	cgit_close_filter(ctx.cfg.about_filter);
    383 done:
    384 	cgit_print_layout_end();
    385 }