summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c10
-rw-r--r--cgit.h4
-rw-r--r--cgitrc.5.txt23
-rw-r--r--cmd.c5
-rw-r--r--shared.c2
-rw-r--r--ui-shared.c2
-rw-r--r--ui-stats.c97
-rw-r--r--ui-stats.h19
8 files changed, 104 insertions, 58 deletions
diff --git a/cgit.c b/cgit.c
index 22b6d7c..57e11cd 100644
--- a/cgit.c
+++ b/cgit.c
@@ -12,6 +12,7 @@
12#include "configfile.h" 12#include "configfile.h"
13#include "html.h" 13#include "html.h"
14#include "ui-shared.h" 14#include "ui-shared.h"
15#include "ui-stats.h"
15#include "scan-tree.h" 16#include "scan-tree.h"
16 17
17const char *cgit_version = CGIT_VERSION; 18const char *cgit_version = CGIT_VERSION;
@@ -54,8 +55,8 @@ void config_cb(const char *name, const char *value)
54 ctx.cfg.enable_log_filecount = atoi(value); 55 ctx.cfg.enable_log_filecount = atoi(value);
55 else if (!strcmp(name, "enable-log-linecount")) 56 else if (!strcmp(name, "enable-log-linecount"))
56 ctx.cfg.enable_log_linecount = atoi(value); 57 ctx.cfg.enable_log_linecount = atoi(value);
57 else if (!strcmp(name, "enable-stats")) 58 else if (!strcmp(name, "max-stats"))
58 ctx.cfg.enable_stats = atoi(value); 59 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
59 else if (!strcmp(name, "cache-size")) 60 else if (!strcmp(name, "cache-size"))
60 ctx.cfg.cache_size = atoi(value); 61 ctx.cfg.cache_size = atoi(value);
61 else if (!strcmp(name, "cache-root")) 62 else if (!strcmp(name, "cache-root"))
@@ -114,8 +115,8 @@ void config_cb(const char *name, const char *value)
114 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 115 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
115 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 116 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
116 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 117 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
117 else if (ctx.repo && !strcmp(name, "repo.enable-stats")) 118 else if (ctx.repo && !strcmp(name, "repo.max-stats"))
118 ctx.repo->enable_stats = ctx.cfg.enable_stats && atoi(value); 119 ctx.repo->max_stats = cgit_find_stats_period(value, NULL);
119 else if (ctx.repo && !strcmp(name, "repo.module-link")) 120 else if (ctx.repo && !strcmp(name, "repo.module-link"))
120 ctx.repo->module_link= xstrdup(value); 121 ctx.repo->module_link= xstrdup(value);
121 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 122 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
@@ -183,6 +184,7 @@ static void prepare_context(struct cgit_context *ctx)
183 ctx->cfg.max_lock_attempts = 5; 184 ctx->cfg.max_lock_attempts = 5;
184 ctx->cfg.max_msg_len = 80; 185 ctx->cfg.max_msg_len = 80;
185 ctx->cfg.max_repodesc_len = 80; 186 ctx->cfg.max_repodesc_len = 80;
187 ctx->cfg.max_stats = 0;
186 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 188 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
187 ctx->cfg.renamelimit = -1; 189 ctx->cfg.renamelimit = -1;
188 ctx->cfg.robots = "index, nofollow"; 190 ctx->cfg.robots = "index, nofollow";
diff --git a/cgit.h b/cgit.h
index 85045c4..f2cb671 100644
--- a/cgit.h
+++ b/cgit.h
@@ -61,7 +61,7 @@ struct cgit_repo {
61 int snapshots; 61 int snapshots;
62 int enable_log_filecount; 62 int enable_log_filecount;
63 int enable_log_linecount; 63 int enable_log_linecount;
64 int enable_stats; 64 int max_stats;
65}; 65};
66 66
67struct cgit_repolist { 67struct cgit_repolist {
@@ -153,13 +153,13 @@ struct cgit_config {
153 int enable_index_links; 153 int enable_index_links;
154 int enable_log_filecount; 154 int enable_log_filecount;
155 int enable_log_linecount; 155 int enable_log_linecount;
156 int enable_stats;
157 int local_time; 156 int local_time;
158 int max_repo_count; 157 int max_repo_count;
159 int max_commit_count; 158 int max_commit_count;
160 int max_lock_attempts; 159 int max_lock_attempts;
161 int max_msg_len; 160 int max_msg_len;
162 int max_repodesc_len; 161 int max_repodesc_len;
162 int max_stats;
163 int nocache; 163 int nocache;
164 int renamelimit; 164 int renamelimit;
165 int snapshots; 165 int snapshots;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 60d3ea4..0bbbea3 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -74,10 +74,6 @@ enable-log-linecount
74 and removed lines for each commit on the repository log page. Default 74 and removed lines for each commit on the repository log page. Default
75 value: "0". 75 value: "0".
76 76
77enable-stats
78 Globally enable/disable statistics for each repository. Default
79 value: "0".
80
81favicon 77favicon
82 Url used as link to a shortcut icon for cgit. If specified, it is 78 Url used as link to a shortcut icon for cgit. If specified, it is
83 suggested to use the value "/favicon.ico" since certain browsers will 79 suggested to use the value "/favicon.ico" since certain browsers will
@@ -133,6 +129,11 @@ max-repodesc-length
133 Specifies the maximum number of repo description characters to display 129 Specifies the maximum number of repo description characters to display
134 on the repository index page. Default value: "80". 130 on the repository index page. Default value: "80".
135 131
132max-stats
133 Set the default maximum statistics period. Valid values are "week",
134 "month", "quarter" and "year". If unspecified, statistics are
135 disabled. Default value: none. See also: "repo.max-stats".
136
136module-link 137module-link
137 Text which will be used as the formatstring for a hyperlink when a 138 Text which will be used as the formatstring for a hyperlink when a
138 submodule is printed in a directory listing. The arguments for the 139 submodule is printed in a directory listing. The arguments for the
@@ -222,9 +223,10 @@ repo.enable-log-linecount
222 A flag which can be used to disable the global setting 223 A flag which can be used to disable the global setting
223 `enable-log-linecount'. Default value: none. 224 `enable-log-linecount'. Default value: none.
224 225
225repo.enable-stats 226repo.max-stats
226 A flag which can be used to disable the global setting 227 Override the default maximum statistics period. Valid values are equal
227 `enable-stats'. Default value: none. 228 to the values specified for the global "max-stats" setting. Default
229 value: none.
228 230
229repo.name 231repo.name
230 The value to show as repository name. Default value: <repo.url>. 232 The value to show as repository name. Default value: <repo.url>.
@@ -284,6 +286,10 @@ favicon=/favicon.ico
284logo=/img/mylogo.png 286logo=/img/mylogo.png
285 287
286 288
289# Enable statistics per week, month and quarter
290max-stats=quarter
291
292
287# Set the title and heading of the repository index page 293# Set the title and heading of the repository index page
288root-title=foobar.com git repositories 294root-title=foobar.com git repositories
289 295
@@ -356,6 +362,9 @@ repo.snapshots=0
356# Disable line-counts for this repo 362# Disable line-counts for this repo
357repo.enable-log-linecount=0 363repo.enable-log-linecount=0
358 364
365# Restrict the max statistics period for this repo
366repo.max-stats=month
367
359 368
360BUGS 369BUGS
361---- 370----
diff --git a/cmd.c b/cmd.c
index 744bf84..763a558 100644
--- a/cmd.c
+++ b/cmd.c
@@ -112,10 +112,7 @@ static void snapshot_fn(struct cgit_context *ctx)
112 112
113static void stats_fn(struct cgit_context *ctx) 113static void stats_fn(struct cgit_context *ctx)
114{ 114{
115 if (ctx->repo->enable_stats) 115 cgit_show_stats(ctx);
116 cgit_show_stats(ctx);
117 else
118 cgit_print_error("Stats disabled for this repo");
119} 116}
120 117
121static void summary_fn(struct cgit_context *ctx) 118static void summary_fn(struct cgit_context *ctx)
diff --git a/shared.c b/shared.c
index 37333f0..7382609 100644
--- a/shared.c
+++ b/shared.c
@@ -58,7 +58,7 @@ struct cgit_repo *cgit_add_repo(const char *url)
58 ret->snapshots = ctx.cfg.snapshots; 58 ret->snapshots = ctx.cfg.snapshots;
59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount; 59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount; 60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
61 ret->enable_stats = ctx.cfg.enable_stats; 61 ret->max_stats = ctx.cfg.max_stats;
62 ret->module_link = ctx.cfg.module_link; 62 ret->module_link = ctx.cfg.module_link;
63 ret->readme = NULL; 63 ret->readme = NULL;
64 return ret; 64 return ret;
diff --git a/ui-shared.c b/ui-shared.c
index 0e688a0..97b9d46 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -641,7 +641,7 @@ void cgit_print_pageheader(struct cgit_context *ctx)
641 ctx->qry.head, ctx->qry.sha1); 641 ctx->qry.head, ctx->qry.sha1);
642 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, 642 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head,
643 ctx->qry.sha1, ctx->qry.sha2, NULL); 643 ctx->qry.sha1, ctx->qry.sha2, NULL);
644 if (ctx->repo->enable_stats) 644 if (ctx->repo->max_stats)
645 reporevlink("stats", "stats", NULL, hc(cmd, "stats"), 645 reporevlink("stats", "stats", NULL, hc(cmd, "stats"),
646 ctx->qry.head, NULL, NULL); 646 ctx->qry.head, NULL, NULL);
647 if (ctx->repo->readme) 647 if (ctx->repo->readme)
diff --git a/ui-stats.c b/ui-stats.c
index 3cc8d70..1104485 100644
--- a/ui-stats.c
+++ b/ui-stats.c
@@ -1,26 +1,12 @@
1#include <string-list.h>
2
1#include "cgit.h" 3#include "cgit.h"
2#include "html.h" 4#include "html.h"
3#include <string-list.h> 5#include "ui-shared.h"
6#include "ui-stats.h"
4 7
5#define MONTHS 6 8#define MONTHS 6
6 9
7struct Period {
8 const char code;
9 const char *name;
10 int max_periods;
11 int count;
12
13 /* Convert a tm value to the first day in the period */
14 void (*trunc)(struct tm *tm);
15
16 /* Update tm value to start of next/previous period */
17 void (*dec)(struct tm *tm);
18 void (*inc)(struct tm *tm);
19
20 /* Pretty-print a tm value */
21 char *(*pretty)(struct tm *tm);
22};
23
24struct authorstat { 10struct authorstat {
25 long total; 11 long total;
26 struct string_list list; 12 struct string_list list;
@@ -137,15 +123,39 @@ static char *pretty_year(struct tm *tm)
137 return fmt("%d", tm->tm_year + 1900); 123 return fmt("%d", tm->tm_year + 1900);
138} 124}
139 125
140struct Period periods[] = { 126struct cgit_period periods[] = {
141 {'w', "week", 12, 4, trunc_week, dec_week, inc_week, pretty_week}, 127 {'w', "week", 12, 4, trunc_week, dec_week, inc_week, pretty_week},
142 {'m', "month", 12, 4, trunc_month, dec_month, inc_month, pretty_month}, 128 {'m', "month", 12, 4, trunc_month, dec_month, inc_month, pretty_month},
143 {'q', "quarter", 12, 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter}, 129 {'q', "quarter", 12, 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter},
144 {'y', "year", 12, 4, trunc_year, dec_year, inc_year, pretty_year}, 130 {'y', "year", 12, 4, trunc_year, dec_year, inc_year, pretty_year},
145}; 131};
146 132
133/* Given a period code or name, return a period index (1, 2, 3 or 4)
134 * and update the period pointer to the correcsponding struct.
135 * If no matching code is found, return 0.
136 */
137int cgit_find_stats_period(const char *expr, struct cgit_period **period)
138{
139 int i;
140 char code = '\0';
141
142 if (!expr)
143 return 0;
144
145 if (strlen(expr) == 1)
146 code = expr[0];
147
148 for (i = 0; i < sizeof(periods) / sizeof(periods[0]); i++)
149 if (periods[i].code == code || !strcmp(periods[i].name, expr)) {
150 if (period)
151 *period = &periods[i];
152 return i+1;
153 }
154 return 0;
155}
156
147static void add_commit(struct string_list *authors, struct commit *commit, 157static void add_commit(struct string_list *authors, struct commit *commit,
148 struct Period *period) 158 struct cgit_period *period)
149{ 159{
150 struct commitinfo *info; 160 struct commitinfo *info;
151 struct string_list_item *author, *item; 161 struct string_list_item *author, *item;
@@ -190,7 +200,7 @@ static int cmp_total_commits(const void *a1, const void *a2)
190 * timeperiod into a nested string_list collection. 200 * timeperiod into a nested string_list collection.
191 */ 201 */
192struct string_list collect_stats(struct cgit_context *ctx, 202struct string_list collect_stats(struct cgit_context *ctx,
193 struct Period *period) 203 struct cgit_period *period)
194{ 204{
195 struct string_list authors; 205 struct string_list authors;
196 struct rev_info rev; 206 struct rev_info rev;
@@ -233,7 +243,7 @@ struct string_list collect_stats(struct cgit_context *ctx,
233 243
234void print_combined_authorrow(struct string_list *authors, int from, int to, 244void print_combined_authorrow(struct string_list *authors, int from, int to,
235 const char *name, const char *leftclass, const char *centerclass, 245 const char *name, const char *leftclass, const char *centerclass,
236 const char *rightclass, struct Period *period) 246 const char *rightclass, struct cgit_period *period)
237{ 247{
238 struct string_list_item *author; 248 struct string_list_item *author;
239 struct authorstat *authorstat; 249 struct authorstat *authorstat;
@@ -271,7 +281,8 @@ void print_combined_authorrow(struct string_list *authors, int from, int to,
271 htmlf("<td class='%s'>%d</td></tr>", rightclass, total); 281 htmlf("<td class='%s'>%d</td></tr>", rightclass, total);
272} 282}
273 283
274void print_authors(struct string_list *authors, int top, struct Period *period) 284void print_authors(struct string_list *authors, int top,
285 struct cgit_period *period)
275{ 286{
276 struct string_list_item *author; 287 struct string_list_item *author;
277 struct authorstat *authorstat; 288 struct authorstat *authorstat;
@@ -339,16 +350,22 @@ void print_authors(struct string_list *authors, int top, struct Period *period)
339void cgit_show_stats(struct cgit_context *ctx) 350void cgit_show_stats(struct cgit_context *ctx)
340{ 351{
341 struct string_list authors; 352 struct string_list authors;
342 struct Period *period; 353 struct cgit_period *period;
343 int top, i; 354 int top, i;
355 const char *code = "w";
344 356
345 period = &periods[0]; 357 if (ctx->qry.period)
346 if (ctx->qry.period) { 358 code = ctx->qry.period;
347 for (i = 0; i < sizeof(periods) / sizeof(periods[0]); i++) 359
348 if (periods[i].code == ctx->qry.period[0]) { 360 i = cgit_find_stats_period(code, &period);
349 period = &periods[i]; 361 if (!i) {
350 break; 362 cgit_print_error(fmt("Unknown statistics type: %c", code));
351 } 363 return;
364 }
365 if (i > ctx->repo->max_stats) {
366 cgit_print_error(fmt("Statistics type disabled: %s",
367 period->name));
368 return;
352 } 369 }
353 authors = collect_stats(ctx, period); 370 authors = collect_stats(ctx, period);
354 qsort(authors.items, authors.nr, sizeof(struct string_list_item), 371 qsort(authors.items, authors.nr, sizeof(struct string_list_item),
@@ -368,14 +385,16 @@ void cgit_show_stats(struct cgit_context *ctx)
368 html("<form method='get' action='.' style='float: right; text-align: right;'>"); 385 html("<form method='get' action='.' style='float: right; text-align: right;'>");
369 if (strcmp(ctx->qry.head, ctx->repo->defbranch)) 386 if (strcmp(ctx->qry.head, ctx->repo->defbranch))
370 htmlf("<input type='hidden' name='h' value='%s'/>", ctx->qry.head); 387 htmlf("<input type='hidden' name='h' value='%s'/>", ctx->qry.head);
371 html("Period: "); 388 if (ctx->repo->max_stats > 1) {
372 html("<select name='period' onchange='this.form.submit();'>"); 389 html("Period: ");
373 for (i = 0; i < sizeof(periods) / sizeof(periods[0]); i++) 390 html("<select name='period' onchange='this.form.submit();'>");
374 htmlf("<option value='%c'%s>%s</option>", 391 for (i = 0; i < ctx->repo->max_stats; i++)
375 periods[i].code, 392 htmlf("<option value='%c'%s>%s</option>",
376 period == &periods[i] ? " selected" : "", 393 periods[i].code,
377 periods[i].name); 394 period == &periods[i] ? " selected" : "",
378 html("</select><br/><br/>"); 395 periods[i].name);
396 html("</select><br/><br/>");
397 }
379 html("Authors: "); 398 html("Authors: ");
380 html(""); 399 html("");
381 html("<select name='ofs' onchange='this.form.submit();'>"); 400 html("<select name='ofs' onchange='this.form.submit();'>");
diff --git a/ui-stats.h b/ui-stats.h
index f1d744c..4f13dba 100644
--- a/ui-stats.h
+++ b/ui-stats.h
@@ -3,6 +3,25 @@
3 3
4#include "cgit.h" 4#include "cgit.h"
5 5
6struct cgit_period {
7 const char code;
8 const char *name;
9 int max_periods;
10 int count;
11
12 /* Convert a tm value to the first day in the period */
13 void (*trunc)(struct tm *tm);
14
15 /* Update tm value to start of next/previous period */
16 void (*dec)(struct tm *tm);
17 void (*inc)(struct tm *tm);
18
19 /* Pretty-print a tm value */
20 char *(*pretty)(struct tm *tm);
21};
22
23extern int cgit_find_stats_period(const char *expr, struct cgit_period **period);
24
6extern void cgit_show_stats(struct cgit_context *ctx); 25extern void cgit_show_stats(struct cgit_context *ctx);
7 26
8#endif /* UI_STATS_H */ 27#endif /* UI_STATS_H */