From 1a66e7f3c15bf8d0d5d1b198c6674911a213779e Mon Sep 17 00:00:00 2001 From: Andy Doan Date: Mon, 12 Sep 2016 22:54:08 -0500 Subject: 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 Reviewed-by: John Keeping Signed-off-by: John Keeping --- cgit.c | 27 +++++++++++++++++++++++---- cgit.h | 13 +++++++++++-- cgitrc.5.txt | 5 +++++ scan-tree.c | 6 +++--- shared.c | 5 ++++- ui-repolist.c | 31 +++++++++++++++++-------------- 6 files changed, 63 insertions(+), 24 deletions(-) diff --git a/cgit.c b/cgit.c index 2f29aa6..3266a98 100644 --- a/cgit.c +++ b/cgit.c @@ -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->util = xstrdup(value); } else if (!strcmp(name, "section")) - repo->section = xstrdup(value); + repo->section = get_or_create_section(value); else if (!strcmp(name, "readme") && value != NULL) { if (repo->readme.items == ctx.cfg.readme.items) 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) { 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")) ctx.repo = cgit_add_repo(value); 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); else if (!strcmp(name, "repository-sort")) 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")) ctx.cfg.section_sort = atoi(value); 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.scan_hidden_path = 0; ctx.cfg.script_name = CGIT_SCRIPT_NAME; - ctx.cfg.section = ""; + ctx.cfg.section = NULL; ctx.cfg.repository_sort = "name"; ctx.cfg.section_sort = 1; ctx.cfg.summary_branches = 10; @@ -794,7 +796,7 @@ static void print_repo(FILE *f, struct cgit_repo *repo) if (repo->module_link) fprintf(f, "repo.module-link=%s\n", repo->module_link); if (repo->section) - fprintf(f, "repo.section=%s\n", repo->section); + fprintf(f, "repo.section=%s\n", repo->section->name); if (repo->homepage) fprintf(f, "repo.homepage=%s\n", repo->homepage); if (repo->clone_url) @@ -1026,6 +1028,23 @@ static int calc_ttl(void) 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) { const char *path; diff --git a/cgit.h b/cgit.h index df42312..5d81fe8 100644 --- a/cgit.h +++ b/cgit.h @@ -75,6 +75,12 @@ struct cgit_exec_filter { int pid; }; +struct cgit_section { + unsigned int collapse : 1; + char *name; + struct cgit_section *next; +}; + struct cgit_repo { char *url; char *name; @@ -85,7 +91,7 @@ struct cgit_repo { char *defbranch; char *module_link; struct string_list readme; - char *section; + struct cgit_section *section; char *clone_url; char *logo; char *logo_link; @@ -208,7 +214,7 @@ struct cgit_config { char *root_desc; char *root_readme; char *script_name; - char *section; + struct cgit_section *section; char *repository_sort; char *virtual_root; /* Always ends with '/'. */ char *strict_export; @@ -305,6 +311,7 @@ struct cgit_context { struct cgit_config cfg; struct cgit_repo *repo; struct cgit_page page; + struct cgit_section *sections; }; 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 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 struct cgit_repo *cgit_add_repo(const char *url); extern struct cgit_repo *cgit_get_repoinfo(const char *url); diff --git a/cgitrc.5.txt b/cgitrc.5.txt index 9fcf445..2762657 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -404,6 +404,11 @@ section:: after this option will inherit the current section name. Default value: 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:: 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 diff --git a/scan-tree.c b/scan-tree.c index 1cb4e5d..fa21fc4 100644 --- a/scan-tree.c +++ b/scan-tree.c @@ -164,10 +164,10 @@ static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn) } if (slash && !n) { *slash = '\0'; - repo->section = xstrdup(rel.buf); + repo->section = get_or_create_section(rel.buf); *slash = '/'; - if (starts_with(repo->name, repo->section)) { - repo->name += strlen(repo->section); + if (starts_with(repo->name, repo->section->name)) { + repo->name += strlen(repo->section->name); if (*repo->name == '/') repo->name++; } diff --git a/shared.c b/shared.c index 571fbba..e4627e1 100644 --- a/shared.c +++ b/shared.c @@ -443,13 +443,16 @@ typedef struct { void cgit_prepare_repo_env(struct cgit_repo * repo) { + char *section = NULL; + if (repo->section) + section = repo->section->name; cgit_env_var env_vars[] = { { .name = "CGIT_REPO_URL", .value = repo->url }, { .name = "CGIT_REPO_NAME", .value = repo->name }, { .name = "CGIT_REPO_PATH", .value = repo->path }, { .name = "CGIT_REPO_OWNER", .value = repo->owner }, { .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 } }; int env_var_count = ARRAY_SIZE(env_vars); diff --git a/ui-repolist.c b/ui-repolist.c index 3f967a8..e9676b8 100644 --- a/ui-repolist.c +++ b/ui-repolist.c @@ -188,10 +188,16 @@ static int sort_section(const void *a, const void *b) { const struct cgit_repo *r1 = a; const struct cgit_repo *r2 = b; + const char *s1 = ""; + const char *s2 = ""; int result; 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 (!strcmp(ctx.cfg.repository_sort, "age")) { // 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) { int i, columns = 3, hits = 0, header = 0; - char *last_section = NULL; - char *section; + struct cgit_section *last_section = NULL; + struct cgit_section *section; int sorted = 0; if (!any_repos_visible()) { @@ -294,12 +300,10 @@ void cgit_print_repolist(void) if (ctx.cfg.index_header) html_include(ctx.cfg.index_header); - if (ctx.qry.sort) sorted = sort_repolist(ctx.qry.sort); else if (ctx.cfg.section_sort) sort_repolist("section"); - html(""); for (i = 0; i < cgit_repolist.count; i++) { ctx.repo = &cgit_repolist.repos[i]; @@ -313,23 +317,22 @@ void cgit_print_repolist(void) if (!header++) print_header(); section = ctx.repo->section; - if (section && !strcmp(section, "")) + if (section && !strcmp(section->name, "")) section = NULL; - if (!sorted && - ((last_section == NULL && section != NULL) || - (last_section != NULL && section == NULL) || - (last_section != NULL && section != NULL && - strcmp(section, last_section)))) { + if (!sorted && section && last_section != section ) { htmlf(""); - last_section = section; } + last_section = section; + if (section && section->collapse && !strstr(ctx.qry.url, section->name)) + continue; + htmlf("
", columns); html(""); - html_txt(section); + html_txt(section->name); html(""); html("
", !sorted && section ? "sublevel-repo" : "toplevel-repo"); cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); -- cgit v1.2.3