##// END OF EJS Templates
Implemented patch from andrewsh ref #565 Add support for {netloc} and {scheme}...
marcink -
r2836:819eb7f8 beta
parent child Browse files
Show More
@@ -1,329 +1,331 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # RhodeCode - Pylons environment configuration #
3 # RhodeCode - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 pdebug = false
10 pdebug = false
11 ################################################################################
11 ################################################################################
12 ## Uncomment and replace with the address which should receive ##
12 ## Uncomment and replace with the address which should receive ##
13 ## any error reports after application crash ##
13 ## any error reports after application crash ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 ################################################################################
15 ################################################################################
16 #email_to = admin@localhost
16 #email_to = admin@localhost
17 #error_email_from = paste_error@localhost
17 #error_email_from = paste_error@localhost
18 #app_email_from = rhodecode-noreply@localhost
18 #app_email_from = rhodecode-noreply@localhost
19 #error_message =
19 #error_message =
20 #email_prefix = [RhodeCode]
20 #email_prefix = [RhodeCode]
21
21
22 #smtp_server = mail.server.com
22 #smtp_server = mail.server.com
23 #smtp_username =
23 #smtp_username =
24 #smtp_password =
24 #smtp_password =
25 #smtp_port =
25 #smtp_port =
26 #smtp_use_tls = false
26 #smtp_use_tls = false
27 #smtp_use_ssl = true
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 #smtp_auth =
29 #smtp_auth =
30
30
31 [server:main]
31 [server:main]
32 ##nr of threads to spawn
32 ##nr of threads to spawn
33 #threadpool_workers = 5
33 #threadpool_workers = 5
34
34
35 ##max request before thread respawn
35 ##max request before thread respawn
36 #threadpool_max_requests = 10
36 #threadpool_max_requests = 10
37
37
38 ##option to use threads of process
38 ##option to use threads of process
39 #use_threadpool = true
39 #use_threadpool = true
40
40
41 #use = egg:Paste#http
41 #use = egg:Paste#http
42 use = egg:waitress#main
42 use = egg:waitress#main
43 host = 0.0.0.0
43 host = 0.0.0.0
44 port = 5000
44 port = 5000
45
45
46 [filter:proxy-prefix]
46 [filter:proxy-prefix]
47 # prefix middleware for rc
47 # prefix middleware for rc
48 use = egg:PasteDeploy#prefix
48 use = egg:PasteDeploy#prefix
49 prefix = /<your-prefix>
49 prefix = /<your-prefix>
50
50
51 [app:main]
51 [app:main]
52 use = egg:rhodecode
52 use = egg:rhodecode
53 #filter-with = proxy-prefix
53 #filter-with = proxy-prefix
54 full_stack = true
54 full_stack = true
55 static_files = true
55 static_files = true
56 # Optional Languages
56 # Optional Languages
57 # en, fr, ja, pt_BR, zh_CN, zh_TW
57 # en, fr, ja, pt_BR, zh_CN, zh_TW
58 lang = en
58 lang = en
59 cache_dir = %(here)s/data
59 cache_dir = %(here)s/data
60 index_dir = %(here)s/data/index
60 index_dir = %(here)s/data/index
61 app_instance_uuid = rc-develop
61 app_instance_uuid = rc-develop
62 cut_off_limit = 256000
62 cut_off_limit = 256000
63 force_https = false
63 force_https = false
64 commit_parse_limit = 25
64 commit_parse_limit = 25
65 use_gravatar = true
65 use_gravatar = true
66
66
67 ## alternative_gravatar_url allows you to use your own avatar server application
67 ## alternative_gravatar_url allows you to use your own avatar server application
68 ## the following parts of the URL will be replaced
68 ## the following parts of the URL will be replaced
69 ## {email} user email
69 ## {email} user email
70 ## {md5email} md5 hash of the user email (like at gravatar.com)
70 ## {md5email} md5 hash of the user email (like at gravatar.com)
71 ## {size} size of the image that is expected from the server application
71 ## {size} size of the image that is expected from the server application
72 ## {scheme} http/https from RhodeCode server
73 ## {netloc} network location from RhodeCode server
72 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
74 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
73 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
75 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
74
76
75 container_auth_enabled = false
77 container_auth_enabled = false
76 proxypass_auth_enabled = false
78 proxypass_auth_enabled = false
77 default_encoding = utf8
79 default_encoding = utf8
78
80
79 ## overwrite schema of clone url
81 ## overwrite schema of clone url
80 ## available vars:
82 ## available vars:
81 ## scheme - http/https
83 ## scheme - http/https
82 ## user - current user
84 ## user - current user
83 ## pass - password
85 ## pass - password
84 ## netloc - network location
86 ## netloc - network location
85 ## path - usually repo_name
87 ## path - usually repo_name
86
88
87 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
89 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
88
90
89 ## issue tracking mapping for commits messages
91 ## issue tracking mapping for commits messages
90 ## comment out issue_pat, issue_server, issue_prefix to enable
92 ## comment out issue_pat, issue_server, issue_prefix to enable
91
93
92 ## pattern to get the issues from commit messages
94 ## pattern to get the issues from commit messages
93 ## default one used here is #<numbers> with a regex passive group for `#`
95 ## default one used here is #<numbers> with a regex passive group for `#`
94 ## {id} will be all groups matched from this pattern
96 ## {id} will be all groups matched from this pattern
95
97
96 issue_pat = (?:\s*#)(\d+)
98 issue_pat = (?:\s*#)(\d+)
97
99
98 ## server url to the issue, each {id} will be replaced with match
100 ## server url to the issue, each {id} will be replaced with match
99 ## fetched from the regex and {repo} is replaced with full repository name
101 ## fetched from the regex and {repo} is replaced with full repository name
100 ## including groups {repo_name} is replaced with just name of repo
102 ## including groups {repo_name} is replaced with just name of repo
101
103
102 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
104 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
103
105
104 ## prefix to add to link to indicate it's an url
106 ## prefix to add to link to indicate it's an url
105 ## #314 will be replaced by <issue_prefix><id>
107 ## #314 will be replaced by <issue_prefix><id>
106
108
107 issue_prefix = #
109 issue_prefix = #
108
110
109 ## instance-id prefix
111 ## instance-id prefix
110 ## a prefix key for this instance used for cache invalidation when running
112 ## a prefix key for this instance used for cache invalidation when running
111 ## multiple instances of rhodecode, make sure it's globally unique for
113 ## multiple instances of rhodecode, make sure it's globally unique for
112 ## all running rhodecode instances. Leave empty if you don't use it
114 ## all running rhodecode instances. Leave empty if you don't use it
113 instance_id =
115 instance_id =
114
116
115 ## alternative return HTTP header for failed authentication. Default HTTP
117 ## alternative return HTTP header for failed authentication. Default HTTP
116 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
118 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
117 ## handling that. Set this variable to 403 to return HTTPForbidden
119 ## handling that. Set this variable to 403 to return HTTPForbidden
118 auth_ret_code =
120 auth_ret_code =
119
121
120 ####################################
122 ####################################
121 ### CELERY CONFIG ####
123 ### CELERY CONFIG ####
122 ####################################
124 ####################################
123 use_celery = false
125 use_celery = false
124 broker.host = localhost
126 broker.host = localhost
125 broker.vhost = rabbitmqhost
127 broker.vhost = rabbitmqhost
126 broker.port = 5672
128 broker.port = 5672
127 broker.user = rabbitmq
129 broker.user = rabbitmq
128 broker.password = qweqwe
130 broker.password = qweqwe
129
131
130 celery.imports = rhodecode.lib.celerylib.tasks
132 celery.imports = rhodecode.lib.celerylib.tasks
131
133
132 celery.result.backend = amqp
134 celery.result.backend = amqp
133 celery.result.dburi = amqp://
135 celery.result.dburi = amqp://
134 celery.result.serialier = json
136 celery.result.serialier = json
135
137
136 #celery.send.task.error.emails = true
138 #celery.send.task.error.emails = true
137 #celery.amqp.task.result.expires = 18000
139 #celery.amqp.task.result.expires = 18000
138
140
139 celeryd.concurrency = 2
141 celeryd.concurrency = 2
140 #celeryd.log.file = celeryd.log
142 #celeryd.log.file = celeryd.log
141 celeryd.log.level = debug
143 celeryd.log.level = debug
142 celeryd.max.tasks.per.child = 1
144 celeryd.max.tasks.per.child = 1
143
145
144 #tasks will never be sent to the queue, but executed locally instead.
146 #tasks will never be sent to the queue, but executed locally instead.
145 celery.always.eager = false
147 celery.always.eager = false
146
148
147 ####################################
149 ####################################
148 ### BEAKER CACHE ####
150 ### BEAKER CACHE ####
149 ####################################
151 ####################################
150 beaker.cache.data_dir=%(here)s/data/cache/data
152 beaker.cache.data_dir=%(here)s/data/cache/data
151 beaker.cache.lock_dir=%(here)s/data/cache/lock
153 beaker.cache.lock_dir=%(here)s/data/cache/lock
152
154
153 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
155 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
154
156
155 beaker.cache.super_short_term.type=memory
157 beaker.cache.super_short_term.type=memory
156 beaker.cache.super_short_term.expire=10
158 beaker.cache.super_short_term.expire=10
157 beaker.cache.super_short_term.key_length = 256
159 beaker.cache.super_short_term.key_length = 256
158
160
159 beaker.cache.short_term.type=memory
161 beaker.cache.short_term.type=memory
160 beaker.cache.short_term.expire=60
162 beaker.cache.short_term.expire=60
161 beaker.cache.short_term.key_length = 256
163 beaker.cache.short_term.key_length = 256
162
164
163 beaker.cache.long_term.type=memory
165 beaker.cache.long_term.type=memory
164 beaker.cache.long_term.expire=36000
166 beaker.cache.long_term.expire=36000
165 beaker.cache.long_term.key_length = 256
167 beaker.cache.long_term.key_length = 256
166
168
167 beaker.cache.sql_cache_short.type=memory
169 beaker.cache.sql_cache_short.type=memory
168 beaker.cache.sql_cache_short.expire=10
170 beaker.cache.sql_cache_short.expire=10
169 beaker.cache.sql_cache_short.key_length = 256
171 beaker.cache.sql_cache_short.key_length = 256
170
172
171 beaker.cache.sql_cache_med.type=memory
173 beaker.cache.sql_cache_med.type=memory
172 beaker.cache.sql_cache_med.expire=360
174 beaker.cache.sql_cache_med.expire=360
173 beaker.cache.sql_cache_med.key_length = 256
175 beaker.cache.sql_cache_med.key_length = 256
174
176
175 beaker.cache.sql_cache_long.type=file
177 beaker.cache.sql_cache_long.type=file
176 beaker.cache.sql_cache_long.expire=3600
178 beaker.cache.sql_cache_long.expire=3600
177 beaker.cache.sql_cache_long.key_length = 256
179 beaker.cache.sql_cache_long.key_length = 256
178
180
179 ####################################
181 ####################################
180 ### BEAKER SESSION ####
182 ### BEAKER SESSION ####
181 ####################################
183 ####################################
182 ## Type of storage used for the session, current types are
184 ## Type of storage used for the session, current types are
183 ## dbm, file, memcached, database, and memory.
185 ## dbm, file, memcached, database, and memory.
184 ## The storage uses the Container API
186 ## The storage uses the Container API
185 ## that is also used by the cache system.
187 ## that is also used by the cache system.
186
188
187 ## db session ##
189 ## db session ##
188 #beaker.session.type = ext:database
190 #beaker.session.type = ext:database
189 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
191 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
190 #beaker.session.table_name = db_session
192 #beaker.session.table_name = db_session
191
193
192 ## encrypted cookie client side session, good for many instances ##
194 ## encrypted cookie client side session, good for many instances ##
193 #beaker.session.type = cookie
195 #beaker.session.type = cookie
194
196
195 ## file based cookies (default) ##
197 ## file based cookies (default) ##
196 #beaker.session.type = file
198 #beaker.session.type = file
197
199
198
200
199 beaker.session.key = rhodecode
201 beaker.session.key = rhodecode
200 ## secure cookie requires AES python libraries ##
202 ## secure cookie requires AES python libraries ##
201 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
203 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
202 #beaker.session.validate_key = 9712sds2212c--zxc123
204 #beaker.session.validate_key = 9712sds2212c--zxc123
203 ## sets session as invalid if it haven't been accessed for given amount of time
205 ## sets session as invalid if it haven't been accessed for given amount of time
204 beaker.session.timeout = 2592000
206 beaker.session.timeout = 2592000
205 beaker.session.httponly = true
207 beaker.session.httponly = true
206 #beaker.session.cookie_path = /<your-prefix>
208 #beaker.session.cookie_path = /<your-prefix>
207
209
208 ## uncomment for https secure cookie ##
210 ## uncomment for https secure cookie ##
209 beaker.session.secure = false
211 beaker.session.secure = false
210
212
211 ## auto save the session to not to use .save() ##
213 ## auto save the session to not to use .save() ##
212 beaker.session.auto = False
214 beaker.session.auto = False
213
215
214 ## default cookie expiration time in seconds `true` expire at browser close ##
216 ## default cookie expiration time in seconds `true` expire at browser close ##
215 #beaker.session.cookie_expires = 3600
217 #beaker.session.cookie_expires = 3600
216
218
217
219
218 ################################################################################
220 ################################################################################
219 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
221 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
220 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
222 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
221 ## execute malicious code after an exception is raised. ##
223 ## execute malicious code after an exception is raised. ##
222 ################################################################################
224 ################################################################################
223 #set debug = false
225 #set debug = false
224
226
225 ##################################
227 ##################################
226 ### LOGVIEW CONFIG ###
228 ### LOGVIEW CONFIG ###
227 ##################################
229 ##################################
228 logview.sqlalchemy = #faa
230 logview.sqlalchemy = #faa
229 logview.pylons.templating = #bfb
231 logview.pylons.templating = #bfb
230 logview.pylons.util = #eee
232 logview.pylons.util = #eee
231
233
232 #########################################################
234 #########################################################
233 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
235 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
234 #########################################################
236 #########################################################
235 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
237 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
236 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
238 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
237 sqlalchemy.db1.echo = false
239 sqlalchemy.db1.echo = false
238 sqlalchemy.db1.pool_recycle = 3600
240 sqlalchemy.db1.pool_recycle = 3600
239 sqlalchemy.db1.convert_unicode = true
241 sqlalchemy.db1.convert_unicode = true
240
242
241 ################################
243 ################################
242 ### LOGGING CONFIGURATION ####
244 ### LOGGING CONFIGURATION ####
243 ################################
245 ################################
244 [loggers]
246 [loggers]
245 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
247 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
246
248
247 [handlers]
249 [handlers]
248 keys = console, console_sql
250 keys = console, console_sql
249
251
250 [formatters]
252 [formatters]
251 keys = generic, color_formatter, color_formatter_sql
253 keys = generic, color_formatter, color_formatter_sql
252
254
253 #############
255 #############
254 ## LOGGERS ##
256 ## LOGGERS ##
255 #############
257 #############
256 [logger_root]
258 [logger_root]
257 level = NOTSET
259 level = NOTSET
258 handlers = console
260 handlers = console
259
261
260 [logger_routes]
262 [logger_routes]
261 level = DEBUG
263 level = DEBUG
262 handlers =
264 handlers =
263 qualname = routes.middleware
265 qualname = routes.middleware
264 # "level = DEBUG" logs the route matched and routing variables.
266 # "level = DEBUG" logs the route matched and routing variables.
265 propagate = 1
267 propagate = 1
266
268
267 [logger_beaker]
269 [logger_beaker]
268 level = DEBUG
270 level = DEBUG
269 handlers =
271 handlers =
270 qualname = beaker.container
272 qualname = beaker.container
271 propagate = 1
273 propagate = 1
272
274
273 [logger_templates]
275 [logger_templates]
274 level = INFO
276 level = INFO
275 handlers =
277 handlers =
276 qualname = pylons.templating
278 qualname = pylons.templating
277 propagate = 1
279 propagate = 1
278
280
279 [logger_rhodecode]
281 [logger_rhodecode]
280 level = DEBUG
282 level = DEBUG
281 handlers =
283 handlers =
282 qualname = rhodecode
284 qualname = rhodecode
283 propagate = 1
285 propagate = 1
284
286
285 [logger_sqlalchemy]
287 [logger_sqlalchemy]
286 level = INFO
288 level = INFO
287 handlers = console_sql
289 handlers = console_sql
288 qualname = sqlalchemy.engine
290 qualname = sqlalchemy.engine
289 propagate = 0
291 propagate = 0
290
292
291 [logger_whoosh_indexer]
293 [logger_whoosh_indexer]
292 level = DEBUG
294 level = DEBUG
293 handlers =
295 handlers =
294 qualname = whoosh_indexer
296 qualname = whoosh_indexer
295 propagate = 1
297 propagate = 1
296
298
297 ##############
299 ##############
298 ## HANDLERS ##
300 ## HANDLERS ##
299 ##############
301 ##############
300
302
301 [handler_console]
303 [handler_console]
302 class = StreamHandler
304 class = StreamHandler
303 args = (sys.stderr,)
305 args = (sys.stderr,)
304 level = DEBUG
306 level = DEBUG
305 formatter = color_formatter
307 formatter = color_formatter
306
308
307 [handler_console_sql]
309 [handler_console_sql]
308 class = StreamHandler
310 class = StreamHandler
309 args = (sys.stderr,)
311 args = (sys.stderr,)
310 level = DEBUG
312 level = DEBUG
311 formatter = color_formatter_sql
313 formatter = color_formatter_sql
312
314
313 ################
315 ################
314 ## FORMATTERS ##
316 ## FORMATTERS ##
315 ################
317 ################
316
318
317 [formatter_generic]
319 [formatter_generic]
318 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
320 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
319 datefmt = %Y-%m-%d %H:%M:%S
321 datefmt = %Y-%m-%d %H:%M:%S
320
322
321 [formatter_color_formatter]
323 [formatter_color_formatter]
322 class=rhodecode.lib.colored_formatter.ColorFormatter
324 class=rhodecode.lib.colored_formatter.ColorFormatter
323 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
325 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
324 datefmt = %Y-%m-%d %H:%M:%S
326 datefmt = %Y-%m-%d %H:%M:%S
325
327
326 [formatter_color_formatter_sql]
328 [formatter_color_formatter_sql]
327 class=rhodecode.lib.colored_formatter.ColorFormatterSql
329 class=rhodecode.lib.colored_formatter.ColorFormatterSql
328 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
330 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
329 datefmt = %Y-%m-%d %H:%M:%S
331 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,329 +1,331 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # RhodeCode - Pylons environment configuration #
3 # RhodeCode - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 pdebug = false
10 pdebug = false
11 ################################################################################
11 ################################################################################
12 ## Uncomment and replace with the address which should receive ##
12 ## Uncomment and replace with the address which should receive ##
13 ## any error reports after application crash ##
13 ## any error reports after application crash ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 ################################################################################
15 ################################################################################
16 #email_to = admin@localhost
16 #email_to = admin@localhost
17 #error_email_from = paste_error@localhost
17 #error_email_from = paste_error@localhost
18 #app_email_from = rhodecode-noreply@localhost
18 #app_email_from = rhodecode-noreply@localhost
19 #error_message =
19 #error_message =
20 #email_prefix = [RhodeCode]
20 #email_prefix = [RhodeCode]
21
21
22 #smtp_server = mail.server.com
22 #smtp_server = mail.server.com
23 #smtp_username =
23 #smtp_username =
24 #smtp_password =
24 #smtp_password =
25 #smtp_port =
25 #smtp_port =
26 #smtp_use_tls = false
26 #smtp_use_tls = false
27 #smtp_use_ssl = true
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 #smtp_auth =
29 #smtp_auth =
30
30
31 [server:main]
31 [server:main]
32 ##nr of threads to spawn
32 ##nr of threads to spawn
33 #threadpool_workers = 5
33 #threadpool_workers = 5
34
34
35 ##max request before thread respawn
35 ##max request before thread respawn
36 #threadpool_max_requests = 10
36 #threadpool_max_requests = 10
37
37
38 ##option to use threads of process
38 ##option to use threads of process
39 #use_threadpool = true
39 #use_threadpool = true
40
40
41 #use = egg:Paste#http
41 #use = egg:Paste#http
42 use = egg:waitress#main
42 use = egg:waitress#main
43 host = 127.0.0.1
43 host = 127.0.0.1
44 port = 8001
44 port = 8001
45
45
46 [filter:proxy-prefix]
46 [filter:proxy-prefix]
47 # prefix middleware for rc
47 # prefix middleware for rc
48 use = egg:PasteDeploy#prefix
48 use = egg:PasteDeploy#prefix
49 prefix = /<your-prefix>
49 prefix = /<your-prefix>
50
50
51 [app:main]
51 [app:main]
52 use = egg:rhodecode
52 use = egg:rhodecode
53 #filter-with = proxy-prefix
53 #filter-with = proxy-prefix
54 full_stack = true
54 full_stack = true
55 static_files = true
55 static_files = true
56 # Optional Languages
56 # Optional Languages
57 # en, fr, ja, pt_BR, zh_CN, zh_TW
57 # en, fr, ja, pt_BR, zh_CN, zh_TW
58 lang = en
58 lang = en
59 cache_dir = %(here)s/data
59 cache_dir = %(here)s/data
60 index_dir = %(here)s/data/index
60 index_dir = %(here)s/data/index
61 app_instance_uuid = rc-production
61 app_instance_uuid = rc-production
62 cut_off_limit = 256000
62 cut_off_limit = 256000
63 force_https = false
63 force_https = false
64 commit_parse_limit = 50
64 commit_parse_limit = 50
65 use_gravatar = true
65 use_gravatar = true
66
66
67 ## alternative_gravatar_url allows you to use your own avatar server application
67 ## alternative_gravatar_url allows you to use your own avatar server application
68 ## the following parts of the URL will be replaced
68 ## the following parts of the URL will be replaced
69 ## {email} user email
69 ## {email} user email
70 ## {md5email} md5 hash of the user email (like at gravatar.com)
70 ## {md5email} md5 hash of the user email (like at gravatar.com)
71 ## {size} size of the image that is expected from the server application
71 ## {size} size of the image that is expected from the server application
72 ## {scheme} http/https from RhodeCode server
73 ## {netloc} network location from RhodeCode server
72 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
74 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
73 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
75 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
74
76
75 container_auth_enabled = false
77 container_auth_enabled = false
76 proxypass_auth_enabled = false
78 proxypass_auth_enabled = false
77 default_encoding = utf8
79 default_encoding = utf8
78
80
79 ## overwrite schema of clone url
81 ## overwrite schema of clone url
80 ## available vars:
82 ## available vars:
81 ## scheme - http/https
83 ## scheme - http/https
82 ## user - current user
84 ## user - current user
83 ## pass - password
85 ## pass - password
84 ## netloc - network location
86 ## netloc - network location
85 ## path - usually repo_name
87 ## path - usually repo_name
86
88
87 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
89 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
88
90
89 ## issue tracking mapping for commits messages
91 ## issue tracking mapping for commits messages
90 ## comment out issue_pat, issue_server, issue_prefix to enable
92 ## comment out issue_pat, issue_server, issue_prefix to enable
91
93
92 ## pattern to get the issues from commit messages
94 ## pattern to get the issues from commit messages
93 ## default one used here is #<numbers> with a regex passive group for `#`
95 ## default one used here is #<numbers> with a regex passive group for `#`
94 ## {id} will be all groups matched from this pattern
96 ## {id} will be all groups matched from this pattern
95
97
96 issue_pat = (?:\s*#)(\d+)
98 issue_pat = (?:\s*#)(\d+)
97
99
98 ## server url to the issue, each {id} will be replaced with match
100 ## server url to the issue, each {id} will be replaced with match
99 ## fetched from the regex and {repo} is replaced with full repository name
101 ## fetched from the regex and {repo} is replaced with full repository name
100 ## including groups {repo_name} is replaced with just name of repo
102 ## including groups {repo_name} is replaced with just name of repo
101
103
102 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
104 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
103
105
104 ## prefix to add to link to indicate it's an url
106 ## prefix to add to link to indicate it's an url
105 ## #314 will be replaced by <issue_prefix><id>
107 ## #314 will be replaced by <issue_prefix><id>
106
108
107 issue_prefix = #
109 issue_prefix = #
108
110
109 ## instance-id prefix
111 ## instance-id prefix
110 ## a prefix key for this instance used for cache invalidation when running
112 ## a prefix key for this instance used for cache invalidation when running
111 ## multiple instances of rhodecode, make sure it's globally unique for
113 ## multiple instances of rhodecode, make sure it's globally unique for
112 ## all running rhodecode instances. Leave empty if you don't use it
114 ## all running rhodecode instances. Leave empty if you don't use it
113 instance_id =
115 instance_id =
114
116
115 ## alternative return HTTP header for failed authentication. Default HTTP
117 ## alternative return HTTP header for failed authentication. Default HTTP
116 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
118 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
117 ## handling that. Set this variable to 403 to return HTTPForbidden
119 ## handling that. Set this variable to 403 to return HTTPForbidden
118 auth_ret_code =
120 auth_ret_code =
119
121
120 ####################################
122 ####################################
121 ### CELERY CONFIG ####
123 ### CELERY CONFIG ####
122 ####################################
124 ####################################
123 use_celery = false
125 use_celery = false
124 broker.host = localhost
126 broker.host = localhost
125 broker.vhost = rabbitmqhost
127 broker.vhost = rabbitmqhost
126 broker.port = 5672
128 broker.port = 5672
127 broker.user = rabbitmq
129 broker.user = rabbitmq
128 broker.password = qweqwe
130 broker.password = qweqwe
129
131
130 celery.imports = rhodecode.lib.celerylib.tasks
132 celery.imports = rhodecode.lib.celerylib.tasks
131
133
132 celery.result.backend = amqp
134 celery.result.backend = amqp
133 celery.result.dburi = amqp://
135 celery.result.dburi = amqp://
134 celery.result.serialier = json
136 celery.result.serialier = json
135
137
136 #celery.send.task.error.emails = true
138 #celery.send.task.error.emails = true
137 #celery.amqp.task.result.expires = 18000
139 #celery.amqp.task.result.expires = 18000
138
140
139 celeryd.concurrency = 2
141 celeryd.concurrency = 2
140 #celeryd.log.file = celeryd.log
142 #celeryd.log.file = celeryd.log
141 celeryd.log.level = debug
143 celeryd.log.level = debug
142 celeryd.max.tasks.per.child = 1
144 celeryd.max.tasks.per.child = 1
143
145
144 #tasks will never be sent to the queue, but executed locally instead.
146 #tasks will never be sent to the queue, but executed locally instead.
145 celery.always.eager = false
147 celery.always.eager = false
146
148
147 ####################################
149 ####################################
148 ### BEAKER CACHE ####
150 ### BEAKER CACHE ####
149 ####################################
151 ####################################
150 beaker.cache.data_dir=%(here)s/data/cache/data
152 beaker.cache.data_dir=%(here)s/data/cache/data
151 beaker.cache.lock_dir=%(here)s/data/cache/lock
153 beaker.cache.lock_dir=%(here)s/data/cache/lock
152
154
153 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
155 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
154
156
155 beaker.cache.super_short_term.type=memory
157 beaker.cache.super_short_term.type=memory
156 beaker.cache.super_short_term.expire=10
158 beaker.cache.super_short_term.expire=10
157 beaker.cache.super_short_term.key_length = 256
159 beaker.cache.super_short_term.key_length = 256
158
160
159 beaker.cache.short_term.type=memory
161 beaker.cache.short_term.type=memory
160 beaker.cache.short_term.expire=60
162 beaker.cache.short_term.expire=60
161 beaker.cache.short_term.key_length = 256
163 beaker.cache.short_term.key_length = 256
162
164
163 beaker.cache.long_term.type=memory
165 beaker.cache.long_term.type=memory
164 beaker.cache.long_term.expire=36000
166 beaker.cache.long_term.expire=36000
165 beaker.cache.long_term.key_length = 256
167 beaker.cache.long_term.key_length = 256
166
168
167 beaker.cache.sql_cache_short.type=memory
169 beaker.cache.sql_cache_short.type=memory
168 beaker.cache.sql_cache_short.expire=10
170 beaker.cache.sql_cache_short.expire=10
169 beaker.cache.sql_cache_short.key_length = 256
171 beaker.cache.sql_cache_short.key_length = 256
170
172
171 beaker.cache.sql_cache_med.type=memory
173 beaker.cache.sql_cache_med.type=memory
172 beaker.cache.sql_cache_med.expire=360
174 beaker.cache.sql_cache_med.expire=360
173 beaker.cache.sql_cache_med.key_length = 256
175 beaker.cache.sql_cache_med.key_length = 256
174
176
175 beaker.cache.sql_cache_long.type=file
177 beaker.cache.sql_cache_long.type=file
176 beaker.cache.sql_cache_long.expire=3600
178 beaker.cache.sql_cache_long.expire=3600
177 beaker.cache.sql_cache_long.key_length = 256
179 beaker.cache.sql_cache_long.key_length = 256
178
180
179 ####################################
181 ####################################
180 ### BEAKER SESSION ####
182 ### BEAKER SESSION ####
181 ####################################
183 ####################################
182 ## Type of storage used for the session, current types are
184 ## Type of storage used for the session, current types are
183 ## dbm, file, memcached, database, and memory.
185 ## dbm, file, memcached, database, and memory.
184 ## The storage uses the Container API
186 ## The storage uses the Container API
185 ## that is also used by the cache system.
187 ## that is also used by the cache system.
186
188
187 ## db session ##
189 ## db session ##
188 #beaker.session.type = ext:database
190 #beaker.session.type = ext:database
189 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
191 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
190 #beaker.session.table_name = db_session
192 #beaker.session.table_name = db_session
191
193
192 ## encrypted cookie client side session, good for many instances ##
194 ## encrypted cookie client side session, good for many instances ##
193 #beaker.session.type = cookie
195 #beaker.session.type = cookie
194
196
195 ## file based cookies (default) ##
197 ## file based cookies (default) ##
196 #beaker.session.type = file
198 #beaker.session.type = file
197
199
198
200
199 beaker.session.key = rhodecode
201 beaker.session.key = rhodecode
200 ## secure cookie requires AES python libraries ##
202 ## secure cookie requires AES python libraries ##
201 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
203 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
202 #beaker.session.validate_key = 9712sds2212c--zxc123
204 #beaker.session.validate_key = 9712sds2212c--zxc123
203 ## sets session as invalid if it haven't been accessed for given amount of time
205 ## sets session as invalid if it haven't been accessed for given amount of time
204 beaker.session.timeout = 2592000
206 beaker.session.timeout = 2592000
205 beaker.session.httponly = true
207 beaker.session.httponly = true
206 #beaker.session.cookie_path = /<your-prefix>
208 #beaker.session.cookie_path = /<your-prefix>
207
209
208 ## uncomment for https secure cookie ##
210 ## uncomment for https secure cookie ##
209 beaker.session.secure = false
211 beaker.session.secure = false
210
212
211 ## auto save the session to not to use .save() ##
213 ## auto save the session to not to use .save() ##
212 beaker.session.auto = False
214 beaker.session.auto = False
213
215
214 ## default cookie expiration time in seconds `true` expire at browser close ##
216 ## default cookie expiration time in seconds `true` expire at browser close ##
215 #beaker.session.cookie_expires = 3600
217 #beaker.session.cookie_expires = 3600
216
218
217
219
218 ################################################################################
220 ################################################################################
219 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
221 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
220 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
222 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
221 ## execute malicious code after an exception is raised. ##
223 ## execute malicious code after an exception is raised. ##
222 ################################################################################
224 ################################################################################
223 set debug = false
225 set debug = false
224
226
225 ##################################
227 ##################################
226 ### LOGVIEW CONFIG ###
228 ### LOGVIEW CONFIG ###
227 ##################################
229 ##################################
228 logview.sqlalchemy = #faa
230 logview.sqlalchemy = #faa
229 logview.pylons.templating = #bfb
231 logview.pylons.templating = #bfb
230 logview.pylons.util = #eee
232 logview.pylons.util = #eee
231
233
232 #########################################################
234 #########################################################
233 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
235 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
234 #########################################################
236 #########################################################
235 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
237 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
236 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
238 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
237 sqlalchemy.db1.echo = false
239 sqlalchemy.db1.echo = false
238 sqlalchemy.db1.pool_recycle = 3600
240 sqlalchemy.db1.pool_recycle = 3600
239 sqlalchemy.db1.convert_unicode = true
241 sqlalchemy.db1.convert_unicode = true
240
242
241 ################################
243 ################################
242 ### LOGGING CONFIGURATION ####
244 ### LOGGING CONFIGURATION ####
243 ################################
245 ################################
244 [loggers]
246 [loggers]
245 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
247 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
246
248
247 [handlers]
249 [handlers]
248 keys = console, console_sql
250 keys = console, console_sql
249
251
250 [formatters]
252 [formatters]
251 keys = generic, color_formatter, color_formatter_sql
253 keys = generic, color_formatter, color_formatter_sql
252
254
253 #############
255 #############
254 ## LOGGERS ##
256 ## LOGGERS ##
255 #############
257 #############
256 [logger_root]
258 [logger_root]
257 level = NOTSET
259 level = NOTSET
258 handlers = console
260 handlers = console
259
261
260 [logger_routes]
262 [logger_routes]
261 level = DEBUG
263 level = DEBUG
262 handlers =
264 handlers =
263 qualname = routes.middleware
265 qualname = routes.middleware
264 # "level = DEBUG" logs the route matched and routing variables.
266 # "level = DEBUG" logs the route matched and routing variables.
265 propagate = 1
267 propagate = 1
266
268
267 [logger_beaker]
269 [logger_beaker]
268 level = DEBUG
270 level = DEBUG
269 handlers =
271 handlers =
270 qualname = beaker.container
272 qualname = beaker.container
271 propagate = 1
273 propagate = 1
272
274
273 [logger_templates]
275 [logger_templates]
274 level = INFO
276 level = INFO
275 handlers =
277 handlers =
276 qualname = pylons.templating
278 qualname = pylons.templating
277 propagate = 1
279 propagate = 1
278
280
279 [logger_rhodecode]
281 [logger_rhodecode]
280 level = DEBUG
282 level = DEBUG
281 handlers =
283 handlers =
282 qualname = rhodecode
284 qualname = rhodecode
283 propagate = 1
285 propagate = 1
284
286
285 [logger_sqlalchemy]
287 [logger_sqlalchemy]
286 level = INFO
288 level = INFO
287 handlers = console_sql
289 handlers = console_sql
288 qualname = sqlalchemy.engine
290 qualname = sqlalchemy.engine
289 propagate = 0
291 propagate = 0
290
292
291 [logger_whoosh_indexer]
293 [logger_whoosh_indexer]
292 level = DEBUG
294 level = DEBUG
293 handlers =
295 handlers =
294 qualname = whoosh_indexer
296 qualname = whoosh_indexer
295 propagate = 1
297 propagate = 1
296
298
297 ##############
299 ##############
298 ## HANDLERS ##
300 ## HANDLERS ##
299 ##############
301 ##############
300
302
301 [handler_console]
303 [handler_console]
302 class = StreamHandler
304 class = StreamHandler
303 args = (sys.stderr,)
305 args = (sys.stderr,)
304 level = INFO
306 level = INFO
305 formatter = generic
307 formatter = generic
306
308
307 [handler_console_sql]
309 [handler_console_sql]
308 class = StreamHandler
310 class = StreamHandler
309 args = (sys.stderr,)
311 args = (sys.stderr,)
310 level = WARN
312 level = WARN
311 formatter = generic
313 formatter = generic
312
314
313 ################
315 ################
314 ## FORMATTERS ##
316 ## FORMATTERS ##
315 ################
317 ################
316
318
317 [formatter_generic]
319 [formatter_generic]
318 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
320 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
319 datefmt = %Y-%m-%d %H:%M:%S
321 datefmt = %Y-%m-%d %H:%M:%S
320
322
321 [formatter_color_formatter]
323 [formatter_color_formatter]
322 class=rhodecode.lib.colored_formatter.ColorFormatter
324 class=rhodecode.lib.colored_formatter.ColorFormatter
323 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
325 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
324 datefmt = %Y-%m-%d %H:%M:%S
326 datefmt = %Y-%m-%d %H:%M:%S
325
327
326 [formatter_color_formatter_sql]
328 [formatter_color_formatter_sql]
327 class=rhodecode.lib.colored_formatter.ColorFormatterSql
329 class=rhodecode.lib.colored_formatter.ColorFormatterSql
328 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
330 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
329 datefmt = %Y-%m-%d %H:%M:%S
331 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,339 +1,341 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # RhodeCode - Pylons environment configuration #
3 # RhodeCode - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 pdebug = false
10 pdebug = false
11 ################################################################################
11 ################################################################################
12 ## Uncomment and replace with the address which should receive ##
12 ## Uncomment and replace with the address which should receive ##
13 ## any error reports after application crash ##
13 ## any error reports after application crash ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 ################################################################################
15 ################################################################################
16 #email_to = admin@localhost
16 #email_to = admin@localhost
17 #error_email_from = paste_error@localhost
17 #error_email_from = paste_error@localhost
18 #app_email_from = rhodecode-noreply@localhost
18 #app_email_from = rhodecode-noreply@localhost
19 #error_message =
19 #error_message =
20 #email_prefix = [RhodeCode]
20 #email_prefix = [RhodeCode]
21
21
22 #smtp_server = mail.server.com
22 #smtp_server = mail.server.com
23 #smtp_username =
23 #smtp_username =
24 #smtp_password =
24 #smtp_password =
25 #smtp_port =
25 #smtp_port =
26 #smtp_use_tls = false
26 #smtp_use_tls = false
27 #smtp_use_ssl = true
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 #smtp_auth =
29 #smtp_auth =
30
30
31 [server:main]
31 [server:main]
32 ##nr of threads to spawn
32 ##nr of threads to spawn
33 #threadpool_workers = 5
33 #threadpool_workers = 5
34
34
35 ##max request before thread respawn
35 ##max request before thread respawn
36 #threadpool_max_requests = 10
36 #threadpool_max_requests = 10
37
37
38 ##option to use threads of process
38 ##option to use threads of process
39 #use_threadpool = true
39 #use_threadpool = true
40
40
41 #use = egg:Paste#http
41 #use = egg:Paste#http
42 use = egg:waitress#main
42 use = egg:waitress#main
43 host = 127.0.0.1
43 host = 127.0.0.1
44 port = 5000
44 port = 5000
45
45
46 [filter:proxy-prefix]
46 [filter:proxy-prefix]
47 # prefix middleware for rc
47 # prefix middleware for rc
48 use = egg:PasteDeploy#prefix
48 use = egg:PasteDeploy#prefix
49 prefix = /<your-prefix>
49 prefix = /<your-prefix>
50
50
51 [app:main]
51 [app:main]
52 use = egg:rhodecode
52 use = egg:rhodecode
53 #filter-with = proxy-prefix
53 #filter-with = proxy-prefix
54 full_stack = true
54 full_stack = true
55 static_files = true
55 static_files = true
56 # Optional Languages
56 # Optional Languages
57 # en, fr, ja, pt_BR, zh_CN, zh_TW
57 # en, fr, ja, pt_BR, zh_CN, zh_TW
58 lang = en
58 lang = en
59 cache_dir = %(here)s/data
59 cache_dir = %(here)s/data
60 index_dir = %(here)s/data/index
60 index_dir = %(here)s/data/index
61 app_instance_uuid = ${app_instance_uuid}
61 app_instance_uuid = ${app_instance_uuid}
62 cut_off_limit = 256000
62 cut_off_limit = 256000
63 force_https = false
63 force_https = false
64 commit_parse_limit = 50
64 commit_parse_limit = 50
65 use_gravatar = true
65 use_gravatar = true
66
66
67 ## alternative_gravatar_url allows you to use your own avatar server application
67 ## alternative_gravatar_url allows you to use your own avatar server application
68 ## the following parts of the URL will be replaced
68 ## the following parts of the URL will be replaced
69 ## {email} user email
69 ## {email} user email
70 ## {md5email} md5 hash of the user email (like at gravatar.com)
70 ## {md5email} md5 hash of the user email (like at gravatar.com)
71 ## {size} size of the image that is expected from the server application
71 ## {size} size of the image that is expected from the server application
72 ## {scheme} http/https from RhodeCode server
73 ## {netloc} network location from RhodeCode server
72 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
74 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
73 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
75 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
74
76
75 container_auth_enabled = false
77 container_auth_enabled = false
76 proxypass_auth_enabled = false
78 proxypass_auth_enabled = false
77 default_encoding = utf8
79 default_encoding = utf8
78
80
79 ## overwrite schema of clone url
81 ## overwrite schema of clone url
80 ## available vars:
82 ## available vars:
81 ## scheme - http/https
83 ## scheme - http/https
82 ## user - current user
84 ## user - current user
83 ## pass - password
85 ## pass - password
84 ## netloc - network location
86 ## netloc - network location
85 ## path - usually repo_name
87 ## path - usually repo_name
86
88
87 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
89 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
88
90
89 ## issue tracking mapping for commits messages
91 ## issue tracking mapping for commits messages
90 ## comment out issue_pat, issue_server, issue_prefix to enable
92 ## comment out issue_pat, issue_server, issue_prefix to enable
91
93
92 ## pattern to get the issues from commit messages
94 ## pattern to get the issues from commit messages
93 ## default one used here is #<numbers> with a regex passive group for `#`
95 ## default one used here is #<numbers> with a regex passive group for `#`
94 ## {id} will be all groups matched from this pattern
96 ## {id} will be all groups matched from this pattern
95
97
96 issue_pat = (?:\s*#)(\d+)
98 issue_pat = (?:\s*#)(\d+)
97
99
98 ## server url to the issue, each {id} will be replaced with match
100 ## server url to the issue, each {id} will be replaced with match
99 ## fetched from the regex and {repo} is replaced with full repository name
101 ## fetched from the regex and {repo} is replaced with full repository name
100 ## including groups {repo_name} is replaced with just name of repo
102 ## including groups {repo_name} is replaced with just name of repo
101
103
102 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
104 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
103
105
104 ## prefix to add to link to indicate it's an url
106 ## prefix to add to link to indicate it's an url
105 ## #314 will be replaced by <issue_prefix><id>
107 ## #314 will be replaced by <issue_prefix><id>
106
108
107 issue_prefix = #
109 issue_prefix = #
108
110
109 ## instance-id prefix
111 ## instance-id prefix
110 ## a prefix key for this instance used for cache invalidation when running
112 ## a prefix key for this instance used for cache invalidation when running
111 ## multiple instances of rhodecode, make sure it's globally unique for
113 ## multiple instances of rhodecode, make sure it's globally unique for
112 ## all running rhodecode instances. Leave empty if you don't use it
114 ## all running rhodecode instances. Leave empty if you don't use it
113 instance_id =
115 instance_id =
114
116
115 ## alternative return HTTP header for failed authentication. Default HTTP
117 ## alternative return HTTP header for failed authentication. Default HTTP
116 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
118 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
117 ## handling that. Set this variable to 403 to return HTTPForbidden
119 ## handling that. Set this variable to 403 to return HTTPForbidden
118 auth_ret_code =
120 auth_ret_code =
119
121
120 ####################################
122 ####################################
121 ### CELERY CONFIG ####
123 ### CELERY CONFIG ####
122 ####################################
124 ####################################
123 use_celery = false
125 use_celery = false
124 broker.host = localhost
126 broker.host = localhost
125 broker.vhost = rabbitmqhost
127 broker.vhost = rabbitmqhost
126 broker.port = 5672
128 broker.port = 5672
127 broker.user = rabbitmq
129 broker.user = rabbitmq
128 broker.password = qweqwe
130 broker.password = qweqwe
129
131
130 celery.imports = rhodecode.lib.celerylib.tasks
132 celery.imports = rhodecode.lib.celerylib.tasks
131
133
132 celery.result.backend = amqp
134 celery.result.backend = amqp
133 celery.result.dburi = amqp://
135 celery.result.dburi = amqp://
134 celery.result.serialier = json
136 celery.result.serialier = json
135
137
136 #celery.send.task.error.emails = true
138 #celery.send.task.error.emails = true
137 #celery.amqp.task.result.expires = 18000
139 #celery.amqp.task.result.expires = 18000
138
140
139 celeryd.concurrency = 2
141 celeryd.concurrency = 2
140 #celeryd.log.file = celeryd.log
142 #celeryd.log.file = celeryd.log
141 celeryd.log.level = debug
143 celeryd.log.level = debug
142 celeryd.max.tasks.per.child = 1
144 celeryd.max.tasks.per.child = 1
143
145
144 #tasks will never be sent to the queue, but executed locally instead.
146 #tasks will never be sent to the queue, but executed locally instead.
145 celery.always.eager = false
147 celery.always.eager = false
146
148
147 ####################################
149 ####################################
148 ### BEAKER CACHE ####
150 ### BEAKER CACHE ####
149 ####################################
151 ####################################
150 beaker.cache.data_dir=%(here)s/data/cache/data
152 beaker.cache.data_dir=%(here)s/data/cache/data
151 beaker.cache.lock_dir=%(here)s/data/cache/lock
153 beaker.cache.lock_dir=%(here)s/data/cache/lock
152
154
153 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
155 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
154
156
155 beaker.cache.super_short_term.type=memory
157 beaker.cache.super_short_term.type=memory
156 beaker.cache.super_short_term.expire=10
158 beaker.cache.super_short_term.expire=10
157 beaker.cache.super_short_term.key_length = 256
159 beaker.cache.super_short_term.key_length = 256
158
160
159 beaker.cache.short_term.type=memory
161 beaker.cache.short_term.type=memory
160 beaker.cache.short_term.expire=60
162 beaker.cache.short_term.expire=60
161 beaker.cache.short_term.key_length = 256
163 beaker.cache.short_term.key_length = 256
162
164
163 beaker.cache.long_term.type=memory
165 beaker.cache.long_term.type=memory
164 beaker.cache.long_term.expire=36000
166 beaker.cache.long_term.expire=36000
165 beaker.cache.long_term.key_length = 256
167 beaker.cache.long_term.key_length = 256
166
168
167 beaker.cache.sql_cache_short.type=memory
169 beaker.cache.sql_cache_short.type=memory
168 beaker.cache.sql_cache_short.expire=10
170 beaker.cache.sql_cache_short.expire=10
169 beaker.cache.sql_cache_short.key_length = 256
171 beaker.cache.sql_cache_short.key_length = 256
170
172
171 beaker.cache.sql_cache_med.type=memory
173 beaker.cache.sql_cache_med.type=memory
172 beaker.cache.sql_cache_med.expire=360
174 beaker.cache.sql_cache_med.expire=360
173 beaker.cache.sql_cache_med.key_length = 256
175 beaker.cache.sql_cache_med.key_length = 256
174
176
175 beaker.cache.sql_cache_long.type=file
177 beaker.cache.sql_cache_long.type=file
176 beaker.cache.sql_cache_long.expire=3600
178 beaker.cache.sql_cache_long.expire=3600
177 beaker.cache.sql_cache_long.key_length = 256
179 beaker.cache.sql_cache_long.key_length = 256
178
180
179 ####################################
181 ####################################
180 ### BEAKER SESSION ####
182 ### BEAKER SESSION ####
181 ####################################
183 ####################################
182 ## Type of storage used for the session, current types are
184 ## Type of storage used for the session, current types are
183 ## dbm, file, memcached, database, and memory.
185 ## dbm, file, memcached, database, and memory.
184 ## The storage uses the Container API
186 ## The storage uses the Container API
185 ## that is also used by the cache system.
187 ## that is also used by the cache system.
186
188
187 ## db session ##
189 ## db session ##
188 #beaker.session.type = ext:database
190 #beaker.session.type = ext:database
189 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
191 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
190 #beaker.session.table_name = db_session
192 #beaker.session.table_name = db_session
191
193
192 ## encrypted cookie client side session, good for many instances ##
194 ## encrypted cookie client side session, good for many instances ##
193 #beaker.session.type = cookie
195 #beaker.session.type = cookie
194
196
195 ## file based cookies (default) ##
197 ## file based cookies (default) ##
196 #beaker.session.type = file
198 #beaker.session.type = file
197
199
198
200
199 beaker.session.key = rhodecode
201 beaker.session.key = rhodecode
200 ## secure cookie requires AES python libraries ##
202 ## secure cookie requires AES python libraries ##
201 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
203 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
202 #beaker.session.validate_key = 9712sds2212c--zxc123
204 #beaker.session.validate_key = 9712sds2212c--zxc123
203 ## sets session as invalid if it haven't been accessed for given amount of time
205 ## sets session as invalid if it haven't been accessed for given amount of time
204 beaker.session.timeout = 2592000
206 beaker.session.timeout = 2592000
205 beaker.session.httponly = true
207 beaker.session.httponly = true
206 #beaker.session.cookie_path = /<your-prefix>
208 #beaker.session.cookie_path = /<your-prefix>
207
209
208 ## uncomment for https secure cookie ##
210 ## uncomment for https secure cookie ##
209 beaker.session.secure = false
211 beaker.session.secure = false
210
212
211 ## auto save the session to not to use .save() ##
213 ## auto save the session to not to use .save() ##
212 beaker.session.auto = False
214 beaker.session.auto = False
213
215
214 ## default cookie expiration time in seconds `true` expire at browser close ##
216 ## default cookie expiration time in seconds `true` expire at browser close ##
215 #beaker.session.cookie_expires = 3600
217 #beaker.session.cookie_expires = 3600
216
218
217
219
218 ################################################################################
220 ################################################################################
219 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
221 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
220 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
222 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
221 ## execute malicious code after an exception is raised. ##
223 ## execute malicious code after an exception is raised. ##
222 ################################################################################
224 ################################################################################
223 set debug = false
225 set debug = false
224
226
225 ##################################
227 ##################################
226 ### LOGVIEW CONFIG ###
228 ### LOGVIEW CONFIG ###
227 ##################################
229 ##################################
228 logview.sqlalchemy = #faa
230 logview.sqlalchemy = #faa
229 logview.pylons.templating = #bfb
231 logview.pylons.templating = #bfb
230 logview.pylons.util = #eee
232 logview.pylons.util = #eee
231
233
232 #########################################################
234 #########################################################
233 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
235 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
234 #########################################################
236 #########################################################
235
237
236 # SQLITE [default]
238 # SQLITE [default]
237 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
239 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
238
240
239 # POSTGRESQL
241 # POSTGRESQL
240 # sqlalchemy.db1.url = postgresql://user:pass@localhost/rhodecode
242 # sqlalchemy.db1.url = postgresql://user:pass@localhost/rhodecode
241
243
242 # MySQL
244 # MySQL
243 # sqlalchemy.db1.url = mysql://user:pass@localhost/rhodecode
245 # sqlalchemy.db1.url = mysql://user:pass@localhost/rhodecode
244
246
245 # see sqlalchemy docs for others
247 # see sqlalchemy docs for others
246
248
247 sqlalchemy.db1.echo = false
249 sqlalchemy.db1.echo = false
248 sqlalchemy.db1.pool_recycle = 3600
250 sqlalchemy.db1.pool_recycle = 3600
249 sqlalchemy.db1.convert_unicode = true
251 sqlalchemy.db1.convert_unicode = true
250
252
251 ################################
253 ################################
252 ### LOGGING CONFIGURATION ####
254 ### LOGGING CONFIGURATION ####
253 ################################
255 ################################
254 [loggers]
256 [loggers]
255 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
257 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
256
258
257 [handlers]
259 [handlers]
258 keys = console, console_sql
260 keys = console, console_sql
259
261
260 [formatters]
262 [formatters]
261 keys = generic, color_formatter, color_formatter_sql
263 keys = generic, color_formatter, color_formatter_sql
262
264
263 #############
265 #############
264 ## LOGGERS ##
266 ## LOGGERS ##
265 #############
267 #############
266 [logger_root]
268 [logger_root]
267 level = NOTSET
269 level = NOTSET
268 handlers = console
270 handlers = console
269
271
270 [logger_routes]
272 [logger_routes]
271 level = DEBUG
273 level = DEBUG
272 handlers =
274 handlers =
273 qualname = routes.middleware
275 qualname = routes.middleware
274 # "level = DEBUG" logs the route matched and routing variables.
276 # "level = DEBUG" logs the route matched and routing variables.
275 propagate = 1
277 propagate = 1
276
278
277 [logger_beaker]
279 [logger_beaker]
278 level = DEBUG
280 level = DEBUG
279 handlers =
281 handlers =
280 qualname = beaker.container
282 qualname = beaker.container
281 propagate = 1
283 propagate = 1
282
284
283 [logger_templates]
285 [logger_templates]
284 level = INFO
286 level = INFO
285 handlers =
287 handlers =
286 qualname = pylons.templating
288 qualname = pylons.templating
287 propagate = 1
289 propagate = 1
288
290
289 [logger_rhodecode]
291 [logger_rhodecode]
290 level = DEBUG
292 level = DEBUG
291 handlers =
293 handlers =
292 qualname = rhodecode
294 qualname = rhodecode
293 propagate = 1
295 propagate = 1
294
296
295 [logger_sqlalchemy]
297 [logger_sqlalchemy]
296 level = INFO
298 level = INFO
297 handlers = console_sql
299 handlers = console_sql
298 qualname = sqlalchemy.engine
300 qualname = sqlalchemy.engine
299 propagate = 0
301 propagate = 0
300
302
301 [logger_whoosh_indexer]
303 [logger_whoosh_indexer]
302 level = DEBUG
304 level = DEBUG
303 handlers =
305 handlers =
304 qualname = whoosh_indexer
306 qualname = whoosh_indexer
305 propagate = 1
307 propagate = 1
306
308
307 ##############
309 ##############
308 ## HANDLERS ##
310 ## HANDLERS ##
309 ##############
311 ##############
310
312
311 [handler_console]
313 [handler_console]
312 class = StreamHandler
314 class = StreamHandler
313 args = (sys.stderr,)
315 args = (sys.stderr,)
314 level = INFO
316 level = INFO
315 formatter = generic
317 formatter = generic
316
318
317 [handler_console_sql]
319 [handler_console_sql]
318 class = StreamHandler
320 class = StreamHandler
319 args = (sys.stderr,)
321 args = (sys.stderr,)
320 level = WARN
322 level = WARN
321 formatter = generic
323 formatter = generic
322
324
323 ################
325 ################
324 ## FORMATTERS ##
326 ## FORMATTERS ##
325 ################
327 ################
326
328
327 [formatter_generic]
329 [formatter_generic]
328 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
330 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
329 datefmt = %Y-%m-%d %H:%M:%S
331 datefmt = %Y-%m-%d %H:%M:%S
330
332
331 [formatter_color_formatter]
333 [formatter_color_formatter]
332 class=rhodecode.lib.colored_formatter.ColorFormatter
334 class=rhodecode.lib.colored_formatter.ColorFormatter
333 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
335 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
334 datefmt = %Y-%m-%d %H:%M:%S
336 datefmt = %Y-%m-%d %H:%M:%S
335
337
336 [formatter_color_formatter_sql]
338 [formatter_color_formatter_sql]
337 class=rhodecode.lib.colored_formatter.ColorFormatterSql
339 class=rhodecode.lib.colored_formatter.ColorFormatterSql
338 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
340 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
339 datefmt = %Y-%m-%d %H:%M:%S
341 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,1072 +1,1077 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 import random
6 import random
7 import hashlib
7 import hashlib
8 import StringIO
8 import StringIO
9 import urllib
9 import urllib
10 import math
10 import math
11 import logging
11 import logging
12 import re
12 import re
13 import urlparse
13
14
14 from datetime import datetime
15 from datetime import datetime
15 from pygments.formatters.html import HtmlFormatter
16 from pygments.formatters.html import HtmlFormatter
16 from pygments import highlight as code_highlight
17 from pygments import highlight as code_highlight
17 from pylons import url, request, config
18 from pylons import url, request, config
18 from pylons.i18n.translation import _, ungettext
19 from pylons.i18n.translation import _, ungettext
19 from hashlib import md5
20 from hashlib import md5
20
21
21 from webhelpers.html import literal, HTML, escape
22 from webhelpers.html import literal, HTML, escape
22 from webhelpers.html.tools import *
23 from webhelpers.html.tools import *
23 from webhelpers.html.builder import make_tag
24 from webhelpers.html.builder import make_tag
24 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
25 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
25 end_form, file, form, hidden, image, javascript_link, link_to, \
26 end_form, file, form, hidden, image, javascript_link, link_to, \
26 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
27 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
27 submit, text, password, textarea, title, ul, xml_declaration, radio
28 submit, text, password, textarea, title, ul, xml_declaration, radio
28 from webhelpers.html.tools import auto_link, button_to, highlight, \
29 from webhelpers.html.tools import auto_link, button_to, highlight, \
29 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
30 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
30 from webhelpers.number import format_byte_size, format_bit_size
31 from webhelpers.number import format_byte_size, format_bit_size
31 from webhelpers.pylonslib import Flash as _Flash
32 from webhelpers.pylonslib import Flash as _Flash
32 from webhelpers.pylonslib.secure_form import secure_form
33 from webhelpers.pylonslib.secure_form import secure_form
33 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
34 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
34 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
35 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
35 replace_whitespace, urlify, truncate, wrap_paragraphs
36 replace_whitespace, urlify, truncate, wrap_paragraphs
36 from webhelpers.date import time_ago_in_words
37 from webhelpers.date import time_ago_in_words
37 from webhelpers.paginate import Page
38 from webhelpers.paginate import Page
38 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
39 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
39 convert_boolean_attrs, NotGiven, _make_safe_id_component
40 convert_boolean_attrs, NotGiven, _make_safe_id_component
40
41
41 from rhodecode.lib.annotate import annotate_highlight
42 from rhodecode.lib.annotate import annotate_highlight
42 from rhodecode.lib.utils import repo_name_slug
43 from rhodecode.lib.utils import repo_name_slug
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
44 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
44 get_changeset_safe, datetime_to_time, time_to_datetime
45 get_changeset_safe, datetime_to_time, time_to_datetime
45 from rhodecode.lib.markup_renderer import MarkupRenderer
46 from rhodecode.lib.markup_renderer import MarkupRenderer
46 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
47 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
49 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
49 from rhodecode.model.changeset_status import ChangesetStatusModel
50 from rhodecode.model.changeset_status import ChangesetStatusModel
50 from rhodecode.model.db import URL_SEP, Permission
51 from rhodecode.model.db import URL_SEP, Permission
51
52
52 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
53
54
54
55
55 html_escape_table = {
56 html_escape_table = {
56 "&": "&amp;",
57 "&": "&amp;",
57 '"': "&quot;",
58 '"': "&quot;",
58 "'": "&apos;",
59 "'": "&apos;",
59 ">": "&gt;",
60 ">": "&gt;",
60 "<": "&lt;",
61 "<": "&lt;",
61 }
62 }
62
63
63
64
64 def html_escape(text):
65 def html_escape(text):
65 """Produce entities within text."""
66 """Produce entities within text."""
66 return "".join(html_escape_table.get(c,c) for c in text)
67 return "".join(html_escape_table.get(c,c) for c in text)
67
68
68
69
69 def shorter(text, size=20):
70 def shorter(text, size=20):
70 postfix = '...'
71 postfix = '...'
71 if len(text) > size:
72 if len(text) > size:
72 return text[:size - len(postfix)] + postfix
73 return text[:size - len(postfix)] + postfix
73 return text
74 return text
74
75
75
76
76 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
77 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
77 """
78 """
78 Reset button
79 Reset button
79 """
80 """
80 _set_input_attrs(attrs, type, name, value)
81 _set_input_attrs(attrs, type, name, value)
81 _set_id_attr(attrs, id, name)
82 _set_id_attr(attrs, id, name)
82 convert_boolean_attrs(attrs, ["disabled"])
83 convert_boolean_attrs(attrs, ["disabled"])
83 return HTML.input(**attrs)
84 return HTML.input(**attrs)
84
85
85 reset = _reset
86 reset = _reset
86 safeid = _make_safe_id_component
87 safeid = _make_safe_id_component
87
88
88
89
89 def FID(raw_id, path):
90 def FID(raw_id, path):
90 """
91 """
91 Creates a uniqe ID for filenode based on it's hash of path and revision
92 Creates a uniqe ID for filenode based on it's hash of path and revision
92 it's safe to use in urls
93 it's safe to use in urls
93
94
94 :param raw_id:
95 :param raw_id:
95 :param path:
96 :param path:
96 """
97 """
97
98
98 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
99 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
99
100
100
101
101 def get_token():
102 def get_token():
102 """Return the current authentication token, creating one if one doesn't
103 """Return the current authentication token, creating one if one doesn't
103 already exist.
104 already exist.
104 """
105 """
105 token_key = "_authentication_token"
106 token_key = "_authentication_token"
106 from pylons import session
107 from pylons import session
107 if not token_key in session:
108 if not token_key in session:
108 try:
109 try:
109 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
110 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
110 except AttributeError: # Python < 2.4
111 except AttributeError: # Python < 2.4
111 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
112 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
112 session[token_key] = token
113 session[token_key] = token
113 if hasattr(session, 'save'):
114 if hasattr(session, 'save'):
114 session.save()
115 session.save()
115 return session[token_key]
116 return session[token_key]
116
117
117
118
118 class _GetError(object):
119 class _GetError(object):
119 """Get error from form_errors, and represent it as span wrapped error
120 """Get error from form_errors, and represent it as span wrapped error
120 message
121 message
121
122
122 :param field_name: field to fetch errors for
123 :param field_name: field to fetch errors for
123 :param form_errors: form errors dict
124 :param form_errors: form errors dict
124 """
125 """
125
126
126 def __call__(self, field_name, form_errors):
127 def __call__(self, field_name, form_errors):
127 tmpl = """<span class="error_msg">%s</span>"""
128 tmpl = """<span class="error_msg">%s</span>"""
128 if form_errors and field_name in form_errors:
129 if form_errors and field_name in form_errors:
129 return literal(tmpl % form_errors.get(field_name))
130 return literal(tmpl % form_errors.get(field_name))
130
131
131 get_error = _GetError()
132 get_error = _GetError()
132
133
133
134
134 class _ToolTip(object):
135 class _ToolTip(object):
135
136
136 def __call__(self, tooltip_title, trim_at=50):
137 def __call__(self, tooltip_title, trim_at=50):
137 """
138 """
138 Special function just to wrap our text into nice formatted
139 Special function just to wrap our text into nice formatted
139 autowrapped text
140 autowrapped text
140
141
141 :param tooltip_title:
142 :param tooltip_title:
142 """
143 """
143 tooltip_title = escape(tooltip_title)
144 tooltip_title = escape(tooltip_title)
144 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
145 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
145 return tooltip_title
146 return tooltip_title
146 tooltip = _ToolTip()
147 tooltip = _ToolTip()
147
148
148
149
149 class _FilesBreadCrumbs(object):
150 class _FilesBreadCrumbs(object):
150
151
151 def __call__(self, repo_name, rev, paths):
152 def __call__(self, repo_name, rev, paths):
152 if isinstance(paths, str):
153 if isinstance(paths, str):
153 paths = safe_unicode(paths)
154 paths = safe_unicode(paths)
154 url_l = [link_to(repo_name, url('files_home',
155 url_l = [link_to(repo_name, url('files_home',
155 repo_name=repo_name,
156 repo_name=repo_name,
156 revision=rev, f_path=''),
157 revision=rev, f_path=''),
157 class_='ypjax-link')]
158 class_='ypjax-link')]
158 paths_l = paths.split('/')
159 paths_l = paths.split('/')
159 for cnt, p in enumerate(paths_l):
160 for cnt, p in enumerate(paths_l):
160 if p != '':
161 if p != '':
161 url_l.append(link_to(p,
162 url_l.append(link_to(p,
162 url('files_home',
163 url('files_home',
163 repo_name=repo_name,
164 repo_name=repo_name,
164 revision=rev,
165 revision=rev,
165 f_path='/'.join(paths_l[:cnt + 1])
166 f_path='/'.join(paths_l[:cnt + 1])
166 ),
167 ),
167 class_='ypjax-link'
168 class_='ypjax-link'
168 )
169 )
169 )
170 )
170
171
171 return literal('/'.join(url_l))
172 return literal('/'.join(url_l))
172
173
173 files_breadcrumbs = _FilesBreadCrumbs()
174 files_breadcrumbs = _FilesBreadCrumbs()
174
175
175
176
176 class CodeHtmlFormatter(HtmlFormatter):
177 class CodeHtmlFormatter(HtmlFormatter):
177 """
178 """
178 My code Html Formatter for source codes
179 My code Html Formatter for source codes
179 """
180 """
180
181
181 def wrap(self, source, outfile):
182 def wrap(self, source, outfile):
182 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
183 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
183
184
184 def _wrap_code(self, source):
185 def _wrap_code(self, source):
185 for cnt, it in enumerate(source):
186 for cnt, it in enumerate(source):
186 i, t = it
187 i, t = it
187 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
188 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
188 yield i, t
189 yield i, t
189
190
190 def _wrap_tablelinenos(self, inner):
191 def _wrap_tablelinenos(self, inner):
191 dummyoutfile = StringIO.StringIO()
192 dummyoutfile = StringIO.StringIO()
192 lncount = 0
193 lncount = 0
193 for t, line in inner:
194 for t, line in inner:
194 if t:
195 if t:
195 lncount += 1
196 lncount += 1
196 dummyoutfile.write(line)
197 dummyoutfile.write(line)
197
198
198 fl = self.linenostart
199 fl = self.linenostart
199 mw = len(str(lncount + fl - 1))
200 mw = len(str(lncount + fl - 1))
200 sp = self.linenospecial
201 sp = self.linenospecial
201 st = self.linenostep
202 st = self.linenostep
202 la = self.lineanchors
203 la = self.lineanchors
203 aln = self.anchorlinenos
204 aln = self.anchorlinenos
204 nocls = self.noclasses
205 nocls = self.noclasses
205 if sp:
206 if sp:
206 lines = []
207 lines = []
207
208
208 for i in range(fl, fl + lncount):
209 for i in range(fl, fl + lncount):
209 if i % st == 0:
210 if i % st == 0:
210 if i % sp == 0:
211 if i % sp == 0:
211 if aln:
212 if aln:
212 lines.append('<a href="#%s%d" class="special">%*d</a>' %
213 lines.append('<a href="#%s%d" class="special">%*d</a>' %
213 (la, i, mw, i))
214 (la, i, mw, i))
214 else:
215 else:
215 lines.append('<span class="special">%*d</span>' % (mw, i))
216 lines.append('<span class="special">%*d</span>' % (mw, i))
216 else:
217 else:
217 if aln:
218 if aln:
218 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
219 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
219 else:
220 else:
220 lines.append('%*d' % (mw, i))
221 lines.append('%*d' % (mw, i))
221 else:
222 else:
222 lines.append('')
223 lines.append('')
223 ls = '\n'.join(lines)
224 ls = '\n'.join(lines)
224 else:
225 else:
225 lines = []
226 lines = []
226 for i in range(fl, fl + lncount):
227 for i in range(fl, fl + lncount):
227 if i % st == 0:
228 if i % st == 0:
228 if aln:
229 if aln:
229 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
230 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
230 else:
231 else:
231 lines.append('%*d' % (mw, i))
232 lines.append('%*d' % (mw, i))
232 else:
233 else:
233 lines.append('')
234 lines.append('')
234 ls = '\n'.join(lines)
235 ls = '\n'.join(lines)
235
236
236 # in case you wonder about the seemingly redundant <div> here: since the
237 # in case you wonder about the seemingly redundant <div> here: since the
237 # content in the other cell also is wrapped in a div, some browsers in
238 # content in the other cell also is wrapped in a div, some browsers in
238 # some configurations seem to mess up the formatting...
239 # some configurations seem to mess up the formatting...
239 if nocls:
240 if nocls:
240 yield 0, ('<table class="%stable">' % self.cssclass +
241 yield 0, ('<table class="%stable">' % self.cssclass +
241 '<tr><td><div class="linenodiv" '
242 '<tr><td><div class="linenodiv" '
242 'style="background-color: #f0f0f0; padding-right: 10px">'
243 'style="background-color: #f0f0f0; padding-right: 10px">'
243 '<pre style="line-height: 125%">' +
244 '<pre style="line-height: 125%">' +
244 ls + '</pre></div></td><td id="hlcode" class="code">')
245 ls + '</pre></div></td><td id="hlcode" class="code">')
245 else:
246 else:
246 yield 0, ('<table class="%stable">' % self.cssclass +
247 yield 0, ('<table class="%stable">' % self.cssclass +
247 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
248 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
248 ls + '</pre></div></td><td id="hlcode" class="code">')
249 ls + '</pre></div></td><td id="hlcode" class="code">')
249 yield 0, dummyoutfile.getvalue()
250 yield 0, dummyoutfile.getvalue()
250 yield 0, '</td></tr></table>'
251 yield 0, '</td></tr></table>'
251
252
252
253
253 def pygmentize(filenode, **kwargs):
254 def pygmentize(filenode, **kwargs):
254 """pygmentize function using pygments
255 """pygmentize function using pygments
255
256
256 :param filenode:
257 :param filenode:
257 """
258 """
258
259
259 return literal(code_highlight(filenode.content,
260 return literal(code_highlight(filenode.content,
260 filenode.lexer, CodeHtmlFormatter(**kwargs)))
261 filenode.lexer, CodeHtmlFormatter(**kwargs)))
261
262
262
263
263 def pygmentize_annotation(repo_name, filenode, **kwargs):
264 def pygmentize_annotation(repo_name, filenode, **kwargs):
264 """
265 """
265 pygmentize function for annotation
266 pygmentize function for annotation
266
267
267 :param filenode:
268 :param filenode:
268 """
269 """
269
270
270 color_dict = {}
271 color_dict = {}
271
272
272 def gen_color(n=10000):
273 def gen_color(n=10000):
273 """generator for getting n of evenly distributed colors using
274 """generator for getting n of evenly distributed colors using
274 hsv color and golden ratio. It always return same order of colors
275 hsv color and golden ratio. It always return same order of colors
275
276
276 :returns: RGB tuple
277 :returns: RGB tuple
277 """
278 """
278
279
279 def hsv_to_rgb(h, s, v):
280 def hsv_to_rgb(h, s, v):
280 if s == 0.0:
281 if s == 0.0:
281 return v, v, v
282 return v, v, v
282 i = int(h * 6.0) # XXX assume int() truncates!
283 i = int(h * 6.0) # XXX assume int() truncates!
283 f = (h * 6.0) - i
284 f = (h * 6.0) - i
284 p = v * (1.0 - s)
285 p = v * (1.0 - s)
285 q = v * (1.0 - s * f)
286 q = v * (1.0 - s * f)
286 t = v * (1.0 - s * (1.0 - f))
287 t = v * (1.0 - s * (1.0 - f))
287 i = i % 6
288 i = i % 6
288 if i == 0:
289 if i == 0:
289 return v, t, p
290 return v, t, p
290 if i == 1:
291 if i == 1:
291 return q, v, p
292 return q, v, p
292 if i == 2:
293 if i == 2:
293 return p, v, t
294 return p, v, t
294 if i == 3:
295 if i == 3:
295 return p, q, v
296 return p, q, v
296 if i == 4:
297 if i == 4:
297 return t, p, v
298 return t, p, v
298 if i == 5:
299 if i == 5:
299 return v, p, q
300 return v, p, q
300
301
301 golden_ratio = 0.618033988749895
302 golden_ratio = 0.618033988749895
302 h = 0.22717784590367374
303 h = 0.22717784590367374
303
304
304 for _ in xrange(n):
305 for _ in xrange(n):
305 h += golden_ratio
306 h += golden_ratio
306 h %= 1
307 h %= 1
307 HSV_tuple = [h, 0.95, 0.95]
308 HSV_tuple = [h, 0.95, 0.95]
308 RGB_tuple = hsv_to_rgb(*HSV_tuple)
309 RGB_tuple = hsv_to_rgb(*HSV_tuple)
309 yield map(lambda x: str(int(x * 256)), RGB_tuple)
310 yield map(lambda x: str(int(x * 256)), RGB_tuple)
310
311
311 cgenerator = gen_color()
312 cgenerator = gen_color()
312
313
313 def get_color_string(cs):
314 def get_color_string(cs):
314 if cs in color_dict:
315 if cs in color_dict:
315 col = color_dict[cs]
316 col = color_dict[cs]
316 else:
317 else:
317 col = color_dict[cs] = cgenerator.next()
318 col = color_dict[cs] = cgenerator.next()
318 return "color: rgb(%s)! important;" % (', '.join(col))
319 return "color: rgb(%s)! important;" % (', '.join(col))
319
320
320 def url_func(repo_name):
321 def url_func(repo_name):
321
322
322 def _url_func(changeset):
323 def _url_func(changeset):
323 author = changeset.author
324 author = changeset.author
324 date = changeset.date
325 date = changeset.date
325 message = tooltip(changeset.message)
326 message = tooltip(changeset.message)
326
327
327 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
328 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
328 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
329 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
329 "</b> %s<br/></div>")
330 "</b> %s<br/></div>")
330
331
331 tooltip_html = tooltip_html % (author, date, message)
332 tooltip_html = tooltip_html % (author, date, message)
332 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
333 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
333 short_id(changeset.raw_id))
334 short_id(changeset.raw_id))
334 uri = link_to(
335 uri = link_to(
335 lnk_format,
336 lnk_format,
336 url('changeset_home', repo_name=repo_name,
337 url('changeset_home', repo_name=repo_name,
337 revision=changeset.raw_id),
338 revision=changeset.raw_id),
338 style=get_color_string(changeset.raw_id),
339 style=get_color_string(changeset.raw_id),
339 class_='tooltip',
340 class_='tooltip',
340 title=tooltip_html
341 title=tooltip_html
341 )
342 )
342
343
343 uri += '\n'
344 uri += '\n'
344 return uri
345 return uri
345 return _url_func
346 return _url_func
346
347
347 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
348 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
348
349
349
350
350 def is_following_repo(repo_name, user_id):
351 def is_following_repo(repo_name, user_id):
351 from rhodecode.model.scm import ScmModel
352 from rhodecode.model.scm import ScmModel
352 return ScmModel().is_following_repo(repo_name, user_id)
353 return ScmModel().is_following_repo(repo_name, user_id)
353
354
354 flash = _Flash()
355 flash = _Flash()
355
356
356 #==============================================================================
357 #==============================================================================
357 # SCM FILTERS available via h.
358 # SCM FILTERS available via h.
358 #==============================================================================
359 #==============================================================================
359 from rhodecode.lib.vcs.utils import author_name, author_email
360 from rhodecode.lib.vcs.utils import author_name, author_email
360 from rhodecode.lib.utils2 import credentials_filter, age as _age
361 from rhodecode.lib.utils2 import credentials_filter, age as _age
361 from rhodecode.model.db import User, ChangesetStatus
362 from rhodecode.model.db import User, ChangesetStatus
362
363
363 age = lambda x: _age(x)
364 age = lambda x: _age(x)
364 capitalize = lambda x: x.capitalize()
365 capitalize = lambda x: x.capitalize()
365 email = author_email
366 email = author_email
366 short_id = lambda x: x[:12]
367 short_id = lambda x: x[:12]
367 hide_credentials = lambda x: ''.join(credentials_filter(x))
368 hide_credentials = lambda x: ''.join(credentials_filter(x))
368
369
369
370
370 def fmt_date(date):
371 def fmt_date(date):
371 if date:
372 if date:
372 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
373 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
373 return date.strftime(_fmt).decode('utf8')
374 return date.strftime(_fmt).decode('utf8')
374
375
375 return ""
376 return ""
376
377
377
378
378 def is_git(repository):
379 def is_git(repository):
379 if hasattr(repository, 'alias'):
380 if hasattr(repository, 'alias'):
380 _type = repository.alias
381 _type = repository.alias
381 elif hasattr(repository, 'repo_type'):
382 elif hasattr(repository, 'repo_type'):
382 _type = repository.repo_type
383 _type = repository.repo_type
383 else:
384 else:
384 _type = repository
385 _type = repository
385 return _type == 'git'
386 return _type == 'git'
386
387
387
388
388 def is_hg(repository):
389 def is_hg(repository):
389 if hasattr(repository, 'alias'):
390 if hasattr(repository, 'alias'):
390 _type = repository.alias
391 _type = repository.alias
391 elif hasattr(repository, 'repo_type'):
392 elif hasattr(repository, 'repo_type'):
392 _type = repository.repo_type
393 _type = repository.repo_type
393 else:
394 else:
394 _type = repository
395 _type = repository
395 return _type == 'hg'
396 return _type == 'hg'
396
397
397
398
398 def email_or_none(author):
399 def email_or_none(author):
399 # extract email from the commit string
400 # extract email from the commit string
400 _email = email(author)
401 _email = email(author)
401 if _email != '':
402 if _email != '':
402 # check it against RhodeCode database, and use the MAIN email for this
403 # check it against RhodeCode database, and use the MAIN email for this
403 # user
404 # user
404 user = User.get_by_email(_email, case_insensitive=True, cache=True)
405 user = User.get_by_email(_email, case_insensitive=True, cache=True)
405 if user is not None:
406 if user is not None:
406 return user.email
407 return user.email
407 return _email
408 return _email
408
409
409 # See if it contains a username we can get an email from
410 # See if it contains a username we can get an email from
410 user = User.get_by_username(author_name(author), case_insensitive=True,
411 user = User.get_by_username(author_name(author), case_insensitive=True,
411 cache=True)
412 cache=True)
412 if user is not None:
413 if user is not None:
413 return user.email
414 return user.email
414
415
415 # No valid email, not a valid user in the system, none!
416 # No valid email, not a valid user in the system, none!
416 return None
417 return None
417
418
418
419
419 def person(author, show_attr="username_and_name"):
420 def person(author, show_attr="username_and_name"):
420 # attr to return from fetched user
421 # attr to return from fetched user
421 person_getter = lambda usr: getattr(usr, show_attr)
422 person_getter = lambda usr: getattr(usr, show_attr)
422
423
423 # Valid email in the attribute passed, see if they're in the system
424 # Valid email in the attribute passed, see if they're in the system
424 _email = email(author)
425 _email = email(author)
425 if _email != '':
426 if _email != '':
426 user = User.get_by_email(_email, case_insensitive=True, cache=True)
427 user = User.get_by_email(_email, case_insensitive=True, cache=True)
427 if user is not None:
428 if user is not None:
428 return person_getter(user)
429 return person_getter(user)
429 return _email
430 return _email
430
431
431 # Maybe it's a username?
432 # Maybe it's a username?
432 _author = author_name(author)
433 _author = author_name(author)
433 user = User.get_by_username(_author, case_insensitive=True,
434 user = User.get_by_username(_author, case_insensitive=True,
434 cache=True)
435 cache=True)
435 if user is not None:
436 if user is not None:
436 return person_getter(user)
437 return person_getter(user)
437
438
438 # Still nothing? Just pass back the author name then
439 # Still nothing? Just pass back the author name then
439 return _author
440 return _author
440
441
441
442
442 def person_by_id(id_, show_attr="username_and_name"):
443 def person_by_id(id_, show_attr="username_and_name"):
443 # attr to return from fetched user
444 # attr to return from fetched user
444 person_getter = lambda usr: getattr(usr, show_attr)
445 person_getter = lambda usr: getattr(usr, show_attr)
445
446
446 #maybe it's an ID ?
447 #maybe it's an ID ?
447 if str(id_).isdigit() or isinstance(id_, int):
448 if str(id_).isdigit() or isinstance(id_, int):
448 id_ = int(id_)
449 id_ = int(id_)
449 user = User.get(id_)
450 user = User.get(id_)
450 if user is not None:
451 if user is not None:
451 return person_getter(user)
452 return person_getter(user)
452 return id_
453 return id_
453
454
454
455
455 def desc_stylize(value):
456 def desc_stylize(value):
456 """
457 """
457 converts tags from value into html equivalent
458 converts tags from value into html equivalent
458
459
459 :param value:
460 :param value:
460 """
461 """
461 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
462 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
462 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
463 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
463 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
464 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
464 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
465 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
465 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z\-\/]*)\]',
466 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z\-\/]*)\]',
466 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
467 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
467 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/]*)\]',
468 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/]*)\]',
468 '<div class="metatag" tag="lang">\\2</div>', value)
469 '<div class="metatag" tag="lang">\\2</div>', value)
469 value = re.sub(r'\[([a-z]+)\]',
470 value = re.sub(r'\[([a-z]+)\]',
470 '<div class="metatag" tag="\\1">\\1</div>', value)
471 '<div class="metatag" tag="\\1">\\1</div>', value)
471
472
472 return value
473 return value
473
474
474
475
475 def bool2icon(value):
476 def bool2icon(value):
476 """Returns True/False values represented as small html image of true/false
477 """Returns True/False values represented as small html image of true/false
477 icons
478 icons
478
479
479 :param value: bool value
480 :param value: bool value
480 """
481 """
481
482
482 if value is True:
483 if value is True:
483 return HTML.tag('img', src=url("/images/icons/accept.png"),
484 return HTML.tag('img', src=url("/images/icons/accept.png"),
484 alt=_('True'))
485 alt=_('True'))
485
486
486 if value is False:
487 if value is False:
487 return HTML.tag('img', src=url("/images/icons/cancel.png"),
488 return HTML.tag('img', src=url("/images/icons/cancel.png"),
488 alt=_('False'))
489 alt=_('False'))
489
490
490 return value
491 return value
491
492
492
493
493 def action_parser(user_log, feed=False):
494 def action_parser(user_log, feed=False):
494 """
495 """
495 This helper will action_map the specified string action into translated
496 This helper will action_map the specified string action into translated
496 fancy names with icons and links
497 fancy names with icons and links
497
498
498 :param user_log: user log instance
499 :param user_log: user log instance
499 :param feed: use output for feeds (no html and fancy icons)
500 :param feed: use output for feeds (no html and fancy icons)
500 """
501 """
501
502
502 action = user_log.action
503 action = user_log.action
503 action_params = ' '
504 action_params = ' '
504
505
505 x = action.split(':')
506 x = action.split(':')
506
507
507 if len(x) > 1:
508 if len(x) > 1:
508 action, action_params = x
509 action, action_params = x
509
510
510 def get_cs_links():
511 def get_cs_links():
511 revs_limit = 3 # display this amount always
512 revs_limit = 3 # display this amount always
512 revs_top_limit = 50 # show upto this amount of changesets hidden
513 revs_top_limit = 50 # show upto this amount of changesets hidden
513 revs_ids = action_params.split(',')
514 revs_ids = action_params.split(',')
514 deleted = user_log.repository is None
515 deleted = user_log.repository is None
515 if deleted:
516 if deleted:
516 return ','.join(revs_ids)
517 return ','.join(revs_ids)
517
518
518 repo_name = user_log.repository.repo_name
519 repo_name = user_log.repository.repo_name
519
520
520 repo = user_log.repository.scm_instance
521 repo = user_log.repository.scm_instance
521
522
522 def lnk(rev, repo_name):
523 def lnk(rev, repo_name):
523
524
524 if isinstance(rev, BaseChangeset):
525 if isinstance(rev, BaseChangeset):
525 lbl = 'r%s:%s' % (rev.revision, rev.short_id)
526 lbl = 'r%s:%s' % (rev.revision, rev.short_id)
526 _url = url('changeset_home', repo_name=repo_name,
527 _url = url('changeset_home', repo_name=repo_name,
527 revision=rev.raw_id)
528 revision=rev.raw_id)
528 title = tooltip(rev.message)
529 title = tooltip(rev.message)
529 else:
530 else:
530 lbl = '%s' % rev
531 lbl = '%s' % rev
531 _url = '#'
532 _url = '#'
532 title = _('Changeset not found')
533 title = _('Changeset not found')
533
534
534 return link_to(lbl, _url, title=title, class_='tooltip',)
535 return link_to(lbl, _url, title=title, class_='tooltip',)
535
536
536 revs = []
537 revs = []
537 if len(filter(lambda v: v != '', revs_ids)) > 0:
538 if len(filter(lambda v: v != '', revs_ids)) > 0:
538 for rev in revs_ids[:revs_top_limit]:
539 for rev in revs_ids[:revs_top_limit]:
539 try:
540 try:
540 rev = repo.get_changeset(rev)
541 rev = repo.get_changeset(rev)
541 revs.append(rev)
542 revs.append(rev)
542 except ChangesetDoesNotExistError:
543 except ChangesetDoesNotExistError:
543 log.error('cannot find revision %s in this repo' % rev)
544 log.error('cannot find revision %s in this repo' % rev)
544 revs.append(rev)
545 revs.append(rev)
545 continue
546 continue
546 cs_links = []
547 cs_links = []
547 cs_links.append(" " + ', '.join(
548 cs_links.append(" " + ', '.join(
548 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
549 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
549 )
550 )
550 )
551 )
551
552
552 compare_view = (
553 compare_view = (
553 ' <div class="compare_view tooltip" title="%s">'
554 ' <div class="compare_view tooltip" title="%s">'
554 '<a href="%s">%s</a> </div>' % (
555 '<a href="%s">%s</a> </div>' % (
555 _('Show all combined changesets %s->%s') % (
556 _('Show all combined changesets %s->%s') % (
556 revs_ids[0], revs_ids[-1]
557 revs_ids[0], revs_ids[-1]
557 ),
558 ),
558 url('changeset_home', repo_name=repo_name,
559 url('changeset_home', repo_name=repo_name,
559 revision='%s...%s' % (revs_ids[0], revs_ids[-1])
560 revision='%s...%s' % (revs_ids[0], revs_ids[-1])
560 ),
561 ),
561 _('compare view')
562 _('compare view')
562 )
563 )
563 )
564 )
564
565
565 # if we have exactly one more than normally displayed
566 # if we have exactly one more than normally displayed
566 # just display it, takes less space than displaying
567 # just display it, takes less space than displaying
567 # "and 1 more revisions"
568 # "and 1 more revisions"
568 if len(revs_ids) == revs_limit + 1:
569 if len(revs_ids) == revs_limit + 1:
569 rev = revs[revs_limit]
570 rev = revs[revs_limit]
570 cs_links.append(", " + lnk(rev, repo_name))
571 cs_links.append(", " + lnk(rev, repo_name))
571
572
572 # hidden-by-default ones
573 # hidden-by-default ones
573 if len(revs_ids) > revs_limit + 1:
574 if len(revs_ids) > revs_limit + 1:
574 uniq_id = revs_ids[0]
575 uniq_id = revs_ids[0]
575 html_tmpl = (
576 html_tmpl = (
576 '<span> %s <a class="show_more" id="_%s" '
577 '<span> %s <a class="show_more" id="_%s" '
577 'href="#more">%s</a> %s</span>'
578 'href="#more">%s</a> %s</span>'
578 )
579 )
579 if not feed:
580 if not feed:
580 cs_links.append(html_tmpl % (
581 cs_links.append(html_tmpl % (
581 _('and'),
582 _('and'),
582 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
583 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
583 _('revisions')
584 _('revisions')
584 )
585 )
585 )
586 )
586
587
587 if not feed:
588 if not feed:
588 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
589 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
589 else:
590 else:
590 html_tmpl = '<span id="%s"> %s </span>'
591 html_tmpl = '<span id="%s"> %s </span>'
591
592
592 morelinks = ', '.join(
593 morelinks = ', '.join(
593 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
594 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
594 )
595 )
595
596
596 if len(revs_ids) > revs_top_limit:
597 if len(revs_ids) > revs_top_limit:
597 morelinks += ', ...'
598 morelinks += ', ...'
598
599
599 cs_links.append(html_tmpl % (uniq_id, morelinks))
600 cs_links.append(html_tmpl % (uniq_id, morelinks))
600 if len(revs) > 1:
601 if len(revs) > 1:
601 cs_links.append(compare_view)
602 cs_links.append(compare_view)
602 return ''.join(cs_links)
603 return ''.join(cs_links)
603
604
604 def get_fork_name():
605 def get_fork_name():
605 repo_name = action_params
606 repo_name = action_params
606 return _('fork name ') + str(link_to(action_params, url('summary_home',
607 return _('fork name ') + str(link_to(action_params, url('summary_home',
607 repo_name=repo_name,)))
608 repo_name=repo_name,)))
608
609
609 def get_user_name():
610 def get_user_name():
610 user_name = action_params
611 user_name = action_params
611 return user_name
612 return user_name
612
613
613 def get_users_group():
614 def get_users_group():
614 group_name = action_params
615 group_name = action_params
615 return group_name
616 return group_name
616
617
617 def get_pull_request():
618 def get_pull_request():
618 pull_request_id = action_params
619 pull_request_id = action_params
619 repo_name = user_log.repository.repo_name
620 repo_name = user_log.repository.repo_name
620 return link_to(_('Pull request #%s') % pull_request_id,
621 return link_to(_('Pull request #%s') % pull_request_id,
621 url('pullrequest_show', repo_name=repo_name,
622 url('pullrequest_show', repo_name=repo_name,
622 pull_request_id=pull_request_id))
623 pull_request_id=pull_request_id))
623
624
624 # action : translated str, callback(extractor), icon
625 # action : translated str, callback(extractor), icon
625 action_map = {
626 action_map = {
626 'user_deleted_repo': (_('[deleted] repository'),
627 'user_deleted_repo': (_('[deleted] repository'),
627 None, 'database_delete.png'),
628 None, 'database_delete.png'),
628 'user_created_repo': (_('[created] repository'),
629 'user_created_repo': (_('[created] repository'),
629 None, 'database_add.png'),
630 None, 'database_add.png'),
630 'user_created_fork': (_('[created] repository as fork'),
631 'user_created_fork': (_('[created] repository as fork'),
631 None, 'arrow_divide.png'),
632 None, 'arrow_divide.png'),
632 'user_forked_repo': (_('[forked] repository'),
633 'user_forked_repo': (_('[forked] repository'),
633 get_fork_name, 'arrow_divide.png'),
634 get_fork_name, 'arrow_divide.png'),
634 'user_updated_repo': (_('[updated] repository'),
635 'user_updated_repo': (_('[updated] repository'),
635 None, 'database_edit.png'),
636 None, 'database_edit.png'),
636 'admin_deleted_repo': (_('[delete] repository'),
637 'admin_deleted_repo': (_('[delete] repository'),
637 None, 'database_delete.png'),
638 None, 'database_delete.png'),
638 'admin_created_repo': (_('[created] repository'),
639 'admin_created_repo': (_('[created] repository'),
639 None, 'database_add.png'),
640 None, 'database_add.png'),
640 'admin_forked_repo': (_('[forked] repository'),
641 'admin_forked_repo': (_('[forked] repository'),
641 None, 'arrow_divide.png'),
642 None, 'arrow_divide.png'),
642 'admin_updated_repo': (_('[updated] repository'),
643 'admin_updated_repo': (_('[updated] repository'),
643 None, 'database_edit.png'),
644 None, 'database_edit.png'),
644 'admin_created_user': (_('[created] user'),
645 'admin_created_user': (_('[created] user'),
645 get_user_name, 'user_add.png'),
646 get_user_name, 'user_add.png'),
646 'admin_updated_user': (_('[updated] user'),
647 'admin_updated_user': (_('[updated] user'),
647 get_user_name, 'user_edit.png'),
648 get_user_name, 'user_edit.png'),
648 'admin_created_users_group': (_('[created] users group'),
649 'admin_created_users_group': (_('[created] users group'),
649 get_users_group, 'group_add.png'),
650 get_users_group, 'group_add.png'),
650 'admin_updated_users_group': (_('[updated] users group'),
651 'admin_updated_users_group': (_('[updated] users group'),
651 get_users_group, 'group_edit.png'),
652 get_users_group, 'group_edit.png'),
652 'user_commented_revision': (_('[commented] on revision in repository'),
653 'user_commented_revision': (_('[commented] on revision in repository'),
653 get_cs_links, 'comment_add.png'),
654 get_cs_links, 'comment_add.png'),
654 'user_commented_pull_request': (_('[commented] on pull request for'),
655 'user_commented_pull_request': (_('[commented] on pull request for'),
655 get_pull_request, 'comment_add.png'),
656 get_pull_request, 'comment_add.png'),
656 'user_closed_pull_request': (_('[closed] pull request for'),
657 'user_closed_pull_request': (_('[closed] pull request for'),
657 get_pull_request, 'tick.png'),
658 get_pull_request, 'tick.png'),
658 'push': (_('[pushed] into'),
659 'push': (_('[pushed] into'),
659 get_cs_links, 'script_add.png'),
660 get_cs_links, 'script_add.png'),
660 'push_local': (_('[committed via RhodeCode] into repository'),
661 'push_local': (_('[committed via RhodeCode] into repository'),
661 get_cs_links, 'script_edit.png'),
662 get_cs_links, 'script_edit.png'),
662 'push_remote': (_('[pulled from remote] into repository'),
663 'push_remote': (_('[pulled from remote] into repository'),
663 get_cs_links, 'connect.png'),
664 get_cs_links, 'connect.png'),
664 'pull': (_('[pulled] from'),
665 'pull': (_('[pulled] from'),
665 None, 'down_16.png'),
666 None, 'down_16.png'),
666 'started_following_repo': (_('[started following] repository'),
667 'started_following_repo': (_('[started following] repository'),
667 None, 'heart_add.png'),
668 None, 'heart_add.png'),
668 'stopped_following_repo': (_('[stopped following] repository'),
669 'stopped_following_repo': (_('[stopped following] repository'),
669 None, 'heart_delete.png'),
670 None, 'heart_delete.png'),
670 }
671 }
671
672
672 action_str = action_map.get(action, action)
673 action_str = action_map.get(action, action)
673 if feed:
674 if feed:
674 action = action_str[0].replace('[', '').replace(']', '')
675 action = action_str[0].replace('[', '').replace(']', '')
675 else:
676 else:
676 action = action_str[0]\
677 action = action_str[0]\
677 .replace('[', '<span class="journal_highlight">')\
678 .replace('[', '<span class="journal_highlight">')\
678 .replace(']', '</span>')
679 .replace(']', '</span>')
679
680
680 action_params_func = lambda: ""
681 action_params_func = lambda: ""
681
682
682 if callable(action_str[1]):
683 if callable(action_str[1]):
683 action_params_func = action_str[1]
684 action_params_func = action_str[1]
684
685
685 def action_parser_icon():
686 def action_parser_icon():
686 action = user_log.action
687 action = user_log.action
687 action_params = None
688 action_params = None
688 x = action.split(':')
689 x = action.split(':')
689
690
690 if len(x) > 1:
691 if len(x) > 1:
691 action, action_params = x
692 action, action_params = x
692
693
693 tmpl = """<img src="%s%s" alt="%s"/>"""
694 tmpl = """<img src="%s%s" alt="%s"/>"""
694 ico = action_map.get(action, ['', '', ''])[2]
695 ico = action_map.get(action, ['', '', ''])[2]
695 return literal(tmpl % ((url('/images/icons/')), ico, action))
696 return literal(tmpl % ((url('/images/icons/')), ico, action))
696
697
697 # returned callbacks we need to call to get
698 # returned callbacks we need to call to get
698 return [lambda: literal(action), action_params_func, action_parser_icon]
699 return [lambda: literal(action), action_params_func, action_parser_icon]
699
700
700
701
701
702
702 #==============================================================================
703 #==============================================================================
703 # PERMS
704 # PERMS
704 #==============================================================================
705 #==============================================================================
705 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
706 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
706 HasRepoPermissionAny, HasRepoPermissionAll
707 HasRepoPermissionAny, HasRepoPermissionAll
707
708
708
709
709 #==============================================================================
710 #==============================================================================
710 # GRAVATAR URL
711 # GRAVATAR URL
711 #==============================================================================
712 #==============================================================================
712
713
713 def gravatar_url(email_address, size=30):
714 def gravatar_url(email_address, size=30):
715 from pylons import url ## doh, we need to re-import url to mock it later
714 if(str2bool(config['app_conf'].get('use_gravatar')) and
716 if(str2bool(config['app_conf'].get('use_gravatar')) and
715 config['app_conf'].get('alternative_gravatar_url')):
717 config['app_conf'].get('alternative_gravatar_url')):
716 tmpl = config['app_conf'].get('alternative_gravatar_url', '')
718 tmpl = config['app_conf'].get('alternative_gravatar_url', '')
719 parsed_url = urlparse.urlparse(url.current(qualified=True))
717 tmpl = tmpl.replace('{email}', email_address)\
720 tmpl = tmpl.replace('{email}', email_address)\
718 .replace('{md5email}', hashlib.md5(email_address.lower()).hexdigest())\
721 .replace('{md5email}', hashlib.md5(email_address.lower()).hexdigest()) \
722 .replace('{netloc}', parsed_url.netloc)\
723 .replace('{scheme}', parsed_url.scheme)\
719 .replace('{size}', str(size))
724 .replace('{size}', str(size))
720 return tmpl
725 return tmpl
721
726
722 if (not str2bool(config['app_conf'].get('use_gravatar')) or
727 if (not str2bool(config['app_conf'].get('use_gravatar')) or
723 not email_address or email_address == 'anonymous@rhodecode.org'):
728 not email_address or email_address == 'anonymous@rhodecode.org'):
724 f = lambda a, l: min(l, key=lambda x: abs(x - a))
729 f = lambda a, l: min(l, key=lambda x: abs(x - a))
725 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
730 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
726
731
727 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
732 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
728 default = 'identicon'
733 default = 'identicon'
729 baseurl_nossl = "http://www.gravatar.com/avatar/"
734 baseurl_nossl = "http://www.gravatar.com/avatar/"
730 baseurl_ssl = "https://secure.gravatar.com/avatar/"
735 baseurl_ssl = "https://secure.gravatar.com/avatar/"
731 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
736 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
732
737
733 if isinstance(email_address, unicode):
738 if isinstance(email_address, unicode):
734 #hashlib crashes on unicode items
739 #hashlib crashes on unicode items
735 email_address = safe_str(email_address)
740 email_address = safe_str(email_address)
736 # construct the url
741 # construct the url
737 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
742 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
738 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
743 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
739
744
740 return gravatar_url
745 return gravatar_url
741
746
742
747
743 #==============================================================================
748 #==============================================================================
744 # REPO PAGER, PAGER FOR REPOSITORY
749 # REPO PAGER, PAGER FOR REPOSITORY
745 #==============================================================================
750 #==============================================================================
746 class RepoPage(Page):
751 class RepoPage(Page):
747
752
748 def __init__(self, collection, page=1, items_per_page=20,
753 def __init__(self, collection, page=1, items_per_page=20,
749 item_count=None, url=None, **kwargs):
754 item_count=None, url=None, **kwargs):
750
755
751 """Create a "RepoPage" instance. special pager for paging
756 """Create a "RepoPage" instance. special pager for paging
752 repository
757 repository
753 """
758 """
754 self._url_generator = url
759 self._url_generator = url
755
760
756 # Safe the kwargs class-wide so they can be used in the pager() method
761 # Safe the kwargs class-wide so they can be used in the pager() method
757 self.kwargs = kwargs
762 self.kwargs = kwargs
758
763
759 # Save a reference to the collection
764 # Save a reference to the collection
760 self.original_collection = collection
765 self.original_collection = collection
761
766
762 self.collection = collection
767 self.collection = collection
763
768
764 # The self.page is the number of the current page.
769 # The self.page is the number of the current page.
765 # The first page has the number 1!
770 # The first page has the number 1!
766 try:
771 try:
767 self.page = int(page) # make it int() if we get it as a string
772 self.page = int(page) # make it int() if we get it as a string
768 except (ValueError, TypeError):
773 except (ValueError, TypeError):
769 self.page = 1
774 self.page = 1
770
775
771 self.items_per_page = items_per_page
776 self.items_per_page = items_per_page
772
777
773 # Unless the user tells us how many items the collections has
778 # Unless the user tells us how many items the collections has
774 # we calculate that ourselves.
779 # we calculate that ourselves.
775 if item_count is not None:
780 if item_count is not None:
776 self.item_count = item_count
781 self.item_count = item_count
777 else:
782 else:
778 self.item_count = len(self.collection)
783 self.item_count = len(self.collection)
779
784
780 # Compute the number of the first and last available page
785 # Compute the number of the first and last available page
781 if self.item_count > 0:
786 if self.item_count > 0:
782 self.first_page = 1
787 self.first_page = 1
783 self.page_count = int(math.ceil(float(self.item_count) /
788 self.page_count = int(math.ceil(float(self.item_count) /
784 self.items_per_page))
789 self.items_per_page))
785 self.last_page = self.first_page + self.page_count - 1
790 self.last_page = self.first_page + self.page_count - 1
786
791
787 # Make sure that the requested page number is the range of
792 # Make sure that the requested page number is the range of
788 # valid pages
793 # valid pages
789 if self.page > self.last_page:
794 if self.page > self.last_page:
790 self.page = self.last_page
795 self.page = self.last_page
791 elif self.page < self.first_page:
796 elif self.page < self.first_page:
792 self.page = self.first_page
797 self.page = self.first_page
793
798
794 # Note: the number of items on this page can be less than
799 # Note: the number of items on this page can be less than
795 # items_per_page if the last page is not full
800 # items_per_page if the last page is not full
796 self.first_item = max(0, (self.item_count) - (self.page *
801 self.first_item = max(0, (self.item_count) - (self.page *
797 items_per_page))
802 items_per_page))
798 self.last_item = ((self.item_count - 1) - items_per_page *
803 self.last_item = ((self.item_count - 1) - items_per_page *
799 (self.page - 1))
804 (self.page - 1))
800
805
801 self.items = list(self.collection[self.first_item:self.last_item + 1])
806 self.items = list(self.collection[self.first_item:self.last_item + 1])
802
807
803 # Links to previous and next page
808 # Links to previous and next page
804 if self.page > self.first_page:
809 if self.page > self.first_page:
805 self.previous_page = self.page - 1
810 self.previous_page = self.page - 1
806 else:
811 else:
807 self.previous_page = None
812 self.previous_page = None
808
813
809 if self.page < self.last_page:
814 if self.page < self.last_page:
810 self.next_page = self.page + 1
815 self.next_page = self.page + 1
811 else:
816 else:
812 self.next_page = None
817 self.next_page = None
813
818
814 # No items available
819 # No items available
815 else:
820 else:
816 self.first_page = None
821 self.first_page = None
817 self.page_count = 0
822 self.page_count = 0
818 self.last_page = None
823 self.last_page = None
819 self.first_item = None
824 self.first_item = None
820 self.last_item = None
825 self.last_item = None
821 self.previous_page = None
826 self.previous_page = None
822 self.next_page = None
827 self.next_page = None
823 self.items = []
828 self.items = []
824
829
825 # This is a subclass of the 'list' type. Initialise the list now.
830 # This is a subclass of the 'list' type. Initialise the list now.
826 list.__init__(self, reversed(self.items))
831 list.__init__(self, reversed(self.items))
827
832
828
833
829 def changed_tooltip(nodes):
834 def changed_tooltip(nodes):
830 """
835 """
831 Generates a html string for changed nodes in changeset page.
836 Generates a html string for changed nodes in changeset page.
832 It limits the output to 30 entries
837 It limits the output to 30 entries
833
838
834 :param nodes: LazyNodesGenerator
839 :param nodes: LazyNodesGenerator
835 """
840 """
836 if nodes:
841 if nodes:
837 pref = ': <br/> '
842 pref = ': <br/> '
838 suf = ''
843 suf = ''
839 if len(nodes) > 30:
844 if len(nodes) > 30:
840 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
845 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
841 return literal(pref + '<br/> '.join([safe_unicode(x.path)
846 return literal(pref + '<br/> '.join([safe_unicode(x.path)
842 for x in nodes[:30]]) + suf)
847 for x in nodes[:30]]) + suf)
843 else:
848 else:
844 return ': ' + _('No Files')
849 return ': ' + _('No Files')
845
850
846
851
847 def repo_link(groups_and_repos):
852 def repo_link(groups_and_repos):
848 """
853 """
849 Makes a breadcrumbs link to repo within a group
854 Makes a breadcrumbs link to repo within a group
850 joins &raquo; on each group to create a fancy link
855 joins &raquo; on each group to create a fancy link
851
856
852 ex::
857 ex::
853 group >> subgroup >> repo
858 group >> subgroup >> repo
854
859
855 :param groups_and_repos:
860 :param groups_and_repos:
856 """
861 """
857 groups, repo_name = groups_and_repos
862 groups, repo_name = groups_and_repos
858
863
859 if not groups:
864 if not groups:
860 return repo_name
865 return repo_name
861 else:
866 else:
862 def make_link(group):
867 def make_link(group):
863 return link_to(group.name, url('repos_group_home',
868 return link_to(group.name, url('repos_group_home',
864 group_name=group.group_name))
869 group_name=group.group_name))
865 return literal(' &raquo; '.join(map(make_link, groups)) + \
870 return literal(' &raquo; '.join(map(make_link, groups)) + \
866 " &raquo; " + repo_name)
871 " &raquo; " + repo_name)
867
872
868
873
869 def fancy_file_stats(stats):
874 def fancy_file_stats(stats):
870 """
875 """
871 Displays a fancy two colored bar for number of added/deleted
876 Displays a fancy two colored bar for number of added/deleted
872 lines of code on file
877 lines of code on file
873
878
874 :param stats: two element list of added/deleted lines of code
879 :param stats: two element list of added/deleted lines of code
875 """
880 """
876
881
877 a, d, t = stats[0], stats[1], stats[0] + stats[1]
882 a, d, t = stats[0], stats[1], stats[0] + stats[1]
878 width = 100
883 width = 100
879 unit = float(width) / (t or 1)
884 unit = float(width) / (t or 1)
880
885
881 # needs > 9% of width to be visible or 0 to be hidden
886 # needs > 9% of width to be visible or 0 to be hidden
882 a_p = max(9, unit * a) if a > 0 else 0
887 a_p = max(9, unit * a) if a > 0 else 0
883 d_p = max(9, unit * d) if d > 0 else 0
888 d_p = max(9, unit * d) if d > 0 else 0
884 p_sum = a_p + d_p
889 p_sum = a_p + d_p
885
890
886 if p_sum > width:
891 if p_sum > width:
887 #adjust the percentage to be == 100% since we adjusted to 9
892 #adjust the percentage to be == 100% since we adjusted to 9
888 if a_p > d_p:
893 if a_p > d_p:
889 a_p = a_p - (p_sum - width)
894 a_p = a_p - (p_sum - width)
890 else:
895 else:
891 d_p = d_p - (p_sum - width)
896 d_p = d_p - (p_sum - width)
892
897
893 a_v = a if a > 0 else ''
898 a_v = a if a > 0 else ''
894 d_v = d if d > 0 else ''
899 d_v = d if d > 0 else ''
895
900
896 def cgen(l_type):
901 def cgen(l_type):
897 mapping = {'tr': 'top-right-rounded-corner-mid',
902 mapping = {'tr': 'top-right-rounded-corner-mid',
898 'tl': 'top-left-rounded-corner-mid',
903 'tl': 'top-left-rounded-corner-mid',
899 'br': 'bottom-right-rounded-corner-mid',
904 'br': 'bottom-right-rounded-corner-mid',
900 'bl': 'bottom-left-rounded-corner-mid'}
905 'bl': 'bottom-left-rounded-corner-mid'}
901 map_getter = lambda x: mapping[x]
906 map_getter = lambda x: mapping[x]
902
907
903 if l_type == 'a' and d_v:
908 if l_type == 'a' and d_v:
904 #case when added and deleted are present
909 #case when added and deleted are present
905 return ' '.join(map(map_getter, ['tl', 'bl']))
910 return ' '.join(map(map_getter, ['tl', 'bl']))
906
911
907 if l_type == 'a' and not d_v:
912 if l_type == 'a' and not d_v:
908 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
913 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
909
914
910 if l_type == 'd' and a_v:
915 if l_type == 'd' and a_v:
911 return ' '.join(map(map_getter, ['tr', 'br']))
916 return ' '.join(map(map_getter, ['tr', 'br']))
912
917
913 if l_type == 'd' and not a_v:
918 if l_type == 'd' and not a_v:
914 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
919 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
915
920
916 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
921 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
917 cgen('a'), a_p, a_v
922 cgen('a'), a_p, a_v
918 )
923 )
919 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
924 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
920 cgen('d'), d_p, d_v
925 cgen('d'), d_p, d_v
921 )
926 )
922 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
927 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
923
928
924
929
925 def urlify_text(text_):
930 def urlify_text(text_):
926 import re
931 import re
927
932
928 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
933 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
929 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
934 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
930
935
931 def url_func(match_obj):
936 def url_func(match_obj):
932 url_full = match_obj.groups()[0]
937 url_full = match_obj.groups()[0]
933 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
938 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
934
939
935 return literal(url_pat.sub(url_func, text_))
940 return literal(url_pat.sub(url_func, text_))
936
941
937
942
938 def urlify_changesets(text_, repository):
943 def urlify_changesets(text_, repository):
939 """
944 """
940 Extract revision ids from changeset and make link from them
945 Extract revision ids from changeset and make link from them
941
946
942 :param text_:
947 :param text_:
943 :param repository:
948 :param repository:
944 """
949 """
945 import re
950 import re
946 URL_PAT = re.compile(r'([0-9a-fA-F]{12,})')
951 URL_PAT = re.compile(r'([0-9a-fA-F]{12,})')
947
952
948 def url_func(match_obj):
953 def url_func(match_obj):
949 rev = match_obj.groups()[0]
954 rev = match_obj.groups()[0]
950 pref = ''
955 pref = ''
951 if match_obj.group().startswith(' '):
956 if match_obj.group().startswith(' '):
952 pref = ' '
957 pref = ' '
953 tmpl = (
958 tmpl = (
954 '%(pref)s<a class="%(cls)s" href="%(url)s">'
959 '%(pref)s<a class="%(cls)s" href="%(url)s">'
955 '%(rev)s'
960 '%(rev)s'
956 '</a>'
961 '</a>'
957 )
962 )
958 return tmpl % {
963 return tmpl % {
959 'pref': pref,
964 'pref': pref,
960 'cls': 'revision-link',
965 'cls': 'revision-link',
961 'url': url('changeset_home', repo_name=repository, revision=rev),
966 'url': url('changeset_home', repo_name=repository, revision=rev),
962 'rev': rev,
967 'rev': rev,
963 }
968 }
964
969
965 newtext = URL_PAT.sub(url_func, text_)
970 newtext = URL_PAT.sub(url_func, text_)
966
971
967 return newtext
972 return newtext
968
973
969
974
970 def urlify_commit(text_, repository=None, link_=None):
975 def urlify_commit(text_, repository=None, link_=None):
971 """
976 """
972 Parses given text message and makes proper links.
977 Parses given text message and makes proper links.
973 issues are linked to given issue-server, and rest is a changeset link
978 issues are linked to given issue-server, and rest is a changeset link
974 if link_ is given, in other case it's a plain text
979 if link_ is given, in other case it's a plain text
975
980
976 :param text_:
981 :param text_:
977 :param repository:
982 :param repository:
978 :param link_: changeset link
983 :param link_: changeset link
979 """
984 """
980 import re
985 import re
981 import traceback
986 import traceback
982
987
983 def escaper(string):
988 def escaper(string):
984 return string.replace('<', '&lt;').replace('>', '&gt;')
989 return string.replace('<', '&lt;').replace('>', '&gt;')
985
990
986 def linkify_others(t, l):
991 def linkify_others(t, l):
987 urls = re.compile(r'(\<a.*?\<\/a\>)',)
992 urls = re.compile(r'(\<a.*?\<\/a\>)',)
988 links = []
993 links = []
989 for e in urls.split(t):
994 for e in urls.split(t):
990 if not urls.match(e):
995 if not urls.match(e):
991 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
996 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
992 else:
997 else:
993 links.append(e)
998 links.append(e)
994
999
995 return ''.join(links)
1000 return ''.join(links)
996
1001
997 # urlify changesets - extrac revisions and make link out of them
1002 # urlify changesets - extrac revisions and make link out of them
998 text_ = urlify_changesets(escaper(text_), repository)
1003 text_ = urlify_changesets(escaper(text_), repository)
999
1004
1000 try:
1005 try:
1001 conf = config['app_conf']
1006 conf = config['app_conf']
1002
1007
1003 URL_PAT = re.compile(r'%s' % conf.get('issue_pat'))
1008 URL_PAT = re.compile(r'%s' % conf.get('issue_pat'))
1004
1009
1005 if URL_PAT:
1010 if URL_PAT:
1006 ISSUE_SERVER_LNK = conf.get('issue_server_link')
1011 ISSUE_SERVER_LNK = conf.get('issue_server_link')
1007 ISSUE_PREFIX = conf.get('issue_prefix')
1012 ISSUE_PREFIX = conf.get('issue_prefix')
1008
1013
1009 def url_func(match_obj):
1014 def url_func(match_obj):
1010 pref = ''
1015 pref = ''
1011 if match_obj.group().startswith(' '):
1016 if match_obj.group().startswith(' '):
1012 pref = ' '
1017 pref = ' '
1013
1018
1014 issue_id = ''.join(match_obj.groups())
1019 issue_id = ''.join(match_obj.groups())
1015 tmpl = (
1020 tmpl = (
1016 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1021 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1017 '%(issue-prefix)s%(id-repr)s'
1022 '%(issue-prefix)s%(id-repr)s'
1018 '</a>'
1023 '</a>'
1019 )
1024 )
1020 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
1025 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
1021 if repository:
1026 if repository:
1022 url = url.replace('{repo}', repository)
1027 url = url.replace('{repo}', repository)
1023 repo_name = repository.split(URL_SEP)[-1]
1028 repo_name = repository.split(URL_SEP)[-1]
1024 url = url.replace('{repo_name}', repo_name)
1029 url = url.replace('{repo_name}', repo_name)
1025 return tmpl % {
1030 return tmpl % {
1026 'pref': pref,
1031 'pref': pref,
1027 'cls': 'issue-tracker-link',
1032 'cls': 'issue-tracker-link',
1028 'url': url,
1033 'url': url,
1029 'id-repr': issue_id,
1034 'id-repr': issue_id,
1030 'issue-prefix': ISSUE_PREFIX,
1035 'issue-prefix': ISSUE_PREFIX,
1031 'serv': ISSUE_SERVER_LNK,
1036 'serv': ISSUE_SERVER_LNK,
1032 }
1037 }
1033
1038
1034 newtext = URL_PAT.sub(url_func, text_)
1039 newtext = URL_PAT.sub(url_func, text_)
1035
1040
1036 if link_:
1041 if link_:
1037 # wrap not links into final link => link_
1042 # wrap not links into final link => link_
1038 newtext = linkify_others(newtext, link_)
1043 newtext = linkify_others(newtext, link_)
1039
1044
1040 return literal(newtext)
1045 return literal(newtext)
1041 except:
1046 except:
1042 log.error(traceback.format_exc())
1047 log.error(traceback.format_exc())
1043 pass
1048 pass
1044
1049
1045 return text_
1050 return text_
1046
1051
1047
1052
1048 def rst(source):
1053 def rst(source):
1049 return literal('<div class="rst-block">%s</div>' %
1054 return literal('<div class="rst-block">%s</div>' %
1050 MarkupRenderer.rst(source))
1055 MarkupRenderer.rst(source))
1051
1056
1052
1057
1053 def rst_w_mentions(source):
1058 def rst_w_mentions(source):
1054 """
1059 """
1055 Wrapped rst renderer with @mention highlighting
1060 Wrapped rst renderer with @mention highlighting
1056
1061
1057 :param source:
1062 :param source:
1058 """
1063 """
1059 return literal('<div class="rst-block">%s</div>' %
1064 return literal('<div class="rst-block">%s</div>' %
1060 MarkupRenderer.rst_with_mentions(source))
1065 MarkupRenderer.rst_with_mentions(source))
1061
1066
1062
1067
1063 def changeset_status(repo, revision):
1068 def changeset_status(repo, revision):
1064 return ChangesetStatusModel().get_status(repo, revision)
1069 return ChangesetStatusModel().get_status(repo, revision)
1065
1070
1066
1071
1067 def changeset_status_lbl(changeset_status):
1072 def changeset_status_lbl(changeset_status):
1068 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1073 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1069
1074
1070
1075
1071 def get_permission_name(key):
1076 def get_permission_name(key):
1072 return dict(Permission.PERMS).get(key)
1077 return dict(Permission.PERMS).get(key)
@@ -1,177 +1,198 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.tests.test_libs
3 rhodecode.tests.test_libs
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6
6
7 Package for testing various lib/helper functions in rhodecode
7 Package for testing various lib/helper functions in rhodecode
8
8
9 :created_on: Jun 9, 2011
9 :created_on: Jun 9, 2011
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import unittest
26 import unittest
27 import datetime
27 import datetime
28 import hashlib
28 import hashlib
29 import mock
29 import mock
30 from rhodecode.tests import *
30 from rhodecode.tests import *
31
31
32 proto = 'http'
32 proto = 'http'
33 TEST_URLS = [
33 TEST_URLS = [
34 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
34 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
35 '%s://127.0.0.1' % proto),
35 '%s://127.0.0.1' % proto),
36 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
36 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
37 '%s://127.0.0.1' % proto),
37 '%s://127.0.0.1' % proto),
38 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
38 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
39 '%s://127.0.0.1' % proto),
39 '%s://127.0.0.1' % proto),
40 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
40 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
41 '%s://127.0.0.1:8080' % proto),
41 '%s://127.0.0.1:8080' % proto),
42 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
42 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
43 '%s://domain.org' % proto),
43 '%s://domain.org' % proto),
44 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
44 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
45 '8080'],
45 '8080'],
46 '%s://domain.org:8080' % proto),
46 '%s://domain.org:8080' % proto),
47 ]
47 ]
48
48
49 proto = 'https'
49 proto = 'https'
50 TEST_URLS += [
50 TEST_URLS += [
51 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
51 ('%s://127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
52 '%s://127.0.0.1' % proto),
52 '%s://127.0.0.1' % proto),
53 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
53 ('%s://marcink@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
54 '%s://127.0.0.1' % proto),
54 '%s://127.0.0.1' % proto),
55 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
55 ('%s://marcink:pass@127.0.0.1' % proto, ['%s://' % proto, '127.0.0.1'],
56 '%s://127.0.0.1' % proto),
56 '%s://127.0.0.1' % proto),
57 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
57 ('%s://127.0.0.1:8080' % proto, ['%s://' % proto, '127.0.0.1', '8080'],
58 '%s://127.0.0.1:8080' % proto),
58 '%s://127.0.0.1:8080' % proto),
59 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
59 ('%s://domain.org' % proto, ['%s://' % proto, 'domain.org'],
60 '%s://domain.org' % proto),
60 '%s://domain.org' % proto),
61 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
61 ('%s://user:pass@domain.org:8080' % proto, ['%s://' % proto, 'domain.org',
62 '8080'],
62 '8080'],
63 '%s://domain.org:8080' % proto),
63 '%s://domain.org:8080' % proto),
64 ]
64 ]
65
65
66
66
67 class TestLibs(unittest.TestCase):
67 class TestLibs(unittest.TestCase):
68
68
69 def test_uri_filter(self):
69 def test_uri_filter(self):
70 from rhodecode.lib.utils2 import uri_filter
70 from rhodecode.lib.utils2 import uri_filter
71
71
72 for url in TEST_URLS:
72 for url in TEST_URLS:
73 self.assertEqual(uri_filter(url[0]), url[1])
73 self.assertEqual(uri_filter(url[0]), url[1])
74
74
75 def test_credentials_filter(self):
75 def test_credentials_filter(self):
76 from rhodecode.lib.utils2 import credentials_filter
76 from rhodecode.lib.utils2 import credentials_filter
77
77
78 for url in TEST_URLS:
78 for url in TEST_URLS:
79 self.assertEqual(credentials_filter(url[0]), url[2])
79 self.assertEqual(credentials_filter(url[0]), url[2])
80
80
81 def test_str2bool(self):
81 def test_str2bool(self):
82 from rhodecode.lib.utils2 import str2bool
82 from rhodecode.lib.utils2 import str2bool
83 test_cases = [
83 test_cases = [
84 ('t', True),
84 ('t', True),
85 ('true', True),
85 ('true', True),
86 ('y', True),
86 ('y', True),
87 ('yes', True),
87 ('yes', True),
88 ('on', True),
88 ('on', True),
89 ('1', True),
89 ('1', True),
90 ('Y', True),
90 ('Y', True),
91 ('yeS', True),
91 ('yeS', True),
92 ('Y', True),
92 ('Y', True),
93 ('TRUE', True),
93 ('TRUE', True),
94 ('T', True),
94 ('T', True),
95 ('False', False),
95 ('False', False),
96 ('F', False),
96 ('F', False),
97 ('FALSE', False),
97 ('FALSE', False),
98 ('0', False),
98 ('0', False),
99 ('-1', False),
99 ('-1', False),
100 ('', False), ]
100 ('', False), ]
101
101
102 for case in test_cases:
102 for case in test_cases:
103 self.assertEqual(str2bool(case[0]), case[1])
103 self.assertEqual(str2bool(case[0]), case[1])
104
104
105 def test_mention_extractor(self):
105 def test_mention_extractor(self):
106 from rhodecode.lib.utils2 import extract_mentioned_users
106 from rhodecode.lib.utils2 import extract_mentioned_users
107 sample = (
107 sample = (
108 "@first hi there @marcink here's my email marcin@email.com "
108 "@first hi there @marcink here's my email marcin@email.com "
109 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three "
109 "@lukaszb check @one_more22 it pls @ ttwelve @D[] @one@two@three "
110 "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl "
110 "@MARCIN @maRCiN @2one_more22 @john please see this http://org.pl "
111 "@marian.user just do it @marco-polo and next extract @marco_polo "
111 "@marian.user just do it @marco-polo and next extract @marco_polo "
112 "user.dot hej ! not-needed maril@domain.org"
112 "user.dot hej ! not-needed maril@domain.org"
113 )
113 )
114
114
115 s = sorted([
115 s = sorted([
116 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john',
116 'first', 'marcink', 'lukaszb', 'one_more22', 'MARCIN', 'maRCiN', 'john',
117 'marian.user', 'marco-polo', 'marco_polo'
117 'marian.user', 'marco-polo', 'marco_polo'
118 ], key=lambda k: k.lower())
118 ], key=lambda k: k.lower())
119 self.assertEqual(s, extract_mentioned_users(sample))
119 self.assertEqual(s, extract_mentioned_users(sample))
120
120
121 def test_age(self):
121 def test_age(self):
122 import calendar
122 import calendar
123 from rhodecode.lib.utils2 import age
123 from rhodecode.lib.utils2 import age
124 n = datetime.datetime.now()
124 n = datetime.datetime.now()
125 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
125 delt = lambda *args, **kwargs: datetime.timedelta(*args, **kwargs)
126 self.assertEqual(age(n), u'just now')
126 self.assertEqual(age(n), u'just now')
127 self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
127 self.assertEqual(age(n - delt(seconds=1)), u'1 second ago')
128 self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
128 self.assertEqual(age(n - delt(seconds=60 * 2)), u'2 minutes ago')
129 self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
129 self.assertEqual(age(n - delt(hours=1)), u'1 hour ago')
130 self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
130 self.assertEqual(age(n - delt(hours=24)), u'1 day ago')
131 self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
131 self.assertEqual(age(n - delt(hours=24 * 5)), u'5 days ago')
132 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month-1] + 2))),
132 self.assertEqual(age(n - delt(hours=24 * (calendar.mdays[n.month-1] + 2))),
133 u'1 month and 2 days ago')
133 u'1 month and 2 days ago')
134 self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
134 self.assertEqual(age(n - delt(hours=24 * 400)), u'1 year and 1 month ago')
135
135
136 def test_tag_exctrator(self):
136 def test_tag_exctrator(self):
137 sample = (
137 sample = (
138 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
138 "hello pta[tag] gog [[]] [[] sda ero[or]d [me =>>< sa]"
139 "[requires] [stale] [see<>=>] [see => http://url.com]"
139 "[requires] [stale] [see<>=>] [see => http://url.com]"
140 "[requires => url] [lang => python] [just a tag]"
140 "[requires => url] [lang => python] [just a tag]"
141 "[,d] [ => ULR ] [obsolete] [desc]]"
141 "[,d] [ => ULR ] [obsolete] [desc]]"
142 )
142 )
143 from rhodecode.lib.helpers import desc_stylize
143 from rhodecode.lib.helpers import desc_stylize
144 res = desc_stylize(sample)
144 res = desc_stylize(sample)
145 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
145 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
146 self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
146 self.assertTrue('<div class="metatag" tag="obsolete">obsolete</div>' in res)
147 self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
147 self.assertTrue('<div class="metatag" tag="stale">stale</div>' in res)
148 self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
148 self.assertTrue('<div class="metatag" tag="lang">python</div>' in res)
149 self.assertTrue('<div class="metatag" tag="requires">requires =&gt; <a href="/url">url</a></div>' in res)
149 self.assertTrue('<div class="metatag" tag="requires">requires =&gt; <a href="/url">url</a></div>' in res)
150 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
150 self.assertTrue('<div class="metatag" tag="tag">tag</div>' in res)
151
151
152 def test_alternative_gravatar(self):
152 def test_alternative_gravatar(self):
153 from rhodecode.lib.helpers import gravatar_url
153 from rhodecode.lib.helpers import gravatar_url
154 _md5 = lambda s: hashlib.md5(s).hexdigest()
154 _md5 = lambda s: hashlib.md5(s).hexdigest()
155
155
156 def fake_conf(**kwargs):
156 def fake_conf(**kwargs):
157 from pylons import config
157 from pylons import config
158 config['app_conf'] = {}
158 config['app_conf'] = {}
159 config['app_conf']['use_gravatar'] = True
159 config['app_conf']['use_gravatar'] = True
160 config['app_conf'].update(kwargs)
160 config['app_conf'].update(kwargs)
161 return config
161 return config
162 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
162
163 with mock.patch('pylons.config', fake):
163 class fake_url():
164 grav = gravatar_url(email_address='test@foo.com', size=24)
164 @classmethod
165 assert grav == 'http://test.com/test@foo.com'
165 def current(cls, *args, **kwargs):
166 return 'https://server.com'
167
168 with mock.patch('pylons.url', fake_url):
169 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
170 with mock.patch('pylons.config', fake):
171 from pylons import url
172 assert url.current() == 'http://server.com'
173 grav = gravatar_url(email_address='test@foo.com', size=24)
174 assert grav == 'http://test.com/test@foo.com'
175
176 fake = fake_conf(alternative_gravatar_url='http://test.com/{email}')
177 with mock.patch('pylons.config', fake):
178 grav = gravatar_url(email_address='test@foo.com', size=24)
179 assert grav == 'http://test.com/test@foo.com'
166
180
167 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}')
181 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}')
168 with mock.patch('pylons.config', fake):
182 with mock.patch('pylons.config', fake):
169 em = 'test@foo.com'
183 em = 'test@foo.com'
170 grav = gravatar_url(email_address=em, size=24)
184 grav = gravatar_url(email_address=em, size=24)
171 assert grav == 'http://test.com/%s' % (_md5(em))
185 assert grav == 'http://test.com/%s' % (_md5(em))
172
186
173 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}')
187 fake = fake_conf(alternative_gravatar_url='http://test.com/{md5email}/{size}')
174 with mock.patch('pylons.config', fake):
188 with mock.patch('pylons.config', fake):
175 em = 'test@foo.com'
189 em = 'test@foo.com'
176 grav = gravatar_url(email_address=em, size=24)
190 grav = gravatar_url(email_address=em, size=24)
177 assert grav == 'http://test.com/%s/%s' % (_md5(em), 24)
191 assert grav == 'http://test.com/%s/%s' % (_md5(em), 24)
192
193 fake = fake_conf(alternative_gravatar_url='{scheme}://{netloc}/{md5email}/{size}')
194 with mock.patch('pylons.config', fake):
195 em = 'test@foo.com'
196 grav = gravatar_url(email_address=em, size=24)
197 assert grav == 'https://server.com/%s/%s' % (_md5(em), 24)
198
General Comments 0
You need to be logged in to leave comments. Login now