ui-repolist: Allow sections to be collapsible

The index page can be difficult to navigate for really large git
servers. This change allows a configuration like:

 section-collapse=people
 section-collapse=tests

And an index page would only display the "people" and "tests" section
headers entries (not their repos) with a hyperlink that can be used to
drill down into each section.

Additionally the boolean logic around displaying sections in
ui-repolist.c was simplified to eliminate an impossible condition.

Signed-off-by: Andy Doan <andy.doan@linaro.org>
Reviewed-by: John Keeping <john@keeping.me.uk>
Signed-off-by: John Keeping <john@keeping.me.uk>
jk/collapsible-sections
Andy Doan 8 years ago committed by John Keeping
parent 84b158abe0
commit 1a66e7f3c1

@ -77,7 +77,7 @@ static void repo_config(struct cgit_repo *repo, const char *name, const char *va
item = string_list_append(&repo->submodules, xstrdup(name + 12)); item = string_list_append(&repo->submodules, xstrdup(name + 12));
item->util = xstrdup(value); item->util = xstrdup(value);
} else if (!strcmp(name, "section")) } else if (!strcmp(name, "section"))
repo->section = xstrdup(value); repo->section = get_or_create_section(value);
else if (!strcmp(name, "readme") && value != NULL) { else if (!strcmp(name, "readme") && value != NULL) {
if (repo->readme.items == ctx.cfg.readme.items) if (repo->readme.items == ctx.cfg.readme.items)
memset(&repo->readme, 0, sizeof(repo->readme)); memset(&repo->readme, 0, sizeof(repo->readme));
@ -107,7 +107,7 @@ static void repo_config(struct cgit_repo *repo, const char *name, const char *va
static void config_cb(const char *name, const char *value) static void config_cb(const char *name, const char *value)
{ {
if (!strcmp(name, "section") || !strcmp(name, "repo.group")) if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
ctx.cfg.section = xstrdup(value); ctx.cfg.section = get_or_create_section(value);
else if (!strcmp(name, "repo.url")) else if (!strcmp(name, "repo.url"))
ctx.repo = cgit_add_repo(value); ctx.repo = cgit_add_repo(value);
else if (ctx.repo && !strcmp(name, "repo.path")) else if (ctx.repo && !strcmp(name, "repo.path"))
@ -242,6 +242,8 @@ static void config_cb(const char *name, const char *value)
ctx.cfg.section_from_path = atoi(value); ctx.cfg.section_from_path = atoi(value);
else if (!strcmp(name, "repository-sort")) else if (!strcmp(name, "repository-sort"))
ctx.cfg.repository_sort = xstrdup(value); ctx.cfg.repository_sort = xstrdup(value);
else if (!strcmp(name, "section-collapse"))
get_or_create_section(value)->collapse = 1;
else if (!strcmp(name, "section-sort")) else if (!strcmp(name, "section-sort"))
ctx.cfg.section_sort = atoi(value); ctx.cfg.section_sort = atoi(value);
else if (!strcmp(name, "source-filter")) else if (!strcmp(name, "source-filter"))
@ -385,7 +387,7 @@ static void prepare_context(void)
ctx.cfg.root_desc = "a fast webinterface for the git dscm"; ctx.cfg.root_desc = "a fast webinterface for the git dscm";
ctx.cfg.scan_hidden_path = 0; ctx.cfg.scan_hidden_path = 0;
ctx.cfg.script_name = CGIT_SCRIPT_NAME; ctx.cfg.script_name = CGIT_SCRIPT_NAME;
ctx.cfg.section = ""; ctx.cfg.section = NULL;
ctx.cfg.repository_sort = "name"; ctx.cfg.repository_sort = "name";
ctx.cfg.section_sort = 1; ctx.cfg.section_sort = 1;
ctx.cfg.summary_branches = 10; ctx.cfg.summary_branches = 10;
@ -794,7 +796,7 @@ static void print_repo(FILE *f, struct cgit_repo *repo)
if (repo->module_link) if (repo->module_link)
fprintf(f, "repo.module-link=%s\n", repo->module_link); fprintf(f, "repo.module-link=%s\n", repo->module_link);
if (repo->section) if (repo->section)
fprintf(f, "repo.section=%s\n", repo->section); fprintf(f, "repo.section=%s\n", repo->section->name);
if (repo->homepage) if (repo->homepage)
fprintf(f, "repo.homepage=%s\n", repo->homepage); fprintf(f, "repo.homepage=%s\n", repo->homepage);
if (repo->clone_url) if (repo->clone_url)
@ -1026,6 +1028,23 @@ static int calc_ttl(void)
return ctx.cfg.cache_repo_ttl; return ctx.cfg.cache_repo_ttl;
} }
struct cgit_section* get_or_create_section(const char *section)
{
struct cgit_section *ptr = ctx.sections;
while(ptr) {
if (!strcmp(section, ptr->name))
return ptr;
ptr = ptr->next;
}
/* Not found insert into head of list */
ptr = xmalloc(sizeof(*ptr));
ptr->name = xstrdup(section);
ptr->collapse = 0;
ptr->next = ctx.sections;
ctx.sections = ptr;
return ptr;
}
int cmd_main(int argc, const char **argv) int cmd_main(int argc, const char **argv)
{ {
const char *path; const char *path;

@ -75,6 +75,12 @@ struct cgit_exec_filter {
int pid; int pid;
}; };
struct cgit_section {
unsigned int collapse : 1;
char *name;
struct cgit_section *next;
};
struct cgit_repo { struct cgit_repo {
char *url; char *url;
char *name; char *name;
@ -85,7 +91,7 @@ struct cgit_repo {
char *defbranch; char *defbranch;
char *module_link; char *module_link;
struct string_list readme; struct string_list readme;
char *section; struct cgit_section *section;
char *clone_url; char *clone_url;
char *logo; char *logo;
char *logo_link; char *logo_link;
@ -208,7 +214,7 @@ struct cgit_config {
char *root_desc; char *root_desc;
char *root_readme; char *root_readme;
char *script_name; char *script_name;
char *section; struct cgit_section *section;
char *repository_sort; char *repository_sort;
char *virtual_root; /* Always ends with '/'. */ char *virtual_root; /* Always ends with '/'. */
char *strict_export; char *strict_export;
@ -305,6 +311,7 @@ struct cgit_context {
struct cgit_config cfg; struct cgit_config cfg;
struct cgit_repo *repo; struct cgit_repo *repo;
struct cgit_page page; struct cgit_page page;
struct cgit_section *sections;
}; };
typedef int (*write_archive_fn_t)(const char *, const char *); typedef int (*write_archive_fn_t)(const char *, const char *);
@ -322,6 +329,8 @@ extern struct cgit_repolist cgit_repolist;
extern struct cgit_context ctx; extern struct cgit_context ctx;
extern const struct cgit_snapshot_format cgit_snapshot_formats[]; extern const struct cgit_snapshot_format cgit_snapshot_formats[];
extern struct cgit_section* get_or_create_section(const char *section);
extern char *cgit_default_repo_desc; extern char *cgit_default_repo_desc;
extern struct cgit_repo *cgit_add_repo(const char *url); extern struct cgit_repo *cgit_add_repo(const char *url);
extern struct cgit_repo *cgit_get_repoinfo(const char *url); extern struct cgit_repo *cgit_get_repoinfo(const char *url);

@ -404,6 +404,11 @@ section::
after this option will inherit the current section name. Default value: after this option will inherit the current section name. Default value:
none. none.
section-collapse::
Name of a section to "collapse" and not display on the index page.
Multiple config entries can be specified and each one will be
collapsed.
section-sort:: section-sort::
Flag which, when set to "1", will sort the sections on the repository Flag which, when set to "1", will sort the sections on the repository
listing by name. Set this flag to "0" if the order in the cgitrc file should listing by name. Set this flag to "0" if the order in the cgitrc file should

@ -164,10 +164,10 @@ static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn)
} }
if (slash && !n) { if (slash && !n) {
*slash = '\0'; *slash = '\0';
repo->section = xstrdup(rel.buf); repo->section = get_or_create_section(rel.buf);
*slash = '/'; *slash = '/';
if (starts_with(repo->name, repo->section)) { if (starts_with(repo->name, repo->section->name)) {
repo->name += strlen(repo->section); repo->name += strlen(repo->section->name);
if (*repo->name == '/') if (*repo->name == '/')
repo->name++; repo->name++;
} }

@ -443,13 +443,16 @@ typedef struct {
void cgit_prepare_repo_env(struct cgit_repo * repo) void cgit_prepare_repo_env(struct cgit_repo * repo)
{ {
char *section = NULL;
if (repo->section)
section = repo->section->name;
cgit_env_var env_vars[] = { cgit_env_var env_vars[] = {
{ .name = "CGIT_REPO_URL", .value = repo->url }, { .name = "CGIT_REPO_URL", .value = repo->url },
{ .name = "CGIT_REPO_NAME", .value = repo->name }, { .name = "CGIT_REPO_NAME", .value = repo->name },
{ .name = "CGIT_REPO_PATH", .value = repo->path }, { .name = "CGIT_REPO_PATH", .value = repo->path },
{ .name = "CGIT_REPO_OWNER", .value = repo->owner }, { .name = "CGIT_REPO_OWNER", .value = repo->owner },
{ .name = "CGIT_REPO_DEFBRANCH", .value = repo->defbranch }, { .name = "CGIT_REPO_DEFBRANCH", .value = repo->defbranch },
{ .name = "CGIT_REPO_SECTION", .value = repo->section }, { .name = "CGIT_REPO_SECTION", .value = section },
{ .name = "CGIT_REPO_CLONE_URL", .value = repo->clone_url } { .name = "CGIT_REPO_CLONE_URL", .value = repo->clone_url }
}; };
int env_var_count = ARRAY_SIZE(env_vars); int env_var_count = ARRAY_SIZE(env_vars);

@ -188,10 +188,16 @@ static int sort_section(const void *a, const void *b)
{ {
const struct cgit_repo *r1 = a; const struct cgit_repo *r1 = a;
const struct cgit_repo *r2 = b; const struct cgit_repo *r2 = b;
const char *s1 = "";
const char *s2 = "";
int result; int result;
time_t t; time_t t;
result = cmp(r1->section, r2->section); if (r1->section)
s1 = r1->section->name;
if (r2->section)
s2 = r2->section->name;
result = cmp(s1, s2);
if (!result) { if (!result) {
if (!strcmp(ctx.cfg.repository_sort, "age")) { if (!strcmp(ctx.cfg.repository_sort, "age")) {
// get_repo_modtime caches the value in r->mtime, so we don't // get_repo_modtime caches the value in r->mtime, so we don't
@ -273,8 +279,8 @@ static int sort_repolist(char *field)
void cgit_print_repolist(void) void cgit_print_repolist(void)
{ {
int i, columns = 3, hits = 0, header = 0; int i, columns = 3, hits = 0, header = 0;
char *last_section = NULL; struct cgit_section *last_section = NULL;
char *section; struct cgit_section *section;
int sorted = 0; int sorted = 0;
if (!any_repos_visible()) { if (!any_repos_visible()) {
@ -294,12 +300,10 @@ void cgit_print_repolist(void)
if (ctx.cfg.index_header) if (ctx.cfg.index_header)
html_include(ctx.cfg.index_header); html_include(ctx.cfg.index_header);
if (ctx.qry.sort) if (ctx.qry.sort)
sorted = sort_repolist(ctx.qry.sort); sorted = sort_repolist(ctx.qry.sort);
else if (ctx.cfg.section_sort) else if (ctx.cfg.section_sort)
sort_repolist("section"); sort_repolist("section");
html("<table summary='repository list' class='list nowrap'>"); html("<table summary='repository list' class='list nowrap'>");
for (i = 0; i < cgit_repolist.count; i++) { for (i = 0; i < cgit_repolist.count; i++) {
ctx.repo = &cgit_repolist.repos[i]; ctx.repo = &cgit_repolist.repos[i];
@ -313,23 +317,22 @@ void cgit_print_repolist(void)
if (!header++) if (!header++)
print_header(); print_header();
section = ctx.repo->section; section = ctx.repo->section;
if (section && !strcmp(section, "")) if (section && !strcmp(section->name, ""))
section = NULL; section = NULL;
if (!sorted && if (!sorted && section && last_section != section ) {
((last_section == NULL && section != NULL) ||
(last_section != NULL && section == NULL) ||
(last_section != NULL && section != NULL &&
strcmp(section, last_section)))) {
htmlf("<tr class='nohover'><td colspan='%d' class='reposection'>", htmlf("<tr class='nohover'><td colspan='%d' class='reposection'>",
columns); columns);
html("<a href='"); html("<a href='");
html_attr(section); html_attr(section->name);
html("'>"); html("'>");
html_txt(section); html_txt(section->name);
html("</a>"); html("</a>");
html("</td></tr>"); html("</td></tr>");
last_section = section;
} }
last_section = section;
if (section && section->collapse && !strstr(ctx.qry.url, section->name))
continue;
htmlf("<tr><td class='%s'>", htmlf("<tr><td class='%s'>",
!sorted && section ? "sublevel-repo" : "toplevel-repo"); !sorted && section ? "sublevel-repo" : "toplevel-repo");
cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);

Loading…
Cancel
Save