##// END OF EJS Templates
Added option to ini files for controlling full cache of VCS instances....
marcink -
r3025:f61adead beta
parent child Browse files
Show More
@@ -1,424 +1,425 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 vcs_full_cache = True
63 force_https = false
64 force_https = false
64 commit_parse_limit = 25
65 commit_parse_limit = 25
65 use_gravatar = true
66 use_gravatar = true
66
67
67 ## alternative_gravatar_url allows you to use your own avatar server application
68 ## alternative_gravatar_url allows you to use your own avatar server application
68 ## the following parts of the URL will be replaced
69 ## the following parts of the URL will be replaced
69 ## {email} user email
70 ## {email} user email
70 ## {md5email} md5 hash of the user email (like at gravatar.com)
71 ## {md5email} md5 hash of the user email (like at gravatar.com)
71 ## {size} size of the image that is expected from the server application
72 ## {size} size of the image that is expected from the server application
72 ## {scheme} http/https from RhodeCode server
73 ## {scheme} http/https from RhodeCode server
73 ## {netloc} network location from RhodeCode server
74 ## {netloc} network location from RhodeCode server
74 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
75 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
75 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
76 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
76
77
77 container_auth_enabled = false
78 container_auth_enabled = false
78 proxypass_auth_enabled = false
79 proxypass_auth_enabled = false
79 ## default encoding used to convert from and to unicode
80 ## default encoding used to convert from and to unicode
80 ## can be also a comma seperated list of encoding in case of mixed encodings
81 ## can be also a comma seperated list of encoding in case of mixed encodings
81 default_encoding = utf8
82 default_encoding = utf8
82
83
83 ## overwrite schema of clone url
84 ## overwrite schema of clone url
84 ## available vars:
85 ## available vars:
85 ## scheme - http/https
86 ## scheme - http/https
86 ## user - current user
87 ## user - current user
87 ## pass - password
88 ## pass - password
88 ## netloc - network location
89 ## netloc - network location
89 ## path - usually repo_name
90 ## path - usually repo_name
90
91
91 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
92 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
92
93
93 ## issue tracking mapping for commits messages
94 ## issue tracking mapping for commits messages
94 ## comment out issue_pat, issue_server, issue_prefix to enable
95 ## comment out issue_pat, issue_server, issue_prefix to enable
95
96
96 ## pattern to get the issues from commit messages
97 ## pattern to get the issues from commit messages
97 ## default one used here is #<numbers> with a regex passive group for `#`
98 ## default one used here is #<numbers> with a regex passive group for `#`
98 ## {id} will be all groups matched from this pattern
99 ## {id} will be all groups matched from this pattern
99
100
100 issue_pat = (?:\s*#)(\d+)
101 issue_pat = (?:\s*#)(\d+)
101
102
102 ## server url to the issue, each {id} will be replaced with match
103 ## server url to the issue, each {id} will be replaced with match
103 ## fetched from the regex and {repo} is replaced with full repository name
104 ## fetched from the regex and {repo} is replaced with full repository name
104 ## including groups {repo_name} is replaced with just name of repo
105 ## including groups {repo_name} is replaced with just name of repo
105
106
106 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
107 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
107
108
108 ## prefix to add to link to indicate it's an url
109 ## prefix to add to link to indicate it's an url
109 ## #314 will be replaced by <issue_prefix><id>
110 ## #314 will be replaced by <issue_prefix><id>
110
111
111 issue_prefix = #
112 issue_prefix = #
112
113
113 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
114 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
114 ## multiple patterns, to other issues server, wiki or others
115 ## multiple patterns, to other issues server, wiki or others
115 ## below an example how to create a wiki pattern
116 ## below an example how to create a wiki pattern
116 # #wiki-some-id -> https://mywiki.com/some-id
117 # #wiki-some-id -> https://mywiki.com/some-id
117
118
118 #issue_pat_wiki = (?:wiki-)(.+)
119 #issue_pat_wiki = (?:wiki-)(.+)
119 #issue_server_link_wiki = https://mywiki.com/{id}
120 #issue_server_link_wiki = https://mywiki.com/{id}
120 #issue_prefix_wiki = WIKI-
121 #issue_prefix_wiki = WIKI-
121
122
122
123
123 ## instance-id prefix
124 ## instance-id prefix
124 ## a prefix key for this instance used for cache invalidation when running
125 ## a prefix key for this instance used for cache invalidation when running
125 ## multiple instances of rhodecode, make sure it's globally unique for
126 ## multiple instances of rhodecode, make sure it's globally unique for
126 ## all running rhodecode instances. Leave empty if you don't use it
127 ## all running rhodecode instances. Leave empty if you don't use it
127 instance_id =
128 instance_id =
128
129
129 ## alternative return HTTP header for failed authentication. Default HTTP
130 ## alternative return HTTP header for failed authentication. Default HTTP
130 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
131 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
131 ## handling that. Set this variable to 403 to return HTTPForbidden
132 ## handling that. Set this variable to 403 to return HTTPForbidden
132 auth_ret_code =
133 auth_ret_code =
133
134
134 ####################################
135 ####################################
135 ### CELERY CONFIG ####
136 ### CELERY CONFIG ####
136 ####################################
137 ####################################
137 use_celery = false
138 use_celery = false
138 broker.host = localhost
139 broker.host = localhost
139 broker.vhost = rabbitmqhost
140 broker.vhost = rabbitmqhost
140 broker.port = 5672
141 broker.port = 5672
141 broker.user = rabbitmq
142 broker.user = rabbitmq
142 broker.password = qweqwe
143 broker.password = qweqwe
143
144
144 celery.imports = rhodecode.lib.celerylib.tasks
145 celery.imports = rhodecode.lib.celerylib.tasks
145
146
146 celery.result.backend = amqp
147 celery.result.backend = amqp
147 celery.result.dburi = amqp://
148 celery.result.dburi = amqp://
148 celery.result.serialier = json
149 celery.result.serialier = json
149
150
150 #celery.send.task.error.emails = true
151 #celery.send.task.error.emails = true
151 #celery.amqp.task.result.expires = 18000
152 #celery.amqp.task.result.expires = 18000
152
153
153 celeryd.concurrency = 2
154 celeryd.concurrency = 2
154 #celeryd.log.file = celeryd.log
155 #celeryd.log.file = celeryd.log
155 celeryd.log.level = debug
156 celeryd.log.level = debug
156 celeryd.max.tasks.per.child = 1
157 celeryd.max.tasks.per.child = 1
157
158
158 #tasks will never be sent to the queue, but executed locally instead.
159 #tasks will never be sent to the queue, but executed locally instead.
159 celery.always.eager = false
160 celery.always.eager = false
160
161
161 ####################################
162 ####################################
162 ### BEAKER CACHE ####
163 ### BEAKER CACHE ####
163 ####################################
164 ####################################
164 beaker.cache.data_dir=%(here)s/data/cache/data
165 beaker.cache.data_dir=%(here)s/data/cache/data
165 beaker.cache.lock_dir=%(here)s/data/cache/lock
166 beaker.cache.lock_dir=%(here)s/data/cache/lock
166
167
167 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
168 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
168
169
169 beaker.cache.super_short_term.type=memory
170 beaker.cache.super_short_term.type=memory
170 beaker.cache.super_short_term.expire=10
171 beaker.cache.super_short_term.expire=10
171 beaker.cache.super_short_term.key_length = 256
172 beaker.cache.super_short_term.key_length = 256
172
173
173 beaker.cache.short_term.type=memory
174 beaker.cache.short_term.type=memory
174 beaker.cache.short_term.expire=60
175 beaker.cache.short_term.expire=60
175 beaker.cache.short_term.key_length = 256
176 beaker.cache.short_term.key_length = 256
176
177
177 beaker.cache.long_term.type=memory
178 beaker.cache.long_term.type=memory
178 beaker.cache.long_term.expire=36000
179 beaker.cache.long_term.expire=36000
179 beaker.cache.long_term.key_length = 256
180 beaker.cache.long_term.key_length = 256
180
181
181 beaker.cache.sql_cache_short.type=memory
182 beaker.cache.sql_cache_short.type=memory
182 beaker.cache.sql_cache_short.expire=10
183 beaker.cache.sql_cache_short.expire=10
183 beaker.cache.sql_cache_short.key_length = 256
184 beaker.cache.sql_cache_short.key_length = 256
184
185
185 beaker.cache.sql_cache_med.type=memory
186 beaker.cache.sql_cache_med.type=memory
186 beaker.cache.sql_cache_med.expire=360
187 beaker.cache.sql_cache_med.expire=360
187 beaker.cache.sql_cache_med.key_length = 256
188 beaker.cache.sql_cache_med.key_length = 256
188
189
189 beaker.cache.sql_cache_long.type=file
190 beaker.cache.sql_cache_long.type=file
190 beaker.cache.sql_cache_long.expire=3600
191 beaker.cache.sql_cache_long.expire=3600
191 beaker.cache.sql_cache_long.key_length = 256
192 beaker.cache.sql_cache_long.key_length = 256
192
193
193 ####################################
194 ####################################
194 ### BEAKER SESSION ####
195 ### BEAKER SESSION ####
195 ####################################
196 ####################################
196 ## Type of storage used for the session, current types are
197 ## Type of storage used for the session, current types are
197 ## dbm, file, memcached, database, and memory.
198 ## dbm, file, memcached, database, and memory.
198 ## The storage uses the Container API
199 ## The storage uses the Container API
199 ## that is also used by the cache system.
200 ## that is also used by the cache system.
200
201
201 ## db session ##
202 ## db session ##
202 #beaker.session.type = ext:database
203 #beaker.session.type = ext:database
203 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
204 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
204 #beaker.session.table_name = db_session
205 #beaker.session.table_name = db_session
205
206
206 ## encrypted cookie client side session, good for many instances ##
207 ## encrypted cookie client side session, good for many instances ##
207 #beaker.session.type = cookie
208 #beaker.session.type = cookie
208
209
209 ## file based cookies (default) ##
210 ## file based cookies (default) ##
210 #beaker.session.type = file
211 #beaker.session.type = file
211
212
212
213
213 beaker.session.key = rhodecode
214 beaker.session.key = rhodecode
214 ## secure cookie requires AES python libraries ##
215 ## secure cookie requires AES python libraries ##
215 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
216 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
216 #beaker.session.validate_key = 9712sds2212c--zxc123
217 #beaker.session.validate_key = 9712sds2212c--zxc123
217 ## sets session as invalid if it haven't been accessed for given amount of time
218 ## sets session as invalid if it haven't been accessed for given amount of time
218 beaker.session.timeout = 2592000
219 beaker.session.timeout = 2592000
219 beaker.session.httponly = true
220 beaker.session.httponly = true
220 #beaker.session.cookie_path = /<your-prefix>
221 #beaker.session.cookie_path = /<your-prefix>
221
222
222 ## uncomment for https secure cookie ##
223 ## uncomment for https secure cookie ##
223 beaker.session.secure = false
224 beaker.session.secure = false
224
225
225 ## auto save the session to not to use .save() ##
226 ## auto save the session to not to use .save() ##
226 beaker.session.auto = False
227 beaker.session.auto = False
227
228
228 ## default cookie expiration time in seconds `true` expire at browser close ##
229 ## default cookie expiration time in seconds `true` expire at browser close ##
229 #beaker.session.cookie_expires = 3600
230 #beaker.session.cookie_expires = 3600
230
231
231
232
232 ############################
233 ############################
233 ## ERROR HANDLING SYSTEMS ##
234 ## ERROR HANDLING SYSTEMS ##
234 ############################
235 ############################
235
236
236 ####################
237 ####################
237 ### [errormator] ###
238 ### [errormator] ###
238 ####################
239 ####################
239
240
240 # Errormator is tailored to work with RhodeCode, see
241 # Errormator is tailored to work with RhodeCode, see
241 # http://errormator.com for details how to obtain an account
242 # http://errormator.com for details how to obtain an account
242 # you must install python package `errormator_client` to make it work
243 # you must install python package `errormator_client` to make it work
243
244
244 # errormator enabled
245 # errormator enabled
245 errormator = true
246 errormator = true
246
247
247 errormator.server_url = https://api.errormator.com
248 errormator.server_url = https://api.errormator.com
248 errormator.api_key = YOUR_API_KEY
249 errormator.api_key = YOUR_API_KEY
249
250
250 # TWEAK AMOUNT OF INFO SENT HERE
251 # TWEAK AMOUNT OF INFO SENT HERE
251
252
252 # enables 404 error logging (default False)
253 # enables 404 error logging (default False)
253 errormator.report_404 = false
254 errormator.report_404 = false
254
255
255 # time in seconds after request is considered being slow (default 1)
256 # time in seconds after request is considered being slow (default 1)
256 errormator.slow_request_time = 1
257 errormator.slow_request_time = 1
257
258
258 # record slow requests in application
259 # record slow requests in application
259 # (needs to be enabled for slow datastore recording and time tracking)
260 # (needs to be enabled for slow datastore recording and time tracking)
260 errormator.slow_requests = true
261 errormator.slow_requests = true
261
262
262 # enable hooking to application loggers
263 # enable hooking to application loggers
263 # errormator.logging = true
264 # errormator.logging = true
264
265
265 # minimum log level for log capture
266 # minimum log level for log capture
266 # errormator.logging.level = WARNING
267 # errormator.logging.level = WARNING
267
268
268 # send logs only from erroneous/slow requests
269 # send logs only from erroneous/slow requests
269 # (saves API quota for intensive logging)
270 # (saves API quota for intensive logging)
270 errormator.logging_on_error = false
271 errormator.logging_on_error = false
271
272
272 # list of additonal keywords that should be grabbed from environ object
273 # list of additonal keywords that should be grabbed from environ object
273 # can be string with comma separated list of words in lowercase
274 # can be string with comma separated list of words in lowercase
274 # (by default client will always send following info:
275 # (by default client will always send following info:
275 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
276 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
276 # start with HTTP* this list be extended with additional keywords here
277 # start with HTTP* this list be extended with additional keywords here
277 errormator.environ_keys_whitelist =
278 errormator.environ_keys_whitelist =
278
279
279
280
280 # list of keywords that should be blanked from request object
281 # list of keywords that should be blanked from request object
281 # can be string with comma separated list of words in lowercase
282 # can be string with comma separated list of words in lowercase
282 # (by default client will always blank keys that contain following words
283 # (by default client will always blank keys that contain following words
283 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
284 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
284 # this list be extended with additional keywords set here
285 # this list be extended with additional keywords set here
285 errormator.request_keys_blacklist =
286 errormator.request_keys_blacklist =
286
287
287
288
288 # list of namespaces that should be ignores when gathering log entries
289 # list of namespaces that should be ignores when gathering log entries
289 # can be string with comma separated list of namespaces
290 # can be string with comma separated list of namespaces
290 # (by default the client ignores own entries: errormator_client.client)
291 # (by default the client ignores own entries: errormator_client.client)
291 errormator.log_namespace_blacklist =
292 errormator.log_namespace_blacklist =
292
293
293
294
294 ################
295 ################
295 ### [sentry] ###
296 ### [sentry] ###
296 ################
297 ################
297
298
298 # sentry is a alternative open source error aggregator
299 # sentry is a alternative open source error aggregator
299 # you must install python packages `sentry` and `raven` to enable
300 # you must install python packages `sentry` and `raven` to enable
300
301
301 sentry.dsn = YOUR_DNS
302 sentry.dsn = YOUR_DNS
302 sentry.servers =
303 sentry.servers =
303 sentry.name =
304 sentry.name =
304 sentry.key =
305 sentry.key =
305 sentry.public_key =
306 sentry.public_key =
306 sentry.secret_key =
307 sentry.secret_key =
307 sentry.project =
308 sentry.project =
308 sentry.site =
309 sentry.site =
309 sentry.include_paths =
310 sentry.include_paths =
310 sentry.exclude_paths =
311 sentry.exclude_paths =
311
312
312
313
313 ################################################################################
314 ################################################################################
314 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
315 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
315 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
316 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
316 ## execute malicious code after an exception is raised. ##
317 ## execute malicious code after an exception is raised. ##
317 ################################################################################
318 ################################################################################
318 #set debug = false
319 #set debug = false
319
320
320 ##################################
321 ##################################
321 ### LOGVIEW CONFIG ###
322 ### LOGVIEW CONFIG ###
322 ##################################
323 ##################################
323 logview.sqlalchemy = #faa
324 logview.sqlalchemy = #faa
324 logview.pylons.templating = #bfb
325 logview.pylons.templating = #bfb
325 logview.pylons.util = #eee
326 logview.pylons.util = #eee
326
327
327 #########################################################
328 #########################################################
328 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
329 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
329 #########################################################
330 #########################################################
330 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
331 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
331 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
332 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
332 sqlalchemy.db1.echo = false
333 sqlalchemy.db1.echo = false
333 sqlalchemy.db1.pool_recycle = 3600
334 sqlalchemy.db1.pool_recycle = 3600
334 sqlalchemy.db1.convert_unicode = true
335 sqlalchemy.db1.convert_unicode = true
335
336
336 ################################
337 ################################
337 ### LOGGING CONFIGURATION ####
338 ### LOGGING CONFIGURATION ####
338 ################################
339 ################################
339 [loggers]
340 [loggers]
340 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
341 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
341
342
342 [handlers]
343 [handlers]
343 keys = console, console_sql
344 keys = console, console_sql
344
345
345 [formatters]
346 [formatters]
346 keys = generic, color_formatter, color_formatter_sql
347 keys = generic, color_formatter, color_formatter_sql
347
348
348 #############
349 #############
349 ## LOGGERS ##
350 ## LOGGERS ##
350 #############
351 #############
351 [logger_root]
352 [logger_root]
352 level = NOTSET
353 level = NOTSET
353 handlers = console
354 handlers = console
354
355
355 [logger_routes]
356 [logger_routes]
356 level = DEBUG
357 level = DEBUG
357 handlers =
358 handlers =
358 qualname = routes.middleware
359 qualname = routes.middleware
359 # "level = DEBUG" logs the route matched and routing variables.
360 # "level = DEBUG" logs the route matched and routing variables.
360 propagate = 1
361 propagate = 1
361
362
362 [logger_beaker]
363 [logger_beaker]
363 level = DEBUG
364 level = DEBUG
364 handlers =
365 handlers =
365 qualname = beaker.container
366 qualname = beaker.container
366 propagate = 1
367 propagate = 1
367
368
368 [logger_templates]
369 [logger_templates]
369 level = INFO
370 level = INFO
370 handlers =
371 handlers =
371 qualname = pylons.templating
372 qualname = pylons.templating
372 propagate = 1
373 propagate = 1
373
374
374 [logger_rhodecode]
375 [logger_rhodecode]
375 level = DEBUG
376 level = DEBUG
376 handlers =
377 handlers =
377 qualname = rhodecode
378 qualname = rhodecode
378 propagate = 1
379 propagate = 1
379
380
380 [logger_sqlalchemy]
381 [logger_sqlalchemy]
381 level = INFO
382 level = INFO
382 handlers = console_sql
383 handlers = console_sql
383 qualname = sqlalchemy.engine
384 qualname = sqlalchemy.engine
384 propagate = 0
385 propagate = 0
385
386
386 [logger_whoosh_indexer]
387 [logger_whoosh_indexer]
387 level = DEBUG
388 level = DEBUG
388 handlers =
389 handlers =
389 qualname = whoosh_indexer
390 qualname = whoosh_indexer
390 propagate = 1
391 propagate = 1
391
392
392 ##############
393 ##############
393 ## HANDLERS ##
394 ## HANDLERS ##
394 ##############
395 ##############
395
396
396 [handler_console]
397 [handler_console]
397 class = StreamHandler
398 class = StreamHandler
398 args = (sys.stderr,)
399 args = (sys.stderr,)
399 level = DEBUG
400 level = DEBUG
400 formatter = color_formatter
401 formatter = color_formatter
401
402
402 [handler_console_sql]
403 [handler_console_sql]
403 class = StreamHandler
404 class = StreamHandler
404 args = (sys.stderr,)
405 args = (sys.stderr,)
405 level = DEBUG
406 level = DEBUG
406 formatter = color_formatter_sql
407 formatter = color_formatter_sql
407
408
408 ################
409 ################
409 ## FORMATTERS ##
410 ## FORMATTERS ##
410 ################
411 ################
411
412
412 [formatter_generic]
413 [formatter_generic]
413 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
414 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
414 datefmt = %Y-%m-%d %H:%M:%S
415 datefmt = %Y-%m-%d %H:%M:%S
415
416
416 [formatter_color_formatter]
417 [formatter_color_formatter]
417 class=rhodecode.lib.colored_formatter.ColorFormatter
418 class=rhodecode.lib.colored_formatter.ColorFormatter
418 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
419 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
419 datefmt = %Y-%m-%d %H:%M:%S
420 datefmt = %Y-%m-%d %H:%M:%S
420
421
421 [formatter_color_formatter_sql]
422 [formatter_color_formatter_sql]
422 class=rhodecode.lib.colored_formatter.ColorFormatterSql
423 class=rhodecode.lib.colored_formatter.ColorFormatterSql
423 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
424 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
424 datefmt = %Y-%m-%d %H:%M:%S
425 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,424 +1,425 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 vcs_full_cache = True
63 force_https = false
64 force_https = false
64 commit_parse_limit = 50
65 commit_parse_limit = 50
65 use_gravatar = true
66 use_gravatar = true
66
67
67 ## alternative_gravatar_url allows you to use your own avatar server application
68 ## alternative_gravatar_url allows you to use your own avatar server application
68 ## the following parts of the URL will be replaced
69 ## the following parts of the URL will be replaced
69 ## {email} user email
70 ## {email} user email
70 ## {md5email} md5 hash of the user email (like at gravatar.com)
71 ## {md5email} md5 hash of the user email (like at gravatar.com)
71 ## {size} size of the image that is expected from the server application
72 ## {size} size of the image that is expected from the server application
72 ## {scheme} http/https from RhodeCode server
73 ## {scheme} http/https from RhodeCode server
73 ## {netloc} network location from RhodeCode server
74 ## {netloc} network location from RhodeCode server
74 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
75 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
75 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
76 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
76
77
77 container_auth_enabled = false
78 container_auth_enabled = false
78 proxypass_auth_enabled = false
79 proxypass_auth_enabled = false
79 ## default encoding used to convert from and to unicode
80 ## default encoding used to convert from and to unicode
80 ## can be also a comma seperated list of encoding in case of mixed encodings
81 ## can be also a comma seperated list of encoding in case of mixed encodings
81 default_encoding = utf8
82 default_encoding = utf8
82
83
83 ## overwrite schema of clone url
84 ## overwrite schema of clone url
84 ## available vars:
85 ## available vars:
85 ## scheme - http/https
86 ## scheme - http/https
86 ## user - current user
87 ## user - current user
87 ## pass - password
88 ## pass - password
88 ## netloc - network location
89 ## netloc - network location
89 ## path - usually repo_name
90 ## path - usually repo_name
90
91
91 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
92 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
92
93
93 ## issue tracking mapping for commits messages
94 ## issue tracking mapping for commits messages
94 ## comment out issue_pat, issue_server, issue_prefix to enable
95 ## comment out issue_pat, issue_server, issue_prefix to enable
95
96
96 ## pattern to get the issues from commit messages
97 ## pattern to get the issues from commit messages
97 ## default one used here is #<numbers> with a regex passive group for `#`
98 ## default one used here is #<numbers> with a regex passive group for `#`
98 ## {id} will be all groups matched from this pattern
99 ## {id} will be all groups matched from this pattern
99
100
100 issue_pat = (?:\s*#)(\d+)
101 issue_pat = (?:\s*#)(\d+)
101
102
102 ## server url to the issue, each {id} will be replaced with match
103 ## server url to the issue, each {id} will be replaced with match
103 ## fetched from the regex and {repo} is replaced with full repository name
104 ## fetched from the regex and {repo} is replaced with full repository name
104 ## including groups {repo_name} is replaced with just name of repo
105 ## including groups {repo_name} is replaced with just name of repo
105
106
106 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
107 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
107
108
108 ## prefix to add to link to indicate it's an url
109 ## prefix to add to link to indicate it's an url
109 ## #314 will be replaced by <issue_prefix><id>
110 ## #314 will be replaced by <issue_prefix><id>
110
111
111 issue_prefix = #
112 issue_prefix = #
112
113
113 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
114 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
114 ## multiple patterns, to other issues server, wiki or others
115 ## multiple patterns, to other issues server, wiki or others
115 ## below an example how to create a wiki pattern
116 ## below an example how to create a wiki pattern
116 # #wiki-some-id -> https://mywiki.com/some-id
117 # #wiki-some-id -> https://mywiki.com/some-id
117
118
118 #issue_pat_wiki = (?:wiki-)(.+)
119 #issue_pat_wiki = (?:wiki-)(.+)
119 #issue_server_link_wiki = https://mywiki.com/{id}
120 #issue_server_link_wiki = https://mywiki.com/{id}
120 #issue_prefix_wiki = WIKI-
121 #issue_prefix_wiki = WIKI-
121
122
122
123
123 ## instance-id prefix
124 ## instance-id prefix
124 ## a prefix key for this instance used for cache invalidation when running
125 ## a prefix key for this instance used for cache invalidation when running
125 ## multiple instances of rhodecode, make sure it's globally unique for
126 ## multiple instances of rhodecode, make sure it's globally unique for
126 ## all running rhodecode instances. Leave empty if you don't use it
127 ## all running rhodecode instances. Leave empty if you don't use it
127 instance_id =
128 instance_id =
128
129
129 ## alternative return HTTP header for failed authentication. Default HTTP
130 ## alternative return HTTP header for failed authentication. Default HTTP
130 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
131 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
131 ## handling that. Set this variable to 403 to return HTTPForbidden
132 ## handling that. Set this variable to 403 to return HTTPForbidden
132 auth_ret_code =
133 auth_ret_code =
133
134
134 ####################################
135 ####################################
135 ### CELERY CONFIG ####
136 ### CELERY CONFIG ####
136 ####################################
137 ####################################
137 use_celery = false
138 use_celery = false
138 broker.host = localhost
139 broker.host = localhost
139 broker.vhost = rabbitmqhost
140 broker.vhost = rabbitmqhost
140 broker.port = 5672
141 broker.port = 5672
141 broker.user = rabbitmq
142 broker.user = rabbitmq
142 broker.password = qweqwe
143 broker.password = qweqwe
143
144
144 celery.imports = rhodecode.lib.celerylib.tasks
145 celery.imports = rhodecode.lib.celerylib.tasks
145
146
146 celery.result.backend = amqp
147 celery.result.backend = amqp
147 celery.result.dburi = amqp://
148 celery.result.dburi = amqp://
148 celery.result.serialier = json
149 celery.result.serialier = json
149
150
150 #celery.send.task.error.emails = true
151 #celery.send.task.error.emails = true
151 #celery.amqp.task.result.expires = 18000
152 #celery.amqp.task.result.expires = 18000
152
153
153 celeryd.concurrency = 2
154 celeryd.concurrency = 2
154 #celeryd.log.file = celeryd.log
155 #celeryd.log.file = celeryd.log
155 celeryd.log.level = debug
156 celeryd.log.level = debug
156 celeryd.max.tasks.per.child = 1
157 celeryd.max.tasks.per.child = 1
157
158
158 #tasks will never be sent to the queue, but executed locally instead.
159 #tasks will never be sent to the queue, but executed locally instead.
159 celery.always.eager = false
160 celery.always.eager = false
160
161
161 ####################################
162 ####################################
162 ### BEAKER CACHE ####
163 ### BEAKER CACHE ####
163 ####################################
164 ####################################
164 beaker.cache.data_dir=%(here)s/data/cache/data
165 beaker.cache.data_dir=%(here)s/data/cache/data
165 beaker.cache.lock_dir=%(here)s/data/cache/lock
166 beaker.cache.lock_dir=%(here)s/data/cache/lock
166
167
167 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
168 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
168
169
169 beaker.cache.super_short_term.type=memory
170 beaker.cache.super_short_term.type=memory
170 beaker.cache.super_short_term.expire=10
171 beaker.cache.super_short_term.expire=10
171 beaker.cache.super_short_term.key_length = 256
172 beaker.cache.super_short_term.key_length = 256
172
173
173 beaker.cache.short_term.type=memory
174 beaker.cache.short_term.type=memory
174 beaker.cache.short_term.expire=60
175 beaker.cache.short_term.expire=60
175 beaker.cache.short_term.key_length = 256
176 beaker.cache.short_term.key_length = 256
176
177
177 beaker.cache.long_term.type=memory
178 beaker.cache.long_term.type=memory
178 beaker.cache.long_term.expire=36000
179 beaker.cache.long_term.expire=36000
179 beaker.cache.long_term.key_length = 256
180 beaker.cache.long_term.key_length = 256
180
181
181 beaker.cache.sql_cache_short.type=memory
182 beaker.cache.sql_cache_short.type=memory
182 beaker.cache.sql_cache_short.expire=10
183 beaker.cache.sql_cache_short.expire=10
183 beaker.cache.sql_cache_short.key_length = 256
184 beaker.cache.sql_cache_short.key_length = 256
184
185
185 beaker.cache.sql_cache_med.type=memory
186 beaker.cache.sql_cache_med.type=memory
186 beaker.cache.sql_cache_med.expire=360
187 beaker.cache.sql_cache_med.expire=360
187 beaker.cache.sql_cache_med.key_length = 256
188 beaker.cache.sql_cache_med.key_length = 256
188
189
189 beaker.cache.sql_cache_long.type=file
190 beaker.cache.sql_cache_long.type=file
190 beaker.cache.sql_cache_long.expire=3600
191 beaker.cache.sql_cache_long.expire=3600
191 beaker.cache.sql_cache_long.key_length = 256
192 beaker.cache.sql_cache_long.key_length = 256
192
193
193 ####################################
194 ####################################
194 ### BEAKER SESSION ####
195 ### BEAKER SESSION ####
195 ####################################
196 ####################################
196 ## Type of storage used for the session, current types are
197 ## Type of storage used for the session, current types are
197 ## dbm, file, memcached, database, and memory.
198 ## dbm, file, memcached, database, and memory.
198 ## The storage uses the Container API
199 ## The storage uses the Container API
199 ## that is also used by the cache system.
200 ## that is also used by the cache system.
200
201
201 ## db session ##
202 ## db session ##
202 #beaker.session.type = ext:database
203 #beaker.session.type = ext:database
203 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
204 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
204 #beaker.session.table_name = db_session
205 #beaker.session.table_name = db_session
205
206
206 ## encrypted cookie client side session, good for many instances ##
207 ## encrypted cookie client side session, good for many instances ##
207 #beaker.session.type = cookie
208 #beaker.session.type = cookie
208
209
209 ## file based cookies (default) ##
210 ## file based cookies (default) ##
210 #beaker.session.type = file
211 #beaker.session.type = file
211
212
212
213
213 beaker.session.key = rhodecode
214 beaker.session.key = rhodecode
214 ## secure cookie requires AES python libraries ##
215 ## secure cookie requires AES python libraries ##
215 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
216 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
216 #beaker.session.validate_key = 9712sds2212c--zxc123
217 #beaker.session.validate_key = 9712sds2212c--zxc123
217 ## sets session as invalid if it haven't been accessed for given amount of time
218 ## sets session as invalid if it haven't been accessed for given amount of time
218 beaker.session.timeout = 2592000
219 beaker.session.timeout = 2592000
219 beaker.session.httponly = true
220 beaker.session.httponly = true
220 #beaker.session.cookie_path = /<your-prefix>
221 #beaker.session.cookie_path = /<your-prefix>
221
222
222 ## uncomment for https secure cookie ##
223 ## uncomment for https secure cookie ##
223 beaker.session.secure = false
224 beaker.session.secure = false
224
225
225 ## auto save the session to not to use .save() ##
226 ## auto save the session to not to use .save() ##
226 beaker.session.auto = False
227 beaker.session.auto = False
227
228
228 ## default cookie expiration time in seconds `true` expire at browser close ##
229 ## default cookie expiration time in seconds `true` expire at browser close ##
229 #beaker.session.cookie_expires = 3600
230 #beaker.session.cookie_expires = 3600
230
231
231
232
232 ############################
233 ############################
233 ## ERROR HANDLING SYSTEMS ##
234 ## ERROR HANDLING SYSTEMS ##
234 ############################
235 ############################
235
236
236 ####################
237 ####################
237 ### [errormator] ###
238 ### [errormator] ###
238 ####################
239 ####################
239
240
240 # Errormator is tailored to work with RhodeCode, see
241 # Errormator is tailored to work with RhodeCode, see
241 # http://errormator.com for details how to obtain an account
242 # http://errormator.com for details how to obtain an account
242 # you must install python package `errormator_client` to make it work
243 # you must install python package `errormator_client` to make it work
243
244
244 # errormator enabled
245 # errormator enabled
245 errormator = true
246 errormator = true
246
247
247 errormator.server_url = https://api.errormator.com
248 errormator.server_url = https://api.errormator.com
248 errormator.api_key = YOUR_API_KEY
249 errormator.api_key = YOUR_API_KEY
249
250
250 # TWEAK AMOUNT OF INFO SENT HERE
251 # TWEAK AMOUNT OF INFO SENT HERE
251
252
252 # enables 404 error logging (default False)
253 # enables 404 error logging (default False)
253 errormator.report_404 = false
254 errormator.report_404 = false
254
255
255 # time in seconds after request is considered being slow (default 1)
256 # time in seconds after request is considered being slow (default 1)
256 errormator.slow_request_time = 1
257 errormator.slow_request_time = 1
257
258
258 # record slow requests in application
259 # record slow requests in application
259 # (needs to be enabled for slow datastore recording and time tracking)
260 # (needs to be enabled for slow datastore recording and time tracking)
260 errormator.slow_requests = true
261 errormator.slow_requests = true
261
262
262 # enable hooking to application loggers
263 # enable hooking to application loggers
263 # errormator.logging = true
264 # errormator.logging = true
264
265
265 # minimum log level for log capture
266 # minimum log level for log capture
266 # errormator.logging.level = WARNING
267 # errormator.logging.level = WARNING
267
268
268 # send logs only from erroneous/slow requests
269 # send logs only from erroneous/slow requests
269 # (saves API quota for intensive logging)
270 # (saves API quota for intensive logging)
270 errormator.logging_on_error = false
271 errormator.logging_on_error = false
271
272
272 # list of additonal keywords that should be grabbed from environ object
273 # list of additonal keywords that should be grabbed from environ object
273 # can be string with comma separated list of words in lowercase
274 # can be string with comma separated list of words in lowercase
274 # (by default client will always send following info:
275 # (by default client will always send following info:
275 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
276 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
276 # start with HTTP* this list be extended with additional keywords here
277 # start with HTTP* this list be extended with additional keywords here
277 errormator.environ_keys_whitelist =
278 errormator.environ_keys_whitelist =
278
279
279
280
280 # list of keywords that should be blanked from request object
281 # list of keywords that should be blanked from request object
281 # can be string with comma separated list of words in lowercase
282 # can be string with comma separated list of words in lowercase
282 # (by default client will always blank keys that contain following words
283 # (by default client will always blank keys that contain following words
283 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
284 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
284 # this list be extended with additional keywords set here
285 # this list be extended with additional keywords set here
285 errormator.request_keys_blacklist =
286 errormator.request_keys_blacklist =
286
287
287
288
288 # list of namespaces that should be ignores when gathering log entries
289 # list of namespaces that should be ignores when gathering log entries
289 # can be string with comma separated list of namespaces
290 # can be string with comma separated list of namespaces
290 # (by default the client ignores own entries: errormator_client.client)
291 # (by default the client ignores own entries: errormator_client.client)
291 errormator.log_namespace_blacklist =
292 errormator.log_namespace_blacklist =
292
293
293
294
294 ################
295 ################
295 ### [sentry] ###
296 ### [sentry] ###
296 ################
297 ################
297
298
298 # sentry is a alternative open source error aggregator
299 # sentry is a alternative open source error aggregator
299 # you must install python packages `sentry` and `raven` to enable
300 # you must install python packages `sentry` and `raven` to enable
300
301
301 sentry.dsn = YOUR_DNS
302 sentry.dsn = YOUR_DNS
302 sentry.servers =
303 sentry.servers =
303 sentry.name =
304 sentry.name =
304 sentry.key =
305 sentry.key =
305 sentry.public_key =
306 sentry.public_key =
306 sentry.secret_key =
307 sentry.secret_key =
307 sentry.project =
308 sentry.project =
308 sentry.site =
309 sentry.site =
309 sentry.include_paths =
310 sentry.include_paths =
310 sentry.exclude_paths =
311 sentry.exclude_paths =
311
312
312
313
313 ################################################################################
314 ################################################################################
314 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
315 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
315 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
316 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
316 ## execute malicious code after an exception is raised. ##
317 ## execute malicious code after an exception is raised. ##
317 ################################################################################
318 ################################################################################
318 set debug = false
319 set debug = false
319
320
320 ##################################
321 ##################################
321 ### LOGVIEW CONFIG ###
322 ### LOGVIEW CONFIG ###
322 ##################################
323 ##################################
323 logview.sqlalchemy = #faa
324 logview.sqlalchemy = #faa
324 logview.pylons.templating = #bfb
325 logview.pylons.templating = #bfb
325 logview.pylons.util = #eee
326 logview.pylons.util = #eee
326
327
327 #########################################################
328 #########################################################
328 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
329 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
329 #########################################################
330 #########################################################
330 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
331 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
331 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
332 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
332 sqlalchemy.db1.echo = false
333 sqlalchemy.db1.echo = false
333 sqlalchemy.db1.pool_recycle = 3600
334 sqlalchemy.db1.pool_recycle = 3600
334 sqlalchemy.db1.convert_unicode = true
335 sqlalchemy.db1.convert_unicode = true
335
336
336 ################################
337 ################################
337 ### LOGGING CONFIGURATION ####
338 ### LOGGING CONFIGURATION ####
338 ################################
339 ################################
339 [loggers]
340 [loggers]
340 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
341 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
341
342
342 [handlers]
343 [handlers]
343 keys = console, console_sql
344 keys = console, console_sql
344
345
345 [formatters]
346 [formatters]
346 keys = generic, color_formatter, color_formatter_sql
347 keys = generic, color_formatter, color_formatter_sql
347
348
348 #############
349 #############
349 ## LOGGERS ##
350 ## LOGGERS ##
350 #############
351 #############
351 [logger_root]
352 [logger_root]
352 level = NOTSET
353 level = NOTSET
353 handlers = console
354 handlers = console
354
355
355 [logger_routes]
356 [logger_routes]
356 level = DEBUG
357 level = DEBUG
357 handlers =
358 handlers =
358 qualname = routes.middleware
359 qualname = routes.middleware
359 # "level = DEBUG" logs the route matched and routing variables.
360 # "level = DEBUG" logs the route matched and routing variables.
360 propagate = 1
361 propagate = 1
361
362
362 [logger_beaker]
363 [logger_beaker]
363 level = DEBUG
364 level = DEBUG
364 handlers =
365 handlers =
365 qualname = beaker.container
366 qualname = beaker.container
366 propagate = 1
367 propagate = 1
367
368
368 [logger_templates]
369 [logger_templates]
369 level = INFO
370 level = INFO
370 handlers =
371 handlers =
371 qualname = pylons.templating
372 qualname = pylons.templating
372 propagate = 1
373 propagate = 1
373
374
374 [logger_rhodecode]
375 [logger_rhodecode]
375 level = DEBUG
376 level = DEBUG
376 handlers =
377 handlers =
377 qualname = rhodecode
378 qualname = rhodecode
378 propagate = 1
379 propagate = 1
379
380
380 [logger_sqlalchemy]
381 [logger_sqlalchemy]
381 level = INFO
382 level = INFO
382 handlers = console_sql
383 handlers = console_sql
383 qualname = sqlalchemy.engine
384 qualname = sqlalchemy.engine
384 propagate = 0
385 propagate = 0
385
386
386 [logger_whoosh_indexer]
387 [logger_whoosh_indexer]
387 level = DEBUG
388 level = DEBUG
388 handlers =
389 handlers =
389 qualname = whoosh_indexer
390 qualname = whoosh_indexer
390 propagate = 1
391 propagate = 1
391
392
392 ##############
393 ##############
393 ## HANDLERS ##
394 ## HANDLERS ##
394 ##############
395 ##############
395
396
396 [handler_console]
397 [handler_console]
397 class = StreamHandler
398 class = StreamHandler
398 args = (sys.stderr,)
399 args = (sys.stderr,)
399 level = INFO
400 level = INFO
400 formatter = generic
401 formatter = generic
401
402
402 [handler_console_sql]
403 [handler_console_sql]
403 class = StreamHandler
404 class = StreamHandler
404 args = (sys.stderr,)
405 args = (sys.stderr,)
405 level = WARN
406 level = WARN
406 formatter = generic
407 formatter = generic
407
408
408 ################
409 ################
409 ## FORMATTERS ##
410 ## FORMATTERS ##
410 ################
411 ################
411
412
412 [formatter_generic]
413 [formatter_generic]
413 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
414 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
414 datefmt = %Y-%m-%d %H:%M:%S
415 datefmt = %Y-%m-%d %H:%M:%S
415
416
416 [formatter_color_formatter]
417 [formatter_color_formatter]
417 class=rhodecode.lib.colored_formatter.ColorFormatter
418 class=rhodecode.lib.colored_formatter.ColorFormatter
418 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
419 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
419 datefmt = %Y-%m-%d %H:%M:%S
420 datefmt = %Y-%m-%d %H:%M:%S
420
421
421 [formatter_color_formatter_sql]
422 [formatter_color_formatter_sql]
422 class=rhodecode.lib.colored_formatter.ColorFormatterSql
423 class=rhodecode.lib.colored_formatter.ColorFormatterSql
423 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
424 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
424 datefmt = %Y-%m-%d %H:%M:%S
425 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,434 +1,435 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 vcs_full_cache = True
63 force_https = false
64 force_https = false
64 commit_parse_limit = 50
65 commit_parse_limit = 50
65 use_gravatar = true
66 use_gravatar = true
66
67
67 ## alternative_gravatar_url allows you to use your own avatar server application
68 ## alternative_gravatar_url allows you to use your own avatar server application
68 ## the following parts of the URL will be replaced
69 ## the following parts of the URL will be replaced
69 ## {email} user email
70 ## {email} user email
70 ## {md5email} md5 hash of the user email (like at gravatar.com)
71 ## {md5email} md5 hash of the user email (like at gravatar.com)
71 ## {size} size of the image that is expected from the server application
72 ## {size} size of the image that is expected from the server application
72 ## {scheme} http/https from RhodeCode server
73 ## {scheme} http/https from RhodeCode server
73 ## {netloc} network location from RhodeCode server
74 ## {netloc} network location from RhodeCode server
74 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
75 #alternative_gravatar_url = http://myavatarserver.com/getbyemail/{email}/{size}
75 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
76 #alternative_gravatar_url = http://myavatarserver.com/getbymd5/{md5email}?s={size}
76
77
77 container_auth_enabled = false
78 container_auth_enabled = false
78 proxypass_auth_enabled = false
79 proxypass_auth_enabled = false
79 ## default encoding used to convert from and to unicode
80 ## default encoding used to convert from and to unicode
80 ## can be also a comma seperated list of encoding in case of mixed encodings
81 ## can be also a comma seperated list of encoding in case of mixed encodings
81 default_encoding = utf8
82 default_encoding = utf8
82
83
83 ## overwrite schema of clone url
84 ## overwrite schema of clone url
84 ## available vars:
85 ## available vars:
85 ## scheme - http/https
86 ## scheme - http/https
86 ## user - current user
87 ## user - current user
87 ## pass - password
88 ## pass - password
88 ## netloc - network location
89 ## netloc - network location
89 ## path - usually repo_name
90 ## path - usually repo_name
90
91
91 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
92 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
92
93
93 ## issue tracking mapping for commits messages
94 ## issue tracking mapping for commits messages
94 ## comment out issue_pat, issue_server, issue_prefix to enable
95 ## comment out issue_pat, issue_server, issue_prefix to enable
95
96
96 ## pattern to get the issues from commit messages
97 ## pattern to get the issues from commit messages
97 ## default one used here is #<numbers> with a regex passive group for `#`
98 ## default one used here is #<numbers> with a regex passive group for `#`
98 ## {id} will be all groups matched from this pattern
99 ## {id} will be all groups matched from this pattern
99
100
100 issue_pat = (?:\s*#)(\d+)
101 issue_pat = (?:\s*#)(\d+)
101
102
102 ## server url to the issue, each {id} will be replaced with match
103 ## server url to the issue, each {id} will be replaced with match
103 ## fetched from the regex and {repo} is replaced with full repository name
104 ## fetched from the regex and {repo} is replaced with full repository name
104 ## including groups {repo_name} is replaced with just name of repo
105 ## including groups {repo_name} is replaced with just name of repo
105
106
106 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
107 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
107
108
108 ## prefix to add to link to indicate it's an url
109 ## prefix to add to link to indicate it's an url
109 ## #314 will be replaced by <issue_prefix><id>
110 ## #314 will be replaced by <issue_prefix><id>
110
111
111 issue_prefix = #
112 issue_prefix = #
112
113
113 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
114 ## issue_pat, issue_server_link, issue_prefix can have suffixes to specify
114 ## multiple patterns, to other issues server, wiki or others
115 ## multiple patterns, to other issues server, wiki or others
115 ## below an example how to create a wiki pattern
116 ## below an example how to create a wiki pattern
116 # #wiki-some-id -> https://mywiki.com/some-id
117 # #wiki-some-id -> https://mywiki.com/some-id
117
118
118 #issue_pat_wiki = (?:wiki-)(.+)
119 #issue_pat_wiki = (?:wiki-)(.+)
119 #issue_server_link_wiki = https://mywiki.com/{id}
120 #issue_server_link_wiki = https://mywiki.com/{id}
120 #issue_prefix_wiki = WIKI-
121 #issue_prefix_wiki = WIKI-
121
122
122
123
123 ## instance-id prefix
124 ## instance-id prefix
124 ## a prefix key for this instance used for cache invalidation when running
125 ## a prefix key for this instance used for cache invalidation when running
125 ## multiple instances of rhodecode, make sure it's globally unique for
126 ## multiple instances of rhodecode, make sure it's globally unique for
126 ## all running rhodecode instances. Leave empty if you don't use it
127 ## all running rhodecode instances. Leave empty if you don't use it
127 instance_id =
128 instance_id =
128
129
129 ## alternative return HTTP header for failed authentication. Default HTTP
130 ## alternative return HTTP header for failed authentication. Default HTTP
130 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
131 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
131 ## handling that. Set this variable to 403 to return HTTPForbidden
132 ## handling that. Set this variable to 403 to return HTTPForbidden
132 auth_ret_code =
133 auth_ret_code =
133
134
134 ####################################
135 ####################################
135 ### CELERY CONFIG ####
136 ### CELERY CONFIG ####
136 ####################################
137 ####################################
137 use_celery = false
138 use_celery = false
138 broker.host = localhost
139 broker.host = localhost
139 broker.vhost = rabbitmqhost
140 broker.vhost = rabbitmqhost
140 broker.port = 5672
141 broker.port = 5672
141 broker.user = rabbitmq
142 broker.user = rabbitmq
142 broker.password = qweqwe
143 broker.password = qweqwe
143
144
144 celery.imports = rhodecode.lib.celerylib.tasks
145 celery.imports = rhodecode.lib.celerylib.tasks
145
146
146 celery.result.backend = amqp
147 celery.result.backend = amqp
147 celery.result.dburi = amqp://
148 celery.result.dburi = amqp://
148 celery.result.serialier = json
149 celery.result.serialier = json
149
150
150 #celery.send.task.error.emails = true
151 #celery.send.task.error.emails = true
151 #celery.amqp.task.result.expires = 18000
152 #celery.amqp.task.result.expires = 18000
152
153
153 celeryd.concurrency = 2
154 celeryd.concurrency = 2
154 #celeryd.log.file = celeryd.log
155 #celeryd.log.file = celeryd.log
155 celeryd.log.level = debug
156 celeryd.log.level = debug
156 celeryd.max.tasks.per.child = 1
157 celeryd.max.tasks.per.child = 1
157
158
158 #tasks will never be sent to the queue, but executed locally instead.
159 #tasks will never be sent to the queue, but executed locally instead.
159 celery.always.eager = false
160 celery.always.eager = false
160
161
161 ####################################
162 ####################################
162 ### BEAKER CACHE ####
163 ### BEAKER CACHE ####
163 ####################################
164 ####################################
164 beaker.cache.data_dir=%(here)s/data/cache/data
165 beaker.cache.data_dir=%(here)s/data/cache/data
165 beaker.cache.lock_dir=%(here)s/data/cache/lock
166 beaker.cache.lock_dir=%(here)s/data/cache/lock
166
167
167 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
168 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
168
169
169 beaker.cache.super_short_term.type=memory
170 beaker.cache.super_short_term.type=memory
170 beaker.cache.super_short_term.expire=10
171 beaker.cache.super_short_term.expire=10
171 beaker.cache.super_short_term.key_length = 256
172 beaker.cache.super_short_term.key_length = 256
172
173
173 beaker.cache.short_term.type=memory
174 beaker.cache.short_term.type=memory
174 beaker.cache.short_term.expire=60
175 beaker.cache.short_term.expire=60
175 beaker.cache.short_term.key_length = 256
176 beaker.cache.short_term.key_length = 256
176
177
177 beaker.cache.long_term.type=memory
178 beaker.cache.long_term.type=memory
178 beaker.cache.long_term.expire=36000
179 beaker.cache.long_term.expire=36000
179 beaker.cache.long_term.key_length = 256
180 beaker.cache.long_term.key_length = 256
180
181
181 beaker.cache.sql_cache_short.type=memory
182 beaker.cache.sql_cache_short.type=memory
182 beaker.cache.sql_cache_short.expire=10
183 beaker.cache.sql_cache_short.expire=10
183 beaker.cache.sql_cache_short.key_length = 256
184 beaker.cache.sql_cache_short.key_length = 256
184
185
185 beaker.cache.sql_cache_med.type=memory
186 beaker.cache.sql_cache_med.type=memory
186 beaker.cache.sql_cache_med.expire=360
187 beaker.cache.sql_cache_med.expire=360
187 beaker.cache.sql_cache_med.key_length = 256
188 beaker.cache.sql_cache_med.key_length = 256
188
189
189 beaker.cache.sql_cache_long.type=file
190 beaker.cache.sql_cache_long.type=file
190 beaker.cache.sql_cache_long.expire=3600
191 beaker.cache.sql_cache_long.expire=3600
191 beaker.cache.sql_cache_long.key_length = 256
192 beaker.cache.sql_cache_long.key_length = 256
192
193
193 ####################################
194 ####################################
194 ### BEAKER SESSION ####
195 ### BEAKER SESSION ####
195 ####################################
196 ####################################
196 ## Type of storage used for the session, current types are
197 ## Type of storage used for the session, current types are
197 ## dbm, file, memcached, database, and memory.
198 ## dbm, file, memcached, database, and memory.
198 ## The storage uses the Container API
199 ## The storage uses the Container API
199 ## that is also used by the cache system.
200 ## that is also used by the cache system.
200
201
201 ## db session ##
202 ## db session ##
202 #beaker.session.type = ext:database
203 #beaker.session.type = ext:database
203 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
204 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
204 #beaker.session.table_name = db_session
205 #beaker.session.table_name = db_session
205
206
206 ## encrypted cookie client side session, good for many instances ##
207 ## encrypted cookie client side session, good for many instances ##
207 #beaker.session.type = cookie
208 #beaker.session.type = cookie
208
209
209 ## file based cookies (default) ##
210 ## file based cookies (default) ##
210 #beaker.session.type = file
211 #beaker.session.type = file
211
212
212
213
213 beaker.session.key = rhodecode
214 beaker.session.key = rhodecode
214 ## secure cookie requires AES python libraries ##
215 ## secure cookie requires AES python libraries ##
215 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
216 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
216 #beaker.session.validate_key = 9712sds2212c--zxc123
217 #beaker.session.validate_key = 9712sds2212c--zxc123
217 ## sets session as invalid if it haven't been accessed for given amount of time
218 ## sets session as invalid if it haven't been accessed for given amount of time
218 beaker.session.timeout = 2592000
219 beaker.session.timeout = 2592000
219 beaker.session.httponly = true
220 beaker.session.httponly = true
220 #beaker.session.cookie_path = /<your-prefix>
221 #beaker.session.cookie_path = /<your-prefix>
221
222
222 ## uncomment for https secure cookie ##
223 ## uncomment for https secure cookie ##
223 beaker.session.secure = false
224 beaker.session.secure = false
224
225
225 ## auto save the session to not to use .save() ##
226 ## auto save the session to not to use .save() ##
226 beaker.session.auto = False
227 beaker.session.auto = False
227
228
228 ## default cookie expiration time in seconds `true` expire at browser close ##
229 ## default cookie expiration time in seconds `true` expire at browser close ##
229 #beaker.session.cookie_expires = 3600
230 #beaker.session.cookie_expires = 3600
230
231
231
232
232 ############################
233 ############################
233 ## ERROR HANDLING SYSTEMS ##
234 ## ERROR HANDLING SYSTEMS ##
234 ############################
235 ############################
235
236
236 ####################
237 ####################
237 ### [errormator] ###
238 ### [errormator] ###
238 ####################
239 ####################
239
240
240 # Errormator is tailored to work with RhodeCode, see
241 # Errormator is tailored to work with RhodeCode, see
241 # http://errormator.com for details how to obtain an account
242 # http://errormator.com for details how to obtain an account
242 # you must install python package `errormator_client` to make it work
243 # you must install python package `errormator_client` to make it work
243
244
244 # errormator enabled
245 # errormator enabled
245 errormator = true
246 errormator = true
246
247
247 errormator.server_url = https://api.errormator.com
248 errormator.server_url = https://api.errormator.com
248 errormator.api_key = YOUR_API_KEY
249 errormator.api_key = YOUR_API_KEY
249
250
250 # TWEAK AMOUNT OF INFO SENT HERE
251 # TWEAK AMOUNT OF INFO SENT HERE
251
252
252 # enables 404 error logging (default False)
253 # enables 404 error logging (default False)
253 errormator.report_404 = false
254 errormator.report_404 = false
254
255
255 # time in seconds after request is considered being slow (default 1)
256 # time in seconds after request is considered being slow (default 1)
256 errormator.slow_request_time = 1
257 errormator.slow_request_time = 1
257
258
258 # record slow requests in application
259 # record slow requests in application
259 # (needs to be enabled for slow datastore recording and time tracking)
260 # (needs to be enabled for slow datastore recording and time tracking)
260 errormator.slow_requests = true
261 errormator.slow_requests = true
261
262
262 # enable hooking to application loggers
263 # enable hooking to application loggers
263 # errormator.logging = true
264 # errormator.logging = true
264
265
265 # minimum log level for log capture
266 # minimum log level for log capture
266 # errormator.logging.level = WARNING
267 # errormator.logging.level = WARNING
267
268
268 # send logs only from erroneous/slow requests
269 # send logs only from erroneous/slow requests
269 # (saves API quota for intensive logging)
270 # (saves API quota for intensive logging)
270 errormator.logging_on_error = false
271 errormator.logging_on_error = false
271
272
272 # list of additonal keywords that should be grabbed from environ object
273 # list of additonal keywords that should be grabbed from environ object
273 # can be string with comma separated list of words in lowercase
274 # can be string with comma separated list of words in lowercase
274 # (by default client will always send following info:
275 # (by default client will always send following info:
275 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
276 # 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
276 # start with HTTP* this list be extended with additional keywords here
277 # start with HTTP* this list be extended with additional keywords here
277 errormator.environ_keys_whitelist =
278 errormator.environ_keys_whitelist =
278
279
279
280
280 # list of keywords that should be blanked from request object
281 # list of keywords that should be blanked from request object
281 # can be string with comma separated list of words in lowercase
282 # can be string with comma separated list of words in lowercase
282 # (by default client will always blank keys that contain following words
283 # (by default client will always blank keys that contain following words
283 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
284 # 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
284 # this list be extended with additional keywords set here
285 # this list be extended with additional keywords set here
285 errormator.request_keys_blacklist =
286 errormator.request_keys_blacklist =
286
287
287
288
288 # list of namespaces that should be ignores when gathering log entries
289 # list of namespaces that should be ignores when gathering log entries
289 # can be string with comma separated list of namespaces
290 # can be string with comma separated list of namespaces
290 # (by default the client ignores own entries: errormator_client.client)
291 # (by default the client ignores own entries: errormator_client.client)
291 errormator.log_namespace_blacklist =
292 errormator.log_namespace_blacklist =
292
293
293
294
294 ################
295 ################
295 ### [sentry] ###
296 ### [sentry] ###
296 ################
297 ################
297
298
298 # sentry is a alternative open source error aggregator
299 # sentry is a alternative open source error aggregator
299 # you must install python packages `sentry` and `raven` to enable
300 # you must install python packages `sentry` and `raven` to enable
300
301
301 sentry.dsn = YOUR_DNS
302 sentry.dsn = YOUR_DNS
302 sentry.servers =
303 sentry.servers =
303 sentry.name =
304 sentry.name =
304 sentry.key =
305 sentry.key =
305 sentry.public_key =
306 sentry.public_key =
306 sentry.secret_key =
307 sentry.secret_key =
307 sentry.project =
308 sentry.project =
308 sentry.site =
309 sentry.site =
309 sentry.include_paths =
310 sentry.include_paths =
310 sentry.exclude_paths =
311 sentry.exclude_paths =
311
312
312
313
313 ################################################################################
314 ################################################################################
314 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
315 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
315 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
316 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
316 ## execute malicious code after an exception is raised. ##
317 ## execute malicious code after an exception is raised. ##
317 ################################################################################
318 ################################################################################
318 set debug = false
319 set debug = false
319
320
320 ##################################
321 ##################################
321 ### LOGVIEW CONFIG ###
322 ### LOGVIEW CONFIG ###
322 ##################################
323 ##################################
323 logview.sqlalchemy = #faa
324 logview.sqlalchemy = #faa
324 logview.pylons.templating = #bfb
325 logview.pylons.templating = #bfb
325 logview.pylons.util = #eee
326 logview.pylons.util = #eee
326
327
327 #########################################################
328 #########################################################
328 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
329 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
329 #########################################################
330 #########################################################
330
331
331 # SQLITE [default]
332 # SQLITE [default]
332 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
333 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
333
334
334 # POSTGRESQL
335 # POSTGRESQL
335 # sqlalchemy.db1.url = postgresql://user:pass@localhost/rhodecode
336 # sqlalchemy.db1.url = postgresql://user:pass@localhost/rhodecode
336
337
337 # MySQL
338 # MySQL
338 # sqlalchemy.db1.url = mysql://user:pass@localhost/rhodecode
339 # sqlalchemy.db1.url = mysql://user:pass@localhost/rhodecode
339
340
340 # see sqlalchemy docs for others
341 # see sqlalchemy docs for others
341
342
342 sqlalchemy.db1.echo = false
343 sqlalchemy.db1.echo = false
343 sqlalchemy.db1.pool_recycle = 3600
344 sqlalchemy.db1.pool_recycle = 3600
344 sqlalchemy.db1.convert_unicode = true
345 sqlalchemy.db1.convert_unicode = true
345
346
346 ################################
347 ################################
347 ### LOGGING CONFIGURATION ####
348 ### LOGGING CONFIGURATION ####
348 ################################
349 ################################
349 [loggers]
350 [loggers]
350 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
351 keys = root, routes, rhodecode, sqlalchemy, beaker, templates, whoosh_indexer
351
352
352 [handlers]
353 [handlers]
353 keys = console, console_sql
354 keys = console, console_sql
354
355
355 [formatters]
356 [formatters]
356 keys = generic, color_formatter, color_formatter_sql
357 keys = generic, color_formatter, color_formatter_sql
357
358
358 #############
359 #############
359 ## LOGGERS ##
360 ## LOGGERS ##
360 #############
361 #############
361 [logger_root]
362 [logger_root]
362 level = NOTSET
363 level = NOTSET
363 handlers = console
364 handlers = console
364
365
365 [logger_routes]
366 [logger_routes]
366 level = DEBUG
367 level = DEBUG
367 handlers =
368 handlers =
368 qualname = routes.middleware
369 qualname = routes.middleware
369 # "level = DEBUG" logs the route matched and routing variables.
370 # "level = DEBUG" logs the route matched and routing variables.
370 propagate = 1
371 propagate = 1
371
372
372 [logger_beaker]
373 [logger_beaker]
373 level = DEBUG
374 level = DEBUG
374 handlers =
375 handlers =
375 qualname = beaker.container
376 qualname = beaker.container
376 propagate = 1
377 propagate = 1
377
378
378 [logger_templates]
379 [logger_templates]
379 level = INFO
380 level = INFO
380 handlers =
381 handlers =
381 qualname = pylons.templating
382 qualname = pylons.templating
382 propagate = 1
383 propagate = 1
383
384
384 [logger_rhodecode]
385 [logger_rhodecode]
385 level = DEBUG
386 level = DEBUG
386 handlers =
387 handlers =
387 qualname = rhodecode
388 qualname = rhodecode
388 propagate = 1
389 propagate = 1
389
390
390 [logger_sqlalchemy]
391 [logger_sqlalchemy]
391 level = INFO
392 level = INFO
392 handlers = console_sql
393 handlers = console_sql
393 qualname = sqlalchemy.engine
394 qualname = sqlalchemy.engine
394 propagate = 0
395 propagate = 0
395
396
396 [logger_whoosh_indexer]
397 [logger_whoosh_indexer]
397 level = DEBUG
398 level = DEBUG
398 handlers =
399 handlers =
399 qualname = whoosh_indexer
400 qualname = whoosh_indexer
400 propagate = 1
401 propagate = 1
401
402
402 ##############
403 ##############
403 ## HANDLERS ##
404 ## HANDLERS ##
404 ##############
405 ##############
405
406
406 [handler_console]
407 [handler_console]
407 class = StreamHandler
408 class = StreamHandler
408 args = (sys.stderr,)
409 args = (sys.stderr,)
409 level = INFO
410 level = INFO
410 formatter = generic
411 formatter = generic
411
412
412 [handler_console_sql]
413 [handler_console_sql]
413 class = StreamHandler
414 class = StreamHandler
414 args = (sys.stderr,)
415 args = (sys.stderr,)
415 level = WARN
416 level = WARN
416 formatter = generic
417 formatter = generic
417
418
418 ################
419 ################
419 ## FORMATTERS ##
420 ## FORMATTERS ##
420 ################
421 ################
421
422
422 [formatter_generic]
423 [formatter_generic]
423 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
424 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
424 datefmt = %Y-%m-%d %H:%M:%S
425 datefmt = %Y-%m-%d %H:%M:%S
425
426
426 [formatter_color_formatter]
427 [formatter_color_formatter]
427 class=rhodecode.lib.colored_formatter.ColorFormatter
428 class=rhodecode.lib.colored_formatter.ColorFormatter
428 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
429 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
429 datefmt = %Y-%m-%d %H:%M:%S
430 datefmt = %Y-%m-%d %H:%M:%S
430
431
431 [formatter_color_formatter_sql]
432 [formatter_color_formatter_sql]
432 class=rhodecode.lib.colored_formatter.ColorFormatterSql
433 class=rhodecode.lib.colored_formatter.ColorFormatterSql
433 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
434 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
434 datefmt = %Y-%m-%d %H:%M:%S
435 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,1811 +1,1814 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-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
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47
47
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
48 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 safe_unicode, remove_suffix
49 safe_unicode, remove_suffix
50 from rhodecode.lib.compat import json
50 from rhodecode.lib.compat import json
51 from rhodecode.lib.caching_query import FromCache
51 from rhodecode.lib.caching_query import FromCache
52
52
53 from rhodecode.model.meta import Base, Session
53 from rhodecode.model.meta import Base, Session
54
54
55 URL_SEP = '/'
55 URL_SEP = '/'
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58 #==============================================================================
58 #==============================================================================
59 # BASE CLASSES
59 # BASE CLASSES
60 #==============================================================================
60 #==============================================================================
61
61
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63
63
64
64
65 class BaseModel(object):
65 class BaseModel(object):
66 """
66 """
67 Base Model for all classess
67 Base Model for all classess
68 """
68 """
69
69
70 @classmethod
70 @classmethod
71 def _get_keys(cls):
71 def _get_keys(cls):
72 """return column names for this model """
72 """return column names for this model """
73 return class_mapper(cls).c.keys()
73 return class_mapper(cls).c.keys()
74
74
75 def get_dict(self):
75 def get_dict(self):
76 """
76 """
77 return dict with keys and values corresponding
77 return dict with keys and values corresponding
78 to this model data """
78 to this model data """
79
79
80 d = {}
80 d = {}
81 for k in self._get_keys():
81 for k in self._get_keys():
82 d[k] = getattr(self, k)
82 d[k] = getattr(self, k)
83
83
84 # also use __json__() if present to get additional fields
84 # also use __json__() if present to get additional fields
85 _json_attr = getattr(self, '__json__', None)
85 _json_attr = getattr(self, '__json__', None)
86 if _json_attr:
86 if _json_attr:
87 # update with attributes from __json__
87 # update with attributes from __json__
88 if callable(_json_attr):
88 if callable(_json_attr):
89 _json_attr = _json_attr()
89 _json_attr = _json_attr()
90 for k, val in _json_attr.iteritems():
90 for k, val in _json_attr.iteritems():
91 d[k] = val
91 d[k] = val
92 return d
92 return d
93
93
94 def get_appstruct(self):
94 def get_appstruct(self):
95 """return list with keys and values tupples corresponding
95 """return list with keys and values tupples corresponding
96 to this model data """
96 to this model data """
97
97
98 l = []
98 l = []
99 for k in self._get_keys():
99 for k in self._get_keys():
100 l.append((k, getattr(self, k),))
100 l.append((k, getattr(self, k),))
101 return l
101 return l
102
102
103 def populate_obj(self, populate_dict):
103 def populate_obj(self, populate_dict):
104 """populate model with data from given populate_dict"""
104 """populate model with data from given populate_dict"""
105
105
106 for k in self._get_keys():
106 for k in self._get_keys():
107 if k in populate_dict:
107 if k in populate_dict:
108 setattr(self, k, populate_dict[k])
108 setattr(self, k, populate_dict[k])
109
109
110 @classmethod
110 @classmethod
111 def query(cls):
111 def query(cls):
112 return Session().query(cls)
112 return Session().query(cls)
113
113
114 @classmethod
114 @classmethod
115 def get(cls, id_):
115 def get(cls, id_):
116 if id_:
116 if id_:
117 return cls.query().get(id_)
117 return cls.query().get(id_)
118
118
119 @classmethod
119 @classmethod
120 def get_or_404(cls, id_):
120 def get_or_404(cls, id_):
121 try:
121 try:
122 id_ = int(id_)
122 id_ = int(id_)
123 except (TypeError, ValueError):
123 except (TypeError, ValueError):
124 raise HTTPNotFound
124 raise HTTPNotFound
125
125
126 res = cls.query().get(id_)
126 res = cls.query().get(id_)
127 if not res:
127 if not res:
128 raise HTTPNotFound
128 raise HTTPNotFound
129 return res
129 return res
130
130
131 @classmethod
131 @classmethod
132 def getAll(cls):
132 def getAll(cls):
133 return cls.query().all()
133 return cls.query().all()
134
134
135 @classmethod
135 @classmethod
136 def delete(cls, id_):
136 def delete(cls, id_):
137 obj = cls.query().get(id_)
137 obj = cls.query().get(id_)
138 Session().delete(obj)
138 Session().delete(obj)
139
139
140 def __repr__(self):
140 def __repr__(self):
141 if hasattr(self, '__unicode__'):
141 if hasattr(self, '__unicode__'):
142 # python repr needs to return str
142 # python repr needs to return str
143 return safe_str(self.__unicode__())
143 return safe_str(self.__unicode__())
144 return '<DB:%s>' % (self.__class__.__name__)
144 return '<DB:%s>' % (self.__class__.__name__)
145
145
146
146
147 class RhodeCodeSetting(Base, BaseModel):
147 class RhodeCodeSetting(Base, BaseModel):
148 __tablename__ = 'rhodecode_settings'
148 __tablename__ = 'rhodecode_settings'
149 __table_args__ = (
149 __table_args__ = (
150 UniqueConstraint('app_settings_name'),
150 UniqueConstraint('app_settings_name'),
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
151 {'extend_existing': True, 'mysql_engine': 'InnoDB',
152 'mysql_charset': 'utf8'}
152 'mysql_charset': 'utf8'}
153 )
153 )
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
154 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
155 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
156 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
157
157
158 def __init__(self, k='', v=''):
158 def __init__(self, k='', v=''):
159 self.app_settings_name = k
159 self.app_settings_name = k
160 self.app_settings_value = v
160 self.app_settings_value = v
161
161
162 @validates('_app_settings_value')
162 @validates('_app_settings_value')
163 def validate_settings_value(self, key, val):
163 def validate_settings_value(self, key, val):
164 assert type(val) == unicode
164 assert type(val) == unicode
165 return val
165 return val
166
166
167 @hybrid_property
167 @hybrid_property
168 def app_settings_value(self):
168 def app_settings_value(self):
169 v = self._app_settings_value
169 v = self._app_settings_value
170 if self.app_settings_name == 'ldap_active':
170 if self.app_settings_name == 'ldap_active':
171 v = str2bool(v)
171 v = str2bool(v)
172 return v
172 return v
173
173
174 @app_settings_value.setter
174 @app_settings_value.setter
175 def app_settings_value(self, val):
175 def app_settings_value(self, val):
176 """
176 """
177 Setter that will always make sure we use unicode in app_settings_value
177 Setter that will always make sure we use unicode in app_settings_value
178
178
179 :param val:
179 :param val:
180 """
180 """
181 self._app_settings_value = safe_unicode(val)
181 self._app_settings_value = safe_unicode(val)
182
182
183 def __unicode__(self):
183 def __unicode__(self):
184 return u"<%s('%s:%s')>" % (
184 return u"<%s('%s:%s')>" % (
185 self.__class__.__name__,
185 self.__class__.__name__,
186 self.app_settings_name, self.app_settings_value
186 self.app_settings_name, self.app_settings_value
187 )
187 )
188
188
189 @classmethod
189 @classmethod
190 def get_by_name(cls, key):
190 def get_by_name(cls, key):
191 return cls.query()\
191 return cls.query()\
192 .filter(cls.app_settings_name == key).scalar()
192 .filter(cls.app_settings_name == key).scalar()
193
193
194 @classmethod
194 @classmethod
195 def get_by_name_or_create(cls, key):
195 def get_by_name_or_create(cls, key):
196 res = cls.get_by_name(key)
196 res = cls.get_by_name(key)
197 if not res:
197 if not res:
198 res = cls(key)
198 res = cls(key)
199 return res
199 return res
200
200
201 @classmethod
201 @classmethod
202 def get_app_settings(cls, cache=False):
202 def get_app_settings(cls, cache=False):
203
203
204 ret = cls.query()
204 ret = cls.query()
205
205
206 if cache:
206 if cache:
207 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
207 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
208
208
209 if not ret:
209 if not ret:
210 raise Exception('Could not get application settings !')
210 raise Exception('Could not get application settings !')
211 settings = {}
211 settings = {}
212 for each in ret:
212 for each in ret:
213 settings['rhodecode_' + each.app_settings_name] = \
213 settings['rhodecode_' + each.app_settings_name] = \
214 each.app_settings_value
214 each.app_settings_value
215
215
216 return settings
216 return settings
217
217
218 @classmethod
218 @classmethod
219 def get_ldap_settings(cls, cache=False):
219 def get_ldap_settings(cls, cache=False):
220 ret = cls.query()\
220 ret = cls.query()\
221 .filter(cls.app_settings_name.startswith('ldap_')).all()
221 .filter(cls.app_settings_name.startswith('ldap_')).all()
222 fd = {}
222 fd = {}
223 for row in ret:
223 for row in ret:
224 fd.update({row.app_settings_name: row.app_settings_value})
224 fd.update({row.app_settings_name: row.app_settings_value})
225
225
226 return fd
226 return fd
227
227
228
228
229 class RhodeCodeUi(Base, BaseModel):
229 class RhodeCodeUi(Base, BaseModel):
230 __tablename__ = 'rhodecode_ui'
230 __tablename__ = 'rhodecode_ui'
231 __table_args__ = (
231 __table_args__ = (
232 UniqueConstraint('ui_key'),
232 UniqueConstraint('ui_key'),
233 {'extend_existing': True, 'mysql_engine': 'InnoDB',
233 {'extend_existing': True, 'mysql_engine': 'InnoDB',
234 'mysql_charset': 'utf8'}
234 'mysql_charset': 'utf8'}
235 )
235 )
236
236
237 HOOK_UPDATE = 'changegroup.update'
237 HOOK_UPDATE = 'changegroup.update'
238 HOOK_REPO_SIZE = 'changegroup.repo_size'
238 HOOK_REPO_SIZE = 'changegroup.repo_size'
239 HOOK_PUSH = 'changegroup.push_logger'
239 HOOK_PUSH = 'changegroup.push_logger'
240 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
240 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
241 HOOK_PULL = 'outgoing.pull_logger'
241 HOOK_PULL = 'outgoing.pull_logger'
242 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
242 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
243
243
244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
245 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
247 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
248 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
249
249
250 @classmethod
250 @classmethod
251 def get_by_key(cls, key):
251 def get_by_key(cls, key):
252 return cls.query().filter(cls.ui_key == key).scalar()
252 return cls.query().filter(cls.ui_key == key).scalar()
253
253
254 @classmethod
254 @classmethod
255 def get_builtin_hooks(cls):
255 def get_builtin_hooks(cls):
256 q = cls.query()
256 q = cls.query()
257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
257 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
258 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
258 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
259 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
259 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
260 return q.all()
260 return q.all()
261
261
262 @classmethod
262 @classmethod
263 def get_custom_hooks(cls):
263 def get_custom_hooks(cls):
264 q = cls.query()
264 q = cls.query()
265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
265 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
266 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
266 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
267 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
267 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
268 q = q.filter(cls.ui_section == 'hooks')
268 q = q.filter(cls.ui_section == 'hooks')
269 return q.all()
269 return q.all()
270
270
271 @classmethod
271 @classmethod
272 def get_repos_location(cls):
272 def get_repos_location(cls):
273 return cls.get_by_key('/').ui_value
273 return cls.get_by_key('/').ui_value
274
274
275 @classmethod
275 @classmethod
276 def create_or_update_hook(cls, key, val):
276 def create_or_update_hook(cls, key, val):
277 new_ui = cls.get_by_key(key) or cls()
277 new_ui = cls.get_by_key(key) or cls()
278 new_ui.ui_section = 'hooks'
278 new_ui.ui_section = 'hooks'
279 new_ui.ui_active = True
279 new_ui.ui_active = True
280 new_ui.ui_key = key
280 new_ui.ui_key = key
281 new_ui.ui_value = val
281 new_ui.ui_value = val
282
282
283 Session().add(new_ui)
283 Session().add(new_ui)
284
284
285 def __repr__(self):
285 def __repr__(self):
286 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
286 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
287 self.ui_value)
287 self.ui_value)
288
288
289
289
290 class User(Base, BaseModel):
290 class User(Base, BaseModel):
291 __tablename__ = 'users'
291 __tablename__ = 'users'
292 __table_args__ = (
292 __table_args__ = (
293 UniqueConstraint('username'), UniqueConstraint('email'),
293 UniqueConstraint('username'), UniqueConstraint('email'),
294 Index('u_username_idx', 'username'),
294 Index('u_username_idx', 'username'),
295 Index('u_email_idx', 'email'),
295 Index('u_email_idx', 'email'),
296 {'extend_existing': True, 'mysql_engine': 'InnoDB',
296 {'extend_existing': True, 'mysql_engine': 'InnoDB',
297 'mysql_charset': 'utf8'}
297 'mysql_charset': 'utf8'}
298 )
298 )
299 DEFAULT_USER = 'default'
299 DEFAULT_USER = 'default'
300 DEFAULT_PERMISSIONS = [
300 DEFAULT_PERMISSIONS = [
301 'hg.register.manual_activate', 'hg.create.repository',
301 'hg.register.manual_activate', 'hg.create.repository',
302 'hg.fork.repository', 'repository.read'
302 'hg.fork.repository', 'repository.read'
303 ]
303 ]
304 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
304 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
305 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
305 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
306 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
306 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
307 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
307 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
308 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
308 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
309 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
309 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
310 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
310 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
311 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
312 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
312 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
313 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
313 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
314 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
314 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
315 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
315 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
316
316
317 user_log = relationship('UserLog', cascade='all')
317 user_log = relationship('UserLog', cascade='all')
318 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
318 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
319
319
320 repositories = relationship('Repository')
320 repositories = relationship('Repository')
321 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
321 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
322 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
322 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
323 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
323 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
324
324
325 group_member = relationship('UsersGroupMember', cascade='all')
325 group_member = relationship('UsersGroupMember', cascade='all')
326
326
327 notifications = relationship('UserNotification', cascade='all')
327 notifications = relationship('UserNotification', cascade='all')
328 # notifications assigned to this user
328 # notifications assigned to this user
329 user_created_notifications = relationship('Notification', cascade='all')
329 user_created_notifications = relationship('Notification', cascade='all')
330 # comments created by this user
330 # comments created by this user
331 user_comments = relationship('ChangesetComment', cascade='all')
331 user_comments = relationship('ChangesetComment', cascade='all')
332 #extra emails for this user
332 #extra emails for this user
333 user_emails = relationship('UserEmailMap', cascade='all')
333 user_emails = relationship('UserEmailMap', cascade='all')
334
334
335 @hybrid_property
335 @hybrid_property
336 def email(self):
336 def email(self):
337 return self._email
337 return self._email
338
338
339 @email.setter
339 @email.setter
340 def email(self, val):
340 def email(self, val):
341 self._email = val.lower() if val else None
341 self._email = val.lower() if val else None
342
342
343 @property
343 @property
344 def firstname(self):
344 def firstname(self):
345 # alias for future
345 # alias for future
346 return self.name
346 return self.name
347
347
348 @property
348 @property
349 def emails(self):
349 def emails(self):
350 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
350 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
351 return [self.email] + [x.email for x in other]
351 return [self.email] + [x.email for x in other]
352
352
353 @property
353 @property
354 def username_and_name(self):
354 def username_and_name(self):
355 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
355 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
356
356
357 @property
357 @property
358 def full_name(self):
358 def full_name(self):
359 return '%s %s' % (self.firstname, self.lastname)
359 return '%s %s' % (self.firstname, self.lastname)
360
360
361 @property
361 @property
362 def full_name_or_username(self):
362 def full_name_or_username(self):
363 return ('%s %s' % (self.firstname, self.lastname)
363 return ('%s %s' % (self.firstname, self.lastname)
364 if (self.firstname and self.lastname) else self.username)
364 if (self.firstname and self.lastname) else self.username)
365
365
366 @property
366 @property
367 def full_contact(self):
367 def full_contact(self):
368 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
368 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
369
369
370 @property
370 @property
371 def short_contact(self):
371 def short_contact(self):
372 return '%s %s' % (self.firstname, self.lastname)
372 return '%s %s' % (self.firstname, self.lastname)
373
373
374 @property
374 @property
375 def is_admin(self):
375 def is_admin(self):
376 return self.admin
376 return self.admin
377
377
378 def __unicode__(self):
378 def __unicode__(self):
379 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
379 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
380 self.user_id, self.username)
380 self.user_id, self.username)
381
381
382 @classmethod
382 @classmethod
383 def get_by_username(cls, username, case_insensitive=False, cache=False):
383 def get_by_username(cls, username, case_insensitive=False, cache=False):
384 if case_insensitive:
384 if case_insensitive:
385 q = cls.query().filter(cls.username.ilike(username))
385 q = cls.query().filter(cls.username.ilike(username))
386 else:
386 else:
387 q = cls.query().filter(cls.username == username)
387 q = cls.query().filter(cls.username == username)
388
388
389 if cache:
389 if cache:
390 q = q.options(FromCache(
390 q = q.options(FromCache(
391 "sql_cache_short",
391 "sql_cache_short",
392 "get_user_%s" % _hash_key(username)
392 "get_user_%s" % _hash_key(username)
393 )
393 )
394 )
394 )
395 return q.scalar()
395 return q.scalar()
396
396
397 @classmethod
397 @classmethod
398 def get_by_api_key(cls, api_key, cache=False):
398 def get_by_api_key(cls, api_key, cache=False):
399 q = cls.query().filter(cls.api_key == api_key)
399 q = cls.query().filter(cls.api_key == api_key)
400
400
401 if cache:
401 if cache:
402 q = q.options(FromCache("sql_cache_short",
402 q = q.options(FromCache("sql_cache_short",
403 "get_api_key_%s" % api_key))
403 "get_api_key_%s" % api_key))
404 return q.scalar()
404 return q.scalar()
405
405
406 @classmethod
406 @classmethod
407 def get_by_email(cls, email, case_insensitive=False, cache=False):
407 def get_by_email(cls, email, case_insensitive=False, cache=False):
408 if case_insensitive:
408 if case_insensitive:
409 q = cls.query().filter(cls.email.ilike(email))
409 q = cls.query().filter(cls.email.ilike(email))
410 else:
410 else:
411 q = cls.query().filter(cls.email == email)
411 q = cls.query().filter(cls.email == email)
412
412
413 if cache:
413 if cache:
414 q = q.options(FromCache("sql_cache_short",
414 q = q.options(FromCache("sql_cache_short",
415 "get_email_key_%s" % email))
415 "get_email_key_%s" % email))
416
416
417 ret = q.scalar()
417 ret = q.scalar()
418 if ret is None:
418 if ret is None:
419 q = UserEmailMap.query()
419 q = UserEmailMap.query()
420 # try fetching in alternate email map
420 # try fetching in alternate email map
421 if case_insensitive:
421 if case_insensitive:
422 q = q.filter(UserEmailMap.email.ilike(email))
422 q = q.filter(UserEmailMap.email.ilike(email))
423 else:
423 else:
424 q = q.filter(UserEmailMap.email == email)
424 q = q.filter(UserEmailMap.email == email)
425 q = q.options(joinedload(UserEmailMap.user))
425 q = q.options(joinedload(UserEmailMap.user))
426 if cache:
426 if cache:
427 q = q.options(FromCache("sql_cache_short",
427 q = q.options(FromCache("sql_cache_short",
428 "get_email_map_key_%s" % email))
428 "get_email_map_key_%s" % email))
429 ret = getattr(q.scalar(), 'user', None)
429 ret = getattr(q.scalar(), 'user', None)
430
430
431 return ret
431 return ret
432
432
433 def update_lastlogin(self):
433 def update_lastlogin(self):
434 """Update user lastlogin"""
434 """Update user lastlogin"""
435 self.last_login = datetime.datetime.now()
435 self.last_login = datetime.datetime.now()
436 Session().add(self)
436 Session().add(self)
437 log.debug('updated user %s lastlogin' % self.username)
437 log.debug('updated user %s lastlogin' % self.username)
438
438
439 def get_api_data(self):
439 def get_api_data(self):
440 """
440 """
441 Common function for generating user related data for API
441 Common function for generating user related data for API
442 """
442 """
443 user = self
443 user = self
444 data = dict(
444 data = dict(
445 user_id=user.user_id,
445 user_id=user.user_id,
446 username=user.username,
446 username=user.username,
447 firstname=user.name,
447 firstname=user.name,
448 lastname=user.lastname,
448 lastname=user.lastname,
449 email=user.email,
449 email=user.email,
450 emails=user.emails,
450 emails=user.emails,
451 api_key=user.api_key,
451 api_key=user.api_key,
452 active=user.active,
452 active=user.active,
453 admin=user.admin,
453 admin=user.admin,
454 ldap_dn=user.ldap_dn,
454 ldap_dn=user.ldap_dn,
455 last_login=user.last_login,
455 last_login=user.last_login,
456 )
456 )
457 return data
457 return data
458
458
459 def __json__(self):
459 def __json__(self):
460 data = dict(
460 data = dict(
461 full_name=self.full_name,
461 full_name=self.full_name,
462 full_name_or_username=self.full_name_or_username,
462 full_name_or_username=self.full_name_or_username,
463 short_contact=self.short_contact,
463 short_contact=self.short_contact,
464 full_contact=self.full_contact
464 full_contact=self.full_contact
465 )
465 )
466 data.update(self.get_api_data())
466 data.update(self.get_api_data())
467 return data
467 return data
468
468
469
469
470 class UserEmailMap(Base, BaseModel):
470 class UserEmailMap(Base, BaseModel):
471 __tablename__ = 'user_email_map'
471 __tablename__ = 'user_email_map'
472 __table_args__ = (
472 __table_args__ = (
473 Index('uem_email_idx', 'email'),
473 Index('uem_email_idx', 'email'),
474 UniqueConstraint('email'),
474 UniqueConstraint('email'),
475 {'extend_existing': True, 'mysql_engine': 'InnoDB',
475 {'extend_existing': True, 'mysql_engine': 'InnoDB',
476 'mysql_charset': 'utf8'}
476 'mysql_charset': 'utf8'}
477 )
477 )
478 __mapper_args__ = {}
478 __mapper_args__ = {}
479
479
480 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
480 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
481 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
481 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
482 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
482 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
483 user = relationship('User', lazy='joined')
483 user = relationship('User', lazy='joined')
484
484
485 @validates('_email')
485 @validates('_email')
486 def validate_email(self, key, email):
486 def validate_email(self, key, email):
487 # check if this email is not main one
487 # check if this email is not main one
488 main_email = Session().query(User).filter(User.email == email).scalar()
488 main_email = Session().query(User).filter(User.email == email).scalar()
489 if main_email is not None:
489 if main_email is not None:
490 raise AttributeError('email %s is present is user table' % email)
490 raise AttributeError('email %s is present is user table' % email)
491 return email
491 return email
492
492
493 @hybrid_property
493 @hybrid_property
494 def email(self):
494 def email(self):
495 return self._email
495 return self._email
496
496
497 @email.setter
497 @email.setter
498 def email(self, val):
498 def email(self, val):
499 self._email = val.lower() if val else None
499 self._email = val.lower() if val else None
500
500
501
501
502 class UserLog(Base, BaseModel):
502 class UserLog(Base, BaseModel):
503 __tablename__ = 'user_logs'
503 __tablename__ = 'user_logs'
504 __table_args__ = (
504 __table_args__ = (
505 {'extend_existing': True, 'mysql_engine': 'InnoDB',
505 {'extend_existing': True, 'mysql_engine': 'InnoDB',
506 'mysql_charset': 'utf8'},
506 'mysql_charset': 'utf8'},
507 )
507 )
508 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
508 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
509 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
509 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
510 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
510 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
511 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
511 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
512 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
512 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
513 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
513 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
514 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
514 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
515
515
516 @property
516 @property
517 def action_as_day(self):
517 def action_as_day(self):
518 return datetime.date(*self.action_date.timetuple()[:3])
518 return datetime.date(*self.action_date.timetuple()[:3])
519
519
520 user = relationship('User')
520 user = relationship('User')
521 repository = relationship('Repository', cascade='')
521 repository = relationship('Repository', cascade='')
522
522
523
523
524 class UsersGroup(Base, BaseModel):
524 class UsersGroup(Base, BaseModel):
525 __tablename__ = 'users_groups'
525 __tablename__ = 'users_groups'
526 __table_args__ = (
526 __table_args__ = (
527 {'extend_existing': True, 'mysql_engine': 'InnoDB',
527 {'extend_existing': True, 'mysql_engine': 'InnoDB',
528 'mysql_charset': 'utf8'},
528 'mysql_charset': 'utf8'},
529 )
529 )
530
530
531 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
531 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
532 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
532 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
533 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
533 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
534 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
534 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
535
535
536 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
536 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
537 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
537 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
538 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
538 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
539
539
540 def __unicode__(self):
540 def __unicode__(self):
541 return u'<userGroup(%s)>' % (self.users_group_name)
541 return u'<userGroup(%s)>' % (self.users_group_name)
542
542
543 @classmethod
543 @classmethod
544 def get_by_group_name(cls, group_name, cache=False,
544 def get_by_group_name(cls, group_name, cache=False,
545 case_insensitive=False):
545 case_insensitive=False):
546 if case_insensitive:
546 if case_insensitive:
547 q = cls.query().filter(cls.users_group_name.ilike(group_name))
547 q = cls.query().filter(cls.users_group_name.ilike(group_name))
548 else:
548 else:
549 q = cls.query().filter(cls.users_group_name == group_name)
549 q = cls.query().filter(cls.users_group_name == group_name)
550 if cache:
550 if cache:
551 q = q.options(FromCache(
551 q = q.options(FromCache(
552 "sql_cache_short",
552 "sql_cache_short",
553 "get_user_%s" % _hash_key(group_name)
553 "get_user_%s" % _hash_key(group_name)
554 )
554 )
555 )
555 )
556 return q.scalar()
556 return q.scalar()
557
557
558 @classmethod
558 @classmethod
559 def get(cls, users_group_id, cache=False):
559 def get(cls, users_group_id, cache=False):
560 users_group = cls.query()
560 users_group = cls.query()
561 if cache:
561 if cache:
562 users_group = users_group.options(FromCache("sql_cache_short",
562 users_group = users_group.options(FromCache("sql_cache_short",
563 "get_users_group_%s" % users_group_id))
563 "get_users_group_%s" % users_group_id))
564 return users_group.get(users_group_id)
564 return users_group.get(users_group_id)
565
565
566 def get_api_data(self):
566 def get_api_data(self):
567 users_group = self
567 users_group = self
568
568
569 data = dict(
569 data = dict(
570 users_group_id=users_group.users_group_id,
570 users_group_id=users_group.users_group_id,
571 group_name=users_group.users_group_name,
571 group_name=users_group.users_group_name,
572 active=users_group.users_group_active,
572 active=users_group.users_group_active,
573 )
573 )
574
574
575 return data
575 return data
576
576
577
577
578 class UsersGroupMember(Base, BaseModel):
578 class UsersGroupMember(Base, BaseModel):
579 __tablename__ = 'users_groups_members'
579 __tablename__ = 'users_groups_members'
580 __table_args__ = (
580 __table_args__ = (
581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
581 {'extend_existing': True, 'mysql_engine': 'InnoDB',
582 'mysql_charset': 'utf8'},
582 'mysql_charset': 'utf8'},
583 )
583 )
584
584
585 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
585 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
586 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
586 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
588
588
589 user = relationship('User', lazy='joined')
589 user = relationship('User', lazy='joined')
590 users_group = relationship('UsersGroup')
590 users_group = relationship('UsersGroup')
591
591
592 def __init__(self, gr_id='', u_id=''):
592 def __init__(self, gr_id='', u_id=''):
593 self.users_group_id = gr_id
593 self.users_group_id = gr_id
594 self.user_id = u_id
594 self.user_id = u_id
595
595
596
596
597 class Repository(Base, BaseModel):
597 class Repository(Base, BaseModel):
598 __tablename__ = 'repositories'
598 __tablename__ = 'repositories'
599 __table_args__ = (
599 __table_args__ = (
600 UniqueConstraint('repo_name'),
600 UniqueConstraint('repo_name'),
601 Index('r_repo_name_idx', 'repo_name'),
601 Index('r_repo_name_idx', 'repo_name'),
602 {'extend_existing': True, 'mysql_engine': 'InnoDB',
602 {'extend_existing': True, 'mysql_engine': 'InnoDB',
603 'mysql_charset': 'utf8'},
603 'mysql_charset': 'utf8'},
604 )
604 )
605
605
606 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
606 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
607 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
608 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
608 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
609 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
609 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
610 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
610 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
611 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
611 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
612 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
612 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
613 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
613 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
614 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
614 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
615 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
615 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
616 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
616 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
617 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
617 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
618 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
618 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
619 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
619 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
620
620
621 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
621 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
622 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
622 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
623
623
624 user = relationship('User')
624 user = relationship('User')
625 fork = relationship('Repository', remote_side=repo_id)
625 fork = relationship('Repository', remote_side=repo_id)
626 group = relationship('RepoGroup')
626 group = relationship('RepoGroup')
627 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
627 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
628 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
628 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
629 stats = relationship('Statistics', cascade='all', uselist=False)
629 stats = relationship('Statistics', cascade='all', uselist=False)
630
630
631 followers = relationship('UserFollowing',
631 followers = relationship('UserFollowing',
632 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
632 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
633 cascade='all')
633 cascade='all')
634
634
635 logs = relationship('UserLog')
635 logs = relationship('UserLog')
636 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
636 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
637
637
638 pull_requests_org = relationship('PullRequest',
638 pull_requests_org = relationship('PullRequest',
639 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
639 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
640 cascade="all, delete, delete-orphan")
640 cascade="all, delete, delete-orphan")
641
641
642 pull_requests_other = relationship('PullRequest',
642 pull_requests_other = relationship('PullRequest',
643 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
643 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
644 cascade="all, delete, delete-orphan")
644 cascade="all, delete, delete-orphan")
645
645
646 def __unicode__(self):
646 def __unicode__(self):
647 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
647 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
648 self.repo_name)
648 self.repo_name)
649
649
650 @hybrid_property
650 @hybrid_property
651 def locked(self):
651 def locked(self):
652 # always should return [user_id, timelocked]
652 # always should return [user_id, timelocked]
653 if self._locked:
653 if self._locked:
654 _lock_info = self._locked.split(':')
654 _lock_info = self._locked.split(':')
655 return int(_lock_info[0]), _lock_info[1]
655 return int(_lock_info[0]), _lock_info[1]
656 return [None, None]
656 return [None, None]
657
657
658 @locked.setter
658 @locked.setter
659 def locked(self, val):
659 def locked(self, val):
660 if val and isinstance(val, (list, tuple)):
660 if val and isinstance(val, (list, tuple)):
661 self._locked = ':'.join(map(str, val))
661 self._locked = ':'.join(map(str, val))
662 else:
662 else:
663 self._locked = None
663 self._locked = None
664
664
665 @classmethod
665 @classmethod
666 def url_sep(cls):
666 def url_sep(cls):
667 return URL_SEP
667 return URL_SEP
668
668
669 @classmethod
669 @classmethod
670 def get_by_repo_name(cls, repo_name):
670 def get_by_repo_name(cls, repo_name):
671 q = Session().query(cls).filter(cls.repo_name == repo_name)
671 q = Session().query(cls).filter(cls.repo_name == repo_name)
672 q = q.options(joinedload(Repository.fork))\
672 q = q.options(joinedload(Repository.fork))\
673 .options(joinedload(Repository.user))\
673 .options(joinedload(Repository.user))\
674 .options(joinedload(Repository.group))
674 .options(joinedload(Repository.group))
675 return q.scalar()
675 return q.scalar()
676
676
677 @classmethod
677 @classmethod
678 def get_by_full_path(cls, repo_full_path):
678 def get_by_full_path(cls, repo_full_path):
679 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
679 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
680 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
680 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
681
681
682 @classmethod
682 @classmethod
683 def get_repo_forks(cls, repo_id):
683 def get_repo_forks(cls, repo_id):
684 return cls.query().filter(Repository.fork_id == repo_id)
684 return cls.query().filter(Repository.fork_id == repo_id)
685
685
686 @classmethod
686 @classmethod
687 def base_path(cls):
687 def base_path(cls):
688 """
688 """
689 Returns base path when all repos are stored
689 Returns base path when all repos are stored
690
690
691 :param cls:
691 :param cls:
692 """
692 """
693 q = Session().query(RhodeCodeUi)\
693 q = Session().query(RhodeCodeUi)\
694 .filter(RhodeCodeUi.ui_key == cls.url_sep())
694 .filter(RhodeCodeUi.ui_key == cls.url_sep())
695 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
695 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
696 return q.one().ui_value
696 return q.one().ui_value
697
697
698 @property
698 @property
699 def forks(self):
699 def forks(self):
700 """
700 """
701 Return forks of this repo
701 Return forks of this repo
702 """
702 """
703 return Repository.get_repo_forks(self.repo_id)
703 return Repository.get_repo_forks(self.repo_id)
704
704
705 @property
705 @property
706 def parent(self):
706 def parent(self):
707 """
707 """
708 Returns fork parent
708 Returns fork parent
709 """
709 """
710 return self.fork
710 return self.fork
711
711
712 @property
712 @property
713 def just_name(self):
713 def just_name(self):
714 return self.repo_name.split(Repository.url_sep())[-1]
714 return self.repo_name.split(Repository.url_sep())[-1]
715
715
716 @property
716 @property
717 def groups_with_parents(self):
717 def groups_with_parents(self):
718 groups = []
718 groups = []
719 if self.group is None:
719 if self.group is None:
720 return groups
720 return groups
721
721
722 cur_gr = self.group
722 cur_gr = self.group
723 groups.insert(0, cur_gr)
723 groups.insert(0, cur_gr)
724 while 1:
724 while 1:
725 gr = getattr(cur_gr, 'parent_group', None)
725 gr = getattr(cur_gr, 'parent_group', None)
726 cur_gr = cur_gr.parent_group
726 cur_gr = cur_gr.parent_group
727 if gr is None:
727 if gr is None:
728 break
728 break
729 groups.insert(0, gr)
729 groups.insert(0, gr)
730
730
731 return groups
731 return groups
732
732
733 @property
733 @property
734 def groups_and_repo(self):
734 def groups_and_repo(self):
735 return self.groups_with_parents, self.just_name
735 return self.groups_with_parents, self.just_name
736
736
737 @LazyProperty
737 @LazyProperty
738 def repo_path(self):
738 def repo_path(self):
739 """
739 """
740 Returns base full path for that repository means where it actually
740 Returns base full path for that repository means where it actually
741 exists on a filesystem
741 exists on a filesystem
742 """
742 """
743 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
743 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
744 Repository.url_sep())
744 Repository.url_sep())
745 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
745 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
746 return q.one().ui_value
746 return q.one().ui_value
747
747
748 @property
748 @property
749 def repo_full_path(self):
749 def repo_full_path(self):
750 p = [self.repo_path]
750 p = [self.repo_path]
751 # we need to split the name by / since this is how we store the
751 # we need to split the name by / since this is how we store the
752 # names in the database, but that eventually needs to be converted
752 # names in the database, but that eventually needs to be converted
753 # into a valid system path
753 # into a valid system path
754 p += self.repo_name.split(Repository.url_sep())
754 p += self.repo_name.split(Repository.url_sep())
755 return os.path.join(*p)
755 return os.path.join(*p)
756
756
757 @property
757 @property
758 def cache_keys(self):
758 def cache_keys(self):
759 """
759 """
760 Returns associated cache keys for that repo
760 Returns associated cache keys for that repo
761 """
761 """
762 return CacheInvalidation.query()\
762 return CacheInvalidation.query()\
763 .filter(CacheInvalidation.cache_args == self.repo_name)\
763 .filter(CacheInvalidation.cache_args == self.repo_name)\
764 .order_by(CacheInvalidation.cache_key)\
764 .order_by(CacheInvalidation.cache_key)\
765 .all()
765 .all()
766
766
767 def get_new_name(self, repo_name):
767 def get_new_name(self, repo_name):
768 """
768 """
769 returns new full repository name based on assigned group and new new
769 returns new full repository name based on assigned group and new new
770
770
771 :param group_name:
771 :param group_name:
772 """
772 """
773 path_prefix = self.group.full_path_splitted if self.group else []
773 path_prefix = self.group.full_path_splitted if self.group else []
774 return Repository.url_sep().join(path_prefix + [repo_name])
774 return Repository.url_sep().join(path_prefix + [repo_name])
775
775
776 @property
776 @property
777 def _ui(self):
777 def _ui(self):
778 """
778 """
779 Creates an db based ui object for this repository
779 Creates an db based ui object for this repository
780 """
780 """
781 from rhodecode.lib.utils import make_ui
781 from rhodecode.lib.utils import make_ui
782 return make_ui('db', clear_session=False)
782 return make_ui('db', clear_session=False)
783
783
784 @classmethod
784 @classmethod
785 def inject_ui(cls, repo, extras={}):
785 def inject_ui(cls, repo, extras={}):
786 from rhodecode.lib.vcs.backends.hg import MercurialRepository
786 from rhodecode.lib.vcs.backends.hg import MercurialRepository
787 from rhodecode.lib.vcs.backends.git import GitRepository
787 from rhodecode.lib.vcs.backends.git import GitRepository
788 required = (MercurialRepository, GitRepository)
788 required = (MercurialRepository, GitRepository)
789 if not isinstance(repo, required):
789 if not isinstance(repo, required):
790 raise Exception('repo must be instance of %s' % required)
790 raise Exception('repo must be instance of %s' % required)
791
791
792 # inject ui extra param to log this action via push logger
792 # inject ui extra param to log this action via push logger
793 for k, v in extras.items():
793 for k, v in extras.items():
794 repo._repo.ui.setconfig('rhodecode_extras', k, v)
794 repo._repo.ui.setconfig('rhodecode_extras', k, v)
795
795
796 @classmethod
796 @classmethod
797 def is_valid(cls, repo_name):
797 def is_valid(cls, repo_name):
798 """
798 """
799 returns True if given repo name is a valid filesystem repository
799 returns True if given repo name is a valid filesystem repository
800
800
801 :param cls:
801 :param cls:
802 :param repo_name:
802 :param repo_name:
803 """
803 """
804 from rhodecode.lib.utils import is_valid_repo
804 from rhodecode.lib.utils import is_valid_repo
805
805
806 return is_valid_repo(repo_name, cls.base_path())
806 return is_valid_repo(repo_name, cls.base_path())
807
807
808 def get_api_data(self):
808 def get_api_data(self):
809 """
809 """
810 Common function for generating repo api data
810 Common function for generating repo api data
811
811
812 """
812 """
813 repo = self
813 repo = self
814 data = dict(
814 data = dict(
815 repo_id=repo.repo_id,
815 repo_id=repo.repo_id,
816 repo_name=repo.repo_name,
816 repo_name=repo.repo_name,
817 repo_type=repo.repo_type,
817 repo_type=repo.repo_type,
818 clone_uri=repo.clone_uri,
818 clone_uri=repo.clone_uri,
819 private=repo.private,
819 private=repo.private,
820 created_on=repo.created_on,
820 created_on=repo.created_on,
821 description=repo.description,
821 description=repo.description,
822 landing_rev=repo.landing_rev,
822 landing_rev=repo.landing_rev,
823 owner=repo.user.username,
823 owner=repo.user.username,
824 fork_of=repo.fork.repo_name if repo.fork else None
824 fork_of=repo.fork.repo_name if repo.fork else None
825 )
825 )
826
826
827 return data
827 return data
828
828
829 @classmethod
829 @classmethod
830 def lock(cls, repo, user_id):
830 def lock(cls, repo, user_id):
831 repo.locked = [user_id, time.time()]
831 repo.locked = [user_id, time.time()]
832 Session().add(repo)
832 Session().add(repo)
833 Session().commit()
833 Session().commit()
834
834
835 @classmethod
835 @classmethod
836 def unlock(cls, repo):
836 def unlock(cls, repo):
837 repo.locked = None
837 repo.locked = None
838 Session().add(repo)
838 Session().add(repo)
839 Session().commit()
839 Session().commit()
840
840
841 @property
841 @property
842 def last_db_change(self):
842 def last_db_change(self):
843 return self.updated_on
843 return self.updated_on
844
844
845 #==========================================================================
845 #==========================================================================
846 # SCM PROPERTIES
846 # SCM PROPERTIES
847 #==========================================================================
847 #==========================================================================
848
848
849 def get_changeset(self, rev=None):
849 def get_changeset(self, rev=None):
850 return get_changeset_safe(self.scm_instance, rev)
850 return get_changeset_safe(self.scm_instance, rev)
851
851
852 def get_landing_changeset(self):
852 def get_landing_changeset(self):
853 """
853 """
854 Returns landing changeset, or if that doesn't exist returns the tip
854 Returns landing changeset, or if that doesn't exist returns the tip
855 """
855 """
856 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
856 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
857 return cs
857 return cs
858
858
859 def update_last_change(self, last_change=None):
859 def update_last_change(self, last_change=None):
860 if last_change is None:
860 if last_change is None:
861 last_change = datetime.datetime.now()
861 last_change = datetime.datetime.now()
862 if self.updated_on is None or self.updated_on != last_change:
862 if self.updated_on is None or self.updated_on != last_change:
863 log.debug('updated repo %s with new date %s' % (self, last_change))
863 log.debug('updated repo %s with new date %s' % (self, last_change))
864 self.updated_on = last_change
864 self.updated_on = last_change
865 Session().add(self)
865 Session().add(self)
866 Session().commit()
866 Session().commit()
867
867
868 @property
868 @property
869 def tip(self):
869 def tip(self):
870 return self.get_changeset('tip')
870 return self.get_changeset('tip')
871
871
872 @property
872 @property
873 def author(self):
873 def author(self):
874 return self.tip.author
874 return self.tip.author
875
875
876 @property
876 @property
877 def last_change(self):
877 def last_change(self):
878 return self.scm_instance.last_change
878 return self.scm_instance.last_change
879
879
880 def get_comments(self, revisions=None):
880 def get_comments(self, revisions=None):
881 """
881 """
882 Returns comments for this repository grouped by revisions
882 Returns comments for this repository grouped by revisions
883
883
884 :param revisions: filter query by revisions only
884 :param revisions: filter query by revisions only
885 """
885 """
886 cmts = ChangesetComment.query()\
886 cmts = ChangesetComment.query()\
887 .filter(ChangesetComment.repo == self)
887 .filter(ChangesetComment.repo == self)
888 if revisions:
888 if revisions:
889 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
889 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
890 grouped = defaultdict(list)
890 grouped = defaultdict(list)
891 for cmt in cmts.all():
891 for cmt in cmts.all():
892 grouped[cmt.revision].append(cmt)
892 grouped[cmt.revision].append(cmt)
893 return grouped
893 return grouped
894
894
895 def statuses(self, revisions=None):
895 def statuses(self, revisions=None):
896 """
896 """
897 Returns statuses for this repository
897 Returns statuses for this repository
898
898
899 :param revisions: list of revisions to get statuses for
899 :param revisions: list of revisions to get statuses for
900 :type revisions: list
900 :type revisions: list
901 """
901 """
902
902
903 statuses = ChangesetStatus.query()\
903 statuses = ChangesetStatus.query()\
904 .filter(ChangesetStatus.repo == self)\
904 .filter(ChangesetStatus.repo == self)\
905 .filter(ChangesetStatus.version == 0)
905 .filter(ChangesetStatus.version == 0)
906 if revisions:
906 if revisions:
907 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
907 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
908 grouped = {}
908 grouped = {}
909
909
910 #maybe we have open new pullrequest without a status ?
910 #maybe we have open new pullrequest without a status ?
911 stat = ChangesetStatus.STATUS_UNDER_REVIEW
911 stat = ChangesetStatus.STATUS_UNDER_REVIEW
912 status_lbl = ChangesetStatus.get_status_lbl(stat)
912 status_lbl = ChangesetStatus.get_status_lbl(stat)
913 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
913 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
914 for rev in pr.revisions:
914 for rev in pr.revisions:
915 pr_id = pr.pull_request_id
915 pr_id = pr.pull_request_id
916 pr_repo = pr.other_repo.repo_name
916 pr_repo = pr.other_repo.repo_name
917 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
917 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
918
918
919 for stat in statuses.all():
919 for stat in statuses.all():
920 pr_id = pr_repo = None
920 pr_id = pr_repo = None
921 if stat.pull_request:
921 if stat.pull_request:
922 pr_id = stat.pull_request.pull_request_id
922 pr_id = stat.pull_request.pull_request_id
923 pr_repo = stat.pull_request.other_repo.repo_name
923 pr_repo = stat.pull_request.other_repo.repo_name
924 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
924 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
925 pr_id, pr_repo]
925 pr_id, pr_repo]
926 return grouped
926 return grouped
927
927
928 #==========================================================================
928 #==========================================================================
929 # SCM CACHE INSTANCE
929 # SCM CACHE INSTANCE
930 #==========================================================================
930 #==========================================================================
931
931
932 @property
932 @property
933 def invalidate(self):
933 def invalidate(self):
934 return CacheInvalidation.invalidate(self.repo_name)
934 return CacheInvalidation.invalidate(self.repo_name)
935
935
936 def set_invalidate(self):
936 def set_invalidate(self):
937 """
937 """
938 set a cache for invalidation for this instance
938 set a cache for invalidation for this instance
939 """
939 """
940 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
940 CacheInvalidation.set_invalidate(repo_name=self.repo_name)
941
941
942 @LazyProperty
942 @LazyProperty
943 def scm_instance(self):
943 def scm_instance(self):
944 return self.scm_instance_cached()
944 import rhodecode
945 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
946 if full_cache:
947 return self.scm_instance_cached()
945 return self.__get_instance()
948 return self.__get_instance()
946
949
947 def scm_instance_cached(self, cache_map=None):
950 def scm_instance_cached(self, cache_map=None):
948 @cache_region('long_term')
951 @cache_region('long_term')
949 def _c(repo_name):
952 def _c(repo_name):
950 return self.__get_instance()
953 return self.__get_instance()
951 rn = self.repo_name
954 rn = self.repo_name
952 log.debug('Getting cached instance of repo')
955 log.debug('Getting cached instance of repo')
953
956
954 if cache_map:
957 if cache_map:
955 # get using prefilled cache_map
958 # get using prefilled cache_map
956 invalidate_repo = cache_map[self.repo_name]
959 invalidate_repo = cache_map[self.repo_name]
957 if invalidate_repo:
960 if invalidate_repo:
958 invalidate_repo = (None if invalidate_repo.cache_active
961 invalidate_repo = (None if invalidate_repo.cache_active
959 else invalidate_repo)
962 else invalidate_repo)
960 else:
963 else:
961 # get from invalidate
964 # get from invalidate
962 invalidate_repo = self.invalidate
965 invalidate_repo = self.invalidate
963
966
964 if invalidate_repo is not None:
967 if invalidate_repo is not None:
965 region_invalidate(_c, None, rn)
968 region_invalidate(_c, None, rn)
966 # update our cache
969 # update our cache
967 CacheInvalidation.set_valid(invalidate_repo.cache_key)
970 CacheInvalidation.set_valid(invalidate_repo.cache_key)
968 return _c(rn)
971 return _c(rn)
969
972
970 def __get_instance(self):
973 def __get_instance(self):
971 repo_full_path = self.repo_full_path
974 repo_full_path = self.repo_full_path
972 try:
975 try:
973 alias = get_scm(repo_full_path)[0]
976 alias = get_scm(repo_full_path)[0]
974 log.debug('Creating instance of %s repository' % alias)
977 log.debug('Creating instance of %s repository' % alias)
975 backend = get_backend(alias)
978 backend = get_backend(alias)
976 except VCSError:
979 except VCSError:
977 log.error(traceback.format_exc())
980 log.error(traceback.format_exc())
978 log.error('Perhaps this repository is in db and not in '
981 log.error('Perhaps this repository is in db and not in '
979 'filesystem run rescan repositories with '
982 'filesystem run rescan repositories with '
980 '"destroy old data " option from admin panel')
983 '"destroy old data " option from admin panel')
981 return
984 return
982
985
983 if alias == 'hg':
986 if alias == 'hg':
984
987
985 repo = backend(safe_str(repo_full_path), create=False,
988 repo = backend(safe_str(repo_full_path), create=False,
986 baseui=self._ui)
989 baseui=self._ui)
987 # skip hidden web repository
990 # skip hidden web repository
988 if repo._get_hidden():
991 if repo._get_hidden():
989 return
992 return
990 else:
993 else:
991 repo = backend(repo_full_path, create=False)
994 repo = backend(repo_full_path, create=False)
992
995
993 return repo
996 return repo
994
997
995
998
996 class RepoGroup(Base, BaseModel):
999 class RepoGroup(Base, BaseModel):
997 __tablename__ = 'groups'
1000 __tablename__ = 'groups'
998 __table_args__ = (
1001 __table_args__ = (
999 UniqueConstraint('group_name', 'group_parent_id'),
1002 UniqueConstraint('group_name', 'group_parent_id'),
1000 CheckConstraint('group_id != group_parent_id'),
1003 CheckConstraint('group_id != group_parent_id'),
1001 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1004 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1002 'mysql_charset': 'utf8'},
1005 'mysql_charset': 'utf8'},
1003 )
1006 )
1004 __mapper_args__ = {'order_by': 'group_name'}
1007 __mapper_args__ = {'order_by': 'group_name'}
1005
1008
1006 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1009 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1007 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1010 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1008 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1011 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1009 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1012 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1010 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1013 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1011
1014
1012 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1015 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1013 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1016 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
1014
1017
1015 parent_group = relationship('RepoGroup', remote_side=group_id)
1018 parent_group = relationship('RepoGroup', remote_side=group_id)
1016
1019
1017 def __init__(self, group_name='', parent_group=None):
1020 def __init__(self, group_name='', parent_group=None):
1018 self.group_name = group_name
1021 self.group_name = group_name
1019 self.parent_group = parent_group
1022 self.parent_group = parent_group
1020
1023
1021 def __unicode__(self):
1024 def __unicode__(self):
1022 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1025 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
1023 self.group_name)
1026 self.group_name)
1024
1027
1025 @classmethod
1028 @classmethod
1026 def groups_choices(cls, check_perms=False):
1029 def groups_choices(cls, check_perms=False):
1027 from webhelpers.html import literal as _literal
1030 from webhelpers.html import literal as _literal
1028 from rhodecode.model.scm import ScmModel
1031 from rhodecode.model.scm import ScmModel
1029 groups = cls.query().all()
1032 groups = cls.query().all()
1030 if check_perms:
1033 if check_perms:
1031 #filter group user have access to, it's done
1034 #filter group user have access to, it's done
1032 #magically inside ScmModel based on current user
1035 #magically inside ScmModel based on current user
1033 groups = ScmModel().get_repos_groups(groups)
1036 groups = ScmModel().get_repos_groups(groups)
1034 repo_groups = [('', '')]
1037 repo_groups = [('', '')]
1035 sep = ' &raquo; '
1038 sep = ' &raquo; '
1036 _name = lambda k: _literal(sep.join(k))
1039 _name = lambda k: _literal(sep.join(k))
1037
1040
1038 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1041 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1039 for x in groups])
1042 for x in groups])
1040
1043
1041 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1044 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1042 return repo_groups
1045 return repo_groups
1043
1046
1044 @classmethod
1047 @classmethod
1045 def url_sep(cls):
1048 def url_sep(cls):
1046 return URL_SEP
1049 return URL_SEP
1047
1050
1048 @classmethod
1051 @classmethod
1049 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1052 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1050 if case_insensitive:
1053 if case_insensitive:
1051 gr = cls.query()\
1054 gr = cls.query()\
1052 .filter(cls.group_name.ilike(group_name))
1055 .filter(cls.group_name.ilike(group_name))
1053 else:
1056 else:
1054 gr = cls.query()\
1057 gr = cls.query()\
1055 .filter(cls.group_name == group_name)
1058 .filter(cls.group_name == group_name)
1056 if cache:
1059 if cache:
1057 gr = gr.options(FromCache(
1060 gr = gr.options(FromCache(
1058 "sql_cache_short",
1061 "sql_cache_short",
1059 "get_group_%s" % _hash_key(group_name)
1062 "get_group_%s" % _hash_key(group_name)
1060 )
1063 )
1061 )
1064 )
1062 return gr.scalar()
1065 return gr.scalar()
1063
1066
1064 @property
1067 @property
1065 def parents(self):
1068 def parents(self):
1066 parents_recursion_limit = 5
1069 parents_recursion_limit = 5
1067 groups = []
1070 groups = []
1068 if self.parent_group is None:
1071 if self.parent_group is None:
1069 return groups
1072 return groups
1070 cur_gr = self.parent_group
1073 cur_gr = self.parent_group
1071 groups.insert(0, cur_gr)
1074 groups.insert(0, cur_gr)
1072 cnt = 0
1075 cnt = 0
1073 while 1:
1076 while 1:
1074 cnt += 1
1077 cnt += 1
1075 gr = getattr(cur_gr, 'parent_group', None)
1078 gr = getattr(cur_gr, 'parent_group', None)
1076 cur_gr = cur_gr.parent_group
1079 cur_gr = cur_gr.parent_group
1077 if gr is None:
1080 if gr is None:
1078 break
1081 break
1079 if cnt == parents_recursion_limit:
1082 if cnt == parents_recursion_limit:
1080 # this will prevent accidental infinit loops
1083 # this will prevent accidental infinit loops
1081 log.error('group nested more than %s' %
1084 log.error('group nested more than %s' %
1082 parents_recursion_limit)
1085 parents_recursion_limit)
1083 break
1086 break
1084
1087
1085 groups.insert(0, gr)
1088 groups.insert(0, gr)
1086 return groups
1089 return groups
1087
1090
1088 @property
1091 @property
1089 def children(self):
1092 def children(self):
1090 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1093 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1091
1094
1092 @property
1095 @property
1093 def name(self):
1096 def name(self):
1094 return self.group_name.split(RepoGroup.url_sep())[-1]
1097 return self.group_name.split(RepoGroup.url_sep())[-1]
1095
1098
1096 @property
1099 @property
1097 def full_path(self):
1100 def full_path(self):
1098 return self.group_name
1101 return self.group_name
1099
1102
1100 @property
1103 @property
1101 def full_path_splitted(self):
1104 def full_path_splitted(self):
1102 return self.group_name.split(RepoGroup.url_sep())
1105 return self.group_name.split(RepoGroup.url_sep())
1103
1106
1104 @property
1107 @property
1105 def repositories(self):
1108 def repositories(self):
1106 return Repository.query()\
1109 return Repository.query()\
1107 .filter(Repository.group == self)\
1110 .filter(Repository.group == self)\
1108 .order_by(Repository.repo_name)
1111 .order_by(Repository.repo_name)
1109
1112
1110 @property
1113 @property
1111 def repositories_recursive_count(self):
1114 def repositories_recursive_count(self):
1112 cnt = self.repositories.count()
1115 cnt = self.repositories.count()
1113
1116
1114 def children_count(group):
1117 def children_count(group):
1115 cnt = 0
1118 cnt = 0
1116 for child in group.children:
1119 for child in group.children:
1117 cnt += child.repositories.count()
1120 cnt += child.repositories.count()
1118 cnt += children_count(child)
1121 cnt += children_count(child)
1119 return cnt
1122 return cnt
1120
1123
1121 return cnt + children_count(self)
1124 return cnt + children_count(self)
1122
1125
1123 def recursive_groups_and_repos(self):
1126 def recursive_groups_and_repos(self):
1124 """
1127 """
1125 Recursive return all groups, with repositories in those groups
1128 Recursive return all groups, with repositories in those groups
1126 """
1129 """
1127 all_ = []
1130 all_ = []
1128
1131
1129 def _get_members(root_gr):
1132 def _get_members(root_gr):
1130 for r in root_gr.repositories:
1133 for r in root_gr.repositories:
1131 all_.append(r)
1134 all_.append(r)
1132 childs = root_gr.children.all()
1135 childs = root_gr.children.all()
1133 if childs:
1136 if childs:
1134 for gr in childs:
1137 for gr in childs:
1135 all_.append(gr)
1138 all_.append(gr)
1136 _get_members(gr)
1139 _get_members(gr)
1137
1140
1138 _get_members(self)
1141 _get_members(self)
1139 return [self] + all_
1142 return [self] + all_
1140
1143
1141 def get_new_name(self, group_name):
1144 def get_new_name(self, group_name):
1142 """
1145 """
1143 returns new full group name based on parent and new name
1146 returns new full group name based on parent and new name
1144
1147
1145 :param group_name:
1148 :param group_name:
1146 """
1149 """
1147 path_prefix = (self.parent_group.full_path_splitted if
1150 path_prefix = (self.parent_group.full_path_splitted if
1148 self.parent_group else [])
1151 self.parent_group else [])
1149 return RepoGroup.url_sep().join(path_prefix + [group_name])
1152 return RepoGroup.url_sep().join(path_prefix + [group_name])
1150
1153
1151
1154
1152 class Permission(Base, BaseModel):
1155 class Permission(Base, BaseModel):
1153 __tablename__ = 'permissions'
1156 __tablename__ = 'permissions'
1154 __table_args__ = (
1157 __table_args__ = (
1155 Index('p_perm_name_idx', 'permission_name'),
1158 Index('p_perm_name_idx', 'permission_name'),
1156 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1159 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1157 'mysql_charset': 'utf8'},
1160 'mysql_charset': 'utf8'},
1158 )
1161 )
1159 PERMS = [
1162 PERMS = [
1160 ('repository.none', _('Repository no access')),
1163 ('repository.none', _('Repository no access')),
1161 ('repository.read', _('Repository read access')),
1164 ('repository.read', _('Repository read access')),
1162 ('repository.write', _('Repository write access')),
1165 ('repository.write', _('Repository write access')),
1163 ('repository.admin', _('Repository admin access')),
1166 ('repository.admin', _('Repository admin access')),
1164
1167
1165 ('group.none', _('Repositories Group no access')),
1168 ('group.none', _('Repositories Group no access')),
1166 ('group.read', _('Repositories Group read access')),
1169 ('group.read', _('Repositories Group read access')),
1167 ('group.write', _('Repositories Group write access')),
1170 ('group.write', _('Repositories Group write access')),
1168 ('group.admin', _('Repositories Group admin access')),
1171 ('group.admin', _('Repositories Group admin access')),
1169
1172
1170 ('hg.admin', _('RhodeCode Administrator')),
1173 ('hg.admin', _('RhodeCode Administrator')),
1171 ('hg.create.none', _('Repository creation disabled')),
1174 ('hg.create.none', _('Repository creation disabled')),
1172 ('hg.create.repository', _('Repository creation enabled')),
1175 ('hg.create.repository', _('Repository creation enabled')),
1173 ('hg.fork.none', _('Repository forking disabled')),
1176 ('hg.fork.none', _('Repository forking disabled')),
1174 ('hg.fork.repository', _('Repository forking enabled')),
1177 ('hg.fork.repository', _('Repository forking enabled')),
1175 ('hg.register.none', _('Register disabled')),
1178 ('hg.register.none', _('Register disabled')),
1176 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1179 ('hg.register.manual_activate', _('Register new user with RhodeCode '
1177 'with manual activation')),
1180 'with manual activation')),
1178
1181
1179 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1182 ('hg.register.auto_activate', _('Register new user with RhodeCode '
1180 'with auto activation')),
1183 'with auto activation')),
1181 ]
1184 ]
1182
1185
1183 # defines which permissions are more important higher the more important
1186 # defines which permissions are more important higher the more important
1184 PERM_WEIGHTS = {
1187 PERM_WEIGHTS = {
1185 'repository.none': 0,
1188 'repository.none': 0,
1186 'repository.read': 1,
1189 'repository.read': 1,
1187 'repository.write': 3,
1190 'repository.write': 3,
1188 'repository.admin': 4,
1191 'repository.admin': 4,
1189
1192
1190 'group.none': 0,
1193 'group.none': 0,
1191 'group.read': 1,
1194 'group.read': 1,
1192 'group.write': 3,
1195 'group.write': 3,
1193 'group.admin': 4,
1196 'group.admin': 4,
1194
1197
1195 'hg.fork.none': 0,
1198 'hg.fork.none': 0,
1196 'hg.fork.repository': 1,
1199 'hg.fork.repository': 1,
1197 'hg.create.none': 0,
1200 'hg.create.none': 0,
1198 'hg.create.repository':1
1201 'hg.create.repository':1
1199 }
1202 }
1200
1203
1201 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1204 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1202 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1205 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1203 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1206 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1204
1207
1205 def __unicode__(self):
1208 def __unicode__(self):
1206 return u"<%s('%s:%s')>" % (
1209 return u"<%s('%s:%s')>" % (
1207 self.__class__.__name__, self.permission_id, self.permission_name
1210 self.__class__.__name__, self.permission_id, self.permission_name
1208 )
1211 )
1209
1212
1210 @classmethod
1213 @classmethod
1211 def get_by_key(cls, key):
1214 def get_by_key(cls, key):
1212 return cls.query().filter(cls.permission_name == key).scalar()
1215 return cls.query().filter(cls.permission_name == key).scalar()
1213
1216
1214 @classmethod
1217 @classmethod
1215 def get_default_perms(cls, default_user_id):
1218 def get_default_perms(cls, default_user_id):
1216 q = Session().query(UserRepoToPerm, Repository, cls)\
1219 q = Session().query(UserRepoToPerm, Repository, cls)\
1217 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1220 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1218 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1221 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1219 .filter(UserRepoToPerm.user_id == default_user_id)
1222 .filter(UserRepoToPerm.user_id == default_user_id)
1220
1223
1221 return q.all()
1224 return q.all()
1222
1225
1223 @classmethod
1226 @classmethod
1224 def get_default_group_perms(cls, default_user_id):
1227 def get_default_group_perms(cls, default_user_id):
1225 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1228 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1226 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1229 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1227 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1230 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1228 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1231 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1229
1232
1230 return q.all()
1233 return q.all()
1231
1234
1232
1235
1233 class UserRepoToPerm(Base, BaseModel):
1236 class UserRepoToPerm(Base, BaseModel):
1234 __tablename__ = 'repo_to_perm'
1237 __tablename__ = 'repo_to_perm'
1235 __table_args__ = (
1238 __table_args__ = (
1236 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1239 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1237 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1240 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1238 'mysql_charset': 'utf8'}
1241 'mysql_charset': 'utf8'}
1239 )
1242 )
1240 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1243 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1241 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1244 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1242 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1245 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1243 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1246 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1244
1247
1245 user = relationship('User')
1248 user = relationship('User')
1246 repository = relationship('Repository')
1249 repository = relationship('Repository')
1247 permission = relationship('Permission')
1250 permission = relationship('Permission')
1248
1251
1249 @classmethod
1252 @classmethod
1250 def create(cls, user, repository, permission):
1253 def create(cls, user, repository, permission):
1251 n = cls()
1254 n = cls()
1252 n.user = user
1255 n.user = user
1253 n.repository = repository
1256 n.repository = repository
1254 n.permission = permission
1257 n.permission = permission
1255 Session().add(n)
1258 Session().add(n)
1256 return n
1259 return n
1257
1260
1258 def __unicode__(self):
1261 def __unicode__(self):
1259 return u'<user:%s => %s >' % (self.user, self.repository)
1262 return u'<user:%s => %s >' % (self.user, self.repository)
1260
1263
1261
1264
1262 class UserToPerm(Base, BaseModel):
1265 class UserToPerm(Base, BaseModel):
1263 __tablename__ = 'user_to_perm'
1266 __tablename__ = 'user_to_perm'
1264 __table_args__ = (
1267 __table_args__ = (
1265 UniqueConstraint('user_id', 'permission_id'),
1268 UniqueConstraint('user_id', 'permission_id'),
1266 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1269 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1267 'mysql_charset': 'utf8'}
1270 'mysql_charset': 'utf8'}
1268 )
1271 )
1269 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1272 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1270 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1273 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1271 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1274 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1272
1275
1273 user = relationship('User')
1276 user = relationship('User')
1274 permission = relationship('Permission', lazy='joined')
1277 permission = relationship('Permission', lazy='joined')
1275
1278
1276
1279
1277 class UsersGroupRepoToPerm(Base, BaseModel):
1280 class UsersGroupRepoToPerm(Base, BaseModel):
1278 __tablename__ = 'users_group_repo_to_perm'
1281 __tablename__ = 'users_group_repo_to_perm'
1279 __table_args__ = (
1282 __table_args__ = (
1280 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1283 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1281 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1284 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1282 'mysql_charset': 'utf8'}
1285 'mysql_charset': 'utf8'}
1283 )
1286 )
1284 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1287 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1285 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1288 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1286 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1289 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1287 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1290 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1288
1291
1289 users_group = relationship('UsersGroup')
1292 users_group = relationship('UsersGroup')
1290 permission = relationship('Permission')
1293 permission = relationship('Permission')
1291 repository = relationship('Repository')
1294 repository = relationship('Repository')
1292
1295
1293 @classmethod
1296 @classmethod
1294 def create(cls, users_group, repository, permission):
1297 def create(cls, users_group, repository, permission):
1295 n = cls()
1298 n = cls()
1296 n.users_group = users_group
1299 n.users_group = users_group
1297 n.repository = repository
1300 n.repository = repository
1298 n.permission = permission
1301 n.permission = permission
1299 Session().add(n)
1302 Session().add(n)
1300 return n
1303 return n
1301
1304
1302 def __unicode__(self):
1305 def __unicode__(self):
1303 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1306 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1304
1307
1305
1308
1306 class UsersGroupToPerm(Base, BaseModel):
1309 class UsersGroupToPerm(Base, BaseModel):
1307 __tablename__ = 'users_group_to_perm'
1310 __tablename__ = 'users_group_to_perm'
1308 __table_args__ = (
1311 __table_args__ = (
1309 UniqueConstraint('users_group_id', 'permission_id',),
1312 UniqueConstraint('users_group_id', 'permission_id',),
1310 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1313 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1311 'mysql_charset': 'utf8'}
1314 'mysql_charset': 'utf8'}
1312 )
1315 )
1313 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1316 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1314 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1317 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1315 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1318 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1316
1319
1317 users_group = relationship('UsersGroup')
1320 users_group = relationship('UsersGroup')
1318 permission = relationship('Permission')
1321 permission = relationship('Permission')
1319
1322
1320
1323
1321 class UserRepoGroupToPerm(Base, BaseModel):
1324 class UserRepoGroupToPerm(Base, BaseModel):
1322 __tablename__ = 'user_repo_group_to_perm'
1325 __tablename__ = 'user_repo_group_to_perm'
1323 __table_args__ = (
1326 __table_args__ = (
1324 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1327 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1325 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1328 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1326 'mysql_charset': 'utf8'}
1329 'mysql_charset': 'utf8'}
1327 )
1330 )
1328
1331
1329 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1332 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1330 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1333 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1331 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1334 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1332 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1335 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1333
1336
1334 user = relationship('User')
1337 user = relationship('User')
1335 group = relationship('RepoGroup')
1338 group = relationship('RepoGroup')
1336 permission = relationship('Permission')
1339 permission = relationship('Permission')
1337
1340
1338
1341
1339 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1342 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1340 __tablename__ = 'users_group_repo_group_to_perm'
1343 __tablename__ = 'users_group_repo_group_to_perm'
1341 __table_args__ = (
1344 __table_args__ = (
1342 UniqueConstraint('users_group_id', 'group_id'),
1345 UniqueConstraint('users_group_id', 'group_id'),
1343 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1346 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1344 'mysql_charset': 'utf8'}
1347 'mysql_charset': 'utf8'}
1345 )
1348 )
1346
1349
1347 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1350 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1348 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1351 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1349 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1352 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1350 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1353 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1351
1354
1352 users_group = relationship('UsersGroup')
1355 users_group = relationship('UsersGroup')
1353 permission = relationship('Permission')
1356 permission = relationship('Permission')
1354 group = relationship('RepoGroup')
1357 group = relationship('RepoGroup')
1355
1358
1356
1359
1357 class Statistics(Base, BaseModel):
1360 class Statistics(Base, BaseModel):
1358 __tablename__ = 'statistics'
1361 __tablename__ = 'statistics'
1359 __table_args__ = (
1362 __table_args__ = (
1360 UniqueConstraint('repository_id'),
1363 UniqueConstraint('repository_id'),
1361 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1364 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1362 'mysql_charset': 'utf8'}
1365 'mysql_charset': 'utf8'}
1363 )
1366 )
1364 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1367 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1365 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1368 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1366 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1369 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1367 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1370 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1368 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1371 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1369 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1372 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1370
1373
1371 repository = relationship('Repository', single_parent=True)
1374 repository = relationship('Repository', single_parent=True)
1372
1375
1373
1376
1374 class UserFollowing(Base, BaseModel):
1377 class UserFollowing(Base, BaseModel):
1375 __tablename__ = 'user_followings'
1378 __tablename__ = 'user_followings'
1376 __table_args__ = (
1379 __table_args__ = (
1377 UniqueConstraint('user_id', 'follows_repository_id'),
1380 UniqueConstraint('user_id', 'follows_repository_id'),
1378 UniqueConstraint('user_id', 'follows_user_id'),
1381 UniqueConstraint('user_id', 'follows_user_id'),
1379 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1382 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1380 'mysql_charset': 'utf8'}
1383 'mysql_charset': 'utf8'}
1381 )
1384 )
1382
1385
1383 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1386 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1384 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1387 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1385 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1388 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1386 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1389 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1387 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1390 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1388
1391
1389 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1392 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1390
1393
1391 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1394 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1392 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1395 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1393
1396
1394 @classmethod
1397 @classmethod
1395 def get_repo_followers(cls, repo_id):
1398 def get_repo_followers(cls, repo_id):
1396 return cls.query().filter(cls.follows_repo_id == repo_id)
1399 return cls.query().filter(cls.follows_repo_id == repo_id)
1397
1400
1398
1401
1399 class CacheInvalidation(Base, BaseModel):
1402 class CacheInvalidation(Base, BaseModel):
1400 __tablename__ = 'cache_invalidation'
1403 __tablename__ = 'cache_invalidation'
1401 __table_args__ = (
1404 __table_args__ = (
1402 UniqueConstraint('cache_key'),
1405 UniqueConstraint('cache_key'),
1403 Index('key_idx', 'cache_key'),
1406 Index('key_idx', 'cache_key'),
1404 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1407 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1405 'mysql_charset': 'utf8'},
1408 'mysql_charset': 'utf8'},
1406 )
1409 )
1407 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1410 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1408 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1411 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1409 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1412 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1410 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1413 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1411
1414
1412 def __init__(self, cache_key, cache_args=''):
1415 def __init__(self, cache_key, cache_args=''):
1413 self.cache_key = cache_key
1416 self.cache_key = cache_key
1414 self.cache_args = cache_args
1417 self.cache_args = cache_args
1415 self.cache_active = False
1418 self.cache_active = False
1416
1419
1417 def __unicode__(self):
1420 def __unicode__(self):
1418 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1421 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1419 self.cache_id, self.cache_key)
1422 self.cache_id, self.cache_key)
1420
1423
1421 @property
1424 @property
1422 def prefix(self):
1425 def prefix(self):
1423 _split = self.cache_key.split(self.cache_args, 1)
1426 _split = self.cache_key.split(self.cache_args, 1)
1424 if _split and len(_split) == 2:
1427 if _split and len(_split) == 2:
1425 return _split[0]
1428 return _split[0]
1426 return ''
1429 return ''
1427
1430
1428 @classmethod
1431 @classmethod
1429 def clear_cache(cls):
1432 def clear_cache(cls):
1430 cls.query().delete()
1433 cls.query().delete()
1431
1434
1432 @classmethod
1435 @classmethod
1433 def _get_key(cls, key):
1436 def _get_key(cls, key):
1434 """
1437 """
1435 Wrapper for generating a key, together with a prefix
1438 Wrapper for generating a key, together with a prefix
1436
1439
1437 :param key:
1440 :param key:
1438 """
1441 """
1439 import rhodecode
1442 import rhodecode
1440 prefix = ''
1443 prefix = ''
1441 org_key = key
1444 org_key = key
1442 iid = rhodecode.CONFIG.get('instance_id')
1445 iid = rhodecode.CONFIG.get('instance_id')
1443 if iid:
1446 if iid:
1444 prefix = iid
1447 prefix = iid
1445
1448
1446 return "%s%s" % (prefix, key), prefix, org_key
1449 return "%s%s" % (prefix, key), prefix, org_key
1447
1450
1448 @classmethod
1451 @classmethod
1449 def get_by_key(cls, key):
1452 def get_by_key(cls, key):
1450 return cls.query().filter(cls.cache_key == key).scalar()
1453 return cls.query().filter(cls.cache_key == key).scalar()
1451
1454
1452 @classmethod
1455 @classmethod
1453 def get_by_repo_name(cls, repo_name):
1456 def get_by_repo_name(cls, repo_name):
1454 return cls.query().filter(cls.cache_args == repo_name).all()
1457 return cls.query().filter(cls.cache_args == repo_name).all()
1455
1458
1456 @classmethod
1459 @classmethod
1457 def _get_or_create_key(cls, key, repo_name, commit=True):
1460 def _get_or_create_key(cls, key, repo_name, commit=True):
1458 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1461 inv_obj = Session().query(cls).filter(cls.cache_key == key).scalar()
1459 if not inv_obj:
1462 if not inv_obj:
1460 try:
1463 try:
1461 inv_obj = CacheInvalidation(key, repo_name)
1464 inv_obj = CacheInvalidation(key, repo_name)
1462 Session().add(inv_obj)
1465 Session().add(inv_obj)
1463 if commit:
1466 if commit:
1464 Session().commit()
1467 Session().commit()
1465 except Exception:
1468 except Exception:
1466 log.error(traceback.format_exc())
1469 log.error(traceback.format_exc())
1467 Session().rollback()
1470 Session().rollback()
1468 return inv_obj
1471 return inv_obj
1469
1472
1470 @classmethod
1473 @classmethod
1471 def invalidate(cls, key):
1474 def invalidate(cls, key):
1472 """
1475 """
1473 Returns Invalidation object if this given key should be invalidated
1476 Returns Invalidation object if this given key should be invalidated
1474 None otherwise. `cache_active = False` means that this cache
1477 None otherwise. `cache_active = False` means that this cache
1475 state is not valid and needs to be invalidated
1478 state is not valid and needs to be invalidated
1476
1479
1477 :param key:
1480 :param key:
1478 """
1481 """
1479 repo_name = key
1482 repo_name = key
1480 repo_name = remove_suffix(repo_name, '_README')
1483 repo_name = remove_suffix(repo_name, '_README')
1481 repo_name = remove_suffix(repo_name, '_RSS')
1484 repo_name = remove_suffix(repo_name, '_RSS')
1482 repo_name = remove_suffix(repo_name, '_ATOM')
1485 repo_name = remove_suffix(repo_name, '_ATOM')
1483
1486
1484 # adds instance prefix
1487 # adds instance prefix
1485 key, _prefix, _org_key = cls._get_key(key)
1488 key, _prefix, _org_key = cls._get_key(key)
1486 inv = cls._get_or_create_key(key, repo_name)
1489 inv = cls._get_or_create_key(key, repo_name)
1487
1490
1488 if inv and inv.cache_active is False:
1491 if inv and inv.cache_active is False:
1489 return inv
1492 return inv
1490
1493
1491 @classmethod
1494 @classmethod
1492 def set_invalidate(cls, key=None, repo_name=None):
1495 def set_invalidate(cls, key=None, repo_name=None):
1493 """
1496 """
1494 Mark this Cache key for invalidation, either by key or whole
1497 Mark this Cache key for invalidation, either by key or whole
1495 cache sets based on repo_name
1498 cache sets based on repo_name
1496
1499
1497 :param key:
1500 :param key:
1498 """
1501 """
1499 if key:
1502 if key:
1500 key, _prefix, _org_key = cls._get_key(key)
1503 key, _prefix, _org_key = cls._get_key(key)
1501 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1504 inv_objs = Session().query(cls).filter(cls.cache_key == key).all()
1502 elif repo_name:
1505 elif repo_name:
1503 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1506 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1504
1507
1505 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1508 log.debug('marking %s key[s] for invalidation based on key=%s,repo_name=%s'
1506 % (len(inv_objs), key, repo_name))
1509 % (len(inv_objs), key, repo_name))
1507 try:
1510 try:
1508 for inv_obj in inv_objs:
1511 for inv_obj in inv_objs:
1509 inv_obj.cache_active = False
1512 inv_obj.cache_active = False
1510 Session().add(inv_obj)
1513 Session().add(inv_obj)
1511 Session().commit()
1514 Session().commit()
1512 except Exception:
1515 except Exception:
1513 log.error(traceback.format_exc())
1516 log.error(traceback.format_exc())
1514 Session().rollback()
1517 Session().rollback()
1515
1518
1516 @classmethod
1519 @classmethod
1517 def set_valid(cls, key):
1520 def set_valid(cls, key):
1518 """
1521 """
1519 Mark this cache key as active and currently cached
1522 Mark this cache key as active and currently cached
1520
1523
1521 :param key:
1524 :param key:
1522 """
1525 """
1523 inv_obj = cls.get_by_key(key)
1526 inv_obj = cls.get_by_key(key)
1524 inv_obj.cache_active = True
1527 inv_obj.cache_active = True
1525 Session().add(inv_obj)
1528 Session().add(inv_obj)
1526 Session().commit()
1529 Session().commit()
1527
1530
1528 @classmethod
1531 @classmethod
1529 def get_cache_map(cls):
1532 def get_cache_map(cls):
1530
1533
1531 class cachemapdict(dict):
1534 class cachemapdict(dict):
1532
1535
1533 def __init__(self, *args, **kwargs):
1536 def __init__(self, *args, **kwargs):
1534 fixkey = kwargs.get('fixkey')
1537 fixkey = kwargs.get('fixkey')
1535 if fixkey:
1538 if fixkey:
1536 del kwargs['fixkey']
1539 del kwargs['fixkey']
1537 self.fixkey = fixkey
1540 self.fixkey = fixkey
1538 super(cachemapdict, self).__init__(*args, **kwargs)
1541 super(cachemapdict, self).__init__(*args, **kwargs)
1539
1542
1540 def __getattr__(self, name):
1543 def __getattr__(self, name):
1541 key = name
1544 key = name
1542 if self.fixkey:
1545 if self.fixkey:
1543 key, _prefix, _org_key = cls._get_key(key)
1546 key, _prefix, _org_key = cls._get_key(key)
1544 if key in self.__dict__:
1547 if key in self.__dict__:
1545 return self.__dict__[key]
1548 return self.__dict__[key]
1546 else:
1549 else:
1547 return self[key]
1550 return self[key]
1548
1551
1549 def __getitem__(self, key):
1552 def __getitem__(self, key):
1550 if self.fixkey:
1553 if self.fixkey:
1551 key, _prefix, _org_key = cls._get_key(key)
1554 key, _prefix, _org_key = cls._get_key(key)
1552 try:
1555 try:
1553 return super(cachemapdict, self).__getitem__(key)
1556 return super(cachemapdict, self).__getitem__(key)
1554 except KeyError:
1557 except KeyError:
1555 return
1558 return
1556
1559
1557 cache_map = cachemapdict(fixkey=True)
1560 cache_map = cachemapdict(fixkey=True)
1558 for obj in cls.query().all():
1561 for obj in cls.query().all():
1559 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1562 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1560 return cache_map
1563 return cache_map
1561
1564
1562
1565
1563 class ChangesetComment(Base, BaseModel):
1566 class ChangesetComment(Base, BaseModel):
1564 __tablename__ = 'changeset_comments'
1567 __tablename__ = 'changeset_comments'
1565 __table_args__ = (
1568 __table_args__ = (
1566 Index('cc_revision_idx', 'revision'),
1569 Index('cc_revision_idx', 'revision'),
1567 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1570 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1568 'mysql_charset': 'utf8'},
1571 'mysql_charset': 'utf8'},
1569 )
1572 )
1570 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1573 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1571 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1574 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1572 revision = Column('revision', String(40), nullable=True)
1575 revision = Column('revision', String(40), nullable=True)
1573 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1576 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1574 line_no = Column('line_no', Unicode(10), nullable=True)
1577 line_no = Column('line_no', Unicode(10), nullable=True)
1575 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1578 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1576 f_path = Column('f_path', Unicode(1000), nullable=True)
1579 f_path = Column('f_path', Unicode(1000), nullable=True)
1577 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1580 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1578 text = Column('text', UnicodeText(25000), nullable=False)
1581 text = Column('text', UnicodeText(25000), nullable=False)
1579 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1582 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1580 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1583 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1581
1584
1582 author = relationship('User', lazy='joined')
1585 author = relationship('User', lazy='joined')
1583 repo = relationship('Repository')
1586 repo = relationship('Repository')
1584 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1587 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1585 pull_request = relationship('PullRequest', lazy='joined')
1588 pull_request = relationship('PullRequest', lazy='joined')
1586
1589
1587 @classmethod
1590 @classmethod
1588 def get_users(cls, revision=None, pull_request_id=None):
1591 def get_users(cls, revision=None, pull_request_id=None):
1589 """
1592 """
1590 Returns user associated with this ChangesetComment. ie those
1593 Returns user associated with this ChangesetComment. ie those
1591 who actually commented
1594 who actually commented
1592
1595
1593 :param cls:
1596 :param cls:
1594 :param revision:
1597 :param revision:
1595 """
1598 """
1596 q = Session().query(User)\
1599 q = Session().query(User)\
1597 .join(ChangesetComment.author)
1600 .join(ChangesetComment.author)
1598 if revision:
1601 if revision:
1599 q = q.filter(cls.revision == revision)
1602 q = q.filter(cls.revision == revision)
1600 elif pull_request_id:
1603 elif pull_request_id:
1601 q = q.filter(cls.pull_request_id == pull_request_id)
1604 q = q.filter(cls.pull_request_id == pull_request_id)
1602 return q.all()
1605 return q.all()
1603
1606
1604
1607
1605 class ChangesetStatus(Base, BaseModel):
1608 class ChangesetStatus(Base, BaseModel):
1606 __tablename__ = 'changeset_statuses'
1609 __tablename__ = 'changeset_statuses'
1607 __table_args__ = (
1610 __table_args__ = (
1608 Index('cs_revision_idx', 'revision'),
1611 Index('cs_revision_idx', 'revision'),
1609 Index('cs_version_idx', 'version'),
1612 Index('cs_version_idx', 'version'),
1610 UniqueConstraint('repo_id', 'revision', 'version'),
1613 UniqueConstraint('repo_id', 'revision', 'version'),
1611 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1614 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1612 'mysql_charset': 'utf8'}
1615 'mysql_charset': 'utf8'}
1613 )
1616 )
1614 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1617 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1615 STATUS_APPROVED = 'approved'
1618 STATUS_APPROVED = 'approved'
1616 STATUS_REJECTED = 'rejected'
1619 STATUS_REJECTED = 'rejected'
1617 STATUS_UNDER_REVIEW = 'under_review'
1620 STATUS_UNDER_REVIEW = 'under_review'
1618
1621
1619 STATUSES = [
1622 STATUSES = [
1620 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1623 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1621 (STATUS_APPROVED, _("Approved")),
1624 (STATUS_APPROVED, _("Approved")),
1622 (STATUS_REJECTED, _("Rejected")),
1625 (STATUS_REJECTED, _("Rejected")),
1623 (STATUS_UNDER_REVIEW, _("Under Review")),
1626 (STATUS_UNDER_REVIEW, _("Under Review")),
1624 ]
1627 ]
1625
1628
1626 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1629 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1627 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1630 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1628 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1631 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1629 revision = Column('revision', String(40), nullable=False)
1632 revision = Column('revision', String(40), nullable=False)
1630 status = Column('status', String(128), nullable=False, default=DEFAULT)
1633 status = Column('status', String(128), nullable=False, default=DEFAULT)
1631 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1634 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1632 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1635 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1633 version = Column('version', Integer(), nullable=False, default=0)
1636 version = Column('version', Integer(), nullable=False, default=0)
1634 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1637 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1635
1638
1636 author = relationship('User', lazy='joined')
1639 author = relationship('User', lazy='joined')
1637 repo = relationship('Repository')
1640 repo = relationship('Repository')
1638 comment = relationship('ChangesetComment', lazy='joined')
1641 comment = relationship('ChangesetComment', lazy='joined')
1639 pull_request = relationship('PullRequest', lazy='joined')
1642 pull_request = relationship('PullRequest', lazy='joined')
1640
1643
1641 def __unicode__(self):
1644 def __unicode__(self):
1642 return u"<%s('%s:%s')>" % (
1645 return u"<%s('%s:%s')>" % (
1643 self.__class__.__name__,
1646 self.__class__.__name__,
1644 self.status, self.author
1647 self.status, self.author
1645 )
1648 )
1646
1649
1647 @classmethod
1650 @classmethod
1648 def get_status_lbl(cls, value):
1651 def get_status_lbl(cls, value):
1649 return dict(cls.STATUSES).get(value)
1652 return dict(cls.STATUSES).get(value)
1650
1653
1651 @property
1654 @property
1652 def status_lbl(self):
1655 def status_lbl(self):
1653 return ChangesetStatus.get_status_lbl(self.status)
1656 return ChangesetStatus.get_status_lbl(self.status)
1654
1657
1655
1658
1656 class PullRequest(Base, BaseModel):
1659 class PullRequest(Base, BaseModel):
1657 __tablename__ = 'pull_requests'
1660 __tablename__ = 'pull_requests'
1658 __table_args__ = (
1661 __table_args__ = (
1659 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1662 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1660 'mysql_charset': 'utf8'},
1663 'mysql_charset': 'utf8'},
1661 )
1664 )
1662
1665
1663 STATUS_NEW = u'new'
1666 STATUS_NEW = u'new'
1664 STATUS_OPEN = u'open'
1667 STATUS_OPEN = u'open'
1665 STATUS_CLOSED = u'closed'
1668 STATUS_CLOSED = u'closed'
1666
1669
1667 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1670 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1668 title = Column('title', Unicode(256), nullable=True)
1671 title = Column('title', Unicode(256), nullable=True)
1669 description = Column('description', UnicodeText(10240), nullable=True)
1672 description = Column('description', UnicodeText(10240), nullable=True)
1670 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1673 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1671 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1674 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1672 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1675 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1673 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1676 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1674 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1677 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1675 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1678 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1676 org_ref = Column('org_ref', Unicode(256), nullable=False)
1679 org_ref = Column('org_ref', Unicode(256), nullable=False)
1677 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1680 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1678 other_ref = Column('other_ref', Unicode(256), nullable=False)
1681 other_ref = Column('other_ref', Unicode(256), nullable=False)
1679
1682
1680 @hybrid_property
1683 @hybrid_property
1681 def revisions(self):
1684 def revisions(self):
1682 return self._revisions.split(':')
1685 return self._revisions.split(':')
1683
1686
1684 @revisions.setter
1687 @revisions.setter
1685 def revisions(self, val):
1688 def revisions(self, val):
1686 self._revisions = ':'.join(val)
1689 self._revisions = ':'.join(val)
1687
1690
1688 author = relationship('User', lazy='joined')
1691 author = relationship('User', lazy='joined')
1689 reviewers = relationship('PullRequestReviewers',
1692 reviewers = relationship('PullRequestReviewers',
1690 cascade="all, delete, delete-orphan")
1693 cascade="all, delete, delete-orphan")
1691 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1694 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1692 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1695 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1693 statuses = relationship('ChangesetStatus')
1696 statuses = relationship('ChangesetStatus')
1694 comments = relationship('ChangesetComment',
1697 comments = relationship('ChangesetComment',
1695 cascade="all, delete, delete-orphan")
1698 cascade="all, delete, delete-orphan")
1696
1699
1697 def is_closed(self):
1700 def is_closed(self):
1698 return self.status == self.STATUS_CLOSED
1701 return self.status == self.STATUS_CLOSED
1699
1702
1700 def __json__(self):
1703 def __json__(self):
1701 return dict(
1704 return dict(
1702 revisions=self.revisions
1705 revisions=self.revisions
1703 )
1706 )
1704
1707
1705
1708
1706 class PullRequestReviewers(Base, BaseModel):
1709 class PullRequestReviewers(Base, BaseModel):
1707 __tablename__ = 'pull_request_reviewers'
1710 __tablename__ = 'pull_request_reviewers'
1708 __table_args__ = (
1711 __table_args__ = (
1709 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1712 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1710 'mysql_charset': 'utf8'},
1713 'mysql_charset': 'utf8'},
1711 )
1714 )
1712
1715
1713 def __init__(self, user=None, pull_request=None):
1716 def __init__(self, user=None, pull_request=None):
1714 self.user = user
1717 self.user = user
1715 self.pull_request = pull_request
1718 self.pull_request = pull_request
1716
1719
1717 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1720 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1718 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1721 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1719 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1722 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1720
1723
1721 user = relationship('User')
1724 user = relationship('User')
1722 pull_request = relationship('PullRequest')
1725 pull_request = relationship('PullRequest')
1723
1726
1724
1727
1725 class Notification(Base, BaseModel):
1728 class Notification(Base, BaseModel):
1726 __tablename__ = 'notifications'
1729 __tablename__ = 'notifications'
1727 __table_args__ = (
1730 __table_args__ = (
1728 Index('notification_type_idx', 'type'),
1731 Index('notification_type_idx', 'type'),
1729 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1732 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1730 'mysql_charset': 'utf8'},
1733 'mysql_charset': 'utf8'},
1731 )
1734 )
1732
1735
1733 TYPE_CHANGESET_COMMENT = u'cs_comment'
1736 TYPE_CHANGESET_COMMENT = u'cs_comment'
1734 TYPE_MESSAGE = u'message'
1737 TYPE_MESSAGE = u'message'
1735 TYPE_MENTION = u'mention'
1738 TYPE_MENTION = u'mention'
1736 TYPE_REGISTRATION = u'registration'
1739 TYPE_REGISTRATION = u'registration'
1737 TYPE_PULL_REQUEST = u'pull_request'
1740 TYPE_PULL_REQUEST = u'pull_request'
1738 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1741 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
1739
1742
1740 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1743 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1741 subject = Column('subject', Unicode(512), nullable=True)
1744 subject = Column('subject', Unicode(512), nullable=True)
1742 body = Column('body', UnicodeText(50000), nullable=True)
1745 body = Column('body', UnicodeText(50000), nullable=True)
1743 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1746 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1744 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1747 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1745 type_ = Column('type', Unicode(256))
1748 type_ = Column('type', Unicode(256))
1746
1749
1747 created_by_user = relationship('User')
1750 created_by_user = relationship('User')
1748 notifications_to_users = relationship('UserNotification', lazy='joined',
1751 notifications_to_users = relationship('UserNotification', lazy='joined',
1749 cascade="all, delete, delete-orphan")
1752 cascade="all, delete, delete-orphan")
1750
1753
1751 @property
1754 @property
1752 def recipients(self):
1755 def recipients(self):
1753 return [x.user for x in UserNotification.query()\
1756 return [x.user for x in UserNotification.query()\
1754 .filter(UserNotification.notification == self)\
1757 .filter(UserNotification.notification == self)\
1755 .order_by(UserNotification.user_id.asc()).all()]
1758 .order_by(UserNotification.user_id.asc()).all()]
1756
1759
1757 @classmethod
1760 @classmethod
1758 def create(cls, created_by, subject, body, recipients, type_=None):
1761 def create(cls, created_by, subject, body, recipients, type_=None):
1759 if type_ is None:
1762 if type_ is None:
1760 type_ = Notification.TYPE_MESSAGE
1763 type_ = Notification.TYPE_MESSAGE
1761
1764
1762 notification = cls()
1765 notification = cls()
1763 notification.created_by_user = created_by
1766 notification.created_by_user = created_by
1764 notification.subject = subject
1767 notification.subject = subject
1765 notification.body = body
1768 notification.body = body
1766 notification.type_ = type_
1769 notification.type_ = type_
1767 notification.created_on = datetime.datetime.now()
1770 notification.created_on = datetime.datetime.now()
1768
1771
1769 for u in recipients:
1772 for u in recipients:
1770 assoc = UserNotification()
1773 assoc = UserNotification()
1771 assoc.notification = notification
1774 assoc.notification = notification
1772 u.notifications.append(assoc)
1775 u.notifications.append(assoc)
1773 Session().add(notification)
1776 Session().add(notification)
1774 return notification
1777 return notification
1775
1778
1776 @property
1779 @property
1777 def description(self):
1780 def description(self):
1778 from rhodecode.model.notification import NotificationModel
1781 from rhodecode.model.notification import NotificationModel
1779 return NotificationModel().make_description(self)
1782 return NotificationModel().make_description(self)
1780
1783
1781
1784
1782 class UserNotification(Base, BaseModel):
1785 class UserNotification(Base, BaseModel):
1783 __tablename__ = 'user_to_notification'
1786 __tablename__ = 'user_to_notification'
1784 __table_args__ = (
1787 __table_args__ = (
1785 UniqueConstraint('user_id', 'notification_id'),
1788 UniqueConstraint('user_id', 'notification_id'),
1786 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1789 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1787 'mysql_charset': 'utf8'}
1790 'mysql_charset': 'utf8'}
1788 )
1791 )
1789 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1792 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1790 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1793 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1791 read = Column('read', Boolean, default=False)
1794 read = Column('read', Boolean, default=False)
1792 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1795 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1793
1796
1794 user = relationship('User', lazy="joined")
1797 user = relationship('User', lazy="joined")
1795 notification = relationship('Notification', lazy="joined",
1798 notification = relationship('Notification', lazy="joined",
1796 order_by=lambda: Notification.created_on.desc(),)
1799 order_by=lambda: Notification.created_on.desc(),)
1797
1800
1798 def mark_as_read(self):
1801 def mark_as_read(self):
1799 self.read = True
1802 self.read = True
1800 Session().add(self)
1803 Session().add(self)
1801
1804
1802
1805
1803 class DbMigrateVersion(Base, BaseModel):
1806 class DbMigrateVersion(Base, BaseModel):
1804 __tablename__ = 'db_migrate_version'
1807 __tablename__ = 'db_migrate_version'
1805 __table_args__ = (
1808 __table_args__ = (
1806 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1809 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1807 'mysql_charset': 'utf8'},
1810 'mysql_charset': 'utf8'},
1808 )
1811 )
1809 repository_id = Column('repository_id', String(250), primary_key=True)
1812 repository_id = Column('repository_id', String(250), primary_key=True)
1810 repository_path = Column('repository_path', Text)
1813 repository_path = Column('repository_path', Text)
1811 version = Column('version', Integer)
1814 version = Column('version', Integer)
General Comments 0
You need to be logged in to leave comments. Login now