[PATCH 2/2] ui-repolist: Allow sections to be collapsible

Andy Doan andy.doan at linaro.org
Tue Sep 13 00:01:50 CEST 2016


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 at linaro.org>
Reviewed-by: John Keeping <john at keeping.me.uk>
---
 cgit.c        | 27 +++++++++++++++++++++++----
 cgit.h        | 13 +++++++++++--
 cgitrc.5.txt  |  5 +++++
 scan-tree.c   |  6 +++---
 shared.c      |  5 ++++-
 ui-repolist.c | 29 ++++++++++++++++-------------
 6 files changed, 62 insertions(+), 23 deletions(-)

diff --git a/cgit.c b/cgit.c
index 9427c4a..c769e35 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 main(int argc, const char **argv)
 {
 	const char *path;
diff --git a/cgit.h b/cgit.h
index 325432b..d940ae3 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 a63633b..8348581 100644
--- a/shared.c
+++ b/shared.c
@@ -444,13 +444,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 f6b6b47..fc8121e 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("<table summary='repository list' class='list nowrap'>");
 	for (i = 0; i < cgit_repolist.count; i++) {
 		ctx.repo = &cgit_repolist.repos[i];
@@ -313,19 +317,18 @@ 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("<tr class='nohover'><td colspan='%d' class='reposection'>",
 			      columns);
-			htmlf("<a href='%s'>%s</a>", section, section);
+			htmlf("<a href='%s'>%s</a>", section->name, section->name);
 			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'>",
 		      !sorted && section ? "sublevel-repo" : "toplevel-repo");
 		cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
--
2.7.4



More information about the CGit mailing list