##// END OF EJS Templates
Implemented show_id function that is a configurable way to display sha hashes in the changelog.
marcink -
r3557:58dcef7e beta
parent child Browse files
Show More
@@ -1,454 +1,477 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # RhodeCode - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 pdebug = false
11 11 ################################################################################
12 12 ## Uncomment and replace with the address which should receive ##
13 13 ## any error reports after application crash ##
14 14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 15 ################################################################################
16 16 #email_to = admin@localhost
17 17 #error_email_from = paste_error@localhost
18 18 #app_email_from = rhodecode-noreply@localhost
19 19 #error_message =
20 20 #email_prefix = [RhodeCode]
21 21
22 22 #smtp_server = mail.server.com
23 23 #smtp_username =
24 24 #smtp_password =
25 25 #smtp_port =
26 26 #smtp_use_tls = false
27 27 #smtp_use_ssl = true
28 # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
28 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
29 29 #smtp_auth =
30 30
31 31 [server:main]
32 32 ## PASTE
33 ##nr of threads to spawn
33 ## nr of threads to spawn
34 34 #threadpool_workers = 5
35 35
36 ##max request before thread respawn
36 ## max request before thread respawn
37 37 #threadpool_max_requests = 10
38 38
39 ##option to use threads of process
39 ## option to use threads of process
40 40 #use_threadpool = true
41 41
42 42 #use = egg:Paste#http
43 43
44 #WAITRESS
44 ## WAITRESS
45 45 threads = 5
46 #100GB
46 ## 100GB
47 47 max_request_body_size = 107374182400
48 48 use = egg:waitress#main
49 49
50 50 host = 0.0.0.0
51 51 port = 5000
52 52
53 [filter:proxy-prefix]
54 # prefix middleware for rc
55 use = egg:PasteDeploy#prefix
56 prefix = /<your-prefix>
53 ## prefix middleware for rc
54 #[filter:proxy-prefix]
55 #use = egg:PasteDeploy#prefix
56 #prefix = /<your-prefix>
57 57
58 58 [app:main]
59 59 use = egg:rhodecode
60 ## enable proxy prefix middleware
60 61 #filter-with = proxy-prefix
62
61 63 full_stack = true
62 64 static_files = true
63 # Optional Languages
64 # en, fr, ja, pt_BR, zh_CN, zh_TW, pl
65 ## Optional Languages
66 ## en, fr, ja, pt_BR, zh_CN, zh_TW, pl
65 67 lang = en
66 68 cache_dir = %(here)s/data
67 69 index_dir = %(here)s/data/index
68 # set this path to use archive download cache
69 #archive_cache_dir = /tmp/rhodecode_tarballcache
70 app_instance_uuid = rc-develop
70
71 ## uncomment and set this path to use archive download cache
72 #archive_cache_dir = /tmp/tarballcache
73
74 ## change this to unique ID for security
75 app_instance_uuid = rc-production
76
77 ## cut off limit for large diffs (size in bytes)
71 78 cut_off_limit = 256000
72 vcs_full_cache = True
73 # force https in RhodeCode, fixes https redirects, assumes it's always https
79
80 ## use cache version of scm repo everywhere
81 vcs_full_cache = true
82
83 ## force https in RhodeCode, fixes https redirects, assumes it's always https
74 84 force_https = false
75 # use Strict-Transport-Security headers
85
86 ## use Strict-Transport-Security headers
76 87 use_htsts = false
88
89 ## number of commits stats will parse on each iteration
77 90 commit_parse_limit = 25
78 # number of items displayed in lightweight dashboard before paginating
91
92 ## number of items displayed in lightweight dashboard before paginating is shown
79 93 dashboard_items = 100
94
95 ## use gravatar service to display avatars
80 96 use_gravatar = true
81 97
82 # path to git executable
98 ## path to git executable
83 99 git_path = git
84 100
85 101 ## RSS feed options
86
87 102 rss_cut_off_limit = 256000
88 103 rss_items_per_page = 10
89 104 rss_include_diff = false
90 105
106 ## show hash options for changelog
107 sha_len = 12
108 sha_rev = true
109
91 110
92 111 ## alternative_gravatar_url allows you to use your own avatar server application
93 112 ## the following parts of the URL will be replaced
94 113 ## {email} user email
95 114 ## {md5email} md5 hash of the user email (like at gravatar.com)
96 115 ## {size} size of the image that is expected from the server application
97 116 ## {scheme} http/https from RhodeCode server
98 117 ## {netloc} network location from RhodeCode server
99 118 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
100 119 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
101 120
121
122 ## container auth options
102 123 container_auth_enabled = false
103 124 proxypass_auth_enabled = false
125
104 126 ## default encoding used to convert from and to unicode
105 127 ## can be also a comma seperated list of encoding in case of mixed encodings
106 128 default_encoding = utf8
107 129
108 130 ## overwrite schema of clone url
109 131 ## available vars:
110 132 ## scheme - http/https
111 133 ## user - current user
112 134 ## pass - password
113 135 ## netloc - network location
114 136 ## path - usually repo_name
115 137
116 138 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
117 139
118 140 ## issue tracking mapping for commits messages
119 141 ## comment out issue_pat, issue_server, issue_prefix to enable
120 142
121 143 ## pattern to get the issues from commit messages
122 144 ## default one used here is #<numbers> with a regex passive group for `#`
123 145 ## {id} will be all groups matched from this pattern
124 146
125 147 issue_pat = (?:\s*#)(\d+)
126 148
127 149 ## server url to the issue, each {id} will be replaced with match
128 150 ## fetched from the regex and {repo} is replaced with full repository name
129 151 ## including groups {repo_name} is replaced with just name of repo
130 152
131 153 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
132 154
133 155 ## prefix to add to link to indicate it's an url
134 156 ## #314 will be replaced by <issue_prefix><id>
135 157
136 158 issue_prefix = #
137 159
138 160 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
139 161 ## multiple patterns, to other issues server, wiki or others
140 162 ## below an example how to create a wiki pattern
141 163 # #wiki-some-id -> https://mywiki.com/some-id
142 164
143 165 #issue_pat_wiki = (?:wiki-)(.+)
144 166 #issue_server_link_wiki = https://mywiki.com/{id}
145 167 #issue_prefix_wiki = WIKI-
146 168
147 169
148 170 ## instance-id prefix
149 171 ## a prefix key for this instance used for cache invalidation when running
150 172 ## multiple instances of rhodecode, make sure it's globally unique for
151 173 ## all running rhodecode instances. Leave empty if you don't use it
152 174 instance_id =
153 175
154 176 ## alternative return HTTP header for failed authentication. Default HTTP
155 177 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
156 178 ## handling that. Set this variable to 403 to return HTTPForbidden
157 179 auth_ret_code =
158 180
159 181 ## locking return code. When repository is locked return this HTTP code. 2XX
160 182 ## codes don't break the transactions while 4XX codes do
161 183 lock_ret_code = 423
162 184
163 185
164 186 ####################################
165 187 ### CELERY CONFIG ####
166 188 ####################################
167 189 use_celery = false
168 190 broker.host = localhost
169 191 broker.vhost = rabbitmqhost
170 192 broker.port = 5672
171 193 broker.user = rabbitmq
172 194 broker.password = qweqwe
173 195
174 196 celery.imports = rhodecode.lib.celerylib.tasks
175 197
176 198 celery.result.backend = amqp
177 199 celery.result.dburi = amqp://
178 200 celery.result.serialier = json
179 201
180 202 #celery.send.task.error.emails = true
181 203 #celery.amqp.task.result.expires = 18000
182 204
183 205 celeryd.concurrency = 2
184 206 #celeryd.log.file = celeryd.log
185 207 celeryd.log.level = debug
186 208 celeryd.max.tasks.per.child = 1
187 209
188 #tasks will never be sent to the queue, but executed locally instead.
210 ## tasks will never be sent to the queue, but executed locally instead.
189 211 celery.always.eager = false
190 212
191 213 ####################################
192 214 ### BEAKER CACHE ####
193 215 ####################################
194 216 beaker.cache.data_dir=%(here)s/data/cache/data
195 217 beaker.cache.lock_dir=%(here)s/data/cache/lock
196 218
197 219 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
198 220
199 221 beaker.cache.super_short_term.type=memory
200 222 beaker.cache.super_short_term.expire=10
201 223 beaker.cache.super_short_term.key_length = 256
202 224
203 225 beaker.cache.short_term.type=memory
204 226 beaker.cache.short_term.expire=60
205 227 beaker.cache.short_term.key_length = 256
206 228
207 229 beaker.cache.long_term.type=memory
208 230 beaker.cache.long_term.expire=36000
209 231 beaker.cache.long_term.key_length = 256
210 232
211 233 beaker.cache.sql_cache_short.type=memory
212 234 beaker.cache.sql_cache_short.expire=10
213 235 beaker.cache.sql_cache_short.key_length = 256
214 236
215 237 beaker.cache.sql_cache_med.type=memory
216 238 beaker.cache.sql_cache_med.expire=360
217 239 beaker.cache.sql_cache_med.key_length = 256
218 240
219 241 beaker.cache.sql_cache_long.type=file
220 242 beaker.cache.sql_cache_long.expire=3600
221 243 beaker.cache.sql_cache_long.key_length = 256
222 244
223 245 ####################################
224 246 ### BEAKER SESSION ####
225 247 ####################################
226 248 ## Type of storage used for the session, current types are
227 249 ## dbm, file, memcached, database, and memory.
228 250 ## The storage uses the Container API
229 251 ## that is also used by the cache system.
230 252
231 253 ## db session ##
232 254 #beaker.session.type = ext:database
233 255 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
234 256 #beaker.session.table_name = db_session
235 257
236 258 ## encrypted cookie client side session, good for many instances ##
237 259 #beaker.session.type = cookie
238 260
239 261 ## file based cookies (default) ##
240 262 #beaker.session.type = file
241 263
242 264
243 265 beaker.session.key = rhodecode
244 ## secure cookie requires AES python libraries ##
245 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
246 #beaker.session.validate_key = 9712sds2212c--zxc123
266 ## secure cookie requires AES python libraries
267 #beaker.session.encrypt_key = <key_for_encryption>
268 #beaker.session.validate_key = <validation_key>
269
247 270 ## sets session as invalid if it haven't been accessed for given amount of time
248 271 beaker.session.timeout = 2592000
249 272 beaker.session.httponly = true
250 273 #beaker.session.cookie_path = /<your-prefix>
251 274
252 ## uncomment for https secure cookie ##
275 ## uncomment for https secure cookie
253 276 beaker.session.secure = false
254 277
255 ## auto save the session to not to use .save() ##
278 ## auto save the session to not to use .save()
256 279 beaker.session.auto = False
257 280
258 281 ## default cookie expiration time in seconds `true` expire at browser close ##
259 282 #beaker.session.cookie_expires = 3600
260 283
261 284
262 285 ############################
263 286 ## ERROR HANDLING SYSTEMS ##
264 287 ############################
265 288
266 289 ####################
267 290 ### [errormator] ###
268 291 ####################
269 292
270 # Errormator is tailored to work with RhodeCode, see
271 # http://errormator.com for details how to obtain an account
272 # you must install python package `errormator_client` to make it work
293 ## Errormator is tailored to work with RhodeCode, see
294 ## http://errormator.com for details how to obtain an account
295 ## you must install python package `errormator_client` to make it work
273 296
274 # errormator enabled
275 errormator = true
297 ## errormator enabled
298 errormator = false
276 299
277 300 errormator.server_url = https://api.errormator.com
278 301 errormator.api_key = YOUR_API_KEY
279 302
280 # TWEAK AMOUNT OF INFO SENT HERE
303 ## TWEAK AMOUNT OF INFO SENT HERE
281 304
282 # enables 404 error logging (default False)
305 ## enables 404 error logging (default False)
283 306 errormator.report_404 = false
284 307
285 # time in seconds after request is considered being slow (default 1)
308 ## time in seconds after request is considered being slow (default 1)
286 309 errormator.slow_request_time = 1
287 310
288 # record slow requests in application
289 # (needs to be enabled for slow datastore recording and time tracking)
311 ## record slow requests in application
312 ## (needs to be enabled for slow datastore recording and time tracking)
290 313 errormator.slow_requests = true
291 314
292 # enable hooking to application loggers
315 ## enable hooking to application loggers
293 316 # errormator.logging = true
294 317
295 # minimum log level for log capture
318 ## minimum log level for log capture
296 319 # errormator.logging.level = WARNING
297 320
298 # send logs only from erroneous/slow requests
299 # (saves API quota for intensive logging)
321 ## send logs only from erroneous/slow requests
322 ## (saves API quota for intensive logging)
300 323 errormator.logging_on_error = false
301 324
302 # list of additonal keywords that should be grabbed from environ object
303 # can be string with comma separated list of words in lowercase
304 # (by default client will always send following info:
305 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
306 # start with HTTP* this list be extended with additional keywords here
325 ## list of additonal keywords that should be grabbed from environ object
326 ## can be string with comma separated list of words in lowercase
327 ## (by default client will always send following info:
328 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
329 ## start with HTTP* this list be extended with additional keywords here
307 330 errormator.environ_keys_whitelist =
308 331
309 332
310 # list of keywords that should be blanked from request object
311 # can be string with comma separated list of words in lowercase
312 # (by default client will always blank keys that contain following words
313 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
314 # this list be extended with additional keywords set here
333 ## list of keywords that should be blanked from request object
334 ## can be string with comma separated list of words in lowercase
335 ## (by default client will always blank keys that contain following words
336 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
337 ## this list be extended with additional keywords set here
315 338 errormator.request_keys_blacklist =
316 339
317 340
318 # list of namespaces that should be ignores when gathering log entries
319 # can be string with comma separated list of namespaces
320 # (by default the client ignores own entries: errormator_client.client)
341 ## list of namespaces that should be ignores when gathering log entries
342 ## can be string with comma separated list of namespaces
343 ## (by default the client ignores own entries: errormator_client.client)
321 344 errormator.log_namespace_blacklist =
322 345
323 346
324 347 ################
325 348 ### [sentry] ###
326 349 ################
327 350
328 # sentry is a alternative open source error aggregator
329 # you must install python packages `sentry` and `raven` to enable
351 ## sentry is a alternative open source error aggregator
352 ## you must install python packages `sentry` and `raven` to enable
330 353
331 354 sentry.dsn = YOUR_DNS
332 355 sentry.servers =
333 356 sentry.name =
334 357 sentry.key =
335 358 sentry.public_key =
336 359 sentry.secret_key =
337 360 sentry.project =
338 361 sentry.site =
339 362 sentry.include_paths =
340 363 sentry.exclude_paths =
341 364
342 365
343 366 ################################################################################
344 367 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
345 368 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
346 369 ## execute malicious code after an exception is raised. ##
347 370 ################################################################################
348 371 #set debug = false
349 372
350 373 ##################################
351 374 ### LOGVIEW CONFIG ###
352 375 ##################################
353 376 logview.sqlalchemy = #faa
354 377 logview.pylons.templating = #bfb
355 378 logview.pylons.util = #eee
356 379
357 380 #########################################################
358 381 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
359 382 #########################################################
360 383 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
361 384 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
362 385 sqlalchemy.db1.echo = false
363 386 sqlalchemy.db1.pool_recycle = 3600
364 387 sqlalchemy.db1.convert_unicode = true
365 388
366 389 ################################
367 390 ### LOGGING CONFIGURATION ####
368 391 ################################
369 392 [loggers]
370 393 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
371 394
372 395 [handlers]
373 396 keys = console, console_sql
374 397
375 398 [formatters]
376 399 keys = generic, color_formatter, color_formatter_sql
377 400
378 401 #############
379 402 ## LOGGERS ##
380 403 #############
381 404 [logger_root]
382 405 level = NOTSET
383 406 handlers = console
384 407
385 408 [logger_routes]
386 409 level = DEBUG
387 410 handlers =
388 411 qualname = routes.middleware
389 # "level = DEBUG" logs the route matched and routing variables.
412 ## "level = DEBUG" logs the route matched and routing variables.
390 413 propagate = 1
391 414
392 415 [logger_beaker]
393 416 level = DEBUG
394 417 handlers =
395 418 qualname = beaker.container
396 419 propagate = 1
397 420
398 421 [logger_templates]
399 422 level = INFO
400 423 handlers =
401 424 qualname = pylons.templating
402 425 propagate = 1
403 426
404 427 [logger_rhodecode]
405 428 level = DEBUG
406 429 handlers =
407 430 qualname = rhodecode
408 431 propagate = 1
409 432
410 433 [logger_sqlalchemy]
411 434 level = INFO
412 435 handlers = console_sql
413 436 qualname = sqlalchemy.engine
414 437 propagate = 0
415 438
416 439 [logger_whoosh_indexer]
417 440 level = DEBUG
418 441 handlers =
419 442 qualname = whoosh_indexer
420 443 propagate = 1
421 444
422 445 ##############
423 446 ## HANDLERS ##
424 447 ##############
425 448
426 449 [handler_console]
427 450 class = StreamHandler
428 451 args = (sys.stderr,)
429 452 level = DEBUG
430 453 formatter = color_formatter
431 454
432 455 [handler_console_sql]
433 456 class = StreamHandler
434 457 args = (sys.stderr,)
435 458 level = DEBUG
436 459 formatter = color_formatter_sql
437 460
438 461 ################
439 462 ## FORMATTERS ##
440 463 ################
441 464
442 465 [formatter_generic]
443 466 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
444 467 datefmt = %Y-%m-%d %H:%M:%S
445 468
446 469 [formatter_color_formatter]
447 470 class=rhodecode.lib.colored_formatter.ColorFormatter
448 471 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
449 472 datefmt = %Y-%m-%d %H:%M:%S
450 473
451 474 [formatter_color_formatter_sql]
452 475 class=rhodecode.lib.colored_formatter.ColorFormatterSql
453 476 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
454 477 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,454 +1,477 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # RhodeCode - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 pdebug = false
11 11 ################################################################################
12 12 ## Uncomment and replace with the address which should receive ##
13 13 ## any error reports after application crash ##
14 14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 15 ################################################################################
16 16 #email_to = admin@localhost
17 17 #error_email_from = paste_error@localhost
18 18 #app_email_from = rhodecode-noreply@localhost
19 19 #error_message =
20 20 #email_prefix = [RhodeCode]
21 21
22 22 #smtp_server = mail.server.com
23 23 #smtp_username =
24 24 #smtp_password =
25 25 #smtp_port =
26 26 #smtp_use_tls = false
27 27 #smtp_use_ssl = true
28 # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
28 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
29 29 #smtp_auth =
30 30
31 31 [server:main]
32 32 ## PASTE
33 ##nr of threads to spawn
33 ## nr of threads to spawn
34 34 #threadpool_workers = 5
35 35
36 ##max request before thread respawn
36 ## max request before thread respawn
37 37 #threadpool_max_requests = 10
38 38
39 ##option to use threads of process
39 ## option to use threads of process
40 40 #use_threadpool = true
41 41
42 42 #use = egg:Paste#http
43 43
44 #WAITRESS
44 ## WAITRESS
45 45 threads = 5
46 #100GB
46 ## 100GB
47 47 max_request_body_size = 107374182400
48 48 use = egg:waitress#main
49 49
50 50 host = 127.0.0.1
51 51 port = 8001
52 52
53 [filter:proxy-prefix]
54 # prefix middleware for rc
55 use = egg:PasteDeploy#prefix
56 prefix = /<your-prefix>
53 ## prefix middleware for rc
54 #[filter:proxy-prefix]
55 #use = egg:PasteDeploy#prefix
56 #prefix = /<your-prefix>
57 57
58 58 [app:main]
59 59 use = egg:rhodecode
60 ## enable proxy prefix middleware
60 61 #filter-with = proxy-prefix
62
61 63 full_stack = true
62 64 static_files = true
63 # Optional Languages
64 # en, fr, ja, pt_BR, zh_CN, zh_TW, pl
65 ## Optional Languages
66 ## en, fr, ja, pt_BR, zh_CN, zh_TW, pl
65 67 lang = en
66 68 cache_dir = %(here)s/data
67 69 index_dir = %(here)s/data/index
68 # set this path to use archive download cache
69 #archive_cache_dir = /tmp/rhodecode_tarballcache
70
71 ## uncomment and set this path to use archive download cache
72 #archive_cache_dir = /tmp/tarballcache
73
74 ## change this to unique ID for security
70 75 app_instance_uuid = rc-production
76
77 ## cut off limit for large diffs (size in bytes)
71 78 cut_off_limit = 256000
72 vcs_full_cache = True
73 # force https in RhodeCode, fixes https redirects, assumes it's always https
79
80 ## use cache version of scm repo everywhere
81 vcs_full_cache = true
82
83 ## force https in RhodeCode, fixes https redirects, assumes it's always https
74 84 force_https = false
75 # use Strict-Transport-Security headers
85
86 ## use Strict-Transport-Security headers
76 87 use_htsts = false
77 commit_parse_limit = 50
78 # number of items displayed in lightweight dashboard before paginating
88
89 ## number of commits stats will parse on each iteration
90 commit_parse_limit = 25
91
92 ## number of items displayed in lightweight dashboard before paginating is shown
79 93 dashboard_items = 100
94
95 ## use gravatar service to display avatars
80 96 use_gravatar = true
81 97
82 # path to git executable
98 ## path to git executable
83 99 git_path = git
84 100
85 101 ## RSS feed options
86
87 102 rss_cut_off_limit = 256000
88 103 rss_items_per_page = 10
89 104 rss_include_diff = false
90 105
106 ## show hash options for changelog
107 sha_len = 12
108 sha_rev = true
109
91 110
92 111 ## alternative_gravatar_url allows you to use your own avatar server application
93 112 ## the following parts of the URL will be replaced
94 113 ## {email} user email
95 114 ## {md5email} md5 hash of the user email (like at gravatar.com)
96 115 ## {size} size of the image that is expected from the server application
97 116 ## {scheme} http/https from RhodeCode server
98 117 ## {netloc} network location from RhodeCode server
99 118 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
100 119 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
101 120
121
122 ## container auth options
102 123 container_auth_enabled = false
103 124 proxypass_auth_enabled = false
125
104 126 ## default encoding used to convert from and to unicode
105 127 ## can be also a comma seperated list of encoding in case of mixed encodings
106 128 default_encoding = utf8
107 129
108 130 ## overwrite schema of clone url
109 131 ## available vars:
110 132 ## scheme - http/https
111 133 ## user - current user
112 134 ## pass - password
113 135 ## netloc - network location
114 136 ## path - usually repo_name
115 137
116 138 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
117 139
118 140 ## issue tracking mapping for commits messages
119 141 ## comment out issue_pat, issue_server, issue_prefix to enable
120 142
121 143 ## pattern to get the issues from commit messages
122 144 ## default one used here is #<numbers> with a regex passive group for `#`
123 145 ## {id} will be all groups matched from this pattern
124 146
125 147 issue_pat = (?:\s*#)(\d+)
126 148
127 149 ## server url to the issue, each {id} will be replaced with match
128 150 ## fetched from the regex and {repo} is replaced with full repository name
129 151 ## including groups {repo_name} is replaced with just name of repo
130 152
131 153 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
132 154
133 155 ## prefix to add to link to indicate it's an url
134 156 ## #314 will be replaced by <issue_prefix><id>
135 157
136 158 issue_prefix = #
137 159
138 160 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
139 161 ## multiple patterns, to other issues server, wiki or others
140 162 ## below an example how to create a wiki pattern
141 163 # #wiki-some-id -> https://mywiki.com/some-id
142 164
143 165 #issue_pat_wiki = (?:wiki-)(.+)
144 166 #issue_server_link_wiki = https://mywiki.com/{id}
145 167 #issue_prefix_wiki = WIKI-
146 168
147 169
148 170 ## instance-id prefix
149 171 ## a prefix key for this instance used for cache invalidation when running
150 172 ## multiple instances of rhodecode, make sure it's globally unique for
151 173 ## all running rhodecode instances. Leave empty if you don't use it
152 174 instance_id =
153 175
154 176 ## alternative return HTTP header for failed authentication. Default HTTP
155 177 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
156 178 ## handling that. Set this variable to 403 to return HTTPForbidden
157 179 auth_ret_code =
158 180
159 181 ## locking return code. When repository is locked return this HTTP code. 2XX
160 182 ## codes don't break the transactions while 4XX codes do
161 183 lock_ret_code = 423
162 184
163 185
164 186 ####################################
165 187 ### CELERY CONFIG ####
166 188 ####################################
167 189 use_celery = false
168 190 broker.host = localhost
169 191 broker.vhost = rabbitmqhost
170 192 broker.port = 5672
171 193 broker.user = rabbitmq
172 194 broker.password = qweqwe
173 195
174 196 celery.imports = rhodecode.lib.celerylib.tasks
175 197
176 198 celery.result.backend = amqp
177 199 celery.result.dburi = amqp://
178 200 celery.result.serialier = json
179 201
180 202 #celery.send.task.error.emails = true
181 203 #celery.amqp.task.result.expires = 18000
182 204
183 205 celeryd.concurrency = 2
184 206 #celeryd.log.file = celeryd.log
185 207 celeryd.log.level = debug
186 208 celeryd.max.tasks.per.child = 1
187 209
188 #tasks will never be sent to the queue, but executed locally instead.
210 ## tasks will never be sent to the queue, but executed locally instead.
189 211 celery.always.eager = false
190 212
191 213 ####################################
192 214 ### BEAKER CACHE ####
193 215 ####################################
194 216 beaker.cache.data_dir=%(here)s/data/cache/data
195 217 beaker.cache.lock_dir=%(here)s/data/cache/lock
196 218
197 219 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
198 220
199 221 beaker.cache.super_short_term.type=memory
200 222 beaker.cache.super_short_term.expire=10
201 223 beaker.cache.super_short_term.key_length = 256
202 224
203 225 beaker.cache.short_term.type=memory
204 226 beaker.cache.short_term.expire=60
205 227 beaker.cache.short_term.key_length = 256
206 228
207 229 beaker.cache.long_term.type=memory
208 230 beaker.cache.long_term.expire=36000
209 231 beaker.cache.long_term.key_length = 256
210 232
211 233 beaker.cache.sql_cache_short.type=memory
212 234 beaker.cache.sql_cache_short.expire=10
213 235 beaker.cache.sql_cache_short.key_length = 256
214 236
215 237 beaker.cache.sql_cache_med.type=memory
216 238 beaker.cache.sql_cache_med.expire=360
217 239 beaker.cache.sql_cache_med.key_length = 256
218 240
219 241 beaker.cache.sql_cache_long.type=file
220 242 beaker.cache.sql_cache_long.expire=3600
221 243 beaker.cache.sql_cache_long.key_length = 256
222 244
223 245 ####################################
224 246 ### BEAKER SESSION ####
225 247 ####################################
226 248 ## Type of storage used for the session, current types are
227 249 ## dbm, file, memcached, database, and memory.
228 250 ## The storage uses the Container API
229 251 ## that is also used by the cache system.
230 252
231 253 ## db session ##
232 254 #beaker.session.type = ext:database
233 255 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
234 256 #beaker.session.table_name = db_session
235 257
236 258 ## encrypted cookie client side session, good for many instances ##
237 259 #beaker.session.type = cookie
238 260
239 261 ## file based cookies (default) ##
240 262 #beaker.session.type = file
241 263
242 264
243 265 beaker.session.key = rhodecode
244 ## secure cookie requires AES python libraries ##
245 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
246 #beaker.session.validate_key = 9712sds2212c--zxc123
266 ## secure cookie requires AES python libraries
267 #beaker.session.encrypt_key = <key_for_encryption>
268 #beaker.session.validate_key = <validation_key>
269
247 270 ## sets session as invalid if it haven't been accessed for given amount of time
248 271 beaker.session.timeout = 2592000
249 272 beaker.session.httponly = true
250 273 #beaker.session.cookie_path = /<your-prefix>
251 274
252 ## uncomment for https secure cookie ##
275 ## uncomment for https secure cookie
253 276 beaker.session.secure = false
254 277
255 ## auto save the session to not to use .save() ##
278 ## auto save the session to not to use .save()
256 279 beaker.session.auto = False
257 280
258 281 ## default cookie expiration time in seconds `true` expire at browser close ##
259 282 #beaker.session.cookie_expires = 3600
260 283
261 284
262 285 ############################
263 286 ## ERROR HANDLING SYSTEMS ##
264 287 ############################
265 288
266 289 ####################
267 290 ### [errormator] ###
268 291 ####################
269 292
270 # Errormator is tailored to work with RhodeCode, see
271 # http://errormator.com for details how to obtain an account
272 # you must install python package `errormator_client` to make it work
293 ## Errormator is tailored to work with RhodeCode, see
294 ## http://errormator.com for details how to obtain an account
295 ## you must install python package `errormator_client` to make it work
273 296
274 # errormator enabled
275 errormator = true
297 ## errormator enabled
298 errormator = false
276 299
277 300 errormator.server_url = https://api.errormator.com
278 301 errormator.api_key = YOUR_API_KEY
279 302
280 # TWEAK AMOUNT OF INFO SENT HERE
303 ## TWEAK AMOUNT OF INFO SENT HERE
281 304
282 # enables 404 error logging (default False)
305 ## enables 404 error logging (default False)
283 306 errormator.report_404 = false
284 307
285 # time in seconds after request is considered being slow (default 1)
308 ## time in seconds after request is considered being slow (default 1)
286 309 errormator.slow_request_time = 1
287 310
288 # record slow requests in application
289 # (needs to be enabled for slow datastore recording and time tracking)
311 ## record slow requests in application
312 ## (needs to be enabled for slow datastore recording and time tracking)
290 313 errormator.slow_requests = true
291 314
292 # enable hooking to application loggers
315 ## enable hooking to application loggers
293 316 # errormator.logging = true
294 317
295 # minimum log level for log capture
318 ## minimum log level for log capture
296 319 # errormator.logging.level = WARNING
297 320
298 # send logs only from erroneous/slow requests
299 # (saves API quota for intensive logging)
321 ## send logs only from erroneous/slow requests
322 ## (saves API quota for intensive logging)
300 323 errormator.logging_on_error = false
301 324
302 # list of additonal keywords that should be grabbed from environ object
303 # can be string with comma separated list of words in lowercase
304 # (by default client will always send following info:
305 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
306 # start with HTTP* this list be extended with additional keywords here
325 ## list of additonal keywords that should be grabbed from environ object
326 ## can be string with comma separated list of words in lowercase
327 ## (by default client will always send following info:
328 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
329 ## start with HTTP* this list be extended with additional keywords here
307 330 errormator.environ_keys_whitelist =
308 331
309 332
310 # list of keywords that should be blanked from request object
311 # can be string with comma separated list of words in lowercase
312 # (by default client will always blank keys that contain following words
313 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
314 # this list be extended with additional keywords set here
333 ## list of keywords that should be blanked from request object
334 ## can be string with comma separated list of words in lowercase
335 ## (by default client will always blank keys that contain following words
336 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
337 ## this list be extended with additional keywords set here
315 338 errormator.request_keys_blacklist =
316 339
317 340
318 # list of namespaces that should be ignores when gathering log entries
319 # can be string with comma separated list of namespaces
320 # (by default the client ignores own entries: errormator_client.client)
341 ## list of namespaces that should be ignores when gathering log entries
342 ## can be string with comma separated list of namespaces
343 ## (by default the client ignores own entries: errormator_client.client)
321 344 errormator.log_namespace_blacklist =
322 345
323 346
324 347 ################
325 348 ### [sentry] ###
326 349 ################
327 350
328 # sentry is a alternative open source error aggregator
329 # you must install python packages `sentry` and `raven` to enable
351 ## sentry is a alternative open source error aggregator
352 ## you must install python packages `sentry` and `raven` to enable
330 353
331 354 sentry.dsn = YOUR_DNS
332 355 sentry.servers =
333 356 sentry.name =
334 357 sentry.key =
335 358 sentry.public_key =
336 359 sentry.secret_key =
337 360 sentry.project =
338 361 sentry.site =
339 362 sentry.include_paths =
340 363 sentry.exclude_paths =
341 364
342 365
343 366 ################################################################################
344 367 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
345 368 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
346 369 ## execute malicious code after an exception is raised. ##
347 370 ################################################################################
348 371 set debug = false
349 372
350 373 ##################################
351 374 ### LOGVIEW CONFIG ###
352 375 ##################################
353 376 logview.sqlalchemy = #faa
354 377 logview.pylons.templating = #bfb
355 378 logview.pylons.util = #eee
356 379
357 380 #########################################################
358 381 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
359 382 #########################################################
360 383 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
361 384 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
362 385 sqlalchemy.db1.echo = false
363 386 sqlalchemy.db1.pool_recycle = 3600
364 387 sqlalchemy.db1.convert_unicode = true
365 388
366 389 ################################
367 390 ### LOGGING CONFIGURATION ####
368 391 ################################
369 392 [loggers]
370 393 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
371 394
372 395 [handlers]
373 396 keys = console, console_sql
374 397
375 398 [formatters]
376 399 keys = generic, color_formatter, color_formatter_sql
377 400
378 401 #############
379 402 ## LOGGERS ##
380 403 #############
381 404 [logger_root]
382 405 level = NOTSET
383 406 handlers = console
384 407
385 408 [logger_routes]
386 409 level = DEBUG
387 410 handlers =
388 411 qualname = routes.middleware
389 # "level = DEBUG" logs the route matched and routing variables.
412 ## "level = DEBUG" logs the route matched and routing variables.
390 413 propagate = 1
391 414
392 415 [logger_beaker]
393 416 level = DEBUG
394 417 handlers =
395 418 qualname = beaker.container
396 419 propagate = 1
397 420
398 421 [logger_templates]
399 422 level = INFO
400 423 handlers =
401 424 qualname = pylons.templating
402 425 propagate = 1
403 426
404 427 [logger_rhodecode]
405 428 level = DEBUG
406 429 handlers =
407 430 qualname = rhodecode
408 431 propagate = 1
409 432
410 433 [logger_sqlalchemy]
411 434 level = INFO
412 435 handlers = console_sql
413 436 qualname = sqlalchemy.engine
414 437 propagate = 0
415 438
416 439 [logger_whoosh_indexer]
417 440 level = DEBUG
418 441 handlers =
419 442 qualname = whoosh_indexer
420 443 propagate = 1
421 444
422 445 ##############
423 446 ## HANDLERS ##
424 447 ##############
425 448
426 449 [handler_console]
427 450 class = StreamHandler
428 451 args = (sys.stderr,)
429 452 level = INFO
430 453 formatter = generic
431 454
432 455 [handler_console_sql]
433 456 class = StreamHandler
434 457 args = (sys.stderr,)
435 458 level = WARN
436 459 formatter = generic
437 460
438 461 ################
439 462 ## FORMATTERS ##
440 463 ################
441 464
442 465 [formatter_generic]
443 466 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
444 467 datefmt = %Y-%m-%d %H:%M:%S
445 468
446 469 [formatter_color_formatter]
447 470 class=rhodecode.lib.colored_formatter.ColorFormatter
448 471 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
449 472 datefmt = %Y-%m-%d %H:%M:%S
450 473
451 474 [formatter_color_formatter_sql]
452 475 class=rhodecode.lib.colored_formatter.ColorFormatterSql
453 476 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
454 477 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,464 +1,487 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # RhodeCode - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 pdebug = false
11 11 ################################################################################
12 12 ## Uncomment and replace with the address which should receive ##
13 13 ## any error reports after application crash ##
14 14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 15 ################################################################################
16 16 #email_to = admin@localhost
17 17 #error_email_from = paste_error@localhost
18 18 #app_email_from = rhodecode-noreply@localhost
19 19 #error_message =
20 20 #email_prefix = [RhodeCode]
21 21
22 22 #smtp_server = mail.server.com
23 23 #smtp_username =
24 24 #smtp_password =
25 25 #smtp_port =
26 26 #smtp_use_tls = false
27 27 #smtp_use_ssl = true
28 # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
28 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
29 29 #smtp_auth =
30 30
31 31 [server:main]
32 32 ## PASTE
33 ##nr of threads to spawn
33 ## nr of threads to spawn
34 34 #threadpool_workers = 5
35 35
36 ##max request before thread respawn
36 ## max request before thread respawn
37 37 #threadpool_max_requests = 10
38 38
39 ##option to use threads of process
39 ## option to use threads of process
40 40 #use_threadpool = true
41 41
42 42 #use = egg:Paste#http
43 43
44 #WAITRESS
44 ## WAITRESS
45 45 threads = 5
46 #100GB
46 ## 100GB
47 47 max_request_body_size = 107374182400
48 48 use = egg:waitress#main
49 49
50 50 host = 127.0.0.1
51 51 port = 5000
52 52
53 [filter:proxy-prefix]
54 # prefix middleware for rc
55 use = egg:PasteDeploy#prefix
56 prefix = /<your-prefix>
53 ## prefix middleware for rc
54 #[filter:proxy-prefix]
55 #use = egg:PasteDeploy#prefix
56 #prefix = /<your-prefix>
57 57
58 58 [app:main]
59 59 use = egg:rhodecode
60 ## enable proxy prefix middleware
60 61 #filter-with = proxy-prefix
62
61 63 full_stack = true
62 64 static_files = true
63 # Optional Languages
64 # en, fr, ja, pt_BR, zh_CN, zh_TW, pl
65 ## Optional Languages
66 ## en, fr, ja, pt_BR, zh_CN, zh_TW, pl
65 67 lang = en
66 68 cache_dir = %(here)s/data
67 69 index_dir = %(here)s/data/index
68 # set this path to use archive download cache
69 #archive_cache_dir = /tmp/rhodecode_tarballcache
70
71 ## uncomment and set this path to use archive download cache
72 #archive_cache_dir = /tmp/tarballcache
73
74 ## change this to unique ID for security
70 75 app_instance_uuid = ${app_instance_uuid}
76
77 ## cut off limit for large diffs (size in bytes)
71 78 cut_off_limit = 256000
72 vcs_full_cache = True
73 # force https in RhodeCode, fixes https redirects, assumes it's always https
79
80 ## use cache version of scm repo everywhere
81 vcs_full_cache = true
82
83 ## force https in RhodeCode, fixes https redirects, assumes it's always https
74 84 force_https = false
75 # use Strict-Transport-Security headers
85
86 ## use Strict-Transport-Security headers
76 87 use_htsts = false
77 commit_parse_limit = 50
78 # number of items displayed in lightweight dashboard before paginating
88
89 ## number of commits stats will parse on each iteration
90 commit_parse_limit = 25
91
92 ## number of items displayed in lightweight dashboard before paginating is shown
79 93 dashboard_items = 100
94
95 ## use gravatar service to display avatars
80 96 use_gravatar = true
81 97
82 # path to git executable
98 ## path to git executable
83 99 git_path = git
84 100
85 101 ## RSS feed options
86
87 102 rss_cut_off_limit = 256000
88 103 rss_items_per_page = 10
89 104 rss_include_diff = false
90 105
106 ## show hash options for changelog
107 sha_len = 12
108 sha_rev = true
109
91 110
92 111 ## alternative_gravatar_url allows you to use your own avatar server application
93 112 ## the following parts of the URL will be replaced
94 113 ## {email} user email
95 114 ## {md5email} md5 hash of the user email (like at gravatar.com)
96 115 ## {size} size of the image that is expected from the server application
97 116 ## {scheme} http/https from RhodeCode server
98 117 ## {netloc} network location from RhodeCode server
99 118 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
100 119 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
101 120
121
122 ## container auth options
102 123 container_auth_enabled = false
103 124 proxypass_auth_enabled = false
125
104 126 ## default encoding used to convert from and to unicode
105 127 ## can be also a comma seperated list of encoding in case of mixed encodings
106 128 default_encoding = utf8
107 129
108 130 ## overwrite schema of clone url
109 131 ## available vars:
110 132 ## scheme - http/https
111 133 ## user - current user
112 134 ## pass - password
113 135 ## netloc - network location
114 136 ## path - usually repo_name
115 137
116 138 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
117 139
118 140 ## issue tracking mapping for commits messages
119 141 ## comment out issue_pat, issue_server, issue_prefix to enable
120 142
121 143 ## pattern to get the issues from commit messages
122 144 ## default one used here is #<numbers> with a regex passive group for `#`
123 145 ## {id} will be all groups matched from this pattern
124 146
125 147 issue_pat = (?:\s*#)(\d+)
126 148
127 149 ## server url to the issue, each {id} will be replaced with match
128 150 ## fetched from the regex and {repo} is replaced with full repository name
129 151 ## including groups {repo_name} is replaced with just name of repo
130 152
131 153 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
132 154
133 155 ## prefix to add to link to indicate it's an url
134 156 ## #314 will be replaced by <issue_prefix><id>
135 157
136 158 issue_prefix = #
137 159
138 160 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
139 161 ## multiple patterns, to other issues server, wiki or others
140 162 ## below an example how to create a wiki pattern
141 163 # #wiki-some-id -> https://mywiki.com/some-id
142 164
143 165 #issue_pat_wiki = (?:wiki-)(.+)
144 166 #issue_server_link_wiki = https://mywiki.com/{id}
145 167 #issue_prefix_wiki = WIKI-
146 168
147 169
148 170 ## instance-id prefix
149 171 ## a prefix key for this instance used for cache invalidation when running
150 172 ## multiple instances of rhodecode, make sure it's globally unique for
151 173 ## all running rhodecode instances. Leave empty if you don't use it
152 174 instance_id =
153 175
154 176 ## alternative return HTTP header for failed authentication. Default HTTP
155 177 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
156 178 ## handling that. Set this variable to 403 to return HTTPForbidden
157 179 auth_ret_code =
158 180
159 181 ## locking return code. When repository is locked return this HTTP code. 2XX
160 182 ## codes don't break the transactions while 4XX codes do
161 183 lock_ret_code = 423
162 184
163 185
164 186 ####################################
165 187 ### CELERY CONFIG ####
166 188 ####################################
167 189 use_celery = false
168 190 broker.host = localhost
169 191 broker.vhost = rabbitmqhost
170 192 broker.port = 5672
171 193 broker.user = rabbitmq
172 194 broker.password = qweqwe
173 195
174 196 celery.imports = rhodecode.lib.celerylib.tasks
175 197
176 198 celery.result.backend = amqp
177 199 celery.result.dburi = amqp://
178 200 celery.result.serialier = json
179 201
180 202 #celery.send.task.error.emails = true
181 203 #celery.amqp.task.result.expires = 18000
182 204
183 205 celeryd.concurrency = 2
184 206 #celeryd.log.file = celeryd.log
185 207 celeryd.log.level = debug
186 208 celeryd.max.tasks.per.child = 1
187 209
188 #tasks will never be sent to the queue, but executed locally instead.
210 ## tasks will never be sent to the queue, but executed locally instead.
189 211 celery.always.eager = false
190 212
191 213 ####################################
192 214 ### BEAKER CACHE ####
193 215 ####################################
194 216 beaker.cache.data_dir=%(here)s/data/cache/data
195 217 beaker.cache.lock_dir=%(here)s/data/cache/lock
196 218
197 219 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
198 220
199 221 beaker.cache.super_short_term.type=memory
200 222 beaker.cache.super_short_term.expire=10
201 223 beaker.cache.super_short_term.key_length = 256
202 224
203 225 beaker.cache.short_term.type=memory
204 226 beaker.cache.short_term.expire=60
205 227 beaker.cache.short_term.key_length = 256
206 228
207 229 beaker.cache.long_term.type=memory
208 230 beaker.cache.long_term.expire=36000
209 231 beaker.cache.long_term.key_length = 256
210 232
211 233 beaker.cache.sql_cache_short.type=memory
212 234 beaker.cache.sql_cache_short.expire=10
213 235 beaker.cache.sql_cache_short.key_length = 256
214 236
215 237 beaker.cache.sql_cache_med.type=memory
216 238 beaker.cache.sql_cache_med.expire=360
217 239 beaker.cache.sql_cache_med.key_length = 256
218 240
219 241 beaker.cache.sql_cache_long.type=file
220 242 beaker.cache.sql_cache_long.expire=3600
221 243 beaker.cache.sql_cache_long.key_length = 256
222 244
223 245 ####################################
224 246 ### BEAKER SESSION ####
225 247 ####################################
226 248 ## Type of storage used for the session, current types are
227 249 ## dbm, file, memcached, database, and memory.
228 250 ## The storage uses the Container API
229 251 ## that is also used by the cache system.
230 252
231 253 ## db session ##
232 254 #beaker.session.type = ext:database
233 255 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
234 256 #beaker.session.table_name = db_session
235 257
236 258 ## encrypted cookie client side session, good for many instances ##
237 259 #beaker.session.type = cookie
238 260
239 261 ## file based cookies (default) ##
240 262 #beaker.session.type = file
241 263
242 264
243 265 beaker.session.key = rhodecode
244 ## secure cookie requires AES python libraries ##
245 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
246 #beaker.session.validate_key = 9712sds2212c--zxc123
266 ## secure cookie requires AES python libraries
267 #beaker.session.encrypt_key = <key_for_encryption>
268 #beaker.session.validate_key = <validation_key>
269
247 270 ## sets session as invalid if it haven't been accessed for given amount of time
248 271 beaker.session.timeout = 2592000
249 272 beaker.session.httponly = true
250 273 #beaker.session.cookie_path = /<your-prefix>
251 274
252 ## uncomment for https secure cookie ##
275 ## uncomment for https secure cookie
253 276 beaker.session.secure = false
254 277
255 ## auto save the session to not to use .save() ##
278 ## auto save the session to not to use .save()
256 279 beaker.session.auto = False
257 280
258 281 ## default cookie expiration time in seconds `true` expire at browser close ##
259 282 #beaker.session.cookie_expires = 3600
260 283
261 284
262 285 ############################
263 286 ## ERROR HANDLING SYSTEMS ##
264 287 ############################
265 288
266 289 ####################
267 290 ### [errormator] ###
268 291 ####################
269 292
270 # Errormator is tailored to work with RhodeCode, see
271 # http://errormator.com for details how to obtain an account
272 # you must install python package `errormator_client` to make it work
293 ## Errormator is tailored to work with RhodeCode, see
294 ## http://errormator.com for details how to obtain an account
295 ## you must install python package `errormator_client` to make it work
273 296
274 # errormator enabled
275 errormator = true
297 ## errormator enabled
298 errormator = false
276 299
277 300 errormator.server_url = https://api.errormator.com
278 301 errormator.api_key = YOUR_API_KEY
279 302
280 # TWEAK AMOUNT OF INFO SENT HERE
303 ## TWEAK AMOUNT OF INFO SENT HERE
281 304
282 # enables 404 error logging (default False)
305 ## enables 404 error logging (default False)
283 306 errormator.report_404 = false
284 307
285 # time in seconds after request is considered being slow (default 1)
308 ## time in seconds after request is considered being slow (default 1)
286 309 errormator.slow_request_time = 1
287 310
288 # record slow requests in application
289 # (needs to be enabled for slow datastore recording and time tracking)
311 ## record slow requests in application
312 ## (needs to be enabled for slow datastore recording and time tracking)
290 313 errormator.slow_requests = true
291 314
292 # enable hooking to application loggers
315 ## enable hooking to application loggers
293 316 # errormator.logging = true
294 317
295 # minimum log level for log capture
318 ## minimum log level for log capture
296 319 # errormator.logging.level = WARNING
297 320
298 # send logs only from erroneous/slow requests
299 # (saves API quota for intensive logging)
321 ## send logs only from erroneous/slow requests
322 ## (saves API quota for intensive logging)
300 323 errormator.logging_on_error = false
301 324
302 # list of additonal keywords that should be grabbed from environ object
303 # can be string with comma separated list of words in lowercase
304 # (by default client will always send following info:
305 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
306 # start with HTTP* this list be extended with additional keywords here
325 ## list of additonal keywords that should be grabbed from environ object
326 ## can be string with comma separated list of words in lowercase
327 ## (by default client will always send following info:
328 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
329 ## start with HTTP* this list be extended with additional keywords here
307 330 errormator.environ_keys_whitelist =
308 331
309 332
310 # list of keywords that should be blanked from request object
311 # can be string with comma separated list of words in lowercase
312 # (by default client will always blank keys that contain following words
313 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
314 # this list be extended with additional keywords set here
333 ## list of keywords that should be blanked from request object
334 ## can be string with comma separated list of words in lowercase
335 ## (by default client will always blank keys that contain following words
336 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
337 ## this list be extended with additional keywords set here
315 338 errormator.request_keys_blacklist =
316 339
317 340
318 # list of namespaces that should be ignores when gathering log entries
319 # can be string with comma separated list of namespaces
320 # (by default the client ignores own entries: errormator_client.client)
341 ## list of namespaces that should be ignores when gathering log entries
342 ## can be string with comma separated list of namespaces
343 ## (by default the client ignores own entries: errormator_client.client)
321 344 errormator.log_namespace_blacklist =
322 345
323 346
324 347 ################
325 348 ### [sentry] ###
326 349 ################
327 350
328 # sentry is a alternative open source error aggregator
329 # you must install python packages `sentry` and `raven` to enable
351 ## sentry is a alternative open source error aggregator
352 ## you must install python packages `sentry` and `raven` to enable
330 353
331 354 sentry.dsn = YOUR_DNS
332 355 sentry.servers =
333 356 sentry.name =
334 357 sentry.key =
335 358 sentry.public_key =
336 359 sentry.secret_key =
337 360 sentry.project =
338 361 sentry.site =
339 362 sentry.include_paths =
340 363 sentry.exclude_paths =
341 364
342 365
343 366 ################################################################################
344 367 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
345 368 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
346 369 ## execute malicious code after an exception is raised. ##
347 370 ################################################################################
348 371 set debug = false
349 372
350 373 ##################################
351 374 ### LOGVIEW CONFIG ###
352 375 ##################################
353 376 logview.sqlalchemy = #faa
354 377 logview.pylons.templating = #bfb
355 378 logview.pylons.util = #eee
356 379
357 380 #########################################################
358 381 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
359 382 #########################################################
360 383
361 384 # SQLITE [default]
362 385 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
363 386
364 387 # POSTGRESQL
365 388 # sqlalchemy.db1.url = postgresql://user:pass@localhost/rhodecode
366 389
367 390 # MySQL
368 391 # sqlalchemy.db1.url = mysql://user:pass@localhost/rhodecode
369 392
370 393 # see sqlalchemy docs for others
371 394
372 395 sqlalchemy.db1.echo = false
373 396 sqlalchemy.db1.pool_recycle = 3600
374 397 sqlalchemy.db1.convert_unicode = true
375 398
376 399 ################################
377 400 ### LOGGING CONFIGURATION ####
378 401 ################################
379 402 [loggers]
380 403 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
381 404
382 405 [handlers]
383 406 keys = console, console_sql
384 407
385 408 [formatters]
386 409 keys = generic, color_formatter, color_formatter_sql
387 410
388 411 #############
389 412 ## LOGGERS ##
390 413 #############
391 414 [logger_root]
392 415 level = NOTSET
393 416 handlers = console
394 417
395 418 [logger_routes]
396 419 level = DEBUG
397 420 handlers =
398 421 qualname = routes.middleware
399 # "level = DEBUG" logs the route matched and routing variables.
422 ## "level = DEBUG" logs the route matched and routing variables.
400 423 propagate = 1
401 424
402 425 [logger_beaker]
403 426 level = DEBUG
404 427 handlers =
405 428 qualname = beaker.container
406 429 propagate = 1
407 430
408 431 [logger_templates]
409 432 level = INFO
410 433 handlers =
411 434 qualname = pylons.templating
412 435 propagate = 1
413 436
414 437 [logger_rhodecode]
415 438 level = DEBUG
416 439 handlers =
417 440 qualname = rhodecode
418 441 propagate = 1
419 442
420 443 [logger_sqlalchemy]
421 444 level = INFO
422 445 handlers = console_sql
423 446 qualname = sqlalchemy.engine
424 447 propagate = 0
425 448
426 449 [logger_whoosh_indexer]
427 450 level = DEBUG
428 451 handlers =
429 452 qualname = whoosh_indexer
430 453 propagate = 1
431 454
432 455 ##############
433 456 ## HANDLERS ##
434 457 ##############
435 458
436 459 [handler_console]
437 460 class = StreamHandler
438 461 args = (sys.stderr,)
439 462 level = INFO
440 463 formatter = generic
441 464
442 465 [handler_console_sql]
443 466 class = StreamHandler
444 467 args = (sys.stderr,)
445 468 level = WARN
446 469 formatter = generic
447 470
448 471 ################
449 472 ## FORMATTERS ##
450 473 ################
451 474
452 475 [formatter_generic]
453 476 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
454 477 datefmt = %Y-%m-%d %H:%M:%S
455 478
456 479 [formatter_color_formatter]
457 480 class=rhodecode.lib.colored_formatter.ColorFormatter
458 481 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
459 482 datefmt = %Y-%m-%d %H:%M:%S
460 483
461 484 [formatter_color_formatter_sql]
462 485 class=rhodecode.lib.colored_formatter.ColorFormatterSql
463 486 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
464 487 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,1195 +1,1214 b''
1 1 """Helper functions
2 2
3 3 Consists of functions to typically be used within templates, but also
4 4 available to Controllers. This module is available to both as 'h'.
5 5 """
6 6 import random
7 7 import hashlib
8 8 import StringIO
9 9 import urllib
10 10 import math
11 11 import logging
12 12 import re
13 13 import urlparse
14 14 import textwrap
15 15
16 16 from datetime import datetime
17 17 from pygments.formatters.html import HtmlFormatter
18 18 from pygments import highlight as code_highlight
19 19 from pylons import url, request, config
20 20 from pylons.i18n.translation import _, ungettext
21 21 from hashlib import md5
22 22
23 23 from webhelpers.html import literal, HTML, escape
24 24 from webhelpers.html.tools import *
25 25 from webhelpers.html.builder import make_tag
26 26 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
27 27 end_form, file, form, hidden, image, javascript_link, link_to, \
28 28 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
29 29 submit, text, password, textarea, title, ul, xml_declaration, radio
30 30 from webhelpers.html.tools import auto_link, button_to, highlight, \
31 31 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
32 32 from webhelpers.number import format_byte_size, format_bit_size
33 33 from webhelpers.pylonslib import Flash as _Flash
34 34 from webhelpers.pylonslib.secure_form import secure_form
35 35 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
36 36 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
37 37 replace_whitespace, urlify, truncate, wrap_paragraphs
38 38 from webhelpers.date import time_ago_in_words
39 39 from webhelpers.paginate import Page
40 40 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
41 41 convert_boolean_attrs, NotGiven, _make_safe_id_component
42 42
43 43 from rhodecode.lib.annotate import annotate_highlight
44 44 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
45 45 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
46 get_changeset_safe, datetime_to_time, time_to_datetime, AttributeDict
46 get_changeset_safe, datetime_to_time, time_to_datetime, AttributeDict,\
47 safe_int
47 48 from rhodecode.lib.markup_renderer import MarkupRenderer
48 49 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
49 50 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyChangeset
50 51 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
51 52 from rhodecode.model.changeset_status import ChangesetStatusModel
52 53 from rhodecode.model.db import URL_SEP, Permission
53 54
54 55 log = logging.getLogger(__name__)
55 56
56 57
57 58 html_escape_table = {
58 59 "&": "&amp;",
59 60 '"': "&quot;",
60 61 "'": "&apos;",
61 62 ">": "&gt;",
62 63 "<": "&lt;",
63 64 }
64 65
65 66
66 67 def html_escape(text):
67 68 """Produce entities within text."""
68 69 return "".join(html_escape_table.get(c, c) for c in text)
69 70
70 71
71 72 def shorter(text, size=20):
72 73 postfix = '...'
73 74 if len(text) > size:
74 75 return text[:size - len(postfix)] + postfix
75 76 return text
76 77
77 78
78 79 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
79 80 """
80 81 Reset button
81 82 """
82 83 _set_input_attrs(attrs, type, name, value)
83 84 _set_id_attr(attrs, id, name)
84 85 convert_boolean_attrs(attrs, ["disabled"])
85 86 return HTML.input(**attrs)
86 87
87 88 reset = _reset
88 89 safeid = _make_safe_id_component
89 90
90 91
91 92 def FID(raw_id, path):
92 93 """
93 94 Creates a uniqe ID for filenode based on it's hash of path and revision
94 95 it's safe to use in urls
95 96
96 97 :param raw_id:
97 98 :param path:
98 99 """
99 100
100 101 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
101 102
102 103
103 104 def get_token():
104 105 """Return the current authentication token, creating one if one doesn't
105 106 already exist.
106 107 """
107 108 token_key = "_authentication_token"
108 109 from pylons import session
109 110 if not token_key in session:
110 111 try:
111 112 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
112 113 except AttributeError: # Python < 2.4
113 114 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
114 115 session[token_key] = token
115 116 if hasattr(session, 'save'):
116 117 session.save()
117 118 return session[token_key]
118 119
119 120
120 121 class _GetError(object):
121 122 """Get error from form_errors, and represent it as span wrapped error
122 123 message
123 124
124 125 :param field_name: field to fetch errors for
125 126 :param form_errors: form errors dict
126 127 """
127 128
128 129 def __call__(self, field_name, form_errors):
129 130 tmpl = """<span class="error_msg">%s</span>"""
130 131 if form_errors and field_name in form_errors:
131 132 return literal(tmpl % form_errors.get(field_name))
132 133
133 134 get_error = _GetError()
134 135
135 136
136 137 class _ToolTip(object):
137 138
138 139 def __call__(self, tooltip_title, trim_at=50):
139 140 """
140 141 Special function just to wrap our text into nice formatted
141 142 autowrapped text
142 143
143 144 :param tooltip_title:
144 145 """
145 146 tooltip_title = escape(tooltip_title)
146 147 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
147 148 return tooltip_title
148 149 tooltip = _ToolTip()
149 150
150 151
151 152 class _FilesBreadCrumbs(object):
152 153
153 154 def __call__(self, repo_name, rev, paths):
154 155 if isinstance(paths, str):
155 156 paths = safe_unicode(paths)
156 157 url_l = [link_to(repo_name, url('files_home',
157 158 repo_name=repo_name,
158 159 revision=rev, f_path=''),
159 160 class_='ypjax-link')]
160 161 paths_l = paths.split('/')
161 162 for cnt, p in enumerate(paths_l):
162 163 if p != '':
163 164 url_l.append(link_to(p,
164 165 url('files_home',
165 166 repo_name=repo_name,
166 167 revision=rev,
167 168 f_path='/'.join(paths_l[:cnt + 1])
168 169 ),
169 170 class_='ypjax-link'
170 171 )
171 172 )
172 173
173 174 return literal('/'.join(url_l))
174 175
175 176 files_breadcrumbs = _FilesBreadCrumbs()
176 177
177 178
178 179 class CodeHtmlFormatter(HtmlFormatter):
179 180 """
180 181 My code Html Formatter for source codes
181 182 """
182 183
183 184 def wrap(self, source, outfile):
184 185 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
185 186
186 187 def _wrap_code(self, source):
187 188 for cnt, it in enumerate(source):
188 189 i, t = it
189 190 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
190 191 yield i, t
191 192
192 193 def _wrap_tablelinenos(self, inner):
193 194 dummyoutfile = StringIO.StringIO()
194 195 lncount = 0
195 196 for t, line in inner:
196 197 if t:
197 198 lncount += 1
198 199 dummyoutfile.write(line)
199 200
200 201 fl = self.linenostart
201 202 mw = len(str(lncount + fl - 1))
202 203 sp = self.linenospecial
203 204 st = self.linenostep
204 205 la = self.lineanchors
205 206 aln = self.anchorlinenos
206 207 nocls = self.noclasses
207 208 if sp:
208 209 lines = []
209 210
210 211 for i in range(fl, fl + lncount):
211 212 if i % st == 0:
212 213 if i % sp == 0:
213 214 if aln:
214 215 lines.append('<a href="#%s%d" class="special">%*d</a>' %
215 216 (la, i, mw, i))
216 217 else:
217 218 lines.append('<span class="special">%*d</span>' % (mw, i))
218 219 else:
219 220 if aln:
220 221 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
221 222 else:
222 223 lines.append('%*d' % (mw, i))
223 224 else:
224 225 lines.append('')
225 226 ls = '\n'.join(lines)
226 227 else:
227 228 lines = []
228 229 for i in range(fl, fl + lncount):
229 230 if i % st == 0:
230 231 if aln:
231 232 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
232 233 else:
233 234 lines.append('%*d' % (mw, i))
234 235 else:
235 236 lines.append('')
236 237 ls = '\n'.join(lines)
237 238
238 239 # in case you wonder about the seemingly redundant <div> here: since the
239 240 # content in the other cell also is wrapped in a div, some browsers in
240 241 # some configurations seem to mess up the formatting...
241 242 if nocls:
242 243 yield 0, ('<table class="%stable">' % self.cssclass +
243 244 '<tr><td><div class="linenodiv" '
244 245 'style="background-color: #f0f0f0; padding-right: 10px">'
245 246 '<pre style="line-height: 125%">' +
246 247 ls + '</pre></div></td><td id="hlcode" class="code">')
247 248 else:
248 249 yield 0, ('<table class="%stable">' % self.cssclass +
249 250 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
250 251 ls + '</pre></div></td><td id="hlcode" class="code">')
251 252 yield 0, dummyoutfile.getvalue()
252 253 yield 0, '</td></tr></table>'
253 254
254 255
255 256 def pygmentize(filenode, **kwargs):
256 257 """
257 258 pygmentize function using pygments
258 259
259 260 :param filenode:
260 261 """
261 262 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
262 263 return literal(code_highlight(filenode.content, lexer,
263 264 CodeHtmlFormatter(**kwargs)))
264 265
265 266
266 267 def pygmentize_annotation(repo_name, filenode, **kwargs):
267 268 """
268 269 pygmentize function for annotation
269 270
270 271 :param filenode:
271 272 """
272 273
273 274 color_dict = {}
274 275
275 276 def gen_color(n=10000):
276 277 """generator for getting n of evenly distributed colors using
277 278 hsv color and golden ratio. It always return same order of colors
278 279
279 280 :returns: RGB tuple
280 281 """
281 282
282 283 def hsv_to_rgb(h, s, v):
283 284 if s == 0.0:
284 285 return v, v, v
285 286 i = int(h * 6.0) # XXX assume int() truncates!
286 287 f = (h * 6.0) - i
287 288 p = v * (1.0 - s)
288 289 q = v * (1.0 - s * f)
289 290 t = v * (1.0 - s * (1.0 - f))
290 291 i = i % 6
291 292 if i == 0:
292 293 return v, t, p
293 294 if i == 1:
294 295 return q, v, p
295 296 if i == 2:
296 297 return p, v, t
297 298 if i == 3:
298 299 return p, q, v
299 300 if i == 4:
300 301 return t, p, v
301 302 if i == 5:
302 303 return v, p, q
303 304
304 305 golden_ratio = 0.618033988749895
305 306 h = 0.22717784590367374
306 307
307 308 for _ in xrange(n):
308 309 h += golden_ratio
309 310 h %= 1
310 311 HSV_tuple = [h, 0.95, 0.95]
311 312 RGB_tuple = hsv_to_rgb(*HSV_tuple)
312 313 yield map(lambda x: str(int(x * 256)), RGB_tuple)
313 314
314 315 cgenerator = gen_color()
315 316
316 317 def get_color_string(cs):
317 318 if cs in color_dict:
318 319 col = color_dict[cs]
319 320 else:
320 321 col = color_dict[cs] = cgenerator.next()
321 322 return "color: rgb(%s)! important;" % (', '.join(col))
322 323
323 324 def url_func(repo_name):
324 325
325 326 def _url_func(changeset):
326 327 author = changeset.author
327 328 date = changeset.date
328 329 message = tooltip(changeset.message)
329 330
330 331 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
331 332 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
332 333 "</b> %s<br/></div>")
333 334
334 335 tooltip_html = tooltip_html % (author, date, message)
335 336 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
336 337 short_id(changeset.raw_id))
337 338 uri = link_to(
338 339 lnk_format,
339 340 url('changeset_home', repo_name=repo_name,
340 341 revision=changeset.raw_id),
341 342 style=get_color_string(changeset.raw_id),
342 343 class_='tooltip',
343 344 title=tooltip_html
344 345 )
345 346
346 347 uri += '\n'
347 348 return uri
348 349 return _url_func
349 350
350 351 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
351 352
352 353
353 354 def is_following_repo(repo_name, user_id):
354 355 from rhodecode.model.scm import ScmModel
355 356 return ScmModel().is_following_repo(repo_name, user_id)
356 357
357 358 flash = _Flash()
358 359
359 360 #==============================================================================
360 361 # SCM FILTERS available via h.
361 362 #==============================================================================
362 363 from rhodecode.lib.vcs.utils import author_name, author_email
363 364 from rhodecode.lib.utils2 import credentials_filter, age as _age
364 365 from rhodecode.model.db import User, ChangesetStatus
365 366
366 age = lambda x,y=False: _age(x,y)
367 age = lambda x, y=False: _age(x, y)
367 368 capitalize = lambda x: x.capitalize()
368 369 email = author_email
369 370 short_id = lambda x: x[:12]
370 371 hide_credentials = lambda x: ''.join(credentials_filter(x))
371 372
372 373
374 def show_id(cs):
375 """
376 Configurable function that shows ID
377 by default it's r123:fffeeefffeee
378
379 :param cs: changeset instance
380 """
381 from rhodecode import CONFIG
382 def_len = safe_int(CONFIG['sha_len'])
383 show_rev = str2bool(CONFIG['sha_rev'])
384
385 raw_id = cs.raw_id[:def_len]
386 if show_rev:
387 return 'r%s:%s' % (cs.revision, raw_id)
388 else:
389 return '%s' % (raw_id)
390
391
373 392 def fmt_date(date):
374 393 if date:
375 394 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
376 395 return date.strftime(_fmt).decode('utf8')
377 396
378 397 return ""
379 398
380 399
381 400 def is_git(repository):
382 401 if hasattr(repository, 'alias'):
383 402 _type = repository.alias
384 403 elif hasattr(repository, 'repo_type'):
385 404 _type = repository.repo_type
386 405 else:
387 406 _type = repository
388 407 return _type == 'git'
389 408
390 409
391 410 def is_hg(repository):
392 411 if hasattr(repository, 'alias'):
393 412 _type = repository.alias
394 413 elif hasattr(repository, 'repo_type'):
395 414 _type = repository.repo_type
396 415 else:
397 416 _type = repository
398 417 return _type == 'hg'
399 418
400 419
401 420 def email_or_none(author):
402 421 # extract email from the commit string
403 422 _email = email(author)
404 423 if _email != '':
405 424 # check it against RhodeCode database, and use the MAIN email for this
406 425 # user
407 426 user = User.get_by_email(_email, case_insensitive=True, cache=True)
408 427 if user is not None:
409 428 return user.email
410 429 return _email
411 430
412 431 # See if it contains a username we can get an email from
413 432 user = User.get_by_username(author_name(author), case_insensitive=True,
414 433 cache=True)
415 434 if user is not None:
416 435 return user.email
417 436
418 437 # No valid email, not a valid user in the system, none!
419 438 return None
420 439
421 440
422 441 def person(author, show_attr="username_and_name"):
423 442 # attr to return from fetched user
424 443 person_getter = lambda usr: getattr(usr, show_attr)
425 444
426 445 # Valid email in the attribute passed, see if they're in the system
427 446 _email = email(author)
428 447 if _email != '':
429 448 user = User.get_by_email(_email, case_insensitive=True, cache=True)
430 449 if user is not None:
431 450 return person_getter(user)
432 451 return _email
433 452
434 453 # Maybe it's a username?
435 454 _author = author_name(author)
436 455 user = User.get_by_username(_author, case_insensitive=True,
437 456 cache=True)
438 457 if user is not None:
439 458 return person_getter(user)
440 459
441 460 # Still nothing? Just pass back the author name then
442 461 return _author
443 462
444 463
445 464 def person_by_id(id_, show_attr="username_and_name"):
446 465 # attr to return from fetched user
447 466 person_getter = lambda usr: getattr(usr, show_attr)
448 467
449 468 #maybe it's an ID ?
450 469 if str(id_).isdigit() or isinstance(id_, int):
451 470 id_ = int(id_)
452 471 user = User.get(id_)
453 472 if user is not None:
454 473 return person_getter(user)
455 474 return id_
456 475
457 476
458 477 def desc_stylize(value):
459 478 """
460 479 converts tags from value into html equivalent
461 480
462 481 :param value:
463 482 """
464 483 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
465 484 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
466 485 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
467 486 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
468 487 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
469 488 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
470 489 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
471 490 '<div class="metatag" tag="lang">\\2</div>', value)
472 491 value = re.sub(r'\[([a-z]+)\]',
473 492 '<div class="metatag" tag="\\1">\\1</div>', value)
474 493
475 494 return value
476 495
477 496
478 497 def bool2icon(value):
479 498 """Returns True/False values represented as small html image of true/false
480 499 icons
481 500
482 501 :param value: bool value
483 502 """
484 503
485 504 if value is True:
486 505 return HTML.tag('img', src=url("/images/icons/accept.png"),
487 506 alt=_('True'))
488 507
489 508 if value is False:
490 509 return HTML.tag('img', src=url("/images/icons/cancel.png"),
491 510 alt=_('False'))
492 511
493 512 return value
494 513
495 514
496 515 def action_parser(user_log, feed=False, parse_cs=False):
497 516 """
498 517 This helper will action_map the specified string action into translated
499 518 fancy names with icons and links
500 519
501 520 :param user_log: user log instance
502 521 :param feed: use output for feeds (no html and fancy icons)
503 522 :param parse_cs: parse Changesets into VCS instances
504 523 """
505 524
506 525 action = user_log.action
507 526 action_params = ' '
508 527
509 528 x = action.split(':')
510 529
511 530 if len(x) > 1:
512 531 action, action_params = x
513 532
514 533 def get_cs_links():
515 534 revs_limit = 3 # display this amount always
516 535 revs_top_limit = 50 # show upto this amount of changesets hidden
517 536 revs_ids = action_params.split(',')
518 537 deleted = user_log.repository is None
519 538 if deleted:
520 539 return ','.join(revs_ids)
521 540
522 541 repo_name = user_log.repository.repo_name
523 542
524 543 def lnk(rev, repo_name):
525 544 if isinstance(rev, BaseChangeset) or isinstance(rev, AttributeDict):
526 545 lazy_cs = True
527 546 if getattr(rev, 'op', None) and getattr(rev, 'ref_name', None):
528 547 lazy_cs = False
529 548 lbl = '?'
530 549 if rev.op == 'delete_branch':
531 550 lbl = '%s' % _('Deleted branch: %s') % rev.ref_name
532 551 title = ''
533 552 elif rev.op == 'tag':
534 553 lbl = '%s' % _('Created tag: %s') % rev.ref_name
535 554 title = ''
536 555 _url = '#'
537 556
538 557 else:
539 558 lbl = '%s' % (rev.short_id[:8])
540 559 _url = url('changeset_home', repo_name=repo_name,
541 560 revision=rev.raw_id)
542 561 title = tooltip(rev.message)
543 562 else:
544 563 ## changeset cannot be found/striped/removed etc.
545 564 lbl = ('%s' % rev)[:12]
546 565 _url = '#'
547 566 title = _('Changeset not found')
548 567 if parse_cs:
549 568 return link_to(lbl, _url, title=title, class_='tooltip')
550 569 return link_to(lbl, _url, raw_id=rev.raw_id, repo_name=repo_name,
551 570 class_='lazy-cs' if lazy_cs else '')
552 571
553 572 def _get_op(rev_txt):
554 573 _op = None
555 574 _name = rev_txt
556 575 if len(rev_txt.split('=>')) == 2:
557 576 _op, _name = rev_txt.split('=>')
558 577 return _op, _name
559 578
560 579 revs = []
561 580 if len(filter(lambda v: v != '', revs_ids)) > 0:
562 581 repo = None
563 582 for rev in revs_ids[:revs_top_limit]:
564 583 _op, _name = _get_op(rev)
565 584
566 585 # we want parsed changesets, or new log store format is bad
567 586 if parse_cs:
568 587 try:
569 588 if repo is None:
570 589 repo = user_log.repository.scm_instance
571 590 _rev = repo.get_changeset(rev)
572 591 revs.append(_rev)
573 592 except ChangesetDoesNotExistError:
574 593 log.error('cannot find revision %s in this repo' % rev)
575 594 revs.append(rev)
576 595 continue
577 596 else:
578 597 _rev = AttributeDict({
579 598 'short_id': rev[:12],
580 599 'raw_id': rev,
581 600 'message': '',
582 601 'op': _op,
583 602 'ref_name': _name
584 603 })
585 604 revs.append(_rev)
586 605 cs_links = []
587 606 cs_links.append(" " + ', '.join(
588 607 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
589 608 )
590 609 )
591 610 _op1, _name1 = _get_op(revs_ids[0])
592 611 _op2, _name2 = _get_op(revs_ids[-1])
593 612
594 613 _rev = '%s...%s' % (_name1, _name2)
595 614
596 615 compare_view = (
597 616 ' <div class="compare_view tooltip" title="%s">'
598 617 '<a href="%s">%s</a> </div>' % (
599 618 _('Show all combined changesets %s->%s') % (
600 619 revs_ids[0][:12], revs_ids[-1][:12]
601 620 ),
602 621 url('changeset_home', repo_name=repo_name,
603 622 revision=_rev
604 623 ),
605 624 _('compare view')
606 625 )
607 626 )
608 627
609 628 # if we have exactly one more than normally displayed
610 629 # just display it, takes less space than displaying
611 630 # "and 1 more revisions"
612 631 if len(revs_ids) == revs_limit + 1:
613 632 rev = revs[revs_limit]
614 633 cs_links.append(", " + lnk(rev, repo_name))
615 634
616 635 # hidden-by-default ones
617 636 if len(revs_ids) > revs_limit + 1:
618 637 uniq_id = revs_ids[0]
619 638 html_tmpl = (
620 639 '<span> %s <a class="show_more" id="_%s" '
621 640 'href="#more">%s</a> %s</span>'
622 641 )
623 642 if not feed:
624 643 cs_links.append(html_tmpl % (
625 644 _('and'),
626 645 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
627 646 _('revisions')
628 647 )
629 648 )
630 649
631 650 if not feed:
632 651 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
633 652 else:
634 653 html_tmpl = '<span id="%s"> %s </span>'
635 654
636 655 morelinks = ', '.join(
637 656 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
638 657 )
639 658
640 659 if len(revs_ids) > revs_top_limit:
641 660 morelinks += ', ...'
642 661
643 662 cs_links.append(html_tmpl % (uniq_id, morelinks))
644 663 if len(revs) > 1:
645 664 cs_links.append(compare_view)
646 665 return ''.join(cs_links)
647 666
648 667 def get_fork_name():
649 668 repo_name = action_params
650 669 _url = url('summary_home', repo_name=repo_name)
651 670 return _('fork name %s') % link_to(action_params, _url)
652 671
653 672 def get_user_name():
654 673 user_name = action_params
655 674 return user_name
656 675
657 676 def get_users_group():
658 677 group_name = action_params
659 678 return group_name
660 679
661 680 def get_pull_request():
662 681 pull_request_id = action_params
663 682 deleted = user_log.repository is None
664 683 if deleted:
665 684 repo_name = user_log.repository_name
666 685 else:
667 686 repo_name = user_log.repository.repo_name
668 687 return link_to(_('Pull request #%s') % pull_request_id,
669 688 url('pullrequest_show', repo_name=repo_name,
670 689 pull_request_id=pull_request_id))
671 690
672 691 # action : translated str, callback(extractor), icon
673 692 action_map = {
674 693 'user_deleted_repo': (_('[deleted] repository'),
675 694 None, 'database_delete.png'),
676 695 'user_created_repo': (_('[created] repository'),
677 696 None, 'database_add.png'),
678 697 'user_created_fork': (_('[created] repository as fork'),
679 698 None, 'arrow_divide.png'),
680 699 'user_forked_repo': (_('[forked] repository'),
681 700 get_fork_name, 'arrow_divide.png'),
682 701 'user_updated_repo': (_('[updated] repository'),
683 702 None, 'database_edit.png'),
684 703 'admin_deleted_repo': (_('[delete] repository'),
685 704 None, 'database_delete.png'),
686 705 'admin_created_repo': (_('[created] repository'),
687 706 None, 'database_add.png'),
688 707 'admin_forked_repo': (_('[forked] repository'),
689 708 None, 'arrow_divide.png'),
690 709 'admin_updated_repo': (_('[updated] repository'),
691 710 None, 'database_edit.png'),
692 711 'admin_created_user': (_('[created] user'),
693 712 get_user_name, 'user_add.png'),
694 713 'admin_updated_user': (_('[updated] user'),
695 714 get_user_name, 'user_edit.png'),
696 715 'admin_created_users_group': (_('[created] user group'),
697 716 get_users_group, 'group_add.png'),
698 717 'admin_updated_users_group': (_('[updated] user group'),
699 718 get_users_group, 'group_edit.png'),
700 719 'user_commented_revision': (_('[commented] on revision in repository'),
701 720 get_cs_links, 'comment_add.png'),
702 721 'user_commented_pull_request': (_('[commented] on pull request for'),
703 722 get_pull_request, 'comment_add.png'),
704 723 'user_closed_pull_request': (_('[closed] pull request for'),
705 724 get_pull_request, 'tick.png'),
706 725 'push': (_('[pushed] into'),
707 726 get_cs_links, 'script_add.png'),
708 727 'push_local': (_('[committed via RhodeCode] into repository'),
709 728 get_cs_links, 'script_edit.png'),
710 729 'push_remote': (_('[pulled from remote] into repository'),
711 730 get_cs_links, 'connect.png'),
712 731 'pull': (_('[pulled] from'),
713 732 None, 'down_16.png'),
714 733 'started_following_repo': (_('[started following] repository'),
715 734 None, 'heart_add.png'),
716 735 'stopped_following_repo': (_('[stopped following] repository'),
717 736 None, 'heart_delete.png'),
718 737 }
719 738
720 739 action_str = action_map.get(action, action)
721 740 if feed:
722 741 action = action_str[0].replace('[', '').replace(']', '')
723 742 else:
724 743 action = action_str[0]\
725 744 .replace('[', '<span class="journal_highlight">')\
726 745 .replace(']', '</span>')
727 746
728 747 action_params_func = lambda: ""
729 748
730 749 if callable(action_str[1]):
731 750 action_params_func = action_str[1]
732 751
733 752 def action_parser_icon():
734 753 action = user_log.action
735 754 action_params = None
736 755 x = action.split(':')
737 756
738 757 if len(x) > 1:
739 758 action, action_params = x
740 759
741 760 tmpl = """<img src="%s%s" alt="%s"/>"""
742 761 ico = action_map.get(action, ['', '', ''])[2]
743 762 return literal(tmpl % ((url('/images/icons/')), ico, action))
744 763
745 764 # returned callbacks we need to call to get
746 765 return [lambda: literal(action), action_params_func, action_parser_icon]
747 766
748 767
749 768
750 769 #==============================================================================
751 770 # PERMS
752 771 #==============================================================================
753 772 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
754 773 HasRepoPermissionAny, HasRepoPermissionAll, HasReposGroupPermissionAll, \
755 774 HasReposGroupPermissionAny
756 775
757 776
758 777 #==============================================================================
759 778 # GRAVATAR URL
760 779 #==============================================================================
761 780
762 781 def gravatar_url(email_address, size=30):
763 782 from pylons import url # doh, we need to re-import url to mock it later
764 783 _def = 'anonymous@rhodecode.org'
765 784 use_gravatar = str2bool(config['app_conf'].get('use_gravatar'))
766 785 email_address = email_address or _def
767 786 if (not use_gravatar or not email_address or email_address == _def):
768 787 f = lambda a, l: min(l, key=lambda x: abs(x - a))
769 788 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
770 789
771 790 if use_gravatar and config['app_conf'].get('alternative_gravatar_url'):
772 791 tmpl = config['app_conf'].get('alternative_gravatar_url', '')
773 792 parsed_url = urlparse.urlparse(url.current(qualified=True))
774 793 tmpl = tmpl.replace('{email}', email_address)\
775 794 .replace('{md5email}', hashlib.md5(email_address.lower()).hexdigest()) \
776 795 .replace('{netloc}', parsed_url.netloc)\
777 796 .replace('{scheme}', parsed_url.scheme)\
778 797 .replace('{size}', str(size))
779 798 return tmpl
780 799
781 800 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
782 801 default = 'identicon'
783 802 baseurl_nossl = "http://www.gravatar.com/avatar/"
784 803 baseurl_ssl = "https://secure.gravatar.com/avatar/"
785 804 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
786 805
787 806 if isinstance(email_address, unicode):
788 807 #hashlib crashes on unicode items
789 808 email_address = safe_str(email_address)
790 809 # construct the url
791 810 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
792 811 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
793 812
794 813 return gravatar_url
795 814
796 815
797 816 #==============================================================================
798 817 # REPO PAGER, PAGER FOR REPOSITORY
799 818 #==============================================================================
800 819 class RepoPage(Page):
801 820
802 821 def __init__(self, collection, page=1, items_per_page=20,
803 822 item_count=None, url=None, **kwargs):
804 823
805 824 """Create a "RepoPage" instance. special pager for paging
806 825 repository
807 826 """
808 827 self._url_generator = url
809 828
810 829 # Safe the kwargs class-wide so they can be used in the pager() method
811 830 self.kwargs = kwargs
812 831
813 832 # Save a reference to the collection
814 833 self.original_collection = collection
815 834
816 835 self.collection = collection
817 836
818 837 # The self.page is the number of the current page.
819 838 # The first page has the number 1!
820 839 try:
821 840 self.page = int(page) # make it int() if we get it as a string
822 841 except (ValueError, TypeError):
823 842 self.page = 1
824 843
825 844 self.items_per_page = items_per_page
826 845
827 846 # Unless the user tells us how many items the collections has
828 847 # we calculate that ourselves.
829 848 if item_count is not None:
830 849 self.item_count = item_count
831 850 else:
832 851 self.item_count = len(self.collection)
833 852
834 853 # Compute the number of the first and last available page
835 854 if self.item_count > 0:
836 855 self.first_page = 1
837 856 self.page_count = int(math.ceil(float(self.item_count) /
838 857 self.items_per_page))
839 858 self.last_page = self.first_page + self.page_count - 1
840 859
841 860 # Make sure that the requested page number is the range of
842 861 # valid pages
843 862 if self.page > self.last_page:
844 863 self.page = self.last_page
845 864 elif self.page < self.first_page:
846 865 self.page = self.first_page
847 866
848 867 # Note: the number of items on this page can be less than
849 868 # items_per_page if the last page is not full
850 869 self.first_item = max(0, (self.item_count) - (self.page *
851 870 items_per_page))
852 871 self.last_item = ((self.item_count - 1) - items_per_page *
853 872 (self.page - 1))
854 873
855 874 self.items = list(self.collection[self.first_item:self.last_item + 1])
856 875
857 876 # Links to previous and next page
858 877 if self.page > self.first_page:
859 878 self.previous_page = self.page - 1
860 879 else:
861 880 self.previous_page = None
862 881
863 882 if self.page < self.last_page:
864 883 self.next_page = self.page + 1
865 884 else:
866 885 self.next_page = None
867 886
868 887 # No items available
869 888 else:
870 889 self.first_page = None
871 890 self.page_count = 0
872 891 self.last_page = None
873 892 self.first_item = None
874 893 self.last_item = None
875 894 self.previous_page = None
876 895 self.next_page = None
877 896 self.items = []
878 897
879 898 # This is a subclass of the 'list' type. Initialise the list now.
880 899 list.__init__(self, reversed(self.items))
881 900
882 901
883 902 def changed_tooltip(nodes):
884 903 """
885 904 Generates a html string for changed nodes in changeset page.
886 905 It limits the output to 30 entries
887 906
888 907 :param nodes: LazyNodesGenerator
889 908 """
890 909 if nodes:
891 910 pref = ': <br/> '
892 911 suf = ''
893 912 if len(nodes) > 30:
894 913 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
895 914 return literal(pref + '<br/> '.join([safe_unicode(x.path)
896 915 for x in nodes[:30]]) + suf)
897 916 else:
898 917 return ': ' + _('No Files')
899 918
900 919
901 920 def repo_link(groups_and_repos, last_url=None):
902 921 """
903 922 Makes a breadcrumbs link to repo within a group
904 923 joins &raquo; on each group to create a fancy link
905 924
906 925 ex::
907 926 group >> subgroup >> repo
908 927
909 928 :param groups_and_repos:
910 929 :param last_url:
911 930 """
912 931 groups, repo_name = groups_and_repos
913 932 last_link = link_to(repo_name, last_url) if last_url else repo_name
914 933
915 934 if not groups:
916 935 if last_url:
917 936 return literal('<span>%s</span>' % last_link)
918 937 return literal('<span>%s</span>' % repo_name)
919 938 else:
920 939 def make_link(group):
921 940 return link_to(group.name,
922 941 url('repos_group_home', group_name=group.group_name))
923 942 return literal(' &raquo; '.join(map(make_link, groups) + ['<span>' + last_link + '</span>']))
924 943
925 944
926 945 def fancy_file_stats(stats):
927 946 """
928 947 Displays a fancy two colored bar for number of added/deleted
929 948 lines of code on file
930 949
931 950 :param stats: two element list of added/deleted lines of code
932 951 """
933 952 def cgen(l_type, a_v, d_v):
934 953 mapping = {'tr': 'top-right-rounded-corner-mid',
935 954 'tl': 'top-left-rounded-corner-mid',
936 955 'br': 'bottom-right-rounded-corner-mid',
937 956 'bl': 'bottom-left-rounded-corner-mid'}
938 957 map_getter = lambda x: mapping[x]
939 958
940 959 if l_type == 'a' and d_v:
941 960 #case when added and deleted are present
942 961 return ' '.join(map(map_getter, ['tl', 'bl']))
943 962
944 963 if l_type == 'a' and not d_v:
945 964 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
946 965
947 966 if l_type == 'd' and a_v:
948 967 return ' '.join(map(map_getter, ['tr', 'br']))
949 968
950 969 if l_type == 'd' and not a_v:
951 970 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
952 971
953 972 a, d = stats[0], stats[1]
954 973 width = 100
955 974
956 975 if a == 'b':
957 976 #binary mode
958 977 b_d = '<div class="bin%s %s" style="width:100%%">%s</div>' % (d, cgen('a', a_v='', d_v=0), 'bin')
959 978 b_a = '<div class="bin1" style="width:0%%">%s</div>' % ('bin')
960 979 return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d))
961 980
962 981 t = stats[0] + stats[1]
963 982 unit = float(width) / (t or 1)
964 983
965 984 # needs > 9% of width to be visible or 0 to be hidden
966 985 a_p = max(9, unit * a) if a > 0 else 0
967 986 d_p = max(9, unit * d) if d > 0 else 0
968 987 p_sum = a_p + d_p
969 988
970 989 if p_sum > width:
971 990 #adjust the percentage to be == 100% since we adjusted to 9
972 991 if a_p > d_p:
973 992 a_p = a_p - (p_sum - width)
974 993 else:
975 994 d_p = d_p - (p_sum - width)
976 995
977 996 a_v = a if a > 0 else ''
978 997 d_v = d if d > 0 else ''
979 998
980 999 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
981 1000 cgen('a', a_v, d_v), a_p, a_v
982 1001 )
983 1002 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
984 1003 cgen('d', a_v, d_v), d_p, d_v
985 1004 )
986 1005 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
987 1006
988 1007
989 1008 def urlify_text(text_, safe=True):
990 1009 """
991 1010 Extrac urls from text and make html links out of them
992 1011
993 1012 :param text_:
994 1013 """
995 1014
996 1015 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
997 1016 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
998 1017
999 1018 def url_func(match_obj):
1000 1019 url_full = match_obj.groups()[0]
1001 1020 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1002 1021 _newtext = url_pat.sub(url_func, text_)
1003 1022 if safe:
1004 1023 return literal(_newtext)
1005 1024 return _newtext
1006 1025
1007 1026
1008 1027 def urlify_changesets(text_, repository):
1009 1028 """
1010 1029 Extract revision ids from changeset and make link from them
1011 1030
1012 1031 :param text_:
1013 1032 :param repository: repo name to build the URL with
1014 1033 """
1015 1034 from pylons import url # doh, we need to re-import url to mock it later
1016 1035 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1017 1036
1018 1037 def url_func(match_obj):
1019 1038 rev = match_obj.groups()[1]
1020 1039 pref = match_obj.groups()[0]
1021 1040 suf = match_obj.groups()[2]
1022 1041
1023 1042 tmpl = (
1024 1043 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1025 1044 '%(rev)s</a>%(suf)s'
1026 1045 )
1027 1046 return tmpl % {
1028 1047 'pref': pref,
1029 1048 'cls': 'revision-link',
1030 1049 'url': url('changeset_home', repo_name=repository, revision=rev),
1031 1050 'rev': rev,
1032 1051 'suf': suf
1033 1052 }
1034 1053
1035 1054 newtext = URL_PAT.sub(url_func, text_)
1036 1055
1037 1056 return newtext
1038 1057
1039 1058
1040 1059 def urlify_commit(text_, repository=None, link_=None):
1041 1060 """
1042 1061 Parses given text message and makes proper links.
1043 1062 issues are linked to given issue-server, and rest is a changeset link
1044 1063 if link_ is given, in other case it's a plain text
1045 1064
1046 1065 :param text_:
1047 1066 :param repository:
1048 1067 :param link_: changeset link
1049 1068 """
1050 1069 import traceback
1051 1070 from pylons import url # doh, we need to re-import url to mock it later
1052 1071
1053 1072 def escaper(string):
1054 1073 return string.replace('<', '&lt;').replace('>', '&gt;')
1055 1074
1056 1075 def linkify_others(t, l):
1057 1076 urls = re.compile(r'(\<a.*?\<\/a\>)',)
1058 1077 links = []
1059 1078 for e in urls.split(t):
1060 1079 if not urls.match(e):
1061 1080 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
1062 1081 else:
1063 1082 links.append(e)
1064 1083
1065 1084 return ''.join(links)
1066 1085
1067 1086 # urlify changesets - extrac revisions and make link out of them
1068 1087 newtext = urlify_changesets(escaper(text_), repository)
1069 1088
1070 1089 # extract http/https links and make them real urls
1071 1090 newtext = urlify_text(newtext, safe=False)
1072 1091
1073 1092 try:
1074 1093 from rhodecode import CONFIG
1075 1094 conf = CONFIG
1076 1095
1077 1096 # allow multiple issue servers to be used
1078 1097 valid_indices = [
1079 1098 x.group(1)
1080 1099 for x in map(lambda x: re.match(r'issue_pat(.*)', x), conf.keys())
1081 1100 if x and 'issue_server_link%s' % x.group(1) in conf
1082 1101 and 'issue_prefix%s' % x.group(1) in conf
1083 1102 ]
1084 1103
1085 1104 log.debug('found issue server suffixes `%s` during valuation of: %s'
1086 1105 % (','.join(valid_indices), newtext))
1087 1106
1088 1107 for pattern_index in valid_indices:
1089 1108 ISSUE_PATTERN = conf.get('issue_pat%s' % pattern_index)
1090 1109 ISSUE_SERVER_LNK = conf.get('issue_server_link%s' % pattern_index)
1091 1110 ISSUE_PREFIX = conf.get('issue_prefix%s' % pattern_index)
1092 1111
1093 1112 log.debug('pattern suffix `%s` PAT:%s SERVER_LINK:%s PREFIX:%s'
1094 1113 % (pattern_index, ISSUE_PATTERN, ISSUE_SERVER_LNK,
1095 1114 ISSUE_PREFIX))
1096 1115
1097 1116 URL_PAT = re.compile(r'%s' % ISSUE_PATTERN)
1098 1117
1099 1118 def url_func(match_obj):
1100 1119 pref = ''
1101 1120 if match_obj.group().startswith(' '):
1102 1121 pref = ' '
1103 1122
1104 1123 issue_id = ''.join(match_obj.groups())
1105 1124 tmpl = (
1106 1125 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1107 1126 '%(issue-prefix)s%(id-repr)s'
1108 1127 '</a>'
1109 1128 )
1110 1129 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
1111 1130 if repository:
1112 1131 url = url.replace('{repo}', repository)
1113 1132 repo_name = repository.split(URL_SEP)[-1]
1114 1133 url = url.replace('{repo_name}', repo_name)
1115 1134
1116 1135 return tmpl % {
1117 1136 'pref': pref,
1118 1137 'cls': 'issue-tracker-link',
1119 1138 'url': url,
1120 1139 'id-repr': issue_id,
1121 1140 'issue-prefix': ISSUE_PREFIX,
1122 1141 'serv': ISSUE_SERVER_LNK,
1123 1142 }
1124 1143 newtext = URL_PAT.sub(url_func, newtext)
1125 1144 log.debug('processed prefix:`%s` => %s' % (pattern_index, newtext))
1126 1145
1127 1146 # if we actually did something above
1128 1147 if link_:
1129 1148 # wrap not links into final link => link_
1130 1149 newtext = linkify_others(newtext, link_)
1131 1150 except:
1132 1151 log.error(traceback.format_exc())
1133 1152 pass
1134 1153
1135 1154 return literal(newtext)
1136 1155
1137 1156
1138 1157 def rst(source):
1139 1158 return literal('<div class="rst-block">%s</div>' %
1140 1159 MarkupRenderer.rst(source))
1141 1160
1142 1161
1143 1162 def rst_w_mentions(source):
1144 1163 """
1145 1164 Wrapped rst renderer with @mention highlighting
1146 1165
1147 1166 :param source:
1148 1167 """
1149 1168 return literal('<div class="rst-block">%s</div>' %
1150 1169 MarkupRenderer.rst_with_mentions(source))
1151 1170
1152 1171
1153 1172 def changeset_status(repo, revision):
1154 1173 return ChangesetStatusModel().get_status(repo, revision)
1155 1174
1156 1175
1157 1176 def changeset_status_lbl(changeset_status):
1158 1177 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1159 1178
1160 1179
1161 1180 def get_permission_name(key):
1162 1181 return dict(Permission.PERMS).get(key)
1163 1182
1164 1183
1165 1184 def journal_filter_help():
1166 1185 return _(textwrap.dedent('''
1167 1186 Example filter terms:
1168 1187 repository:vcs
1169 1188 username:marcin
1170 1189 action:*push*
1171 1190 ip:127.0.0.1
1172 1191 date:20120101
1173 1192 date:[20120101100000 TO 20120102]
1174 1193
1175 1194 Generate wildcards using '*' character:
1176 1195 "repositroy:vcs*" - search everything starting with 'vcs'
1177 1196 "repository:*vcs*" - search for repository containing 'vcs'
1178 1197
1179 1198 Optional AND / OR operators in queries
1180 1199 "repository:vcs OR repository:test"
1181 1200 "username:test AND repository:test*"
1182 1201 '''))
1183 1202
1184 1203
1185 1204 def not_mapped_error(repo_name):
1186 1205 flash(_('%s repository is not mapped to db perhaps'
1187 1206 ' it was created or renamed from the filesystem'
1188 1207 ' please run the application again'
1189 1208 ' in order to rescan repositories') % repo_name, category='error')
1190 1209
1191 1210
1192 1211 def ip_range(ip_addr):
1193 1212 from rhodecode.model.db import UserIpMap
1194 1213 s, e = UserIpMap._get_ip_range(ip_addr)
1195 1214 return '%s - %s' % (s, e)
@@ -1,263 +1,262 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%inherit file="/base/base.html"/>
4 4
5 5 <%def name="title()">
6 6 ${_('%s Changelog') % c.repo_name} - ${c.rhodecode_name}
7 7 </%def>
8 8
9 9 <%def name="breadcrumbs_links()">
10 10 <% size = c.size if c.size <= c.total_cs else c.total_cs %>
11 11 ${_('Changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)}
12 12 </%def>
13 13
14 14 <%def name="page_nav()">
15 15 ${self.menu('changelog')}
16 16 </%def>
17 17
18 18 <%def name="main()">
19 19 ${self.context_bar('changelog')}
20 20 <div class="box">
21 21 <!-- box / title -->
22 22 <div class="title">
23 23 ${self.breadcrumbs()}
24 24 </div>
25 25 <div class="table">
26 26 % if c.pagination:
27 27 <div id="graph">
28 28 <div class="info_box" style="clear: both;padding: 10px 6px;min-height: 12px;text-align: right;">
29 29 <a href="#" class="ui-btn small" id="rev_range_container" style="display:none"></a>
30 30 <a href="#" class="ui-btn small" id="rev_range_clear" style="display:none">${_('Clear selection')}</a>
31 31
32 32 %if c.rhodecode_db_repo.fork:
33 33 <a id="compare_fork" title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,org_ref_type='branch',org_ref='default',other_repo=c.repo_name,other_ref_type='branch',other_ref=request.GET.get('branch') or 'default',merge=1)}" class="ui-btn small">${_('Compare fork with parent')}</a>
34 34 %endif
35 35 %if h.is_hg(c.rhodecode_repo):
36 36 <a id="open_new_pr" href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a>
37 37 %endif
38 38 </div>
39 39 <div class="container_header">
40 40 ${h.form(h.url.current(),method='get')}
41 41 <div style="float:left">
42 42 ${h.submit('set',_('Show'),class_="ui-btn")}
43 43 ${h.text('size',size=1,value=c.size)}
44 44 ${_('revisions')}
45 45 </div>
46 46 ${h.end_form()}
47 47 <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
48 48 </div>
49 49 <div id="graph_nodes">
50 50 <canvas id="graph_canvas"></canvas>
51 51 </div>
52 52 <div id="graph_content">
53 53
54 54 <table id="changesets">
55 55 <tbody>
56 56 %for cnt,cs in enumerate(c.pagination):
57 57 <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
58 58 <td class="checkbox">
59 59 ${h.checkbox(cs.raw_id,class_="changeset_range")}
60 60 </td>
61 61 <td class="author">
62 62 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
63 63 <span title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</span>
64 64 </td>
65 65 <td class="hash">
66 66 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}">
67 <span class="changeset_id">${cs.revision}:</span>
68 <span class="changeset_hash">${h.short_id(cs.raw_id)}</span>
67 <span class="changeset_hash">${h.show_id(cs)}</span>
69 68 </a>
70 69 </td>
71 70 <td class="date">
72 71 <div class="date">${h.age(cs.date,True)}</div>
73 72 </td>
74 73 <td class="mid">
75 74 <div class="log-container">
76 75 <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
77 76 <div class="expand"><span class="expandtext">&darr; ${_('Show more')} &darr;</span></div>
78 77 <div class="extra-container">
79 78 %if c.comments.get(cs.raw_id,[]):
80 79 <div class="comments-container">
81 80 <div class="comments-cnt" title="${('comments')}">
82 81 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
83 82 ${len(c.comments[cs.raw_id])}
84 83 </a>
85 84 </div>
86 85 </div>
87 86 %endif
88 87 %if h.is_hg(c.rhodecode_repo):
89 88 %for book in cs.bookmarks:
90 89 <div class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
91 90 ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
92 91 </div>
93 92 %endfor
94 93 %endif
95 94 %for tag in cs.tags:
96 95 <div class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
97 96 ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
98 97 </div>
99 98 %endfor
100 99 %if (not c.branch_name) and cs.branch:
101 100 <div class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
102 101 ${h.link_to(h.shorter(cs.branch),h.url('changelog_home',repo_name=c.repo_name,branch=cs.branch))}
103 102 </div>
104 103 %endif
105 104 </div>
106 105 </div>
107 106 </td>
108 107 </tr>
109 108 %endfor
110 109 </tbody>
111 110 </table>
112 111
113 112 <div class="pagination-wh pagination-left">
114 113 ${c.pagination.pager('$link_previous ~2~ $link_next')}
115 114 </div>
116 115 </div>
117 116 </div>
118 117
119 118 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
120 119 <script type="text/javascript">
121 120 YAHOO.util.Event.onDOMReady(function(){
122 121
123 122 //Monitor range checkboxes and build a link to changesets
124 123 //ranges
125 124 var checkboxes = YUD.getElementsByClassName('changeset_range');
126 125 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
127 126 var pr_tmpl = "${h.url('pullrequest_home',repo_name=c.repo_name)}";
128 127
129 128 var checkbox_checker = function(e){
130 129 var checked_checkboxes = [];
131 130 for (pos in checkboxes){
132 131 if(checkboxes[pos].checked){
133 132 checked_checkboxes.push(checkboxes[pos]);
134 133 }
135 134 }
136 135 if(YUD.get('open_new_pr')){
137 136 if(checked_checkboxes.length>1){
138 137 YUD.setStyle('open_new_pr','display','none');
139 138 } else {
140 139 YUD.setStyle('open_new_pr','display','');
141 140 if(checked_checkboxes.length>0){
142 141 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request for selected changesets'];
143 142 }else{
144 143 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request'];
145 144 }
146 145 }
147 146 }
148 147
149 148 if(checked_checkboxes.length>0){
150 149 var rev_end = checked_checkboxes[0].name;
151 150 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
152 151 var url = url_tmpl.replace('__REVRANGE__',
153 152 rev_start+'...'+rev_end);
154 153
155 154 var link = (rev_start == rev_end)
156 155 ? _TM['Show selected change __S']
157 156 : _TM['Show selected changes __S -> __E'];
158 157
159 158 link = link.replace('__S',rev_start.substr(0,6));
160 159 link = link.replace('__E',rev_end.substr(0,6));
161 160 YUD.get('rev_range_container').href = url;
162 161 YUD.get('rev_range_container').innerHTML = link;
163 162 YUD.setStyle('rev_range_container','display','');
164 163 YUD.setStyle('rev_range_clear','display','');
165 164
166 165 YUD.get('open_new_pr').href = pr_tmpl + '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end);
167 166 YUD.setStyle('compare_fork','display','none');
168 167 }else{
169 168 YUD.setStyle('rev_range_container','display','none');
170 169 YUD.setStyle('rev_range_clear','display','none');
171 170 if (checkboxes){
172 171 YUD.get('open_new_pr').href = pr_tmpl + '?rev_end={0}'.format(checkboxes[0].name);
173 172 }
174 173 YUD.setStyle('compare_fork','display','');
175 174 }
176 175 };
177 176 YUE.onDOMReady(checkbox_checker);
178 177 YUE.on(checkboxes,'click', checkbox_checker);
179 178
180 179 YUE.on('rev_range_clear','click',function(e){
181 180 for (var i=0; i<checkboxes.length; i++){
182 181 var cb = checkboxes[i];
183 182 cb.checked = false;
184 183 }
185 184 checkbox_checker();
186 185 YUE.preventDefault(e);
187 186 });
188 187
189 188 var msgs = YUQ('.message');
190 189 // get first element height
191 190 var el = YUQ('#graph_content .container')[0];
192 191 var row_h = el.clientHeight;
193 192 for(var i=0;i<msgs.length;i++){
194 193 var m = msgs[i];
195 194
196 195 var h = m.clientHeight;
197 196 var pad = YUD.getStyle(m,'padding');
198 197 if(h > row_h){
199 198 var offset = row_h - (h+12);
200 199 YUD.setStyle(m.nextElementSibling,'display','block');
201 200 YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
202 201 };
203 202 }
204 203 YUE.on(YUQ('.expand'),'click',function(e){
205 204 var elem = e.currentTarget.parentNode.parentNode;
206 205 YUD.setStyle(e.currentTarget,'display','none');
207 206 YUD.setStyle(elem,'height','auto');
208 207
209 208 //redraw the graph, line_count and jsdata are global vars
210 209 set_canvas(100);
211 210
212 211 var r = new BranchRenderer();
213 212 r.render(jsdata,100,line_count);
214 213
215 214 });
216 215
217 216 // change branch filter
218 217 YUE.on(YUD.get('branch_filter'),'change',function(e){
219 218 var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
220 219 var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
221 220 var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
222 221 var url = url.replace('__BRANCH__',selected_branch);
223 222 if(selected_branch != ''){
224 223 window.location = url;
225 224 }else{
226 225 window.location = url_main;
227 226 }
228 227
229 228 });
230 229
231 230 function set_canvas(width) {
232 231 var c = document.getElementById('graph_nodes');
233 232 var t = document.getElementById('graph_content');
234 233 canvas = document.getElementById('graph_canvas');
235 234 var div_h = t.clientHeight;
236 235 canvas.setAttribute('height',div_h);
237 236 canvas.setAttribute('width',width);
238 237 };
239 238 var heads = 1;
240 239 var line_count = 0;
241 240 var jsdata = ${c.jsdata|n};
242 241
243 242 for (var i=0;i<jsdata.length;i++) {
244 243 var in_l = jsdata[i][2];
245 244 for (var j in in_l) {
246 245 var m = in_l[j][1];
247 246 if (m > line_count)
248 247 line_count = m;
249 248 }
250 249 }
251 250 set_canvas(100);
252 251
253 252 var r = new BranchRenderer();
254 253 r.render(jsdata,100,line_count);
255 254
256 255 });
257 256 </script>
258 257 %else:
259 258 ${_('There are no changes yet')}
260 259 %endif
261 260 </div>
262 261 </div>
263 262 </%def>
@@ -1,74 +1,126 b''
1 1 from rhodecode.tests import *
2 2
3 3
4 4 class TestChangelogController(TestController):
5 5
6 6 def test_index_hg(self):
7 7 self.log_user()
8 8 response = self.app.get(url(controller='changelog', action='index',
9 9 repo_name=HG_REPO))
10 10
11 11 response.mustcontain('''id="chg_20" class="container tablerow1"''')
12 12 response.mustcontain(
13 13 """<input class="changeset_range" """
14 14 """id="5e204e7583b9c8e7b93a020bd036564b1e731dae" """
15 15 """name="5e204e7583b9c8e7b93a020bd036564b1e731dae" """
16 16 """type="checkbox" value="1" />"""
17 17 )
18
18 19 response.mustcontain(
19 """<span class="changeset_id">154:</span>"""
20 )
21 response.mustcontain(
22 """<span class="changeset_hash">5e204e7583b9</span>"""
20 """<span class="changeset_hash">r154:5e204e7583b9</span>"""
23 21 )
24 22
25 23 response.mustcontain("""Small update at simplevcs app""")
26 24
27 25 # response.mustcontain(
28 26 # """<div id="changed_total_5e204e7583b9c8e7b93a020bd036564b1e731dae" """
29 27 # """style="float:right;" class="changed_total tooltip" """
30 28 # """title="Affected number of files, click to show """
31 29 # """more details">3</div>"""
32 30 # )
33 31
32 def test_index_pagination_hg(self):
33 self.log_user()
34 34 #pagination
35 response = self.app.get(url(controller='changelog', action='index',
35 self.app.get(url(controller='changelog', action='index',
36 36 repo_name=HG_REPO), {'page': 1})
37 response = self.app.get(url(controller='changelog', action='index',
37 self.app.get(url(controller='changelog', action='index',
38 38 repo_name=HG_REPO), {'page': 2})
39 response = self.app.get(url(controller='changelog', action='index',
39 self.app.get(url(controller='changelog', action='index',
40 40 repo_name=HG_REPO), {'page': 3})
41 response = self.app.get(url(controller='changelog', action='index',
41 self.app.get(url(controller='changelog', action='index',
42 42 repo_name=HG_REPO), {'page': 4})
43 response = self.app.get(url(controller='changelog', action='index',
43 self.app.get(url(controller='changelog', action='index',
44 44 repo_name=HG_REPO), {'page': 5})
45 45 response = self.app.get(url(controller='changelog', action='index',
46 46 repo_name=HG_REPO), {'page': 6})
47 47
48 48 # Test response after pagination...
49 49 response.mustcontain(
50 50 """<input class="changeset_range" """
51 51 """id="46ad32a4f974e45472a898c6b0acb600320579b1" """
52 52 """name="46ad32a4f974e45472a898c6b0acb600320579b1" """
53 53 """type="checkbox" value="1" />"""
54 54 )
55
55 56 response.mustcontain(
56 """<span class="changeset_id">64:</span>"""
57 )
58 response.mustcontain(
59 """<span class="changeset_hash">46ad32a4f974</span>"""
57 """<span class="changeset_hash">r64:46ad32a4f974</span>"""
60 58 )
61 59
62 60 # response.mustcontain(
63 61 # """<div id="changed_total_46ad32a4f974e45472a898c6b0acb600320579b1" """
64 62 # """style="float:right;" class="changed_total tooltip" """
65 63 # """title="Affected number of files, click to show """
66 64 # """more details">21</div>"""
67 65 # )
68 66 #
69 67 # response.mustcontain(
70 68 # """<a href="/%s/changeset/"""
71 69 # """46ad32a4f974e45472a898c6b0acb600320579b1" """
72 70 # """title="Merge with 2e6a2bf9356ca56df08807f4ad86d480da72a8f4">"""
73 71 # """46ad32a4f974</a>""" % HG_REPO
74 72 # )
73
74 def test_index_git(self):
75 self.log_user()
76 response = self.app.get(url(controller='changelog', action='index',
77 repo_name=GIT_REPO))
78
79 response.mustcontain('''id="chg_20" class="container tablerow1"''')
80 response.mustcontain(
81 """<input class="changeset_range" """
82 """id="95f9a91d775b0084b2368ae7779e44931c849c0e" """
83 """name="95f9a91d775b0084b2368ae7779e44931c849c0e" """
84 """type="checkbox" value="1" />"""
85 )
86
87 response.mustcontain(
88 """<span class="changeset_hash">r613:95f9a91d775b</span>"""
89 )
90
91 response.mustcontain("""fixing stupid typo in context for mercurial""")
92
93 # response.mustcontain(
94 # """<div id="changed_total_5e204e7583b9c8e7b93a020bd036564b1e731dae" """
95 # """style="float:right;" class="changed_total tooltip" """
96 # """title="Affected number of files, click to show """
97 # """more details">3</div>"""
98 # )
99
100 def test_index_pagination_git(self):
101 self.log_user()
102 #pagination
103 self.app.get(url(controller='changelog', action='index',
104 repo_name=GIT_REPO), {'page': 1})
105 self.app.get(url(controller='changelog', action='index',
106 repo_name=GIT_REPO), {'page': 2})
107 self.app.get(url(controller='changelog', action='index',
108 repo_name=GIT_REPO), {'page': 3})
109 self.app.get(url(controller='changelog', action='index',
110 repo_name=GIT_REPO), {'page': 4})
111 self.app.get(url(controller='changelog', action='index',
112 repo_name=GIT_REPO), {'page': 5})
113 response = self.app.get(url(controller='changelog', action='index',
114 repo_name=GIT_REPO), {'page': 6})
115
116 # Test response after pagination...
117 response.mustcontain(
118 """<input class="changeset_range" """
119 """id="636ed213f2f11ef91071b9c24f2d5e6bd01a6ed5" """
120 """name="636ed213f2f11ef91071b9c24f2d5e6bd01a6ed5" """
121 """type="checkbox" value="1" />"""
122 )
123
124 response.mustcontain(
125 """<span class="changeset_hash">r515:636ed213f2f1</span>"""
126 )
@@ -1,439 +1,478 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # RhodeCode - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 pdebug = false
11 11 ################################################################################
12 12 ## Uncomment and replace with the address which should receive ##
13 13 ## any error reports after application crash ##
14 14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 15 ################################################################################
16 16 #email_to = admin@localhost
17 17 #error_email_from = paste_error@localhost
18 18 #app_email_from = rhodecode-noreply@localhost
19 19 #error_message =
20 20 #email_prefix = [RhodeCode]
21 21
22 22 #smtp_server = mail.server.com
23 23 #smtp_username =
24 24 #smtp_password =
25 25 #smtp_port =
26 26 #smtp_use_tls = false
27 27 #smtp_use_ssl = true
28 # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
28 ## Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
29 29 #smtp_auth =
30 30
31 31 [server:main]
32 32 ## PASTE
33 ##nr of threads to spawn
33 ## nr of threads to spawn
34 34 #threadpool_workers = 5
35 35
36 ##max request before thread respawn
36 ## max request before thread respawn
37 37 #threadpool_max_requests = 10
38 38
39 ##option to use threads of process
39 ## option to use threads of process
40 40 #use_threadpool = true
41 41
42 42 #use = egg:Paste#http
43 43
44 #WAITRESS
44 ## WAITRESS
45 45 threads = 5
46 #100GB
46 ## 100GB
47 47 max_request_body_size = 107374182400
48 48 use = egg:waitress#main
49 49
50 50 host = 127.0.0.1
51 51 port = 5000
52 52
53 [filter:proxy-prefix]
54 # prefix middleware for rc
55 use = egg:PasteDeploy#prefix
56 prefix = /<your-prefix>
53 ## prefix middleware for rc
54 #[filter:proxy-prefix]
55 #use = egg:PasteDeploy#prefix
56 #prefix = /<your-prefix>
57 57
58 58 [app:main]
59 59 use = egg:rhodecode
60 ## enable proxy prefix middleware
60 61 #filter-with = proxy-prefix
62
61 63 full_stack = true
62 64 static_files = true
63 # Optional Languages
64 # en, fr, ja, pt_BR, zh_CN, zh_TW, pl
65 ## Optional Languages
66 ## en, fr, ja, pt_BR, zh_CN, zh_TW, pl
65 67 lang = en
66 68 cache_dir = /tmp/rc/data
67 69 index_dir = /tmp/rc/index
68 # set this path to use archive download cache
69 #archive_cache_dir = /tmp/rhodecode_tarballcache
70 app_instance_uuid = develop-test
70
71 ## uncomment and set this path to use archive download cache
72 #archive_cache_dir = /tmp/tarballcache
73
74 ## change this to unique ID for security
75 app_instance_uuid = rc-production
76
77 ## cut off limit for large diffs (size in bytes)
71 78 cut_off_limit = 256000
72 vcs_full_cache = False
73 # force https in RhodeCode, fixes https redirects, assumes it's always https
79
80 ## use cache version of scm repo everywhere
81 vcs_full_cache = false
82
83 ## force https in RhodeCode, fixes https redirects, assumes it's always https
74 84 force_https = false
75 # use Strict-Transport-Security headers
85
86 ## use Strict-Transport-Security headers
76 87 use_htsts = false
88
89 ## number of commits stats will parse on each iteration
77 90 commit_parse_limit = 25
78 # number of items displayed in lightweight dashboard before paginating
91
92 ## number of items displayed in lightweight dashboard before paginating is shown
79 93 dashboard_items = 100
94
95 ## use gravatar service to display avatars
80 96 use_gravatar = true
81 97
82 # path to git executable
98 ## path to git executable
83 99 git_path = git
84 100
85 101 ## RSS feed options
86
87 102 rss_cut_off_limit = 256000
88 103 rss_items_per_page = 10
89 104 rss_include_diff = false
90 105
106 ## show hash options for changelog
107 sha_len = 12
108 sha_rev = true
109
91 110
92 111 ## alternative_gravatar_url allows you to use your own avatar server application
93 112 ## the following parts of the URL will be replaced
94 113 ## {email} user email
95 114 ## {md5email} md5 hash of the user email (like at gravatar.com)
96 115 ## {size} size of the image that is expected from the server application
97 116 ## {scheme} http/https from RhodeCode server
98 117 ## {netloc} network location from RhodeCode server
99 118 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
100 119 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
101 120
121
122 ## container auth options
102 123 container_auth_enabled = false
103 124 proxypass_auth_enabled = false
125
104 126 ## default encoding used to convert from and to unicode
105 127 ## can be also a comma seperated list of encoding in case of mixed encodings
106 128 default_encoding = utf8
107 129
108 130 ## overwrite schema of clone url
109 131 ## available vars:
110 132 ## scheme - http/https
111 133 ## user - current user
112 134 ## pass - password
113 135 ## netloc - network location
114 136 ## path - usually repo_name
115 137
116 138 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
117 139
118 140 ## issue tracking mapping for commits messages
119 141 ## comment out issue_pat, issue_server, issue_prefix to enable
120 142
121 143 ## pattern to get the issues from commit messages
122 144 ## default one used here is #<numbers> with a regex passive group for `#`
123 145 ## {id} will be all groups matched from this pattern
124 146
125 147 issue_pat = (?:\s*#)(\d+)
126 148
127 149 ## server url to the issue, each {id} will be replaced with match
128 150 ## fetched from the regex and {repo} is replaced with full repository name
129 151 ## including groups {repo_name} is replaced with just name of repo
130 152
131 153 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
132 154
133 155 ## prefix to add to link to indicate it's an url
134 156 ## #314 will be replaced by <issue_prefix><id>
135 157
136 158 issue_prefix = #
137 159
138 160 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
139 161 ## multiple patterns, to other issues server, wiki or others
140 162 ## below an example how to create a wiki pattern
141 163 # #wiki-some-id -> https://mywiki.com/some-id
142 164
143 165 #issue_pat_wiki = (?:wiki-)(.+)
144 166 #issue_server_link_wiki = https://mywiki.com/{id}
145 167 #issue_prefix_wiki = WIKI-
146 168
147 169
148 170 ## instance-id prefix
149 171 ## a prefix key for this instance used for cache invalidation when running
150 172 ## multiple instances of rhodecode, make sure it's globally unique for
151 173 ## all running rhodecode instances. Leave empty if you don't use it
152 174 instance_id =
153 175
154 176 ## alternative return HTTP header for failed authentication. Default HTTP
155 177 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
156 178 ## handling that. Set this variable to 403 to return HTTPForbidden
157 179 auth_ret_code =
158 180
181 ## locking return code. When repository is locked return this HTTP code. 2XX
182 ## codes don't break the transactions while 4XX codes do
183 lock_ret_code = 423
184
185
159 186 ####################################
160 187 ### CELERY CONFIG ####
161 188 ####################################
162 189 use_celery = false
163 190 broker.host = localhost
164 191 broker.vhost = rabbitmqhost
165 192 broker.port = 5672
166 193 broker.user = rabbitmq
167 194 broker.password = qweqwe
168 195
169 196 celery.imports = rhodecode.lib.celerylib.tasks
170 197
171 198 celery.result.backend = amqp
172 199 celery.result.dburi = amqp://
173 200 celery.result.serialier = json
174 201
175 202 #celery.send.task.error.emails = true
176 203 #celery.amqp.task.result.expires = 18000
177 204
178 205 celeryd.concurrency = 2
179 206 #celeryd.log.file = celeryd.log
180 207 celeryd.log.level = debug
181 208 celeryd.max.tasks.per.child = 1
182 209
183 #tasks will never be sent to the queue, but executed locally instead.
210 ## tasks will never be sent to the queue, but executed locally instead.
184 211 celery.always.eager = false
185 212
186 213 ####################################
187 214 ### BEAKER CACHE ####
188 215 ####################################
189 216 beaker.cache.data_dir=/tmp/rc/data/cache/data
190 217 beaker.cache.lock_dir=/tmp/rc/data/cache/lock
191 218
192 219 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
193 220
194 221 beaker.cache.super_short_term.type=memory
195 222 beaker.cache.super_short_term.expire=1
196 223 beaker.cache.super_short_term.key_length = 256
197 224
198 225 beaker.cache.short_term.type=memory
199 226 beaker.cache.short_term.expire=60
200 227 beaker.cache.short_term.key_length = 256
201 228
202 229 beaker.cache.long_term.type=memory
203 230 beaker.cache.long_term.expire=36000
204 231 beaker.cache.long_term.key_length = 256
205 232
206 233 beaker.cache.sql_cache_short.type=memory
207 234 beaker.cache.sql_cache_short.expire=1
208 235 beaker.cache.sql_cache_short.key_length = 256
209 236
210 237 beaker.cache.sql_cache_med.type=memory
211 238 beaker.cache.sql_cache_med.expire=360
212 239 beaker.cache.sql_cache_med.key_length = 256
213 240
214 241 beaker.cache.sql_cache_long.type=file
215 242 beaker.cache.sql_cache_long.expire=3600
216 243 beaker.cache.sql_cache_long.key_length = 256
217 244
218 245 ####################################
219 246 ### BEAKER SESSION ####
220 247 ####################################
221 248 ## Type of storage used for the session, current types are
222 249 ## dbm, file, memcached, database, and memory.
223 250 ## The storage uses the Container API
224 251 ## that is also used by the cache system.
225 252
226 253 ## db session ##
227 254 #beaker.session.type = ext:database
228 255 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
229 256 #beaker.session.table_name = db_session
230 257
231 258 ## encrypted cookie client side session, good for many instances ##
232 259 #beaker.session.type = cookie
233 260
234 261 ## file based cookies (default) ##
235 262 #beaker.session.type = file
236 263
237 264
238 265 beaker.session.key = rhodecode
239 ## secure cookie requires AES python libraries ##
240 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
241 #beaker.session.validate_key = 9712sds2212c--zxc123
266 ## secure cookie requires AES python libraries
267 #beaker.session.encrypt_key = <key_for_encryption>
268 #beaker.session.validate_key = <validation_key>
269
242 270 ## sets session as invalid if it haven't been accessed for given amount of time
243 271 beaker.session.timeout = 3600
244 272 beaker.session.httponly = true
245 273 #beaker.session.cookie_path = /<your-prefix>
246 274
247 ## uncomment for https secure cookie ##
275 ## uncomment for https secure cookie
248 276 beaker.session.secure = false
249 277
250 ## auto save the session to not to use .save() ##
278 ## auto save the session to not to use .save()
251 279 beaker.session.auto = False
252 280
253 281 ## default cookie expiration time in seconds `true` expire at browser close ##
254 282 #beaker.session.cookie_expires = 3600
255 283
256 284
257 285 ############################
258 286 ## ERROR HANDLING SYSTEMS ##
259 287 ############################
260 288
261 289 ####################
262 290 ### [errormator] ###
263 291 ####################
264 292
265 # Errormator is tailored to work with RhodeCode, see
266 # http://errormator.com for details how to obtain an account
267 # you must install python package `errormator_client` to make it work
293 ## Errormator is tailored to work with RhodeCode, see
294 ## http://errormator.com for details how to obtain an account
295 ## you must install python package `errormator_client` to make it work
268 296
269 # errormator enabled
270 errormator = true
297 ## errormator enabled
298 errormator = false
271 299
272 300 errormator.server_url = https://api.errormator.com
273 301 errormator.api_key = YOUR_API_KEY
274 302
275 # TWEAK AMOUNT OF INFO SENT HERE
303 ## TWEAK AMOUNT OF INFO SENT HERE
276 304
277 # enables 404 error logging (default False)
305 ## enables 404 error logging (default False)
278 306 errormator.report_404 = false
279 307
280 # time in seconds after request is considered being slow (default 1)
308 ## time in seconds after request is considered being slow (default 1)
281 309 errormator.slow_request_time = 1
282 310
283 # record slow requests in application
284 # (needs to be enabled for slow datastore recording and time tracking)
311 ## record slow requests in application
312 ## (needs to be enabled for slow datastore recording and time tracking)
285 313 errormator.slow_requests = true
286 314
287 # enable hooking to application loggers
315 ## enable hooking to application loggers
288 316 # errormator.logging = true
289 317
290 # minimum log level for log capture
318 ## minimum log level for log capture
291 319 # errormator.logging.level = WARNING
292 320
293 # send logs only from erroneous/slow requests
294 # (saves API quota for intensive logging)
321 ## send logs only from erroneous/slow requests
322 ## (saves API quota for intensive logging)
295 323 errormator.logging_on_error = false
296 324
297 # list of additonal keywords that should be grabbed from environ object
298 # can be string with comma separated list of words in lowercase
299 # (by default client will always send following info:
300 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
301 # start with HTTP* this list be extended with additional keywords here
325 ## list of additonal keywords that should be grabbed from environ object
326 ## can be string with comma separated list of words in lowercase
327 ## (by default client will always send following info:
328 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
329 ## start with HTTP* this list be extended with additional keywords here
302 330 errormator.environ_keys_whitelist =
303 331
304 332
305 # list of keywords that should be blanked from request object
306 # can be string with comma separated list of words in lowercase
307 # (by default client will always blank keys that contain following words
308 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
309 # this list be extended with additional keywords set here
333 ## list of keywords that should be blanked from request object
334 ## can be string with comma separated list of words in lowercase
335 ## (by default client will always blank keys that contain following words
336 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
337 ## this list be extended with additional keywords set here
310 338 errormator.request_keys_blacklist =
311 339
312 340
313 # list of namespaces that should be ignores when gathering log entries
314 # can be string with comma separated list of namespaces
315 # (by default the client ignores own entries: errormator_client.client)
341 ## list of namespaces that should be ignores when gathering log entries
342 ## can be string with comma separated list of namespaces
343 ## (by default the client ignores own entries: errormator_client.client)
316 344 errormator.log_namespace_blacklist =
317 345
318 346
319 347 ################
320 348 ### [sentry] ###
321 349 ################
322 350
323 # sentry is a alternative open source error aggregator
324 # you must install python packages `sentry` and `raven` to enable
351 ## sentry is a alternative open source error aggregator
352 ## you must install python packages `sentry` and `raven` to enable
325 353
326 354 sentry.dsn = YOUR_DNS
327 355 sentry.servers =
328 356 sentry.name =
329 357 sentry.key =
330 358 sentry.public_key =
331 359 sentry.secret_key =
332 360 sentry.project =
333 361 sentry.site =
334 362 sentry.include_paths =
335 363 sentry.exclude_paths =
336 364
337 365
338 366 ################################################################################
339 367 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
340 368 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
341 369 ## execute malicious code after an exception is raised. ##
342 370 ################################################################################
343 371 set debug = false
344 372
345 373 ##################################
346 374 ### LOGVIEW CONFIG ###
347 375 ##################################
348 376 logview.sqlalchemy = #faa
349 377 logview.pylons.templating = #bfb
350 378 logview.pylons.util = #eee
351 379
352 380 #########################################################
353 381 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
354 382 #########################################################
355 383 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.sqlite
356 384 #sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_test
357 385 #sqlalchemy.db1.url = mysql://root:qwe@localhost/rhodecode_test
358 386 sqlalchemy.db1.echo = false
359 387 sqlalchemy.db1.pool_recycle = 3600
360 388 sqlalchemy.db1.convert_unicode = true
361 389
362 390 ################################
363 391 ### LOGGING CONFIGURATION ####
364 392 ################################
365 393 [loggers]
366 394 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
367 395
368 396 [handlers]
369 keys = console
397 keys = console, console_sql
370 398
371 399 [formatters]
372 keys = generic, color_formatter
400 keys = generic, color_formatter, color_formatter_sql
373 401
374 402 #############
375 403 ## LOGGERS ##
376 404 #############
377 405 [logger_root]
378 406 level = DEBUG
379 407 handlers = console
380 408
381 409 [logger_routes]
382 410 level = DEBUG
383 411 handlers =
384 412 qualname = routes.middleware
385 # "level = DEBUG" logs the route matched and routing variables.
413 ## "level = DEBUG" logs the route matched and routing variables.
386 414 propagate = 1
387 415
388 416 [logger_beaker]
389 417 level = DEBUG
390 418 handlers =
391 419 qualname = beaker.container
392 420 propagate = 1
393 421
394 422 [logger_templates]
395 423 level = INFO
396 424 handlers =
397 425 qualname = pylons.templating
398 426 propagate = 1
399 427
400 428 [logger_rhodecode]
401 429 level = DEBUG
402 430 handlers =
403 431 qualname = rhodecode
404 432 propagate = 1
405 433
406 434 [logger_sqlalchemy]
407 435 level = ERROR
408 436 handlers = console
409 437 qualname = sqlalchemy.engine
410 438 propagate = 0
411 439
412 440 [logger_whoosh_indexer]
413 441 level = DEBUG
414 442 handlers =
415 443 qualname = whoosh_indexer
416 444 propagate = 1
417 445
418 446 ##############
419 447 ## HANDLERS ##
420 448 ##############
421 449
422 450 [handler_console]
423 451 class = StreamHandler
424 452 args = (sys.stderr,)
425 453 level = NOTSET
426 454 formatter = generic
427 455
456 [handler_console_sql]
457 class = StreamHandler
458 args = (sys.stderr,)
459 level = WARN
460 formatter = generic
461
428 462 ################
429 463 ## FORMATTERS ##
430 464 ################
431 465
432 466 [formatter_generic]
433 467 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
434 468 datefmt = %Y-%m-%d %H:%M:%S
435 469
436 470 [formatter_color_formatter]
437 471 class=rhodecode.lib.colored_formatter.ColorFormatter
438 472 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
439 473 datefmt = %Y-%m-%d %H:%M:%S
474
475 [formatter_color_formatter_sql]
476 class=rhodecode.lib.colored_formatter.ColorFormatterSql
477 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
478 datefmt = %Y-%m-%d %H:%M:%S
General Comments 0
You need to be logged in to leave comments. Login now