##// END OF EJS Templates
implements #212 moved default encoding variable into rhodecode-config. It's now possible to change...
marcink -
r2016:6020e388 beta
parent child Browse files
Show More
@@ -1,288 +1,294 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 host = 0.0.0.0
42 host = 0.0.0.0
43 port = 5000
43 port = 5000
44
44
45 [app:main]
45 [app:main]
46 use = egg:rhodecode
46 use = egg:rhodecode
47 full_stack = true
47 full_stack = true
48 static_files = true
48 static_files = true
49 lang=en
49 lang = en
50 cache_dir = %(here)s/data
50 cache_dir = %(here)s/data
51 index_dir = %(here)s/data/index
51 index_dir = %(here)s/data/index
52 app_instance_uuid = develop
52 app_instance_uuid = rc-develop
53 cut_off_limit = 256000
53 cut_off_limit = 256000
54 force_https = false
54 force_https = false
55 commit_parse_limit = 25
55 commit_parse_limit = 25
56 use_gravatar = true
56 use_gravatar = true
57 container_auth_enabled = false
57 container_auth_enabled = false
58 proxypass_auth_enabled = false
58 proxypass_auth_enabled = false
59 default_encoding = utf8
59
60
60 ## overwrite schema of clone url
61 ## overwrite schema of clone url
61 ## available vars:
62 ## available vars:
62 ## scheme - http/https
63 ## scheme - http/https
63 ## user - current user
64 ## user - current user
64 ## pass - password
65 ## pass - password
65 ## netloc - network location
66 ## netloc - network location
66 ## path - usually repo_name
67 ## path - usually repo_name
67
68
68 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
69 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
69
70
70 ## issue tracking mapping for commits messages
71 ## issue tracking mapping for commits messages
71 ## comment out issue_pat, issue_server, issue_prefix to enable
72 ## comment out issue_pat, issue_server, issue_prefix to enable
72
73
73 ## pattern to get the issues from commit messages
74 ## pattern to get the issues from commit messages
74 ## default one used here is #<numbers> with a regex passive group for `#`
75 ## default one used here is #<numbers> with a regex passive group for `#`
75 ## {id} will be all groups matched from this pattern
76 ## {id} will be all groups matched from this pattern
76
77
77 issue_pat = (?:\s*#)(\d+)
78 issue_pat = (?:\s*#)(\d+)
78
79
79 ## server url to the issue, each {id} will be replaced with match
80 ## server url to the issue, each {id} will be replaced with match
80 ## fetched from the regex and {repo} is replaced with repository name
81 ## fetched from the regex and {repo} is replaced with repository name
81
82
82 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
83 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
83
84
84 ## prefix to add to link to indicate it's an url
85 ## prefix to add to link to indicate it's an url
85 ## #314 will be replaced by <issue_prefix><id>
86 ## #314 will be replaced by <issue_prefix><id>
86
87
87 issue_prefix = #
88 issue_prefix = #
88
89
90 ## instance-id prefix
91 ## a prefix key for this instance used for cache invalidation when running
92 ## multiple instances of rhodecode, make sure it's globally unique for
93 ## all running rhodecode instances. Leave empty if you don't use it
94 instance_id =
89
95
90 ####################################
96 ####################################
91 ### CELERY CONFIG ####
97 ### CELERY CONFIG ####
92 ####################################
98 ####################################
93 use_celery = false
99 use_celery = false
94 broker.host = localhost
100 broker.host = localhost
95 broker.vhost = rabbitmqhost
101 broker.vhost = rabbitmqhost
96 broker.port = 5672
102 broker.port = 5672
97 broker.user = rabbitmq
103 broker.user = rabbitmq
98 broker.password = qweqwe
104 broker.password = qweqwe
99
105
100 celery.imports = rhodecode.lib.celerylib.tasks
106 celery.imports = rhodecode.lib.celerylib.tasks
101
107
102 celery.result.backend = amqp
108 celery.result.backend = amqp
103 celery.result.dburi = amqp://
109 celery.result.dburi = amqp://
104 celery.result.serialier = json
110 celery.result.serialier = json
105
111
106 #celery.send.task.error.emails = true
112 #celery.send.task.error.emails = true
107 #celery.amqp.task.result.expires = 18000
113 #celery.amqp.task.result.expires = 18000
108
114
109 celeryd.concurrency = 2
115 celeryd.concurrency = 2
110 #celeryd.log.file = celeryd.log
116 #celeryd.log.file = celeryd.log
111 celeryd.log.level = debug
117 celeryd.log.level = debug
112 celeryd.max.tasks.per.child = 1
118 celeryd.max.tasks.per.child = 1
113
119
114 #tasks will never be sent to the queue, but executed locally instead.
120 #tasks will never be sent to the queue, but executed locally instead.
115 celery.always.eager = false
121 celery.always.eager = false
116
122
117 ####################################
123 ####################################
118 ### BEAKER CACHE ####
124 ### BEAKER CACHE ####
119 ####################################
125 ####################################
120 beaker.cache.data_dir=%(here)s/data/cache/data
126 beaker.cache.data_dir=%(here)s/data/cache/data
121 beaker.cache.lock_dir=%(here)s/data/cache/lock
127 beaker.cache.lock_dir=%(here)s/data/cache/lock
122
128
123 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
129 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
124
130
125 beaker.cache.super_short_term.type=memory
131 beaker.cache.super_short_term.type=memory
126 beaker.cache.super_short_term.expire=10
132 beaker.cache.super_short_term.expire=10
127 beaker.cache.super_short_term.key_length = 256
133 beaker.cache.super_short_term.key_length = 256
128
134
129 beaker.cache.short_term.type=memory
135 beaker.cache.short_term.type=memory
130 beaker.cache.short_term.expire=60
136 beaker.cache.short_term.expire=60
131 beaker.cache.short_term.key_length = 256
137 beaker.cache.short_term.key_length = 256
132
138
133 beaker.cache.long_term.type=memory
139 beaker.cache.long_term.type=memory
134 beaker.cache.long_term.expire=36000
140 beaker.cache.long_term.expire=36000
135 beaker.cache.long_term.key_length = 256
141 beaker.cache.long_term.key_length = 256
136
142
137 beaker.cache.sql_cache_short.type=memory
143 beaker.cache.sql_cache_short.type=memory
138 beaker.cache.sql_cache_short.expire=10
144 beaker.cache.sql_cache_short.expire=10
139 beaker.cache.sql_cache_short.key_length = 256
145 beaker.cache.sql_cache_short.key_length = 256
140
146
141 beaker.cache.sql_cache_med.type=memory
147 beaker.cache.sql_cache_med.type=memory
142 beaker.cache.sql_cache_med.expire=360
148 beaker.cache.sql_cache_med.expire=360
143 beaker.cache.sql_cache_med.key_length = 256
149 beaker.cache.sql_cache_med.key_length = 256
144
150
145 beaker.cache.sql_cache_long.type=file
151 beaker.cache.sql_cache_long.type=file
146 beaker.cache.sql_cache_long.expire=3600
152 beaker.cache.sql_cache_long.expire=3600
147 beaker.cache.sql_cache_long.key_length = 256
153 beaker.cache.sql_cache_long.key_length = 256
148
154
149 ####################################
155 ####################################
150 ### BEAKER SESSION ####
156 ### BEAKER SESSION ####
151 ####################################
157 ####################################
152 ## Type of storage used for the session, current types are
158 ## Type of storage used for the session, current types are
153 ## dbm, file, memcached, database, and memory.
159 ## dbm, file, memcached, database, and memory.
154 ## The storage uses the Container API
160 ## The storage uses the Container API
155 ## that is also used by the cache system.
161 ## that is also used by the cache system.
156
162
157 ## db session example
163 ## db session example
158
164
159 #beaker.session.type = ext:database
165 #beaker.session.type = ext:database
160 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
166 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
161 #beaker.session.table_name = db_session
167 #beaker.session.table_name = db_session
162
168
163 ## encrypted cookie session, good for many instances
169 ## encrypted cookie session, good for many instances
164 #beaker.session.type = cookie
170 #beaker.session.type = cookie
165
171
166 beaker.session.type = file
172 beaker.session.type = file
167 beaker.session.key = rhodecode
173 beaker.session.key = rhodecode
168 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
174 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
169 #beaker.session.validate_key = 9712sds2212c--zxc123
175 #beaker.session.validate_key = 9712sds2212c--zxc123
170 beaker.session.timeout = 36000
176 beaker.session.timeout = 36000
171 beaker.session.httponly = true
177 beaker.session.httponly = true
172
178
173 ## uncomment for https secure cookie
179 ## uncomment for https secure cookie
174 beaker.session.secure = false
180 beaker.session.secure = false
175
181
176 ##auto save the session to not to use .save()
182 ##auto save the session to not to use .save()
177 beaker.session.auto = False
183 beaker.session.auto = False
178
184
179 ##true exire at browser close
185 ##true exire at browser close
180 #beaker.session.cookie_expires = 3600
186 #beaker.session.cookie_expires = 3600
181
187
182
188
183 ################################################################################
189 ################################################################################
184 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
190 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
185 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
191 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
186 ## execute malicious code after an exception is raised. ##
192 ## execute malicious code after an exception is raised. ##
187 ################################################################################
193 ################################################################################
188 #set debug = false
194 #set debug = false
189
195
190 ##################################
196 ##################################
191 ### LOGVIEW CONFIG ###
197 ### LOGVIEW CONFIG ###
192 ##################################
198 ##################################
193 logview.sqlalchemy = #faa
199 logview.sqlalchemy = #faa
194 logview.pylons.templating = #bfb
200 logview.pylons.templating = #bfb
195 logview.pylons.util = #eee
201 logview.pylons.util = #eee
196
202
197 #########################################################
203 #########################################################
198 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
204 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
199 #########################################################
205 #########################################################
200 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
206 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
201 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
207 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
202 sqlalchemy.db1.echo = false
208 sqlalchemy.db1.echo = false
203 sqlalchemy.db1.pool_recycle = 3600
209 sqlalchemy.db1.pool_recycle = 3600
204 sqlalchemy.convert_unicode = true
210 sqlalchemy.convert_unicode = true
205
211
206 ################################
212 ################################
207 ### LOGGING CONFIGURATION ####
213 ### LOGGING CONFIGURATION ####
208 ################################
214 ################################
209 [loggers]
215 [loggers]
210 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
216 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
211
217
212 [handlers]
218 [handlers]
213 keys = console, console_sql
219 keys = console, console_sql
214
220
215 [formatters]
221 [formatters]
216 keys = generic, color_formatter, color_formatter_sql
222 keys = generic, color_formatter, color_formatter_sql
217
223
218 #############
224 #############
219 ## LOGGERS ##
225 ## LOGGERS ##
220 #############
226 #############
221 [logger_root]
227 [logger_root]
222 level = NOTSET
228 level = NOTSET
223 handlers = console
229 handlers = console
224
230
225 [logger_routes]
231 [logger_routes]
226 level = DEBUG
232 level = DEBUG
227 handlers =
233 handlers =
228 qualname = routes.middleware
234 qualname = routes.middleware
229 # "level = DEBUG" logs the route matched and routing variables.
235 # "level = DEBUG" logs the route matched and routing variables.
230 propagate = 1
236 propagate = 1
231
237
232 [logger_beaker]
238 [logger_beaker]
233 level = DEBUG
239 level = DEBUG
234 handlers =
240 handlers =
235 qualname = beaker.container
241 qualname = beaker.container
236 propagate = 1
242 propagate = 1
237
243
238 [logger_templates]
244 [logger_templates]
239 level = INFO
245 level = INFO
240 handlers =
246 handlers =
241 qualname = pylons.templating
247 qualname = pylons.templating
242 propagate = 1
248 propagate = 1
243
249
244 [logger_rhodecode]
250 [logger_rhodecode]
245 level = DEBUG
251 level = DEBUG
246 handlers =
252 handlers =
247 qualname = rhodecode
253 qualname = rhodecode
248 propagate = 1
254 propagate = 1
249
255
250 [logger_sqlalchemy]
256 [logger_sqlalchemy]
251 level = INFO
257 level = INFO
252 handlers = console_sql
258 handlers = console_sql
253 qualname = sqlalchemy.engine
259 qualname = sqlalchemy.engine
254 propagate = 0
260 propagate = 0
255
261
256 ##############
262 ##############
257 ## HANDLERS ##
263 ## HANDLERS ##
258 ##############
264 ##############
259
265
260 [handler_console]
266 [handler_console]
261 class = StreamHandler
267 class = StreamHandler
262 args = (sys.stderr,)
268 args = (sys.stderr,)
263 level = DEBUG
269 level = DEBUG
264 formatter = color_formatter
270 formatter = color_formatter
265
271
266 [handler_console_sql]
272 [handler_console_sql]
267 class = StreamHandler
273 class = StreamHandler
268 args = (sys.stderr,)
274 args = (sys.stderr,)
269 level = DEBUG
275 level = DEBUG
270 formatter = color_formatter_sql
276 formatter = color_formatter_sql
271
277
272 ################
278 ################
273 ## FORMATTERS ##
279 ## FORMATTERS ##
274 ################
280 ################
275
281
276 [formatter_generic]
282 [formatter_generic]
277 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
283 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
278 datefmt = %Y-%m-%d %H:%M:%S
284 datefmt = %Y-%m-%d %H:%M:%S
279
285
280 [formatter_color_formatter]
286 [formatter_color_formatter]
281 class=rhodecode.lib.colored_formatter.ColorFormatter
287 class=rhodecode.lib.colored_formatter.ColorFormatter
282 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
288 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
283 datefmt = %Y-%m-%d %H:%M:%S
289 datefmt = %Y-%m-%d %H:%M:%S
284
290
285 [formatter_color_formatter_sql]
291 [formatter_color_formatter_sql]
286 class=rhodecode.lib.colored_formatter.ColorFormatterSql
292 class=rhodecode.lib.colored_formatter.ColorFormatterSql
287 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
293 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
288 datefmt = %Y-%m-%d %H:%M:%S
294 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,288 +1,295 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 host = 127.0.0.1
42 host = 127.0.0.1
43 port = 8001
43 port = 8001
44
44
45 [app:main]
45 [app:main]
46 use = egg:rhodecode
46 use = egg:rhodecode
47 full_stack = true
47 full_stack = true
48 static_files = true
48 static_files = true
49 lang=en
49 lang = en
50 cache_dir = %(here)s/data
50 cache_dir = %(here)s/data
51 index_dir = %(here)s/data/index
51 index_dir = %(here)s/data/index
52 app_instance_uuid = prod1234
52 app_instance_uuid = rc-production
53 cut_off_limit = 256000
53 cut_off_limit = 256000
54 force_https = false
54 force_https = false
55 commit_parse_limit = 50
55 commit_parse_limit = 50
56 use_gravatar = true
56 use_gravatar = true
57 container_auth_enabled = false
57 container_auth_enabled = false
58 proxypass_auth_enabled = false
58 proxypass_auth_enabled = false
59 default_encoding = utf8
59
60
60 ## overwrite schema of clone url
61 ## overwrite schema of clone url
61 ## available vars:
62 ## available vars:
62 ## scheme - http/https
63 ## scheme - http/https
63 ## user - current user
64 ## user - current user
64 ## pass - password
65 ## pass - password
65 ## netloc - network location
66 ## netloc - network location
66 ## path - usually repo_name
67 ## path - usually repo_name
67
68
68 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
69 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
69
70
70 ## issue tracking mapping for commits messages
71 ## issue tracking mapping for commits messages
71 ## comment out issue_pat, issue_server, issue_prefix to enable
72 ## comment out issue_pat, issue_server, issue_prefix to enable
72
73
73 ## pattern to get the issues from commit messages
74 ## pattern to get the issues from commit messages
74 ## default one used here is #<numbers> with a regex passive group for `#`
75 ## default one used here is #<numbers> with a regex passive group for `#`
75 ## {id} will be all groups matched from this pattern
76 ## {id} will be all groups matched from this pattern
76
77
77 issue_pat = (?:\s*#)(\d+)
78 issue_pat = (?:\s*#)(\d+)
78
79
79 ## server url to the issue, each {id} will be replaced with match
80 ## server url to the issue, each {id} will be replaced with match
80 ## fetched from the regex and {repo} is replaced with repository name
81 ## fetched from the regex and {repo} is replaced with repository name
81
82
82 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
83 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
83
84
84 ## prefix to add to link to indicate it's an url
85 ## prefix to add to link to indicate it's an url
85 ## #314 will be replaced by <issue_prefix><id>
86 ## #314 will be replaced by <issue_prefix><id>
86
87
87 issue_prefix = #
88 issue_prefix = #
88
89
90 ## instance-id prefix
91 ## a prefix key for this instance used for cache invalidation when running
92 ## multiple instances of rhodecode, make sure it's globally unique for
93 ## all running rhodecode instances. Leave empty if you don't use it
94 instance_id =
89
95
90 ####################################
96 ####################################
91 ### CELERY CONFIG ####
97 ### CELERY CONFIG ####
92 ####################################
98 ####################################
93 use_celery = false
99 use_celery = false
94 broker.host = localhost
100 broker.host = localhost
95 broker.vhost = rabbitmqhost
101 broker.vhost = rabbitmqhost
96 broker.port = 5672
102 broker.port = 5672
97 broker.user = rabbitmq
103 broker.user = rabbitmq
98 broker.password = qweqwe
104 broker.password = qweqwe
99
105
100 celery.imports = rhodecode.lib.celerylib.tasks
106 celery.imports = rhodecode.lib.celerylib.tasks
101
107
102 celery.result.backend = amqp
108 celery.result.backend = amqp
103 celery.result.dburi = amqp://
109 celery.result.dburi = amqp://
104 celery.result.serialier = json
110 celery.result.serialier = json
105
111
106 #celery.send.task.error.emails = true
112 #celery.send.task.error.emails = true
107 #celery.amqp.task.result.expires = 18000
113 #celery.amqp.task.result.expires = 18000
108
114
109 celeryd.concurrency = 2
115 celeryd.concurrency = 2
110 #celeryd.log.file = celeryd.log
116 #celeryd.log.file = celeryd.log
111 celeryd.log.level = debug
117 celeryd.log.level = debug
112 celeryd.max.tasks.per.child = 1
118 celeryd.max.tasks.per.child = 1
113
119
114 #tasks will never be sent to the queue, but executed locally instead.
120 #tasks will never be sent to the queue, but executed locally instead.
115 celery.always.eager = false
121 celery.always.eager = false
116
122
117 ####################################
123 ####################################
118 ### BEAKER CACHE ####
124 ### BEAKER CACHE ####
119 ####################################
125 ####################################
120 beaker.cache.data_dir=%(here)s/data/cache/data
126 beaker.cache.data_dir=%(here)s/data/cache/data
121 beaker.cache.lock_dir=%(here)s/data/cache/lock
127 beaker.cache.lock_dir=%(here)s/data/cache/lock
122
128
123 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
129 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
124
130
125 beaker.cache.super_short_term.type=memory
131 beaker.cache.super_short_term.type=memory
126 beaker.cache.super_short_term.expire=10
132 beaker.cache.super_short_term.expire=10
127 beaker.cache.super_short_term.key_length = 256
133 beaker.cache.super_short_term.key_length = 256
128
134
129 beaker.cache.short_term.type=memory
135 beaker.cache.short_term.type=memory
130 beaker.cache.short_term.expire=60
136 beaker.cache.short_term.expire=60
131 beaker.cache.short_term.key_length = 256
137 beaker.cache.short_term.key_length = 256
132
138
133 beaker.cache.long_term.type=memory
139 beaker.cache.long_term.type=memory
134 beaker.cache.long_term.expire=36000
140 beaker.cache.long_term.expire=36000
135 beaker.cache.long_term.key_length = 256
141 beaker.cache.long_term.key_length = 256
136
142
137 beaker.cache.sql_cache_short.type=memory
143 beaker.cache.sql_cache_short.type=memory
138 beaker.cache.sql_cache_short.expire=10
144 beaker.cache.sql_cache_short.expire=10
139 beaker.cache.sql_cache_short.key_length = 256
145 beaker.cache.sql_cache_short.key_length = 256
140
146
141 beaker.cache.sql_cache_med.type=memory
147 beaker.cache.sql_cache_med.type=memory
142 beaker.cache.sql_cache_med.expire=360
148 beaker.cache.sql_cache_med.expire=360
143 beaker.cache.sql_cache_med.key_length = 256
149 beaker.cache.sql_cache_med.key_length = 256
144
150
145 beaker.cache.sql_cache_long.type=file
151 beaker.cache.sql_cache_long.type=file
146 beaker.cache.sql_cache_long.expire=3600
152 beaker.cache.sql_cache_long.expire=3600
147 beaker.cache.sql_cache_long.key_length = 256
153 beaker.cache.sql_cache_long.key_length = 256
148
154
149 ####################################
155 ####################################
150 ### BEAKER SESSION ####
156 ### BEAKER SESSION ####
151 ####################################
157 ####################################
152 ## Type of storage used for the session, current types are
158 ## Type of storage used for the session, current types are
153 ## dbm, file, memcached, database, and memory.
159 ## dbm, file, memcached, database, and memory.
154 ## The storage uses the Container API
160 ## The storage uses the Container API
155 ## that is also used by the cache system.
161 ## that is also used by the cache system.
156
162
157 ## db session example
163 ## db session example
158
164
159 #beaker.session.type = ext:database
165 #beaker.session.type = ext:database
160 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
166 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
161 #beaker.session.table_name = db_session
167 #beaker.session.table_name = db_session
162
168
163 ## encrypted cookie session, good for many instances
169 ## encrypted cookie session, good for many instances
164 #beaker.session.type = cookie
170 #beaker.session.type = cookie
165
171
166 beaker.session.type = file
172 beaker.session.type = file
167 beaker.session.key = rhodecode
173 beaker.session.key = rhodecode
174 # secure cookie requires AES python libraries
168 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
175 #beaker.session.encrypt_key = g654dcno0-9873jhgfreyu
169 #beaker.session.validate_key = 9712sds2212c--zxc123
176 #beaker.session.validate_key = 9712sds2212c--zxc123
170 beaker.session.timeout = 36000
177 beaker.session.timeout = 36000
171 beaker.session.httponly = true
178 beaker.session.httponly = true
172
179
173 ## uncomment for https secure cookie
180 ## uncomment for https secure cookie
174 beaker.session.secure = false
181 beaker.session.secure = false
175
182
176 ##auto save the session to not to use .save()
183 ##auto save the session to not to use .save()
177 beaker.session.auto = False
184 beaker.session.auto = False
178
185
179 ##true exire at browser close
186 ##true exire at browser close
180 #beaker.session.cookie_expires = 3600
187 #beaker.session.cookie_expires = 3600
181
188
182
189
183 ################################################################################
190 ################################################################################
184 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
191 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
185 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
192 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
186 ## execute malicious code after an exception is raised. ##
193 ## execute malicious code after an exception is raised. ##
187 ################################################################################
194 ################################################################################
188 set debug = false
195 set debug = false
189
196
190 ##################################
197 ##################################
191 ### LOGVIEW CONFIG ###
198 ### LOGVIEW CONFIG ###
192 ##################################
199 ##################################
193 logview.sqlalchemy = #faa
200 logview.sqlalchemy = #faa
194 logview.pylons.templating = #bfb
201 logview.pylons.templating = #bfb
195 logview.pylons.util = #eee
202 logview.pylons.util = #eee
196
203
197 #########################################################
204 #########################################################
198 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
205 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
199 #########################################################
206 #########################################################
200 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
207 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
201 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
208 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
202 sqlalchemy.db1.echo = false
209 sqlalchemy.db1.echo = false
203 sqlalchemy.db1.pool_recycle = 3600
210 sqlalchemy.db1.pool_recycle = 3600
204 sqlalchemy.convert_unicode = true
211 sqlalchemy.convert_unicode = true
205
212
206 ################################
213 ################################
207 ### LOGGING CONFIGURATION ####
214 ### LOGGING CONFIGURATION ####
208 ################################
215 ################################
209 [loggers]
216 [loggers]
210 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
217 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
211
218
212 [handlers]
219 [handlers]
213 keys = console, console_sql
220 keys = console, console_sql
214
221
215 [formatters]
222 [formatters]
216 keys = generic, color_formatter, color_formatter_sql
223 keys = generic, color_formatter, color_formatter_sql
217
224
218 #############
225 #############
219 ## LOGGERS ##
226 ## LOGGERS ##
220 #############
227 #############
221 [logger_root]
228 [logger_root]
222 level = NOTSET
229 level = NOTSET
223 handlers = console
230 handlers = console
224
231
225 [logger_routes]
232 [logger_routes]
226 level = DEBUG
233 level = DEBUG
227 handlers =
234 handlers =
228 qualname = routes.middleware
235 qualname = routes.middleware
229 # "level = DEBUG" logs the route matched and routing variables.
236 # "level = DEBUG" logs the route matched and routing variables.
230 propagate = 1
237 propagate = 1
231
238
232 [logger_beaker]
239 [logger_beaker]
233 level = DEBUG
240 level = DEBUG
234 handlers =
241 handlers =
235 qualname = beaker.container
242 qualname = beaker.container
236 propagate = 1
243 propagate = 1
237
244
238 [logger_templates]
245 [logger_templates]
239 level = INFO
246 level = INFO
240 handlers =
247 handlers =
241 qualname = pylons.templating
248 qualname = pylons.templating
242 propagate = 1
249 propagate = 1
243
250
244 [logger_rhodecode]
251 [logger_rhodecode]
245 level = DEBUG
252 level = DEBUG
246 handlers =
253 handlers =
247 qualname = rhodecode
254 qualname = rhodecode
248 propagate = 1
255 propagate = 1
249
256
250 [logger_sqlalchemy]
257 [logger_sqlalchemy]
251 level = INFO
258 level = INFO
252 handlers = console_sql
259 handlers = console_sql
253 qualname = sqlalchemy.engine
260 qualname = sqlalchemy.engine
254 propagate = 0
261 propagate = 0
255
262
256 ##############
263 ##############
257 ## HANDLERS ##
264 ## HANDLERS ##
258 ##############
265 ##############
259
266
260 [handler_console]
267 [handler_console]
261 class = StreamHandler
268 class = StreamHandler
262 args = (sys.stderr,)
269 args = (sys.stderr,)
263 level = INFO
270 level = INFO
264 formatter = generic
271 formatter = generic
265
272
266 [handler_console_sql]
273 [handler_console_sql]
267 class = StreamHandler
274 class = StreamHandler
268 args = (sys.stderr,)
275 args = (sys.stderr,)
269 level = WARN
276 level = WARN
270 formatter = generic
277 formatter = generic
271
278
272 ################
279 ################
273 ## FORMATTERS ##
280 ## FORMATTERS ##
274 ################
281 ################
275
282
276 [formatter_generic]
283 [formatter_generic]
277 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
284 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
278 datefmt = %Y-%m-%d %H:%M:%S
285 datefmt = %Y-%m-%d %H:%M:%S
279
286
280 [formatter_color_formatter]
287 [formatter_color_formatter]
281 class=rhodecode.lib.colored_formatter.ColorFormatter
288 class=rhodecode.lib.colored_formatter.ColorFormatter
282 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
289 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
283 datefmt = %Y-%m-%d %H:%M:%S
290 datefmt = %Y-%m-%d %H:%M:%S
284
291
285 [formatter_color_formatter_sql]
292 [formatter_color_formatter_sql]
286 class=rhodecode.lib.colored_formatter.ColorFormatterSql
293 class=rhodecode.lib.colored_formatter.ColorFormatterSql
287 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
294 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
288 datefmt = %Y-%m-%d %H:%M:%S
295 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,299 +1,305 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 host = 127.0.0.1
42 host = 127.0.0.1
43 port = 5000
43 port = 5000
44
44
45 [app:main]
45 [app:main]
46 use = egg:rhodecode
46 use = egg:rhodecode
47 full_stack = true
47 full_stack = true
48 static_files = true
48 static_files = true
49 lang=en
49 lang = en
50 cache_dir = %(here)s/data
50 cache_dir = %(here)s/data
51 index_dir = %(here)s/data/index
51 index_dir = %(here)s/data/index
52 app_instance_uuid = ${app_instance_uuid}
52 app_instance_uuid = ${app_instance_uuid}
53 cut_off_limit = 256000
53 cut_off_limit = 256000
54 force_https = false
54 force_https = false
55 commit_parse_limit = 50
55 commit_parse_limit = 50
56 use_gravatar = true
56 use_gravatar = true
57 container_auth_enabled = false
57 container_auth_enabled = false
58 proxypass_auth_enabled = false
58 proxypass_auth_enabled = false
59 default_encoding = utf8
59
60
60 ## overwrite schema of clone url
61 ## overwrite schema of clone url
61 ## available vars:
62 ## available vars:
62 ## scheme - http/https
63 ## scheme - http/https
63 ## user - current user
64 ## user - current user
64 ## pass - password
65 ## pass - password
65 ## netloc - network location
66 ## netloc - network location
66 ## path - usually repo_name
67 ## path - usually repo_name
67
68
68 # clone_uri = {scheme}://{user}{pass}{netloc}{path}
69 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
69
70
70 ## issue tracking mapping for commits messages
71 ## issue tracking mapping for commits messages
71 ## comment out issue_pat, issue_server, issue_prefix to enable
72 ## comment out issue_pat, issue_server, issue_prefix to enable
72
73
73 ## pattern to get the issues from commit messages
74 ## pattern to get the issues from commit messages
74 ## default one used here is #<numbers> with a regex passive group for `#`
75 ## default one used here is #<numbers> with a regex passive group for `#`
75 ## {id} will be all groups matched from this pattern
76 ## {id} will be all groups matched from this pattern
76
77
77 issue_pat = (?:\s*#)(\d+)
78 issue_pat = (?:\s*#)(\d+)
78
79
79 ## server url to the issue, each {id} will be replaced with match
80 ## server url to the issue, each {id} will be replaced with match
80 ## fetched from the regex and {repo} is replaced with repository name
81 ## fetched from the regex and {repo} is replaced with repository name
81
82
82 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
83 issue_server_link = https://myissueserver.com/{repo}/issue/{id}
83
84
84 ## prefix to add to link to indicate it's an url
85 ## prefix to add to link to indicate it's an url
85 ## #314 will be replaced by <issue_prefix><id>
86 ## #314 will be replaced by <issue_prefix><id>
86
87
87 issue_prefix = #
88 issue_prefix = #
88
89
90 ## instance-id prefix
91 ## a prefix key for this instance used for cache invalidation when running
92 ## multiple instances of rhodecode, make sure it's globally unique for
93 ## all running rhodecode instances. Leave empty if you don't use it
94 instance_id =
89
95
90 ####################################
96 ####################################
91 ### CELERY CONFIG ####
97 ### CELERY CONFIG ####
92 ####################################
98 ####################################
93 use_celery = false
99 use_celery = false
94 broker.host = localhost
100 broker.host = localhost
95 broker.vhost = rabbitmqhost
101 broker.vhost = rabbitmqhost
96 broker.port = 5672
102 broker.port = 5672
97 broker.user = rabbitmq
103 broker.user = rabbitmq
98 broker.password = qweqwe
104 broker.password = qweqwe
99
105
100 celery.imports = rhodecode.lib.celerylib.tasks
106 celery.imports = rhodecode.lib.celerylib.tasks
101
107
102 celery.result.backend = amqp
108 celery.result.backend = amqp
103 celery.result.dburi = amqp://
109 celery.result.dburi = amqp://
104 celery.result.serialier = json
110 celery.result.serialier = json
105
111
106 #celery.send.task.error.emails = true
112 #celery.send.task.error.emails = true
107 #celery.amqp.task.result.expires = 18000
113 #celery.amqp.task.result.expires = 18000
108
114
109 celeryd.concurrency = 2
115 celeryd.concurrency = 2
110 #celeryd.log.file = celeryd.log
116 #celeryd.log.file = celeryd.log
111 celeryd.log.level = debug
117 celeryd.log.level = debug
112 celeryd.max.tasks.per.child = 1
118 celeryd.max.tasks.per.child = 1
113
119
114 #tasks will never be sent to the queue, but executed locally instead.
120 #tasks will never be sent to the queue, but executed locally instead.
115 celery.always.eager = false
121 celery.always.eager = false
116
122
117 ####################################
123 ####################################
118 ### BEAKER CACHE ####
124 ### BEAKER CACHE ####
119 ####################################
125 ####################################
120 beaker.cache.data_dir=%(here)s/data/cache/data
126 beaker.cache.data_dir=%(here)s/data/cache/data
121 beaker.cache.lock_dir=%(here)s/data/cache/lock
127 beaker.cache.lock_dir=%(here)s/data/cache/lock
122
128
123 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
129 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
124
130
125 beaker.cache.super_short_term.type=memory
131 beaker.cache.super_short_term.type=memory
126 beaker.cache.super_short_term.expire=10
132 beaker.cache.super_short_term.expire=10
127 beaker.cache.super_short_term.key_length = 256
133 beaker.cache.super_short_term.key_length = 256
128
134
129 beaker.cache.short_term.type=memory
135 beaker.cache.short_term.type=memory
130 beaker.cache.short_term.expire=60
136 beaker.cache.short_term.expire=60
131 beaker.cache.short_term.key_length = 256
137 beaker.cache.short_term.key_length = 256
132
138
133 beaker.cache.long_term.type=memory
139 beaker.cache.long_term.type=memory
134 beaker.cache.long_term.expire=36000
140 beaker.cache.long_term.expire=36000
135 beaker.cache.long_term.key_length = 256
141 beaker.cache.long_term.key_length = 256
136
142
137 beaker.cache.sql_cache_short.type=memory
143 beaker.cache.sql_cache_short.type=memory
138 beaker.cache.sql_cache_short.expire=10
144 beaker.cache.sql_cache_short.expire=10
139 beaker.cache.sql_cache_short.key_length = 256
145 beaker.cache.sql_cache_short.key_length = 256
140
146
141 beaker.cache.sql_cache_med.type=memory
147 beaker.cache.sql_cache_med.type=memory
142 beaker.cache.sql_cache_med.expire=360
148 beaker.cache.sql_cache_med.expire=360
143 beaker.cache.sql_cache_med.key_length = 256
149 beaker.cache.sql_cache_med.key_length = 256
144
150
145 beaker.cache.sql_cache_long.type=file
151 beaker.cache.sql_cache_long.type=file
146 beaker.cache.sql_cache_long.expire=3600
152 beaker.cache.sql_cache_long.expire=3600
147 beaker.cache.sql_cache_long.key_length = 256
153 beaker.cache.sql_cache_long.key_length = 256
148
154
149 ####################################
155 ####################################
150 ### BEAKER SESSION ####
156 ### BEAKER SESSION ####
151 ####################################
157 ####################################
152 ## Type of storage used for the session, current types are
158 ## Type of storage used for the session, current types are
153 ## dbm, file, memcached, database, and memory.
159 ## dbm, file, memcached, database, and memory.
154 ## The storage uses the Container API
160 ## The storage uses the Container API
155 ## that is also used by the cache system.
161 ## that is also used by the cache system.
156
162
157 ## db session example
163 ## db session example
158
164
159 #beaker.session.type = ext:database
165 #beaker.session.type = ext:database
160 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
166 #beaker.session.sa.url = postgresql://postgres:qwe@localhost/rhodecode
161 #beaker.session.table_name = db_session
167 #beaker.session.table_name = db_session
162
168
163 ## encrypted cookie session, good for many instances
169 ## encrypted cookie session, good for many instances
164 #beaker.session.type = cookie
170 #beaker.session.type = cookie
165
171
166 beaker.session.type = file
172 beaker.session.type = file
167 beaker.session.key = rhodecode
173 beaker.session.key = rhodecode
168 # secure cookie requires AES python libraries
174 # secure cookie requires AES python libraries
169 #beaker.session.encrypt_key = ${app_instance_secret}
175 #beaker.session.encrypt_key = ${app_instance_secret}
170 #beaker.session.validate_key = ${app_instance_secret}
176 #beaker.session.validate_key = ${app_instance_secret}
171 beaker.session.timeout = 36000
177 beaker.session.timeout = 36000
172 beaker.session.httponly = true
178 beaker.session.httponly = true
173
179
174 ## uncomment for https secure cookie
180 ## uncomment for https secure cookie
175 beaker.session.secure = false
181 beaker.session.secure = false
176
182
177 ##auto save the session to not to use .save()
183 ##auto save the session to not to use .save()
178 beaker.session.auto = False
184 beaker.session.auto = False
179
185
180 ##true exire at browser close
186 ##true exire at browser close
181 #beaker.session.cookie_expires = 3600
187 #beaker.session.cookie_expires = 3600
182
188
183
189
184 ################################################################################
190 ################################################################################
185 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
191 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
186 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
192 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
187 ## execute malicious code after an exception is raised. ##
193 ## execute malicious code after an exception is raised. ##
188 ################################################################################
194 ################################################################################
189 set debug = false
195 set debug = false
190
196
191 ##################################
197 ##################################
192 ### LOGVIEW CONFIG ###
198 ### LOGVIEW CONFIG ###
193 ##################################
199 ##################################
194 logview.sqlalchemy = #faa
200 logview.sqlalchemy = #faa
195 logview.pylons.templating = #bfb
201 logview.pylons.templating = #bfb
196 logview.pylons.util = #eee
202 logview.pylons.util = #eee
197
203
198 #########################################################
204 #########################################################
199 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
205 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
200 #########################################################
206 #########################################################
201
207
202 # SQLITE [default]
208 # SQLITE [default]
203 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
209 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
204
210
205 # POSTGRESQL
211 # POSTGRESQL
206 # sqlalchemy.db1.url = postgresql://user:pass@localhost/rhodecode
212 # sqlalchemy.db1.url = postgresql://user:pass@localhost/rhodecode
207
213
208 # MySQL
214 # MySQL
209 # sqlalchemy.db1.url = mysql://user:pass@localhost/rhodecode
215 # sqlalchemy.db1.url = mysql://user:pass@localhost/rhodecode
210
216
211 # see sqlalchemy docs for others
217 # see sqlalchemy docs for others
212
218
213 sqlalchemy.db1.echo = false
219 sqlalchemy.db1.echo = false
214 sqlalchemy.db1.pool_recycle = 3600
220 sqlalchemy.db1.pool_recycle = 3600
215 sqlalchemy.convert_unicode = true
221 sqlalchemy.convert_unicode = true
216
222
217 ################################
223 ################################
218 ### LOGGING CONFIGURATION ####
224 ### LOGGING CONFIGURATION ####
219 ################################
225 ################################
220 [loggers]
226 [loggers]
221 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
227 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
222
228
223 [handlers]
229 [handlers]
224 keys = console, console_sql
230 keys = console, console_sql
225
231
226 [formatters]
232 [formatters]
227 keys = generic, color_formatter, color_formatter_sql
233 keys = generic, color_formatter, color_formatter_sql
228
234
229 #############
235 #############
230 ## LOGGERS ##
236 ## LOGGERS ##
231 #############
237 #############
232 [logger_root]
238 [logger_root]
233 level = NOTSET
239 level = NOTSET
234 handlers = console
240 handlers = console
235
241
236 [logger_routes]
242 [logger_routes]
237 level = DEBUG
243 level = DEBUG
238 handlers =
244 handlers =
239 qualname = routes.middleware
245 qualname = routes.middleware
240 # "level = DEBUG" logs the route matched and routing variables.
246 # "level = DEBUG" logs the route matched and routing variables.
241 propagate = 1
247 propagate = 1
242
248
243 [logger_beaker]
249 [logger_beaker]
244 level = DEBUG
250 level = DEBUG
245 handlers =
251 handlers =
246 qualname = beaker.container
252 qualname = beaker.container
247 propagate = 1
253 propagate = 1
248
254
249 [logger_templates]
255 [logger_templates]
250 level = INFO
256 level = INFO
251 handlers =
257 handlers =
252 qualname = pylons.templating
258 qualname = pylons.templating
253 propagate = 1
259 propagate = 1
254
260
255 [logger_rhodecode]
261 [logger_rhodecode]
256 level = DEBUG
262 level = DEBUG
257 handlers =
263 handlers =
258 qualname = rhodecode
264 qualname = rhodecode
259 propagate = 1
265 propagate = 1
260
266
261 [logger_sqlalchemy]
267 [logger_sqlalchemy]
262 level = INFO
268 level = INFO
263 handlers = console_sql
269 handlers = console_sql
264 qualname = sqlalchemy.engine
270 qualname = sqlalchemy.engine
265 propagate = 0
271 propagate = 0
266
272
267 ##############
273 ##############
268 ## HANDLERS ##
274 ## HANDLERS ##
269 ##############
275 ##############
270
276
271 [handler_console]
277 [handler_console]
272 class = StreamHandler
278 class = StreamHandler
273 args = (sys.stderr,)
279 args = (sys.stderr,)
274 level = INFO
280 level = INFO
275 formatter = color_formatter
281 formatter = generic
276
282
277 [handler_console_sql]
283 [handler_console_sql]
278 class = StreamHandler
284 class = StreamHandler
279 args = (sys.stderr,)
285 args = (sys.stderr,)
280 level = WARN
286 level = WARN
281 formatter = color_formatter_sql
287 formatter = generic
282
288
283 ################
289 ################
284 ## FORMATTERS ##
290 ## FORMATTERS ##
285 ################
291 ################
286
292
287 [formatter_generic]
293 [formatter_generic]
288 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
294 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
289 datefmt = %Y-%m-%d %H:%M:%S
295 datefmt = %Y-%m-%d %H:%M:%S
290
296
291 [formatter_color_formatter]
297 [formatter_color_formatter]
292 class=rhodecode.lib.colored_formatter.ColorFormatter
298 class=rhodecode.lib.colored_formatter.ColorFormatter
293 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
299 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
294 datefmt = %Y-%m-%d %H:%M:%S
300 datefmt = %Y-%m-%d %H:%M:%S
295
301
296 [formatter_color_formatter_sql]
302 [formatter_color_formatter_sql]
297 class=rhodecode.lib.colored_formatter.ColorFormatterSql
303 class=rhodecode.lib.colored_formatter.ColorFormatterSql
298 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
304 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
299 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
305 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,85 +1,87 b''
1 """Pylons environment configuration"""
1 """Pylons environment configuration"""
2
2
3 import os
3 import os
4 import logging
4 import logging
5
5
6 from mako.lookup import TemplateLookup
6 from mako.lookup import TemplateLookup
7 from pylons.configuration import PylonsConfig
7 from pylons.configuration import PylonsConfig
8 from pylons.error import handle_mako_error
8 from pylons.error import handle_mako_error
9
9
10 import rhodecode
10 import rhodecode
11 import rhodecode.lib.app_globals as app_globals
11 import rhodecode.lib.app_globals as app_globals
12 import rhodecode.lib.helpers
12 import rhodecode.lib.helpers
13
13
14 from rhodecode.config.routing import make_map
14 from rhodecode.config.routing import make_map
15 # don't remove this import it does magic for celery
15 # don't remove this import it does magic for celery
16 from rhodecode.lib import celerypylons, str2bool
16 from rhodecode.lib import celerypylons, str2bool
17 from rhodecode.lib import engine_from_config
17 from rhodecode.lib import engine_from_config
18 from rhodecode.lib.auth import set_available_permissions
18 from rhodecode.lib.auth import set_available_permissions
19 from rhodecode.lib.utils import repo2db_mapper, make_ui, set_rhodecode_config
19 from rhodecode.lib.utils import repo2db_mapper, make_ui, set_rhodecode_config
20 from rhodecode.model import init_model
20 from rhodecode.model import init_model
21 from rhodecode.model.scm import ScmModel
21 from rhodecode.model.scm import ScmModel
22
22
23 log = logging.getLogger(__name__)
23 log = logging.getLogger(__name__)
24
24
25
25
26 def load_environment(global_conf, app_conf, initial=False):
26 def load_environment(global_conf, app_conf, initial=False):
27 """Configure the Pylons environment via the ``pylons.config``
27 """Configure the Pylons environment via the ``pylons.config``
28 object
28 object
29 """
29 """
30 config = PylonsConfig()
30 config = PylonsConfig()
31
31
32 # Pylons paths
32 # Pylons paths
33 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
33 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
34 paths = dict(root=root,
34 paths = dict(root=root,
35 controllers=os.path.join(root, 'controllers'),
35 controllers=os.path.join(root, 'controllers'),
36 static_files=os.path.join(root, 'public'),
36 static_files=os.path.join(root, 'public'),
37 templates=[os.path.join(root, 'templates')])
37 templates=[os.path.join(root, 'templates')])
38
38
39 # Initialize config with the basic options
39 # Initialize config with the basic options
40 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
40 config.init_app(global_conf, app_conf, package='rhodecode', paths=paths)
41
41
42 # store some globals into rhodecode
42 # store some globals into rhodecode
43 rhodecode.CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
43 rhodecode.CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
44 rhodecode.CONFIG = config
45
44
46 config['routes.map'] = make_map(config)
45 config['routes.map'] = make_map(config)
47 config['pylons.app_globals'] = app_globals.Globals(config)
46 config['pylons.app_globals'] = app_globals.Globals(config)
48 config['pylons.h'] = rhodecode.lib.helpers
47 config['pylons.h'] = rhodecode.lib.helpers
49
48 rhodecode.CONFIG = config
50 # Setup cache object as early as possible
49 # Setup cache object as early as possible
51 import pylons
50 import pylons
52 pylons.cache._push_object(config['pylons.app_globals'].cache)
51 pylons.cache._push_object(config['pylons.app_globals'].cache)
53
52
54 # Create the Mako TemplateLookup, with the default auto-escaping
53 # Create the Mako TemplateLookup, with the default auto-escaping
55 config['pylons.app_globals'].mako_lookup = TemplateLookup(
54 config['pylons.app_globals'].mako_lookup = TemplateLookup(
56 directories=paths['templates'],
55 directories=paths['templates'],
57 error_handler=handle_mako_error,
56 error_handler=handle_mako_error,
58 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
57 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
59 input_encoding='utf-8', default_filters=['escape'],
58 input_encoding='utf-8', default_filters=['escape'],
60 imports=['from webhelpers.html import escape'])
59 imports=['from webhelpers.html import escape'])
61
60
62 #sets the c attribute access when don't existing attribute are accessed
61 # sets the c attribute access when don't existing attribute are accessed
63 config['pylons.strict_tmpl_context'] = True
62 config['pylons.strict_tmpl_context'] = True
64 test = os.path.split(config['__file__'])[-1] == 'test.ini'
63 test = os.path.split(config['__file__'])[-1] == 'test.ini'
65 if test:
64 if test:
66 from rhodecode.lib.utils import create_test_env, create_test_index
65 from rhodecode.lib.utils import create_test_env, create_test_index
67 from rhodecode.tests import TESTS_TMP_PATH
66 from rhodecode.tests import TESTS_TMP_PATH
68 create_test_env(TESTS_TMP_PATH, config)
67 create_test_env(TESTS_TMP_PATH, config)
69 create_test_index(TESTS_TMP_PATH, config, True)
68 create_test_index(TESTS_TMP_PATH, config, True)
70
69
71 #MULTIPLE DB configs
70 # MULTIPLE DB configs
72 # Setup the SQLAlchemy database engine
71 # Setup the SQLAlchemy database engine
73 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
72 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
74
73
75 init_model(sa_engine_db1)
74 init_model(sa_engine_db1)
76
75
77 repos_path = make_ui('db').configitems('paths')[0][1]
76 repos_path = make_ui('db').configitems('paths')[0][1]
78 repo2db_mapper(ScmModel().repo_scan(repos_path))
77 repo2db_mapper(ScmModel().repo_scan(repos_path))
79 set_available_permissions(config)
78 set_available_permissions(config)
80 config['base_path'] = repos_path
79 config['base_path'] = repos_path
81 set_rhodecode_config(config)
80 set_rhodecode_config(config)
82 # CONFIGURATION OPTIONS HERE (note: all config options will override
81 # CONFIGURATION OPTIONS HERE (note: all config options will override
83 # any Pylons config options)
82 # any Pylons config options)
84
83
84 # store config reference into our module to skip import magic of
85 # pylons
86 rhodecode.CONFIG.update(config)
85 return config
87 return config
@@ -1,454 +1,464 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.__init__
3 rhodecode.lib.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Some simple helper functions
6 Some simple helper functions
7
7
8 :created_on: Jan 5, 2011
8 :created_on: Jan 5, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 from rhodecode.lib.vcs.utils.lazy import LazyProperty
28 from rhodecode.lib.vcs.utils.lazy import LazyProperty
29
29
30
30
31 def __get_lem():
31 def __get_lem():
32 from pygments import lexers
32 from pygments import lexers
33 from string import lower
33 from string import lower
34 from collections import defaultdict
34 from collections import defaultdict
35
35
36 d = defaultdict(lambda: [])
36 d = defaultdict(lambda: [])
37
37
38 def __clean(s):
38 def __clean(s):
39 s = s.lstrip('*')
39 s = s.lstrip('*')
40 s = s.lstrip('.')
40 s = s.lstrip('.')
41
41
42 if s.find('[') != -1:
42 if s.find('[') != -1:
43 exts = []
43 exts = []
44 start, stop = s.find('['), s.find(']')
44 start, stop = s.find('['), s.find(']')
45
45
46 for suffix in s[start + 1:stop]:
46 for suffix in s[start + 1:stop]:
47 exts.append(s[:s.find('[')] + suffix)
47 exts.append(s[:s.find('[')] + suffix)
48 return map(lower, exts)
48 return map(lower, exts)
49 else:
49 else:
50 return map(lower, [s])
50 return map(lower, [s])
51
51
52 for lx, t in sorted(lexers.LEXERS.items()):
52 for lx, t in sorted(lexers.LEXERS.items()):
53 m = map(__clean, t[-2])
53 m = map(__clean, t[-2])
54 if m:
54 if m:
55 m = reduce(lambda x, y: x + y, m)
55 m = reduce(lambda x, y: x + y, m)
56 for ext in m:
56 for ext in m:
57 desc = lx.replace('Lexer', '')
57 desc = lx.replace('Lexer', '')
58 d[ext].append(desc)
58 d[ext].append(desc)
59
59
60 return dict(d)
60 return dict(d)
61
61
62 # language map is also used by whoosh indexer, which for those specified
62 # language map is also used by whoosh indexer, which for those specified
63 # extensions will index it's content
63 # extensions will index it's content
64 LANGUAGES_EXTENSIONS_MAP = __get_lem()
64 LANGUAGES_EXTENSIONS_MAP = __get_lem()
65
65
66 # Additional mappings that are not present in the pygments lexers
66 # Additional mappings that are not present in the pygments lexers
67 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
67 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
68 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
68 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
69
69
70 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
70 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
71
71
72 # list of readme files to search in file tree and display in summary
72 # list of readme files to search in file tree and display in summary
73 # attached weights defines the search order lower is first
73 # attached weights defines the search order lower is first
74 ALL_READMES = [
74 ALL_READMES = [
75 ('readme', 0), ('README', 0), ('Readme', 0),
75 ('readme', 0), ('README', 0), ('Readme', 0),
76 ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
76 ('doc/readme', 1), ('doc/README', 1), ('doc/Readme', 1),
77 ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
77 ('Docs/readme', 2), ('Docs/README', 2), ('Docs/Readme', 2),
78 ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
78 ('DOCS/readme', 2), ('DOCS/README', 2), ('DOCS/Readme', 2),
79 ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
79 ('docs/readme', 2), ('docs/README', 2), ('docs/Readme', 2),
80 ]
80 ]
81
81
82 # extension together with weights to search lower is first
82 # extension together with weights to search lower is first
83 RST_EXTS = [
83 RST_EXTS = [
84 ('', 0), ('.rst', 1), ('.rest', 1),
84 ('', 0), ('.rst', 1), ('.rest', 1),
85 ('.RST', 2), ('.REST', 2),
85 ('.RST', 2), ('.REST', 2),
86 ('.txt', 3), ('.TXT', 3)
86 ('.txt', 3), ('.TXT', 3)
87 ]
87 ]
88
88
89 MARKDOWN_EXTS = [
89 MARKDOWN_EXTS = [
90 ('.md', 1), ('.MD', 1),
90 ('.md', 1), ('.MD', 1),
91 ('.mkdn', 2), ('.MKDN', 2),
91 ('.mkdn', 2), ('.MKDN', 2),
92 ('.mdown', 3), ('.MDOWN', 3),
92 ('.mdown', 3), ('.MDOWN', 3),
93 ('.markdown', 4), ('.MARKDOWN', 4)
93 ('.markdown', 4), ('.MARKDOWN', 4)
94 ]
94 ]
95
95
96 PLAIN_EXTS = [('.text', 2), ('.TEXT', 2)]
96 PLAIN_EXTS = [('.text', 2), ('.TEXT', 2)]
97
97
98 ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
98 ALL_EXTS = MARKDOWN_EXTS + RST_EXTS + PLAIN_EXTS
99
99
100
100
101 def str2bool(_str):
101 def str2bool(_str):
102 """
102 """
103 returs True/False value from given string, it tries to translate the
103 returs True/False value from given string, it tries to translate the
104 string into boolean
104 string into boolean
105
105
106 :param _str: string value to translate into boolean
106 :param _str: string value to translate into boolean
107 :rtype: boolean
107 :rtype: boolean
108 :returns: boolean from given string
108 :returns: boolean from given string
109 """
109 """
110 if _str is None:
110 if _str is None:
111 return False
111 return False
112 if _str in (True, False):
112 if _str in (True, False):
113 return _str
113 return _str
114 _str = str(_str).strip().lower()
114 _str = str(_str).strip().lower()
115 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
115 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
116
116
117
117
118 def convert_line_endings(line, mode):
118 def convert_line_endings(line, mode):
119 """
119 """
120 Converts a given line "line end" accordingly to given mode
120 Converts a given line "line end" accordingly to given mode
121
121
122 Available modes are::
122 Available modes are::
123 0 - Unix
123 0 - Unix
124 1 - Mac
124 1 - Mac
125 2 - DOS
125 2 - DOS
126
126
127 :param line: given line to convert
127 :param line: given line to convert
128 :param mode: mode to convert to
128 :param mode: mode to convert to
129 :rtype: str
129 :rtype: str
130 :return: converted line according to mode
130 :return: converted line according to mode
131 """
131 """
132 from string import replace
132 from string import replace
133
133
134 if mode == 0:
134 if mode == 0:
135 line = replace(line, '\r\n', '\n')
135 line = replace(line, '\r\n', '\n')
136 line = replace(line, '\r', '\n')
136 line = replace(line, '\r', '\n')
137 elif mode == 1:
137 elif mode == 1:
138 line = replace(line, '\r\n', '\r')
138 line = replace(line, '\r\n', '\r')
139 line = replace(line, '\n', '\r')
139 line = replace(line, '\n', '\r')
140 elif mode == 2:
140 elif mode == 2:
141 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
141 line = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", line)
142 return line
142 return line
143
143
144
144
145 def detect_mode(line, default):
145 def detect_mode(line, default):
146 """
146 """
147 Detects line break for given line, if line break couldn't be found
147 Detects line break for given line, if line break couldn't be found
148 given default value is returned
148 given default value is returned
149
149
150 :param line: str line
150 :param line: str line
151 :param default: default
151 :param default: default
152 :rtype: int
152 :rtype: int
153 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
153 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
154 """
154 """
155 if line.endswith('\r\n'):
155 if line.endswith('\r\n'):
156 return 2
156 return 2
157 elif line.endswith('\n'):
157 elif line.endswith('\n'):
158 return 0
158 return 0
159 elif line.endswith('\r'):
159 elif line.endswith('\r'):
160 return 1
160 return 1
161 else:
161 else:
162 return default
162 return default
163
163
164
164
165 def generate_api_key(username, salt=None):
165 def generate_api_key(username, salt=None):
166 """
166 """
167 Generates unique API key for given username, if salt is not given
167 Generates unique API key for given username, if salt is not given
168 it'll be generated from some random string
168 it'll be generated from some random string
169
169
170 :param username: username as string
170 :param username: username as string
171 :param salt: salt to hash generate KEY
171 :param salt: salt to hash generate KEY
172 :rtype: str
172 :rtype: str
173 :returns: sha1 hash from username+salt
173 :returns: sha1 hash from username+salt
174 """
174 """
175 from tempfile import _RandomNameSequence
175 from tempfile import _RandomNameSequence
176 import hashlib
176 import hashlib
177
177
178 if salt is None:
178 if salt is None:
179 salt = _RandomNameSequence().next()
179 salt = _RandomNameSequence().next()
180
180
181 return hashlib.sha1(username + salt).hexdigest()
181 return hashlib.sha1(username + salt).hexdigest()
182
182
183
183
184 def safe_unicode(str_, from_encoding='utf8'):
184 def safe_unicode(str_, from_encoding=None):
185 """
185 """
186 safe unicode function. Does few trick to turn str_ into unicode
186 safe unicode function. Does few trick to turn str_ into unicode
187
187
188 In case of UnicodeDecode error we try to return it with encoding detected
188 In case of UnicodeDecode error we try to return it with encoding detected
189 by chardet library if it fails fallback to unicode with errors replaced
189 by chardet library if it fails fallback to unicode with errors replaced
190
190
191 :param str_: string to decode
191 :param str_: string to decode
192 :rtype: unicode
192 :rtype: unicode
193 :returns: unicode object
193 :returns: unicode object
194 """
194 """
195 if isinstance(str_, unicode):
195 if isinstance(str_, unicode):
196 return str_
196 return str_
197
197
198 if not from_encoding:
199 import rhodecode
200 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
201 from_encoding = DEFAULT_ENCODING
202
198 try:
203 try:
199 return unicode(str_)
204 return unicode(str_)
200 except UnicodeDecodeError:
205 except UnicodeDecodeError:
201 pass
206 pass
202
207
203 try:
208 try:
204 return unicode(str_, from_encoding)
209 return unicode(str_, from_encoding)
205 except UnicodeDecodeError:
210 except UnicodeDecodeError:
206 pass
211 pass
207
212
208 try:
213 try:
209 import chardet
214 import chardet
210 encoding = chardet.detect(str_)['encoding']
215 encoding = chardet.detect(str_)['encoding']
211 if encoding is None:
216 if encoding is None:
212 raise Exception()
217 raise Exception()
213 return str_.decode(encoding)
218 return str_.decode(encoding)
214 except (ImportError, UnicodeDecodeError, Exception):
219 except (ImportError, UnicodeDecodeError, Exception):
215 return unicode(str_, from_encoding, 'replace')
220 return unicode(str_, from_encoding, 'replace')
216
221
217
222
218 def safe_str(unicode_, to_encoding='utf8'):
223 def safe_str(unicode_, to_encoding=None):
219 """
224 """
220 safe str function. Does few trick to turn unicode_ into string
225 safe str function. Does few trick to turn unicode_ into string
221
226
222 In case of UnicodeEncodeError we try to return it with encoding detected
227 In case of UnicodeEncodeError we try to return it with encoding detected
223 by chardet library if it fails fallback to string with errors replaced
228 by chardet library if it fails fallback to string with errors replaced
224
229
225 :param unicode_: unicode to encode
230 :param unicode_: unicode to encode
226 :rtype: str
231 :rtype: str
227 :returns: str object
232 :returns: str object
228 """
233 """
229
234
230 if not isinstance(unicode_, basestring):
235 if not isinstance(unicode_, basestring):
231 return str(unicode_)
236 return str(unicode_)
232
237
233 if isinstance(unicode_, str):
238 if isinstance(unicode_, str):
234 return unicode_
239 return unicode_
235
240
241 if not to_encoding:
242 import rhodecode
243 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
244 to_encoding = DEFAULT_ENCODING
245
236 try:
246 try:
237 return unicode_.encode(to_encoding)
247 return unicode_.encode(to_encoding)
238 except UnicodeEncodeError:
248 except UnicodeEncodeError:
239 pass
249 pass
240
250
241 try:
251 try:
242 import chardet
252 import chardet
243 encoding = chardet.detect(unicode_)['encoding']
253 encoding = chardet.detect(unicode_)['encoding']
244 print encoding
254 print encoding
245 if encoding is None:
255 if encoding is None:
246 raise UnicodeEncodeError()
256 raise UnicodeEncodeError()
247
257
248 return unicode_.encode(encoding)
258 return unicode_.encode(encoding)
249 except (ImportError, UnicodeEncodeError):
259 except (ImportError, UnicodeEncodeError):
250 return unicode_.encode(to_encoding, 'replace')
260 return unicode_.encode(to_encoding, 'replace')
251
261
252 return safe_str
262 return safe_str
253
263
254
264
255 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
265 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
256 """
266 """
257 Custom engine_from_config functions that makes sure we use NullPool for
267 Custom engine_from_config functions that makes sure we use NullPool for
258 file based sqlite databases. This prevents errors on sqlite. This only
268 file based sqlite databases. This prevents errors on sqlite. This only
259 applies to sqlalchemy versions < 0.7.0
269 applies to sqlalchemy versions < 0.7.0
260
270
261 """
271 """
262 import sqlalchemy
272 import sqlalchemy
263 from sqlalchemy import engine_from_config as efc
273 from sqlalchemy import engine_from_config as efc
264 import logging
274 import logging
265
275
266 if int(sqlalchemy.__version__.split('.')[1]) < 7:
276 if int(sqlalchemy.__version__.split('.')[1]) < 7:
267
277
268 # This solution should work for sqlalchemy < 0.7.0, and should use
278 # This solution should work for sqlalchemy < 0.7.0, and should use
269 # proxy=TimerProxy() for execution time profiling
279 # proxy=TimerProxy() for execution time profiling
270
280
271 from sqlalchemy.pool import NullPool
281 from sqlalchemy.pool import NullPool
272 url = configuration[prefix + 'url']
282 url = configuration[prefix + 'url']
273
283
274 if url.startswith('sqlite'):
284 if url.startswith('sqlite'):
275 kwargs.update({'poolclass': NullPool})
285 kwargs.update({'poolclass': NullPool})
276 return efc(configuration, prefix, **kwargs)
286 return efc(configuration, prefix, **kwargs)
277 else:
287 else:
278 import time
288 import time
279 from sqlalchemy import event
289 from sqlalchemy import event
280 from sqlalchemy.engine import Engine
290 from sqlalchemy.engine import Engine
281
291
282 log = logging.getLogger('sqlalchemy.engine')
292 log = logging.getLogger('sqlalchemy.engine')
283 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
293 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
284 engine = efc(configuration, prefix, **kwargs)
294 engine = efc(configuration, prefix, **kwargs)
285
295
286 def color_sql(sql):
296 def color_sql(sql):
287 COLOR_SEQ = "\033[1;%dm"
297 COLOR_SEQ = "\033[1;%dm"
288 COLOR_SQL = YELLOW
298 COLOR_SQL = YELLOW
289 normal = '\x1b[0m'
299 normal = '\x1b[0m'
290 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
300 return ''.join([COLOR_SEQ % COLOR_SQL, sql, normal])
291
301
292 if configuration['debug']:
302 if configuration['debug']:
293 #attach events only for debug configuration
303 #attach events only for debug configuration
294
304
295 def before_cursor_execute(conn, cursor, statement,
305 def before_cursor_execute(conn, cursor, statement,
296 parameters, context, executemany):
306 parameters, context, executemany):
297 context._query_start_time = time.time()
307 context._query_start_time = time.time()
298 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
308 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
299
309
300
310
301 def after_cursor_execute(conn, cursor, statement,
311 def after_cursor_execute(conn, cursor, statement,
302 parameters, context, executemany):
312 parameters, context, executemany):
303 total = time.time() - context._query_start_time
313 total = time.time() - context._query_start_time
304 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
314 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
305
315
306 event.listen(engine, "before_cursor_execute",
316 event.listen(engine, "before_cursor_execute",
307 before_cursor_execute)
317 before_cursor_execute)
308 event.listen(engine, "after_cursor_execute",
318 event.listen(engine, "after_cursor_execute",
309 after_cursor_execute)
319 after_cursor_execute)
310
320
311 return engine
321 return engine
312
322
313
323
314 def age(curdate):
324 def age(curdate):
315 """
325 """
316 turns a datetime into an age string.
326 turns a datetime into an age string.
317
327
318 :param curdate: datetime object
328 :param curdate: datetime object
319 :rtype: unicode
329 :rtype: unicode
320 :returns: unicode words describing age
330 :returns: unicode words describing age
321 """
331 """
322
332
323 from datetime import datetime
333 from datetime import datetime
324 from webhelpers.date import time_ago_in_words
334 from webhelpers.date import time_ago_in_words
325
335
326 _ = lambda s: s
336 _ = lambda s: s
327
337
328 if not curdate:
338 if not curdate:
329 return ''
339 return ''
330
340
331 agescales = [(_(u"year"), 3600 * 24 * 365),
341 agescales = [(_(u"year"), 3600 * 24 * 365),
332 (_(u"month"), 3600 * 24 * 30),
342 (_(u"month"), 3600 * 24 * 30),
333 (_(u"day"), 3600 * 24),
343 (_(u"day"), 3600 * 24),
334 (_(u"hour"), 3600),
344 (_(u"hour"), 3600),
335 (_(u"minute"), 60),
345 (_(u"minute"), 60),
336 (_(u"second"), 1), ]
346 (_(u"second"), 1), ]
337
347
338 age = datetime.now() - curdate
348 age = datetime.now() - curdate
339 age_seconds = (age.days * agescales[2][1]) + age.seconds
349 age_seconds = (age.days * agescales[2][1]) + age.seconds
340 pos = 1
350 pos = 1
341 for scale in agescales:
351 for scale in agescales:
342 if scale[1] <= age_seconds:
352 if scale[1] <= age_seconds:
343 if pos == 6:
353 if pos == 6:
344 pos = 5
354 pos = 5
345 return '%s %s' % (time_ago_in_words(curdate,
355 return '%s %s' % (time_ago_in_words(curdate,
346 agescales[pos][0]), _('ago'))
356 agescales[pos][0]), _('ago'))
347 pos += 1
357 pos += 1
348
358
349 return _(u'just now')
359 return _(u'just now')
350
360
351
361
352 def uri_filter(uri):
362 def uri_filter(uri):
353 """
363 """
354 Removes user:password from given url string
364 Removes user:password from given url string
355
365
356 :param uri:
366 :param uri:
357 :rtype: unicode
367 :rtype: unicode
358 :returns: filtered list of strings
368 :returns: filtered list of strings
359 """
369 """
360 if not uri:
370 if not uri:
361 return ''
371 return ''
362
372
363 proto = ''
373 proto = ''
364
374
365 for pat in ('https://', 'http://'):
375 for pat in ('https://', 'http://'):
366 if uri.startswith(pat):
376 if uri.startswith(pat):
367 uri = uri[len(pat):]
377 uri = uri[len(pat):]
368 proto = pat
378 proto = pat
369 break
379 break
370
380
371 # remove passwords and username
381 # remove passwords and username
372 uri = uri[uri.find('@') + 1:]
382 uri = uri[uri.find('@') + 1:]
373
383
374 # get the port
384 # get the port
375 cred_pos = uri.find(':')
385 cred_pos = uri.find(':')
376 if cred_pos == -1:
386 if cred_pos == -1:
377 host, port = uri, None
387 host, port = uri, None
378 else:
388 else:
379 host, port = uri[:cred_pos], uri[cred_pos + 1:]
389 host, port = uri[:cred_pos], uri[cred_pos + 1:]
380
390
381 return filter(None, [proto, host, port])
391 return filter(None, [proto, host, port])
382
392
383
393
384 def credentials_filter(uri):
394 def credentials_filter(uri):
385 """
395 """
386 Returns a url with removed credentials
396 Returns a url with removed credentials
387
397
388 :param uri:
398 :param uri:
389 """
399 """
390
400
391 uri = uri_filter(uri)
401 uri = uri_filter(uri)
392 #check if we have port
402 #check if we have port
393 if len(uri) > 2 and uri[2]:
403 if len(uri) > 2 and uri[2]:
394 uri[2] = ':' + uri[2]
404 uri[2] = ':' + uri[2]
395
405
396 return ''.join(uri)
406 return ''.join(uri)
397
407
398
408
399 def get_changeset_safe(repo, rev):
409 def get_changeset_safe(repo, rev):
400 """
410 """
401 Safe version of get_changeset if this changeset doesn't exists for a
411 Safe version of get_changeset if this changeset doesn't exists for a
402 repo it returns a Dummy one instead
412 repo it returns a Dummy one instead
403
413
404 :param repo:
414 :param repo:
405 :param rev:
415 :param rev:
406 """
416 """
407 from rhodecode.lib.vcs.backends.base import BaseRepository
417 from rhodecode.lib.vcs.backends.base import BaseRepository
408 from rhodecode.lib.vcs.exceptions import RepositoryError
418 from rhodecode.lib.vcs.exceptions import RepositoryError
409 if not isinstance(repo, BaseRepository):
419 if not isinstance(repo, BaseRepository):
410 raise Exception('You must pass an Repository '
420 raise Exception('You must pass an Repository '
411 'object as first argument got %s', type(repo))
421 'object as first argument got %s', type(repo))
412
422
413 try:
423 try:
414 cs = repo.get_changeset(rev)
424 cs = repo.get_changeset(rev)
415 except RepositoryError:
425 except RepositoryError:
416 from rhodecode.lib.utils import EmptyChangeset
426 from rhodecode.lib.utils import EmptyChangeset
417 cs = EmptyChangeset(requested_revision=rev)
427 cs = EmptyChangeset(requested_revision=rev)
418 return cs
428 return cs
419
429
420
430
421 def get_current_revision(quiet=False):
431 def get_current_revision(quiet=False):
422 """
432 """
423 Returns tuple of (number, id) from repository containing this package
433 Returns tuple of (number, id) from repository containing this package
424 or None if repository could not be found.
434 or None if repository could not be found.
425
435
426 :param quiet: prints error for fetching revision if True
436 :param quiet: prints error for fetching revision if True
427 """
437 """
428
438
429 try:
439 try:
430 from rhodecode.lib.vcs import get_repo
440 from rhodecode.lib.vcs import get_repo
431 from rhodecode.lib.vcs.utils.helpers import get_scm
441 from rhodecode.lib.vcs.utils.helpers import get_scm
432 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
442 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
433 scm = get_scm(repopath)[0]
443 scm = get_scm(repopath)[0]
434 repo = get_repo(path=repopath, alias=scm)
444 repo = get_repo(path=repopath, alias=scm)
435 tip = repo.get_changeset()
445 tip = repo.get_changeset()
436 return (tip.revision, tip.short_id)
446 return (tip.revision, tip.short_id)
437 except Exception, err:
447 except Exception, err:
438 if not quiet:
448 if not quiet:
439 print ("Cannot retrieve rhodecode's revision. Original error "
449 print ("Cannot retrieve rhodecode's revision. Original error "
440 "was: %s" % err)
450 "was: %s" % err)
441 return None
451 return None
442
452
443
453
444 def extract_mentioned_users(s):
454 def extract_mentioned_users(s):
445 """
455 """
446 Returns unique usernames from given string s that have @mention
456 Returns unique usernames from given string s that have @mention
447
457
448 :param s: string to get mentions
458 :param s: string to get mentions
449 """
459 """
450 usrs = {}
460 usrs = {}
451 for username in re.findall(r'(?:^@|\s@)(\w+)', s):
461 for username in re.findall(r'(?:^@|\s@)(\w+)', s):
452 usrs[username] = username
462 usrs[username] = username
453
463
454 return sorted(usrs.keys())
464 return sorted(usrs.keys())
@@ -1,180 +1,181 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6 import time
6 import time
7 import traceback
7 import traceback
8
8
9 from paste.auth.basic import AuthBasicAuthenticator
9 from paste.auth.basic import AuthBasicAuthenticator
10
10
11 from pylons import config, tmpl_context as c, request, session, url
11 from pylons import config, tmpl_context as c, request, session, url
12 from pylons.controllers import WSGIController
12 from pylons.controllers import WSGIController
13 from pylons.controllers.util import redirect
13 from pylons.controllers.util import redirect
14 from pylons.templating import render_mako as render
14 from pylons.templating import render_mako as render
15
15
16 from rhodecode import __version__, BACKENDS
16 from rhodecode import __version__, BACKENDS
17
17
18 from rhodecode.lib import str2bool
18 from rhodecode.lib import str2bool
19 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
19 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
20 HasPermissionAnyMiddleware
20 HasPermissionAnyMiddleware
21 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
21 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
22 from rhodecode.model import meta
22 from rhodecode.model import meta
23
23
24 from rhodecode.model.db import Repository
24 from rhodecode.model.db import Repository
25 from rhodecode.model.notification import NotificationModel
25 from rhodecode.model.notification import NotificationModel
26 from rhodecode.model.scm import ScmModel
26 from rhodecode.model.scm import ScmModel
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 class BaseVCSController(object):
31 class BaseVCSController(object):
32
32
33 def __init__(self, application, config):
33 def __init__(self, application, config):
34 self.application = application
34 self.application = application
35 self.config = config
35 self.config = config
36 # base path of repo locations
36 # base path of repo locations
37 self.basepath = self.config['base_path']
37 self.basepath = self.config['base_path']
38 #authenticate this mercurial request using authfunc
38 #authenticate this mercurial request using authfunc
39 self.authenticate = AuthBasicAuthenticator('', authfunc)
39 self.authenticate = AuthBasicAuthenticator('', authfunc)
40 self.ipaddr = '0.0.0.0'
40 self.ipaddr = '0.0.0.0'
41
41
42 def _handle_request(self, environ, start_response):
42 def _handle_request(self, environ, start_response):
43 raise NotImplementedError()
43 raise NotImplementedError()
44
44
45 def _get_by_id(self, repo_name):
45 def _get_by_id(self, repo_name):
46 """
46 """
47 Get's a special pattern _<ID> from clone url and tries to replace it
47 Get's a special pattern _<ID> from clone url and tries to replace it
48 with a repository_name for support of _<ID> non changable urls
48 with a repository_name for support of _<ID> non changable urls
49
49
50 :param repo_name:
50 :param repo_name:
51 """
51 """
52 try:
52 try:
53 data = repo_name.split('/')
53 data = repo_name.split('/')
54 if len(data) >= 2:
54 if len(data) >= 2:
55 by_id = data[1].split('_')
55 by_id = data[1].split('_')
56 if len(by_id) == 2 and by_id[1].isdigit():
56 if len(by_id) == 2 and by_id[1].isdigit():
57 _repo_name = Repository.get(by_id[1]).repo_name
57 _repo_name = Repository.get(by_id[1]).repo_name
58 data[1] = _repo_name
58 data[1] = _repo_name
59 except:
59 except:
60 log.debug('Failed to extract repo_name from id %s' % (
60 log.debug('Failed to extract repo_name from id %s' % (
61 traceback.format_exc()
61 traceback.format_exc()
62 )
62 )
63 )
63 )
64
64
65 return '/'.join(data)
65 return '/'.join(data)
66
66
67 def _invalidate_cache(self, repo_name):
67 def _invalidate_cache(self, repo_name):
68 """
68 """
69 Set's cache for this repository for invalidation on next access
69 Set's cache for this repository for invalidation on next access
70
70
71 :param repo_name: full repo name, also a cache key
71 :param repo_name: full repo name, also a cache key
72 """
72 """
73 invalidate_cache('get_repo_cached_%s' % repo_name)
73 invalidate_cache('get_repo_cached_%s' % repo_name)
74
74
75 def _check_permission(self, action, user, repo_name):
75 def _check_permission(self, action, user, repo_name):
76 """
76 """
77 Checks permissions using action (push/pull) user and repository
77 Checks permissions using action (push/pull) user and repository
78 name
78 name
79
79
80 :param action: push or pull action
80 :param action: push or pull action
81 :param user: user instance
81 :param user: user instance
82 :param repo_name: repository name
82 :param repo_name: repository name
83 """
83 """
84 if action == 'push':
84 if action == 'push':
85 if not HasPermissionAnyMiddleware('repository.write',
85 if not HasPermissionAnyMiddleware('repository.write',
86 'repository.admin')(user,
86 'repository.admin')(user,
87 repo_name):
87 repo_name):
88 return False
88 return False
89
89
90 else:
90 else:
91 #any other action need at least read permission
91 #any other action need at least read permission
92 if not HasPermissionAnyMiddleware('repository.read',
92 if not HasPermissionAnyMiddleware('repository.read',
93 'repository.write',
93 'repository.write',
94 'repository.admin')(user,
94 'repository.admin')(user,
95 repo_name):
95 repo_name):
96 return False
96 return False
97
97
98 return True
98 return True
99
99
100 def __call__(self, environ, start_response):
100 def __call__(self, environ, start_response):
101 start = time.time()
101 start = time.time()
102 try:
102 try:
103 return self._handle_request(environ, start_response)
103 return self._handle_request(environ, start_response)
104 finally:
104 finally:
105 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
105 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
106 log.debug('Request time: %.3fs' % (time.time() - start))
106 log.debug('Request time: %.3fs' % (time.time() - start))
107 meta.Session.remove()
107 meta.Session.remove()
108
108
109
109
110 class BaseController(WSGIController):
110 class BaseController(WSGIController):
111
111
112 def __before__(self):
112 def __before__(self):
113 c.rhodecode_version = __version__
113 c.rhodecode_version = __version__
114 c.rhodecode_instanceid = config.get('instance_id')
114 c.rhodecode_name = config.get('rhodecode_title')
115 c.rhodecode_name = config.get('rhodecode_title')
115 c.use_gravatar = str2bool(config.get('use_gravatar'))
116 c.use_gravatar = str2bool(config.get('use_gravatar'))
116 c.ga_code = config.get('rhodecode_ga_code')
117 c.ga_code = config.get('rhodecode_ga_code')
117 c.repo_name = get_repo_slug(request)
118 c.repo_name = get_repo_slug(request)
118 c.backends = BACKENDS.keys()
119 c.backends = BACKENDS.keys()
119 c.unread_notifications = NotificationModel()\
120 c.unread_notifications = NotificationModel()\
120 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
121 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
121 self.cut_off_limit = int(config.get('cut_off_limit'))
122 self.cut_off_limit = int(config.get('cut_off_limit'))
122
123
123 self.sa = meta.Session
124 self.sa = meta.Session
124 self.scm_model = ScmModel(self.sa)
125 self.scm_model = ScmModel(self.sa)
125
126
126 def __call__(self, environ, start_response):
127 def __call__(self, environ, start_response):
127 """Invoke the Controller"""
128 """Invoke the Controller"""
128 # WSGIController.__call__ dispatches to the Controller method
129 # WSGIController.__call__ dispatches to the Controller method
129 # the request is routed to. This routing information is
130 # the request is routed to. This routing information is
130 # available in environ['pylons.routes_dict']
131 # available in environ['pylons.routes_dict']
131 start = time.time()
132 start = time.time()
132 try:
133 try:
133 # make sure that we update permissions each time we call controller
134 # make sure that we update permissions each time we call controller
134 api_key = request.GET.get('api_key')
135 api_key = request.GET.get('api_key')
135 cookie_store = session.get('rhodecode_user') or {}
136 cookie_store = session.get('rhodecode_user') or {}
136 user_id = cookie_store.get('user_id', None)
137 user_id = cookie_store.get('user_id', None)
137 username = get_container_username(environ, config)
138 username = get_container_username(environ, config)
138
139
139 auth_user = AuthUser(user_id, api_key, username)
140 auth_user = AuthUser(user_id, api_key, username)
140 request.user = auth_user
141 request.user = auth_user
141 self.rhodecode_user = c.rhodecode_user = auth_user
142 self.rhodecode_user = c.rhodecode_user = auth_user
142 if not self.rhodecode_user.is_authenticated and \
143 if not self.rhodecode_user.is_authenticated and \
143 self.rhodecode_user.user_id is not None:
144 self.rhodecode_user.user_id is not None:
144 self.rhodecode_user\
145 self.rhodecode_user\
145 .set_authenticated(cookie_store.get('is_authenticated'))
146 .set_authenticated(cookie_store.get('is_authenticated'))
146
147
147 session['rhodecode_user'] = self.rhodecode_user.get_cookie_store()
148 session['rhodecode_user'] = self.rhodecode_user.get_cookie_store()
148 session.save()
149 session.save()
149 return WSGIController.__call__(self, environ, start_response)
150 return WSGIController.__call__(self, environ, start_response)
150 finally:
151 finally:
151 log.debug('Request time: %.3fs' % (time.time() - start))
152 log.debug('Request time: %.3fs' % (time.time() - start))
152 meta.Session.remove()
153 meta.Session.remove()
153
154
154
155
155 class BaseRepoController(BaseController):
156 class BaseRepoController(BaseController):
156 """
157 """
157 Base class for controllers responsible for loading all needed data for
158 Base class for controllers responsible for loading all needed data for
158 repository loaded items are
159 repository loaded items are
159
160
160 c.rhodecode_repo: instance of scm repository
161 c.rhodecode_repo: instance of scm repository
161 c.rhodecode_db_repo: instance of db
162 c.rhodecode_db_repo: instance of db
162 c.repository_followers: number of followers
163 c.repository_followers: number of followers
163 c.repository_forks: number of forks
164 c.repository_forks: number of forks
164 """
165 """
165
166
166 def __before__(self):
167 def __before__(self):
167 super(BaseRepoController, self).__before__()
168 super(BaseRepoController, self).__before__()
168 if c.repo_name:
169 if c.repo_name:
169
170
170 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
171 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
171 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
172 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
172
173
173 if c.rhodecode_repo is None:
174 if c.rhodecode_repo is None:
174 log.error('%s this repository is present in database but it '
175 log.error('%s this repository is present in database but it '
175 'cannot be created as an scm instance', c.repo_name)
176 'cannot be created as an scm instance', c.repo_name)
176
177
177 redirect(url('home'))
178 redirect(url('home'))
178
179
179 c.repository_followers = self.scm_model.get_followers(c.repo_name)
180 c.repository_followers = self.scm_model.get_followers(c.repo_name)
180 c.repository_forks = self.scm_model.get_forks(c.repo_name)
181 c.repository_forks = self.scm_model.get_forks(c.repo_name)
@@ -1,133 +1,139 b''
1 """
1 """
2 This module provides some useful tools for ``vcs`` like annotate/diff html
2 This module provides some useful tools for ``vcs`` like annotate/diff html
3 output. It also includes some internal helpers.
3 output. It also includes some internal helpers.
4 """
4 """
5 import sys
5 import sys
6 import time
6 import time
7 import datetime
7 import datetime
8
8
9
9
10 def makedate():
10 def makedate():
11 lt = time.localtime()
11 lt = time.localtime()
12 if lt[8] == 1 and time.daylight:
12 if lt[8] == 1 and time.daylight:
13 tz = time.altzone
13 tz = time.altzone
14 else:
14 else:
15 tz = time.timezone
15 tz = time.timezone
16 return time.mktime(lt), tz
16 return time.mktime(lt), tz
17
17
18
18
19 def date_fromtimestamp(unixts, tzoffset=0):
19 def date_fromtimestamp(unixts, tzoffset=0):
20 """
20 """
21 Makes a local datetime object out of unix timestamp
21 Makes a local datetime object out of unix timestamp
22
22
23 :param unixts:
23 :param unixts:
24 :param tzoffset:
24 :param tzoffset:
25 """
25 """
26
26
27 return datetime.datetime.fromtimestamp(float(unixts))
27 return datetime.datetime.fromtimestamp(float(unixts))
28
28
29
29
30 def safe_unicode(str_, from_encoding='utf8'):
30 def safe_unicode(str_, from_encoding=None):
31 """
31 """
32 safe unicode function. Does few trick to turn str_ into unicode
32 safe unicode function. Does few trick to turn str_ into unicode
33
33
34 In case of UnicodeDecode error we try to return it with encoding detected
34 In case of UnicodeDecode error we try to return it with encoding detected
35 by chardet library if it fails fallback to unicode with errors replaced
35 by chardet library if it fails fallback to unicode with errors replaced
36
36
37 :param str_: string to decode
37 :param str_: string to decode
38 :rtype: unicode
38 :rtype: unicode
39 :returns: unicode object
39 :returns: unicode object
40 """
40 """
41 if isinstance(str_, unicode):
41 if isinstance(str_, unicode):
42 return str_
42 return str_
43
43 if not from_encoding:
44 import rhodecode
45 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
46 from_encoding = DEFAULT_ENCODING
44 try:
47 try:
45 return unicode(str_)
48 return unicode(str_)
46 except UnicodeDecodeError:
49 except UnicodeDecodeError:
47 pass
50 pass
48
51
49 try:
52 try:
50 return unicode(str_, from_encoding)
53 return unicode(str_, from_encoding)
51 except UnicodeDecodeError:
54 except UnicodeDecodeError:
52 pass
55 pass
53
56
54 try:
57 try:
55 import chardet
58 import chardet
56 encoding = chardet.detect(str_)['encoding']
59 encoding = chardet.detect(str_)['encoding']
57 if encoding is None:
60 if encoding is None:
58 raise Exception()
61 raise Exception()
59 return str_.decode(encoding)
62 return str_.decode(encoding)
60 except (ImportError, UnicodeDecodeError, Exception):
63 except (ImportError, UnicodeDecodeError, Exception):
61 return unicode(str_, from_encoding, 'replace')
64 return unicode(str_, from_encoding, 'replace')
62
65
63
66
64 def safe_str(unicode_, to_encoding='utf8'):
67 def safe_str(unicode_, to_encoding=None):
65 """
68 """
66 safe str function. Does few trick to turn unicode_ into string
69 safe str function. Does few trick to turn unicode_ into string
67
70
68 In case of UnicodeEncodeError we try to return it with encoding detected
71 In case of UnicodeEncodeError we try to return it with encoding detected
69 by chardet library if it fails fallback to string with errors replaced
72 by chardet library if it fails fallback to string with errors replaced
70
73
71 :param unicode_: unicode to encode
74 :param unicode_: unicode to encode
72 :rtype: str
75 :rtype: str
73 :returns: str object
76 :returns: str object
74 """
77 """
75
78
76 if isinstance(unicode_, str):
79 if isinstance(unicode_, str):
77 return unicode_
80 return unicode_
78
81 if not to_encoding:
82 import rhodecode
83 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
84 to_encoding = DEFAULT_ENCODING
79 try:
85 try:
80 return unicode_.encode(to_encoding)
86 return unicode_.encode(to_encoding)
81 except UnicodeEncodeError:
87 except UnicodeEncodeError:
82 pass
88 pass
83
89
84 try:
90 try:
85 import chardet
91 import chardet
86 encoding = chardet.detect(unicode_)['encoding']
92 encoding = chardet.detect(unicode_)['encoding']
87 print encoding
93 print encoding
88 if encoding is None:
94 if encoding is None:
89 raise UnicodeEncodeError()
95 raise UnicodeEncodeError()
90
96
91 return unicode_.encode(encoding)
97 return unicode_.encode(encoding)
92 except (ImportError, UnicodeEncodeError):
98 except (ImportError, UnicodeEncodeError):
93 return unicode_.encode(to_encoding, 'replace')
99 return unicode_.encode(to_encoding, 'replace')
94
100
95 return safe_str
101 return safe_str
96
102
97
103
98 def author_email(author):
104 def author_email(author):
99 """
105 """
100 returns email address of given author.
106 returns email address of given author.
101 If any of <,> sign are found, it fallbacks to regex findall()
107 If any of <,> sign are found, it fallbacks to regex findall()
102 and returns first found result or empty string
108 and returns first found result or empty string
103
109
104 Regex taken from http://www.regular-expressions.info/email.html
110 Regex taken from http://www.regular-expressions.info/email.html
105 """
111 """
106 import re
112 import re
107 r = author.find('>')
113 r = author.find('>')
108 l = author.find('<')
114 l = author.find('<')
109
115
110 if l == -1 or r == -1:
116 if l == -1 or r == -1:
111 # fallback to regex match of email out of a string
117 # fallback to regex match of email out of a string
112 email_re = re.compile(r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!"""
118 email_re = re.compile(r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!"""
113 r"""#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z"""
119 r"""#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z"""
114 r"""0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]"""
120 r"""0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]"""
115 r"""*[a-z0-9])?""", re.IGNORECASE)
121 r"""*[a-z0-9])?""", re.IGNORECASE)
116 m = re.findall(email_re, author)
122 m = re.findall(email_re, author)
117 return m[0] if m else ''
123 return m[0] if m else ''
118
124
119 return author[l + 1:r].strip()
125 return author[l + 1:r].strip()
120
126
121
127
122 def author_name(author):
128 def author_name(author):
123 """
129 """
124 get name of author, or else username.
130 get name of author, or else username.
125 It'll try to find an email in the author string and just cut it off
131 It'll try to find an email in the author string and just cut it off
126 to get the username
132 to get the username
127 """
133 """
128
134
129 if not '@' in author:
135 if not '@' in author:
130 return author
136 return author
131 else:
137 else:
132 return author.replace(author_email(author), '').replace('<', '')\
138 return author.replace(author_email(author), '').replace('<', '')\
133 .replace('>', '').strip()
139 .replace('>', '').strip()
@@ -1,1198 +1,1203 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 from collections import defaultdict
30 from collections import defaultdict
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from beaker.cache import cache_region, region_invalidate
35 from beaker.cache import cache_region, region_invalidate
36
36
37 from rhodecode.lib.vcs import get_backend
37 from rhodecode.lib.vcs import get_backend
38 from rhodecode.lib.vcs.utils.helpers import get_scm
38 from rhodecode.lib.vcs.utils.helpers import get_scm
39 from rhodecode.lib.vcs.exceptions import VCSError
39 from rhodecode.lib.vcs.exceptions import VCSError
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
40 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41
41
42 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
42 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
43 from rhodecode.lib.compat import json
43 from rhodecode.lib.compat import json
44 from rhodecode.lib.caching_query import FromCache
44 from rhodecode.lib.caching_query import FromCache
45
45
46 from rhodecode.model.meta import Base, Session
46 from rhodecode.model.meta import Base, Session
47
47
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51 #==============================================================================
51 #==============================================================================
52 # BASE CLASSES
52 # BASE CLASSES
53 #==============================================================================
53 #==============================================================================
54
54
55
55
56 class ModelSerializer(json.JSONEncoder):
56 class ModelSerializer(json.JSONEncoder):
57 """
57 """
58 Simple Serializer for JSON,
58 Simple Serializer for JSON,
59
59
60 usage::
60 usage::
61
61
62 to make object customized for serialization implement a __json__
62 to make object customized for serialization implement a __json__
63 method that will return a dict for serialization into json
63 method that will return a dict for serialization into json
64
64
65 example::
65 example::
66
66
67 class Task(object):
67 class Task(object):
68
68
69 def __init__(self, name, value):
69 def __init__(self, name, value):
70 self.name = name
70 self.name = name
71 self.value = value
71 self.value = value
72
72
73 def __json__(self):
73 def __json__(self):
74 return dict(name=self.name,
74 return dict(name=self.name,
75 value=self.value)
75 value=self.value)
76
76
77 """
77 """
78
78
79 def default(self, obj):
79 def default(self, obj):
80
80
81 if hasattr(obj, '__json__'):
81 if hasattr(obj, '__json__'):
82 return obj.__json__()
82 return obj.__json__()
83 else:
83 else:
84 return json.JSONEncoder.default(self, obj)
84 return json.JSONEncoder.default(self, obj)
85
85
86
86
87 class BaseModel(object):
87 class BaseModel(object):
88 """
88 """
89 Base Model for all classess
89 Base Model for all classess
90 """
90 """
91
91
92 @classmethod
92 @classmethod
93 def _get_keys(cls):
93 def _get_keys(cls):
94 """return column names for this model """
94 """return column names for this model """
95 return class_mapper(cls).c.keys()
95 return class_mapper(cls).c.keys()
96
96
97 def get_dict(self):
97 def get_dict(self):
98 """
98 """
99 return dict with keys and values corresponding
99 return dict with keys and values corresponding
100 to this model data """
100 to this model data """
101
101
102 d = {}
102 d = {}
103 for k in self._get_keys():
103 for k in self._get_keys():
104 d[k] = getattr(self, k)
104 d[k] = getattr(self, k)
105
105
106 # also use __json__() if present to get additional fields
106 # also use __json__() if present to get additional fields
107 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
107 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
108 d[k] = val
108 d[k] = val
109 return d
109 return d
110
110
111 def get_appstruct(self):
111 def get_appstruct(self):
112 """return list with keys and values tupples corresponding
112 """return list with keys and values tupples corresponding
113 to this model data """
113 to this model data """
114
114
115 l = []
115 l = []
116 for k in self._get_keys():
116 for k in self._get_keys():
117 l.append((k, getattr(self, k),))
117 l.append((k, getattr(self, k),))
118 return l
118 return l
119
119
120 def populate_obj(self, populate_dict):
120 def populate_obj(self, populate_dict):
121 """populate model with data from given populate_dict"""
121 """populate model with data from given populate_dict"""
122
122
123 for k in self._get_keys():
123 for k in self._get_keys():
124 if k in populate_dict:
124 if k in populate_dict:
125 setattr(self, k, populate_dict[k])
125 setattr(self, k, populate_dict[k])
126
126
127 @classmethod
127 @classmethod
128 def query(cls):
128 def query(cls):
129 return Session.query(cls)
129 return Session.query(cls)
130
130
131 @classmethod
131 @classmethod
132 def get(cls, id_):
132 def get(cls, id_):
133 if id_:
133 if id_:
134 return cls.query().get(id_)
134 return cls.query().get(id_)
135
135
136 @classmethod
136 @classmethod
137 def getAll(cls):
137 def getAll(cls):
138 return cls.query().all()
138 return cls.query().all()
139
139
140 @classmethod
140 @classmethod
141 def delete(cls, id_):
141 def delete(cls, id_):
142 obj = cls.query().get(id_)
142 obj = cls.query().get(id_)
143 Session.delete(obj)
143 Session.delete(obj)
144
144
145
145
146 class RhodeCodeSetting(Base, BaseModel):
146 class RhodeCodeSetting(Base, BaseModel):
147 __tablename__ = 'rhodecode_settings'
147 __tablename__ = 'rhodecode_settings'
148 __table_args__ = (
148 __table_args__ = (
149 UniqueConstraint('app_settings_name'),
149 UniqueConstraint('app_settings_name'),
150 {'extend_existing': True}
150 {'extend_existing': True}
151 )
151 )
152 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
152 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
153 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
153 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
154 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
154 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
155
155
156 def __init__(self, k='', v=''):
156 def __init__(self, k='', v=''):
157 self.app_settings_name = k
157 self.app_settings_name = k
158 self.app_settings_value = v
158 self.app_settings_value = v
159
159
160 @validates('_app_settings_value')
160 @validates('_app_settings_value')
161 def validate_settings_value(self, key, val):
161 def validate_settings_value(self, key, val):
162 assert type(val) == unicode
162 assert type(val) == unicode
163 return val
163 return val
164
164
165 @hybrid_property
165 @hybrid_property
166 def app_settings_value(self):
166 def app_settings_value(self):
167 v = self._app_settings_value
167 v = self._app_settings_value
168 if self.app_settings_name == 'ldap_active':
168 if self.app_settings_name == 'ldap_active':
169 v = str2bool(v)
169 v = str2bool(v)
170 return v
170 return v
171
171
172 @app_settings_value.setter
172 @app_settings_value.setter
173 def app_settings_value(self, val):
173 def app_settings_value(self, val):
174 """
174 """
175 Setter that will always make sure we use unicode in app_settings_value
175 Setter that will always make sure we use unicode in app_settings_value
176
176
177 :param val:
177 :param val:
178 """
178 """
179 self._app_settings_value = safe_unicode(val)
179 self._app_settings_value = safe_unicode(val)
180
180
181 def __repr__(self):
181 def __repr__(self):
182 return "<%s('%s:%s')>" % (
182 return "<%s('%s:%s')>" % (
183 self.__class__.__name__,
183 self.__class__.__name__,
184 self.app_settings_name, self.app_settings_value
184 self.app_settings_name, self.app_settings_value
185 )
185 )
186
186
187 @classmethod
187 @classmethod
188 def get_by_name(cls, ldap_key):
188 def get_by_name(cls, ldap_key):
189 return cls.query()\
189 return cls.query()\
190 .filter(cls.app_settings_name == ldap_key).scalar()
190 .filter(cls.app_settings_name == ldap_key).scalar()
191
191
192 @classmethod
192 @classmethod
193 def get_app_settings(cls, cache=False):
193 def get_app_settings(cls, cache=False):
194
194
195 ret = cls.query()
195 ret = cls.query()
196
196
197 if cache:
197 if cache:
198 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
198 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
199
199
200 if not ret:
200 if not ret:
201 raise Exception('Could not get application settings !')
201 raise Exception('Could not get application settings !')
202 settings = {}
202 settings = {}
203 for each in ret:
203 for each in ret:
204 settings['rhodecode_' + each.app_settings_name] = \
204 settings['rhodecode_' + each.app_settings_name] = \
205 each.app_settings_value
205 each.app_settings_value
206
206
207 return settings
207 return settings
208
208
209 @classmethod
209 @classmethod
210 def get_ldap_settings(cls, cache=False):
210 def get_ldap_settings(cls, cache=False):
211 ret = cls.query()\
211 ret = cls.query()\
212 .filter(cls.app_settings_name.startswith('ldap_')).all()
212 .filter(cls.app_settings_name.startswith('ldap_')).all()
213 fd = {}
213 fd = {}
214 for row in ret:
214 for row in ret:
215 fd.update({row.app_settings_name:row.app_settings_value})
215 fd.update({row.app_settings_name:row.app_settings_value})
216
216
217 return fd
217 return fd
218
218
219
219
220 class RhodeCodeUi(Base, BaseModel):
220 class RhodeCodeUi(Base, BaseModel):
221 __tablename__ = 'rhodecode_ui'
221 __tablename__ = 'rhodecode_ui'
222 __table_args__ = (
222 __table_args__ = (
223 UniqueConstraint('ui_key'),
223 UniqueConstraint('ui_key'),
224 {'extend_existing': True}
224 {'extend_existing': True}
225 )
225 )
226
226
227 HOOK_UPDATE = 'changegroup.update'
227 HOOK_UPDATE = 'changegroup.update'
228 HOOK_REPO_SIZE = 'changegroup.repo_size'
228 HOOK_REPO_SIZE = 'changegroup.repo_size'
229 HOOK_PUSH = 'pretxnchangegroup.push_logger'
229 HOOK_PUSH = 'pretxnchangegroup.push_logger'
230 HOOK_PULL = 'preoutgoing.pull_logger'
230 HOOK_PULL = 'preoutgoing.pull_logger'
231
231
232 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
232 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
233 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
233 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
234 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
234 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
235 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
235 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
236 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
236 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
237
237
238 @classmethod
238 @classmethod
239 def get_by_key(cls, key):
239 def get_by_key(cls, key):
240 return cls.query().filter(cls.ui_key == key)
240 return cls.query().filter(cls.ui_key == key)
241
241
242 @classmethod
242 @classmethod
243 def get_builtin_hooks(cls):
243 def get_builtin_hooks(cls):
244 q = cls.query()
244 q = cls.query()
245 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
245 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
246 cls.HOOK_REPO_SIZE,
246 cls.HOOK_REPO_SIZE,
247 cls.HOOK_PUSH, cls.HOOK_PULL]))
247 cls.HOOK_PUSH, cls.HOOK_PULL]))
248 return q.all()
248 return q.all()
249
249
250 @classmethod
250 @classmethod
251 def get_custom_hooks(cls):
251 def get_custom_hooks(cls):
252 q = cls.query()
252 q = cls.query()
253 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
253 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
254 cls.HOOK_REPO_SIZE,
254 cls.HOOK_REPO_SIZE,
255 cls.HOOK_PUSH, cls.HOOK_PULL]))
255 cls.HOOK_PUSH, cls.HOOK_PULL]))
256 q = q.filter(cls.ui_section == 'hooks')
256 q = q.filter(cls.ui_section == 'hooks')
257 return q.all()
257 return q.all()
258
258
259 @classmethod
259 @classmethod
260 def create_or_update_hook(cls, key, val):
260 def create_or_update_hook(cls, key, val):
261 new_ui = cls.get_by_key(key).scalar() or cls()
261 new_ui = cls.get_by_key(key).scalar() or cls()
262 new_ui.ui_section = 'hooks'
262 new_ui.ui_section = 'hooks'
263 new_ui.ui_active = True
263 new_ui.ui_active = True
264 new_ui.ui_key = key
264 new_ui.ui_key = key
265 new_ui.ui_value = val
265 new_ui.ui_value = val
266
266
267 Session.add(new_ui)
267 Session.add(new_ui)
268
268
269
269
270 class User(Base, BaseModel):
270 class User(Base, BaseModel):
271 __tablename__ = 'users'
271 __tablename__ = 'users'
272 __table_args__ = (
272 __table_args__ = (
273 UniqueConstraint('username'), UniqueConstraint('email'),
273 UniqueConstraint('username'), UniqueConstraint('email'),
274 {'extend_existing': True}
274 {'extend_existing': True}
275 )
275 )
276 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
276 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
277 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
277 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
279 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
279 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
280 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
280 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
281 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
281 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
282 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
282 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
283 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
283 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
284 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
284 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
285 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
285 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
286 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
286 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
287
287
288 user_log = relationship('UserLog', cascade='all')
288 user_log = relationship('UserLog', cascade='all')
289 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
289 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
290
290
291 repositories = relationship('Repository')
291 repositories = relationship('Repository')
292 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
292 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
293 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
293 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
294
294
295 group_member = relationship('UsersGroupMember', cascade='all')
295 group_member = relationship('UsersGroupMember', cascade='all')
296
296
297 notifications = relationship('UserNotification',)
297 notifications = relationship('UserNotification',)
298
298
299 @hybrid_property
299 @hybrid_property
300 def email(self):
300 def email(self):
301 return self._email
301 return self._email
302
302
303 @email.setter
303 @email.setter
304 def email(self, val):
304 def email(self, val):
305 self._email = val.lower() if val else None
305 self._email = val.lower() if val else None
306
306
307 @property
307 @property
308 def full_name(self):
308 def full_name(self):
309 return '%s %s' % (self.name, self.lastname)
309 return '%s %s' % (self.name, self.lastname)
310
310
311 @property
311 @property
312 def full_name_or_username(self):
312 def full_name_or_username(self):
313 return ('%s %s' % (self.name, self.lastname)
313 return ('%s %s' % (self.name, self.lastname)
314 if (self.name and self.lastname) else self.username)
314 if (self.name and self.lastname) else self.username)
315
315
316 @property
316 @property
317 def full_contact(self):
317 def full_contact(self):
318 return '%s %s <%s>' % (self.name, self.lastname, self.email)
318 return '%s %s <%s>' % (self.name, self.lastname, self.email)
319
319
320 @property
320 @property
321 def short_contact(self):
321 def short_contact(self):
322 return '%s %s' % (self.name, self.lastname)
322 return '%s %s' % (self.name, self.lastname)
323
323
324 @property
324 @property
325 def is_admin(self):
325 def is_admin(self):
326 return self.admin
326 return self.admin
327
327
328 def __repr__(self):
328 def __repr__(self):
329 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
329 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
330 self.user_id, self.username)
330 self.user_id, self.username)
331
331
332 @classmethod
332 @classmethod
333 def get_by_username(cls, username, case_insensitive=False, cache=False):
333 def get_by_username(cls, username, case_insensitive=False, cache=False):
334 if case_insensitive:
334 if case_insensitive:
335 q = cls.query().filter(cls.username.ilike(username))
335 q = cls.query().filter(cls.username.ilike(username))
336 else:
336 else:
337 q = cls.query().filter(cls.username == username)
337 q = cls.query().filter(cls.username == username)
338
338
339 if cache:
339 if cache:
340 q = q.options(FromCache("sql_cache_short",
340 q = q.options(FromCache("sql_cache_short",
341 "get_user_%s" % username))
341 "get_user_%s" % username))
342 return q.scalar()
342 return q.scalar()
343
343
344 @classmethod
344 @classmethod
345 def get_by_api_key(cls, api_key, cache=False):
345 def get_by_api_key(cls, api_key, cache=False):
346 q = cls.query().filter(cls.api_key == api_key)
346 q = cls.query().filter(cls.api_key == api_key)
347
347
348 if cache:
348 if cache:
349 q = q.options(FromCache("sql_cache_short",
349 q = q.options(FromCache("sql_cache_short",
350 "get_api_key_%s" % api_key))
350 "get_api_key_%s" % api_key))
351 return q.scalar()
351 return q.scalar()
352
352
353 @classmethod
353 @classmethod
354 def get_by_email(cls, email, case_insensitive=False, cache=False):
354 def get_by_email(cls, email, case_insensitive=False, cache=False):
355 if case_insensitive:
355 if case_insensitive:
356 q = cls.query().filter(cls.email.ilike(email))
356 q = cls.query().filter(cls.email.ilike(email))
357 else:
357 else:
358 q = cls.query().filter(cls.email == email)
358 q = cls.query().filter(cls.email == email)
359
359
360 if cache:
360 if cache:
361 q = q.options(FromCache("sql_cache_short",
361 q = q.options(FromCache("sql_cache_short",
362 "get_api_key_%s" % email))
362 "get_api_key_%s" % email))
363 return q.scalar()
363 return q.scalar()
364
364
365 def update_lastlogin(self):
365 def update_lastlogin(self):
366 """Update user lastlogin"""
366 """Update user lastlogin"""
367 self.last_login = datetime.datetime.now()
367 self.last_login = datetime.datetime.now()
368 Session.add(self)
368 Session.add(self)
369 log.debug('updated user %s lastlogin' % self.username)
369 log.debug('updated user %s lastlogin' % self.username)
370
370
371 def __json__(self):
371 def __json__(self):
372 return dict(
372 return dict(
373 email=self.email,
373 email=self.email,
374 full_name=self.full_name,
374 full_name=self.full_name,
375 full_name_or_username=self.full_name_or_username,
375 full_name_or_username=self.full_name_or_username,
376 short_contact=self.short_contact,
376 short_contact=self.short_contact,
377 full_contact=self.full_contact
377 full_contact=self.full_contact
378 )
378 )
379
379
380
380
381 class UserLog(Base, BaseModel):
381 class UserLog(Base, BaseModel):
382 __tablename__ = 'user_logs'
382 __tablename__ = 'user_logs'
383 __table_args__ = {'extend_existing': True}
383 __table_args__ = {'extend_existing': True}
384 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
384 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
385 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
385 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
386 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
386 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
387 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
387 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
388 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
388 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
389 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
389 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
390 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
390 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
391
391
392 @property
392 @property
393 def action_as_day(self):
393 def action_as_day(self):
394 return datetime.date(*self.action_date.timetuple()[:3])
394 return datetime.date(*self.action_date.timetuple()[:3])
395
395
396 user = relationship('User')
396 user = relationship('User')
397 repository = relationship('Repository',cascade='')
397 repository = relationship('Repository',cascade='')
398
398
399
399
400 class UsersGroup(Base, BaseModel):
400 class UsersGroup(Base, BaseModel):
401 __tablename__ = 'users_groups'
401 __tablename__ = 'users_groups'
402 __table_args__ = {'extend_existing': True}
402 __table_args__ = {'extend_existing': True}
403
403
404 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
404 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
405 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
405 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
406 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
406 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
407
407
408 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
408 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
409
409
410 def __repr__(self):
410 def __repr__(self):
411 return '<userGroup(%s)>' % (self.users_group_name)
411 return '<userGroup(%s)>' % (self.users_group_name)
412
412
413 @classmethod
413 @classmethod
414 def get_by_group_name(cls, group_name, cache=False,
414 def get_by_group_name(cls, group_name, cache=False,
415 case_insensitive=False):
415 case_insensitive=False):
416 if case_insensitive:
416 if case_insensitive:
417 q = cls.query().filter(cls.users_group_name.ilike(group_name))
417 q = cls.query().filter(cls.users_group_name.ilike(group_name))
418 else:
418 else:
419 q = cls.query().filter(cls.users_group_name == group_name)
419 q = cls.query().filter(cls.users_group_name == group_name)
420 if cache:
420 if cache:
421 q = q.options(FromCache("sql_cache_short",
421 q = q.options(FromCache("sql_cache_short",
422 "get_user_%s" % group_name))
422 "get_user_%s" % group_name))
423 return q.scalar()
423 return q.scalar()
424
424
425 @classmethod
425 @classmethod
426 def get(cls, users_group_id, cache=False):
426 def get(cls, users_group_id, cache=False):
427 users_group = cls.query()
427 users_group = cls.query()
428 if cache:
428 if cache:
429 users_group = users_group.options(FromCache("sql_cache_short",
429 users_group = users_group.options(FromCache("sql_cache_short",
430 "get_users_group_%s" % users_group_id))
430 "get_users_group_%s" % users_group_id))
431 return users_group.get(users_group_id)
431 return users_group.get(users_group_id)
432
432
433
433
434 class UsersGroupMember(Base, BaseModel):
434 class UsersGroupMember(Base, BaseModel):
435 __tablename__ = 'users_groups_members'
435 __tablename__ = 'users_groups_members'
436 __table_args__ = {'extend_existing': True}
436 __table_args__ = {'extend_existing': True}
437
437
438 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
439 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
439 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
440 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
440 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
441
441
442 user = relationship('User', lazy='joined')
442 user = relationship('User', lazy='joined')
443 users_group = relationship('UsersGroup')
443 users_group = relationship('UsersGroup')
444
444
445 def __init__(self, gr_id='', u_id=''):
445 def __init__(self, gr_id='', u_id=''):
446 self.users_group_id = gr_id
446 self.users_group_id = gr_id
447 self.user_id = u_id
447 self.user_id = u_id
448
448
449
449
450 class Repository(Base, BaseModel):
450 class Repository(Base, BaseModel):
451 __tablename__ = 'repositories'
451 __tablename__ = 'repositories'
452 __table_args__ = (
452 __table_args__ = (
453 UniqueConstraint('repo_name'),
453 UniqueConstraint('repo_name'),
454 {'extend_existing': True},
454 {'extend_existing': True},
455 )
455 )
456
456
457 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
457 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
458 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
458 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
459 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
459 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
460 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
460 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
461 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
461 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
462 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
462 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
463 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
463 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
464 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
464 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
465 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
465 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
466 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
466 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
467
467
468 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
468 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
469 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
469 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
470
470
471 user = relationship('User')
471 user = relationship('User')
472 fork = relationship('Repository', remote_side=repo_id)
472 fork = relationship('Repository', remote_side=repo_id)
473 group = relationship('RepoGroup')
473 group = relationship('RepoGroup')
474 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
474 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
475 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
475 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
476 stats = relationship('Statistics', cascade='all', uselist=False)
476 stats = relationship('Statistics', cascade='all', uselist=False)
477
477
478 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
478 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
479
479
480 logs = relationship('UserLog')
480 logs = relationship('UserLog')
481
481
482 def __repr__(self):
482 def __repr__(self):
483 return "<%s('%s:%s')>" % (self.__class__.__name__,
483 return "<%s('%s:%s')>" % (self.__class__.__name__,
484 self.repo_id, self.repo_name)
484 self.repo_id, self.repo_name)
485
485
486 @classmethod
486 @classmethod
487 def url_sep(cls):
487 def url_sep(cls):
488 return '/'
488 return '/'
489
489
490 @classmethod
490 @classmethod
491 def get_by_repo_name(cls, repo_name):
491 def get_by_repo_name(cls, repo_name):
492 q = Session.query(cls).filter(cls.repo_name == repo_name)
492 q = Session.query(cls).filter(cls.repo_name == repo_name)
493 q = q.options(joinedload(Repository.fork))\
493 q = q.options(joinedload(Repository.fork))\
494 .options(joinedload(Repository.user))\
494 .options(joinedload(Repository.user))\
495 .options(joinedload(Repository.group))
495 .options(joinedload(Repository.group))
496 return q.scalar()
496 return q.scalar()
497
497
498 @classmethod
498 @classmethod
499 def get_repo_forks(cls, repo_id):
499 def get_repo_forks(cls, repo_id):
500 return cls.query().filter(Repository.fork_id == repo_id)
500 return cls.query().filter(Repository.fork_id == repo_id)
501
501
502 @classmethod
502 @classmethod
503 def base_path(cls):
503 def base_path(cls):
504 """
504 """
505 Returns base path when all repos are stored
505 Returns base path when all repos are stored
506
506
507 :param cls:
507 :param cls:
508 """
508 """
509 q = Session.query(RhodeCodeUi)\
509 q = Session.query(RhodeCodeUi)\
510 .filter(RhodeCodeUi.ui_key == cls.url_sep())
510 .filter(RhodeCodeUi.ui_key == cls.url_sep())
511 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
511 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
512 return q.one().ui_value
512 return q.one().ui_value
513
513
514 @property
514 @property
515 def just_name(self):
515 def just_name(self):
516 return self.repo_name.split(Repository.url_sep())[-1]
516 return self.repo_name.split(Repository.url_sep())[-1]
517
517
518 @property
518 @property
519 def groups_with_parents(self):
519 def groups_with_parents(self):
520 groups = []
520 groups = []
521 if self.group is None:
521 if self.group is None:
522 return groups
522 return groups
523
523
524 cur_gr = self.group
524 cur_gr = self.group
525 groups.insert(0, cur_gr)
525 groups.insert(0, cur_gr)
526 while 1:
526 while 1:
527 gr = getattr(cur_gr, 'parent_group', None)
527 gr = getattr(cur_gr, 'parent_group', None)
528 cur_gr = cur_gr.parent_group
528 cur_gr = cur_gr.parent_group
529 if gr is None:
529 if gr is None:
530 break
530 break
531 groups.insert(0, gr)
531 groups.insert(0, gr)
532
532
533 return groups
533 return groups
534
534
535 @property
535 @property
536 def groups_and_repo(self):
536 def groups_and_repo(self):
537 return self.groups_with_parents, self.just_name
537 return self.groups_with_parents, self.just_name
538
538
539 @LazyProperty
539 @LazyProperty
540 def repo_path(self):
540 def repo_path(self):
541 """
541 """
542 Returns base full path for that repository means where it actually
542 Returns base full path for that repository means where it actually
543 exists on a filesystem
543 exists on a filesystem
544 """
544 """
545 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
545 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
546 Repository.url_sep())
546 Repository.url_sep())
547 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
547 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
548 return q.one().ui_value
548 return q.one().ui_value
549
549
550 @property
550 @property
551 def repo_full_path(self):
551 def repo_full_path(self):
552 p = [self.repo_path]
552 p = [self.repo_path]
553 # we need to split the name by / since this is how we store the
553 # we need to split the name by / since this is how we store the
554 # names in the database, but that eventually needs to be converted
554 # names in the database, but that eventually needs to be converted
555 # into a valid system path
555 # into a valid system path
556 p += self.repo_name.split(Repository.url_sep())
556 p += self.repo_name.split(Repository.url_sep())
557 return os.path.join(*p)
557 return os.path.join(*p)
558
558
559 def get_new_name(self, repo_name):
559 def get_new_name(self, repo_name):
560 """
560 """
561 returns new full repository name based on assigned group and new new
561 returns new full repository name based on assigned group and new new
562
562
563 :param group_name:
563 :param group_name:
564 """
564 """
565 path_prefix = self.group.full_path_splitted if self.group else []
565 path_prefix = self.group.full_path_splitted if self.group else []
566 return Repository.url_sep().join(path_prefix + [repo_name])
566 return Repository.url_sep().join(path_prefix + [repo_name])
567
567
568 @property
568 @property
569 def _ui(self):
569 def _ui(self):
570 """
570 """
571 Creates an db based ui object for this repository
571 Creates an db based ui object for this repository
572 """
572 """
573 from mercurial import ui
573 from mercurial import ui
574 from mercurial import config
574 from mercurial import config
575 baseui = ui.ui()
575 baseui = ui.ui()
576
576
577 #clean the baseui object
577 #clean the baseui object
578 baseui._ocfg = config.config()
578 baseui._ocfg = config.config()
579 baseui._ucfg = config.config()
579 baseui._ucfg = config.config()
580 baseui._tcfg = config.config()
580 baseui._tcfg = config.config()
581
581
582 ret = RhodeCodeUi.query()\
582 ret = RhodeCodeUi.query()\
583 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
583 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
584
584
585 hg_ui = ret
585 hg_ui = ret
586 for ui_ in hg_ui:
586 for ui_ in hg_ui:
587 if ui_.ui_active:
587 if ui_.ui_active:
588 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
588 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
589 ui_.ui_key, ui_.ui_value)
589 ui_.ui_key, ui_.ui_value)
590 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
590 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
591
591
592 return baseui
592 return baseui
593
593
594 @classmethod
594 @classmethod
595 def is_valid(cls, repo_name):
595 def is_valid(cls, repo_name):
596 """
596 """
597 returns True if given repo name is a valid filesystem repository
597 returns True if given repo name is a valid filesystem repository
598
598
599 :param cls:
599 :param cls:
600 :param repo_name:
600 :param repo_name:
601 """
601 """
602 from rhodecode.lib.utils import is_valid_repo
602 from rhodecode.lib.utils import is_valid_repo
603
603
604 return is_valid_repo(repo_name, cls.base_path())
604 return is_valid_repo(repo_name, cls.base_path())
605
605
606 #==========================================================================
606 #==========================================================================
607 # SCM PROPERTIES
607 # SCM PROPERTIES
608 #==========================================================================
608 #==========================================================================
609
609
610 def get_changeset(self, rev):
610 def get_changeset(self, rev):
611 return get_changeset_safe(self.scm_instance, rev)
611 return get_changeset_safe(self.scm_instance, rev)
612
612
613 @property
613 @property
614 def tip(self):
614 def tip(self):
615 return self.get_changeset('tip')
615 return self.get_changeset('tip')
616
616
617 @property
617 @property
618 def author(self):
618 def author(self):
619 return self.tip.author
619 return self.tip.author
620
620
621 @property
621 @property
622 def last_change(self):
622 def last_change(self):
623 return self.scm_instance.last_change
623 return self.scm_instance.last_change
624
624
625 def comments(self, revisions=None):
625 def comments(self, revisions=None):
626 """
626 """
627 Returns comments for this repository grouped by revisions
627 Returns comments for this repository grouped by revisions
628
628
629 :param revisions: filter query by revisions only
629 :param revisions: filter query by revisions only
630 """
630 """
631 cmts = ChangesetComment.query()\
631 cmts = ChangesetComment.query()\
632 .filter(ChangesetComment.repo == self)
632 .filter(ChangesetComment.repo == self)
633 if revisions:
633 if revisions:
634 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
634 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
635 grouped = defaultdict(list)
635 grouped = defaultdict(list)
636 for cmt in cmts.all():
636 for cmt in cmts.all():
637 grouped[cmt.revision].append(cmt)
637 grouped[cmt.revision].append(cmt)
638 return grouped
638 return grouped
639
639
640 #==========================================================================
640 #==========================================================================
641 # SCM CACHE INSTANCE
641 # SCM CACHE INSTANCE
642 #==========================================================================
642 #==========================================================================
643
643
644 @property
644 @property
645 def invalidate(self):
645 def invalidate(self):
646 return CacheInvalidation.invalidate(self.repo_name)
646 return CacheInvalidation.invalidate(self.repo_name)
647
647
648 def set_invalidate(self):
648 def set_invalidate(self):
649 """
649 """
650 set a cache for invalidation for this instance
650 set a cache for invalidation for this instance
651 """
651 """
652 CacheInvalidation.set_invalidate(self.repo_name)
652 CacheInvalidation.set_invalidate(self.repo_name)
653
653
654 @LazyProperty
654 @LazyProperty
655 def scm_instance(self):
655 def scm_instance(self):
656 return self.__get_instance()
656 return self.__get_instance()
657
657
658 @property
658 @property
659 def scm_instance_cached(self):
659 def scm_instance_cached(self):
660 @cache_region('long_term')
660 @cache_region('long_term')
661 def _c(repo_name):
661 def _c(repo_name):
662 return self.__get_instance()
662 return self.__get_instance()
663 rn = self.repo_name
663 rn = self.repo_name
664 log.debug('Getting cached instance of repo')
664 log.debug('Getting cached instance of repo')
665 inv = self.invalidate
665 inv = self.invalidate
666 if inv is not None:
666 if inv is not None:
667 region_invalidate(_c, None, rn)
667 region_invalidate(_c, None, rn)
668 # update our cache
668 # update our cache
669 CacheInvalidation.set_valid(inv.cache_key)
669 CacheInvalidation.set_valid(inv.cache_key)
670 return _c(rn)
670 return _c(rn)
671
671
672 def __get_instance(self):
672 def __get_instance(self):
673 repo_full_path = self.repo_full_path
673 repo_full_path = self.repo_full_path
674 try:
674 try:
675 alias = get_scm(repo_full_path)[0]
675 alias = get_scm(repo_full_path)[0]
676 log.debug('Creating instance of %s repository' % alias)
676 log.debug('Creating instance of %s repository' % alias)
677 backend = get_backend(alias)
677 backend = get_backend(alias)
678 except VCSError:
678 except VCSError:
679 log.error(traceback.format_exc())
679 log.error(traceback.format_exc())
680 log.error('Perhaps this repository is in db and not in '
680 log.error('Perhaps this repository is in db and not in '
681 'filesystem run rescan repositories with '
681 'filesystem run rescan repositories with '
682 '"destroy old data " option from admin panel')
682 '"destroy old data " option from admin panel')
683 return
683 return
684
684
685 if alias == 'hg':
685 if alias == 'hg':
686
686
687 repo = backend(safe_str(repo_full_path), create=False,
687 repo = backend(safe_str(repo_full_path), create=False,
688 baseui=self._ui)
688 baseui=self._ui)
689 # skip hidden web repository
689 # skip hidden web repository
690 if repo._get_hidden():
690 if repo._get_hidden():
691 return
691 return
692 else:
692 else:
693 repo = backend(repo_full_path, create=False)
693 repo = backend(repo_full_path, create=False)
694
694
695 return repo
695 return repo
696
696
697
697
698 class RepoGroup(Base, BaseModel):
698 class RepoGroup(Base, BaseModel):
699 __tablename__ = 'groups'
699 __tablename__ = 'groups'
700 __table_args__ = (
700 __table_args__ = (
701 UniqueConstraint('group_name', 'group_parent_id'),
701 UniqueConstraint('group_name', 'group_parent_id'),
702 CheckConstraint('group_id != group_parent_id'),
702 CheckConstraint('group_id != group_parent_id'),
703 {'extend_existing': True},
703 {'extend_existing': True},
704 )
704 )
705 __mapper_args__ = {'order_by': 'group_name'}
705 __mapper_args__ = {'order_by': 'group_name'}
706
706
707 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
707 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
708 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
708 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
709 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
709 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
710 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
710 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
711
711
712 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
712 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
713 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
713 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
714
714
715 parent_group = relationship('RepoGroup', remote_side=group_id)
715 parent_group = relationship('RepoGroup', remote_side=group_id)
716
716
717 def __init__(self, group_name='', parent_group=None):
717 def __init__(self, group_name='', parent_group=None):
718 self.group_name = group_name
718 self.group_name = group_name
719 self.parent_group = parent_group
719 self.parent_group = parent_group
720
720
721 def __repr__(self):
721 def __repr__(self):
722 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
722 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
723 self.group_name)
723 self.group_name)
724
724
725 @classmethod
725 @classmethod
726 def groups_choices(cls):
726 def groups_choices(cls):
727 from webhelpers.html import literal as _literal
727 from webhelpers.html import literal as _literal
728 repo_groups = [('', '')]
728 repo_groups = [('', '')]
729 sep = ' &raquo; '
729 sep = ' &raquo; '
730 _name = lambda k: _literal(sep.join(k))
730 _name = lambda k: _literal(sep.join(k))
731
731
732 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
732 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
733 for x in cls.query().all()])
733 for x in cls.query().all()])
734
734
735 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
735 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
736 return repo_groups
736 return repo_groups
737
737
738 @classmethod
738 @classmethod
739 def url_sep(cls):
739 def url_sep(cls):
740 return '/'
740 return '/'
741
741
742 @classmethod
742 @classmethod
743 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
743 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
744 if case_insensitive:
744 if case_insensitive:
745 gr = cls.query()\
745 gr = cls.query()\
746 .filter(cls.group_name.ilike(group_name))
746 .filter(cls.group_name.ilike(group_name))
747 else:
747 else:
748 gr = cls.query()\
748 gr = cls.query()\
749 .filter(cls.group_name == group_name)
749 .filter(cls.group_name == group_name)
750 if cache:
750 if cache:
751 gr = gr.options(FromCache("sql_cache_short",
751 gr = gr.options(FromCache("sql_cache_short",
752 "get_group_%s" % group_name))
752 "get_group_%s" % group_name))
753 return gr.scalar()
753 return gr.scalar()
754
754
755 @property
755 @property
756 def parents(self):
756 def parents(self):
757 parents_recursion_limit = 5
757 parents_recursion_limit = 5
758 groups = []
758 groups = []
759 if self.parent_group is None:
759 if self.parent_group is None:
760 return groups
760 return groups
761 cur_gr = self.parent_group
761 cur_gr = self.parent_group
762 groups.insert(0, cur_gr)
762 groups.insert(0, cur_gr)
763 cnt = 0
763 cnt = 0
764 while 1:
764 while 1:
765 cnt += 1
765 cnt += 1
766 gr = getattr(cur_gr, 'parent_group', None)
766 gr = getattr(cur_gr, 'parent_group', None)
767 cur_gr = cur_gr.parent_group
767 cur_gr = cur_gr.parent_group
768 if gr is None:
768 if gr is None:
769 break
769 break
770 if cnt == parents_recursion_limit:
770 if cnt == parents_recursion_limit:
771 # this will prevent accidental infinit loops
771 # this will prevent accidental infinit loops
772 log.error('group nested more than %s' %
772 log.error('group nested more than %s' %
773 parents_recursion_limit)
773 parents_recursion_limit)
774 break
774 break
775
775
776 groups.insert(0, gr)
776 groups.insert(0, gr)
777 return groups
777 return groups
778
778
779 @property
779 @property
780 def children(self):
780 def children(self):
781 return RepoGroup.query().filter(RepoGroup.parent_group == self)
781 return RepoGroup.query().filter(RepoGroup.parent_group == self)
782
782
783 @property
783 @property
784 def name(self):
784 def name(self):
785 return self.group_name.split(RepoGroup.url_sep())[-1]
785 return self.group_name.split(RepoGroup.url_sep())[-1]
786
786
787 @property
787 @property
788 def full_path(self):
788 def full_path(self):
789 return self.group_name
789 return self.group_name
790
790
791 @property
791 @property
792 def full_path_splitted(self):
792 def full_path_splitted(self):
793 return self.group_name.split(RepoGroup.url_sep())
793 return self.group_name.split(RepoGroup.url_sep())
794
794
795 @property
795 @property
796 def repositories(self):
796 def repositories(self):
797 return Repository.query().filter(Repository.group == self)
797 return Repository.query().filter(Repository.group == self)
798
798
799 @property
799 @property
800 def repositories_recursive_count(self):
800 def repositories_recursive_count(self):
801 cnt = self.repositories.count()
801 cnt = self.repositories.count()
802
802
803 def children_count(group):
803 def children_count(group):
804 cnt = 0
804 cnt = 0
805 for child in group.children:
805 for child in group.children:
806 cnt += child.repositories.count()
806 cnt += child.repositories.count()
807 cnt += children_count(child)
807 cnt += children_count(child)
808 return cnt
808 return cnt
809
809
810 return cnt + children_count(self)
810 return cnt + children_count(self)
811
811
812 def get_new_name(self, group_name):
812 def get_new_name(self, group_name):
813 """
813 """
814 returns new full group name based on parent and new name
814 returns new full group name based on parent and new name
815
815
816 :param group_name:
816 :param group_name:
817 """
817 """
818 path_prefix = (self.parent_group.full_path_splitted if
818 path_prefix = (self.parent_group.full_path_splitted if
819 self.parent_group else [])
819 self.parent_group else [])
820 return RepoGroup.url_sep().join(path_prefix + [group_name])
820 return RepoGroup.url_sep().join(path_prefix + [group_name])
821
821
822
822
823 class Permission(Base, BaseModel):
823 class Permission(Base, BaseModel):
824 __tablename__ = 'permissions'
824 __tablename__ = 'permissions'
825 __table_args__ = {'extend_existing': True}
825 __table_args__ = {'extend_existing': True}
826 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
826 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
827 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
827 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
828 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
828 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
829
829
830 def __repr__(self):
830 def __repr__(self):
831 return "<%s('%s:%s')>" % (
831 return "<%s('%s:%s')>" % (
832 self.__class__.__name__, self.permission_id, self.permission_name
832 self.__class__.__name__, self.permission_id, self.permission_name
833 )
833 )
834
834
835 @classmethod
835 @classmethod
836 def get_by_key(cls, key):
836 def get_by_key(cls, key):
837 return cls.query().filter(cls.permission_name == key).scalar()
837 return cls.query().filter(cls.permission_name == key).scalar()
838
838
839 @classmethod
839 @classmethod
840 def get_default_perms(cls, default_user_id):
840 def get_default_perms(cls, default_user_id):
841 q = Session.query(UserRepoToPerm, Repository, cls)\
841 q = Session.query(UserRepoToPerm, Repository, cls)\
842 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
842 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
843 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
843 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
844 .filter(UserRepoToPerm.user_id == default_user_id)
844 .filter(UserRepoToPerm.user_id == default_user_id)
845
845
846 return q.all()
846 return q.all()
847
847
848 @classmethod
848 @classmethod
849 def get_default_group_perms(cls, default_user_id):
849 def get_default_group_perms(cls, default_user_id):
850 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
850 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
851 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
851 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
852 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
852 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
853 .filter(UserRepoGroupToPerm.user_id == default_user_id)
853 .filter(UserRepoGroupToPerm.user_id == default_user_id)
854
854
855 return q.all()
855 return q.all()
856
856
857
857
858 class UserRepoToPerm(Base, BaseModel):
858 class UserRepoToPerm(Base, BaseModel):
859 __tablename__ = 'repo_to_perm'
859 __tablename__ = 'repo_to_perm'
860 __table_args__ = (
860 __table_args__ = (
861 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
861 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
862 {'extend_existing': True}
862 {'extend_existing': True}
863 )
863 )
864 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
864 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
865 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
865 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
866 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
866 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
867 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
867 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
868
868
869 user = relationship('User')
869 user = relationship('User')
870 repository = relationship('Repository')
870 repository = relationship('Repository')
871 permission = relationship('Permission')
871 permission = relationship('Permission')
872
872
873 @classmethod
873 @classmethod
874 def create(cls, user, repository, permission):
874 def create(cls, user, repository, permission):
875 n = cls()
875 n = cls()
876 n.user = user
876 n.user = user
877 n.repository = repository
877 n.repository = repository
878 n.permission = permission
878 n.permission = permission
879 Session.add(n)
879 Session.add(n)
880 return n
880 return n
881
881
882 def __repr__(self):
882 def __repr__(self):
883 return '<user:%s => %s >' % (self.user, self.repository)
883 return '<user:%s => %s >' % (self.user, self.repository)
884
884
885
885
886 class UserToPerm(Base, BaseModel):
886 class UserToPerm(Base, BaseModel):
887 __tablename__ = 'user_to_perm'
887 __tablename__ = 'user_to_perm'
888 __table_args__ = (
888 __table_args__ = (
889 UniqueConstraint('user_id', 'permission_id'),
889 UniqueConstraint('user_id', 'permission_id'),
890 {'extend_existing': True}
890 {'extend_existing': True}
891 )
891 )
892 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
892 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
893 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
893 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
894 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
894 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
895
895
896 user = relationship('User')
896 user = relationship('User')
897 permission = relationship('Permission', lazy='joined')
897 permission = relationship('Permission', lazy='joined')
898
898
899
899
900 class UsersGroupRepoToPerm(Base, BaseModel):
900 class UsersGroupRepoToPerm(Base, BaseModel):
901 __tablename__ = 'users_group_repo_to_perm'
901 __tablename__ = 'users_group_repo_to_perm'
902 __table_args__ = (
902 __table_args__ = (
903 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
903 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
904 {'extend_existing': True}
904 {'extend_existing': True}
905 )
905 )
906 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
906 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
907 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
907 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
908 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
908 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
909 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
909 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
910
910
911 users_group = relationship('UsersGroup')
911 users_group = relationship('UsersGroup')
912 permission = relationship('Permission')
912 permission = relationship('Permission')
913 repository = relationship('Repository')
913 repository = relationship('Repository')
914
914
915 @classmethod
915 @classmethod
916 def create(cls, users_group, repository, permission):
916 def create(cls, users_group, repository, permission):
917 n = cls()
917 n = cls()
918 n.users_group = users_group
918 n.users_group = users_group
919 n.repository = repository
919 n.repository = repository
920 n.permission = permission
920 n.permission = permission
921 Session.add(n)
921 Session.add(n)
922 return n
922 return n
923
923
924 def __repr__(self):
924 def __repr__(self):
925 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
925 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
926
926
927
927
928 class UsersGroupToPerm(Base, BaseModel):
928 class UsersGroupToPerm(Base, BaseModel):
929 __tablename__ = 'users_group_to_perm'
929 __tablename__ = 'users_group_to_perm'
930 __table_args__ = (
930 __table_args__ = (
931 UniqueConstraint('users_group_id', 'permission_id',),
931 UniqueConstraint('users_group_id', 'permission_id',),
932 {'extend_existing': True}
932 {'extend_existing': True}
933 )
933 )
934 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
934 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
935 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
935 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
936 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
936 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
937
937
938 users_group = relationship('UsersGroup')
938 users_group = relationship('UsersGroup')
939 permission = relationship('Permission')
939 permission = relationship('Permission')
940
940
941
941
942 class UserRepoGroupToPerm(Base, BaseModel):
942 class UserRepoGroupToPerm(Base, BaseModel):
943 __tablename__ = 'user_repo_group_to_perm'
943 __tablename__ = 'user_repo_group_to_perm'
944 __table_args__ = (
944 __table_args__ = (
945 UniqueConstraint('user_id', 'group_id', 'permission_id'),
945 UniqueConstraint('user_id', 'group_id', 'permission_id'),
946 {'extend_existing': True}
946 {'extend_existing': True}
947 )
947 )
948
948
949 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
949 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
950 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
950 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
951 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
951 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
952 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
952 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
953
953
954 user = relationship('User')
954 user = relationship('User')
955 group = relationship('RepoGroup')
955 group = relationship('RepoGroup')
956 permission = relationship('Permission')
956 permission = relationship('Permission')
957
957
958
958
959 class UsersGroupRepoGroupToPerm(Base, BaseModel):
959 class UsersGroupRepoGroupToPerm(Base, BaseModel):
960 __tablename__ = 'users_group_repo_group_to_perm'
960 __tablename__ = 'users_group_repo_group_to_perm'
961 __table_args__ = (
961 __table_args__ = (
962 UniqueConstraint('users_group_id', 'group_id'),
962 UniqueConstraint('users_group_id', 'group_id'),
963 {'extend_existing': True}
963 {'extend_existing': True}
964 )
964 )
965
965
966 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)
966 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)
967 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
967 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
968 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
968 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
969 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
969 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
970
970
971 users_group = relationship('UsersGroup')
971 users_group = relationship('UsersGroup')
972 permission = relationship('Permission')
972 permission = relationship('Permission')
973 group = relationship('RepoGroup')
973 group = relationship('RepoGroup')
974
974
975
975
976 class Statistics(Base, BaseModel):
976 class Statistics(Base, BaseModel):
977 __tablename__ = 'statistics'
977 __tablename__ = 'statistics'
978 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing': True})
978 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing': True})
979 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
979 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
980 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
980 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
981 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
981 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
982 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
982 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
983 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
983 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
984 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
984 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
985
985
986 repository = relationship('Repository', single_parent=True)
986 repository = relationship('Repository', single_parent=True)
987
987
988
988
989 class UserFollowing(Base, BaseModel):
989 class UserFollowing(Base, BaseModel):
990 __tablename__ = 'user_followings'
990 __tablename__ = 'user_followings'
991 __table_args__ = (
991 __table_args__ = (
992 UniqueConstraint('user_id', 'follows_repository_id'),
992 UniqueConstraint('user_id', 'follows_repository_id'),
993 UniqueConstraint('user_id', 'follows_user_id'),
993 UniqueConstraint('user_id', 'follows_user_id'),
994 {'extend_existing': True}
994 {'extend_existing': True}
995 )
995 )
996
996
997 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
997 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
998 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
998 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
999 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
999 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1000 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1000 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1001 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1001 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1002
1002
1003 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1003 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1004
1004
1005 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1005 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1006 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1006 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1007
1007
1008 @classmethod
1008 @classmethod
1009 def get_repo_followers(cls, repo_id):
1009 def get_repo_followers(cls, repo_id):
1010 return cls.query().filter(cls.follows_repo_id == repo_id)
1010 return cls.query().filter(cls.follows_repo_id == repo_id)
1011
1011
1012
1012
1013 class CacheInvalidation(Base, BaseModel):
1013 class CacheInvalidation(Base, BaseModel):
1014 __tablename__ = 'cache_invalidation'
1014 __tablename__ = 'cache_invalidation'
1015 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing': True})
1015 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing': True})
1016 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1016 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1017 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1017 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1018 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1018 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1019 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1019 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1020
1020
1021 def __init__(self, cache_key, cache_args=''):
1021 def __init__(self, cache_key, cache_args=''):
1022 self.cache_key = cache_key
1022 self.cache_key = cache_key
1023 self.cache_args = cache_args
1023 self.cache_args = cache_args
1024 self.cache_active = False
1024 self.cache_active = False
1025
1025
1026 def __repr__(self):
1026 def __repr__(self):
1027 return "<%s('%s:%s')>" % (self.__class__.__name__,
1027 return "<%s('%s:%s')>" % (self.__class__.__name__,
1028 self.cache_id, self.cache_key)
1028 self.cache_id, self.cache_key)
1029
1029
1030 @classmethod
1030 @classmethod
1031 def _get_key(cls, key):
1031 def _get_key(cls, key):
1032 """
1032 """
1033 Wrapper for generating a key
1033 Wrapper for generating a key
1034
1034
1035 :param key:
1035 :param key:
1036 """
1036 """
1037 return "%s" % (key)
1037 import rhodecode
1038 prefix = ''
1039 iid = rhodecode.CONFIG.get('instance_id')
1040 if iid:
1041 prefix = iid
1042 return "%s%s" % (prefix, key)
1038
1043
1039 @classmethod
1044 @classmethod
1040 def get_by_key(cls, key):
1045 def get_by_key(cls, key):
1041 return cls.query().filter(cls.cache_key == key).scalar()
1046 return cls.query().filter(cls.cache_key == key).scalar()
1042
1047
1043 @classmethod
1048 @classmethod
1044 def invalidate(cls, key):
1049 def invalidate(cls, key):
1045 """
1050 """
1046 Returns Invalidation object if this given key should be invalidated
1051 Returns Invalidation object if this given key should be invalidated
1047 None otherwise. `cache_active = False` means that this cache
1052 None otherwise. `cache_active = False` means that this cache
1048 state is not valid and needs to be invalidated
1053 state is not valid and needs to be invalidated
1049
1054
1050 :param key:
1055 :param key:
1051 """
1056 """
1052 return cls.query()\
1057 return cls.query()\
1053 .filter(CacheInvalidation.cache_key == key)\
1058 .filter(CacheInvalidation.cache_key == key)\
1054 .filter(CacheInvalidation.cache_active == False)\
1059 .filter(CacheInvalidation.cache_active == False)\
1055 .scalar()
1060 .scalar()
1056
1061
1057 @classmethod
1062 @classmethod
1058 def set_invalidate(cls, key):
1063 def set_invalidate(cls, key):
1059 """
1064 """
1060 Mark this Cache key for invalidation
1065 Mark this Cache key for invalidation
1061
1066
1062 :param key:
1067 :param key:
1063 """
1068 """
1064
1069
1065 log.debug('marking %s for invalidation' % key)
1070 log.debug('marking %s for invalidation' % key)
1066 inv_obj = Session.query(cls)\
1071 inv_obj = Session.query(cls)\
1067 .filter(cls.cache_key == key).scalar()
1072 .filter(cls.cache_key == key).scalar()
1068 if inv_obj:
1073 if inv_obj:
1069 inv_obj.cache_active = False
1074 inv_obj.cache_active = False
1070 else:
1075 else:
1071 log.debug('cache key not found in invalidation db -> creating one')
1076 log.debug('cache key not found in invalidation db -> creating one')
1072 inv_obj = CacheInvalidation(key)
1077 inv_obj = CacheInvalidation(key)
1073
1078
1074 try:
1079 try:
1075 Session.add(inv_obj)
1080 Session.add(inv_obj)
1076 Session.commit()
1081 Session.commit()
1077 except Exception:
1082 except Exception:
1078 log.error(traceback.format_exc())
1083 log.error(traceback.format_exc())
1079 Session.rollback()
1084 Session.rollback()
1080
1085
1081 @classmethod
1086 @classmethod
1082 def set_valid(cls, key):
1087 def set_valid(cls, key):
1083 """
1088 """
1084 Mark this cache key as active and currently cached
1089 Mark this cache key as active and currently cached
1085
1090
1086 :param key:
1091 :param key:
1087 """
1092 """
1088 inv_obj = cls.get_by_key(key)
1093 inv_obj = cls.get_by_key(key)
1089 inv_obj.cache_active = True
1094 inv_obj.cache_active = True
1090 Session.add(inv_obj)
1095 Session.add(inv_obj)
1091 Session.commit()
1096 Session.commit()
1092
1097
1093
1098
1094 class ChangesetComment(Base, BaseModel):
1099 class ChangesetComment(Base, BaseModel):
1095 __tablename__ = 'changeset_comments'
1100 __tablename__ = 'changeset_comments'
1096 __table_args__ = ({'extend_existing': True},)
1101 __table_args__ = ({'extend_existing': True},)
1097 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1102 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1098 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1103 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1099 revision = Column('revision', String(40), nullable=False)
1104 revision = Column('revision', String(40), nullable=False)
1100 line_no = Column('line_no', Unicode(10), nullable=True)
1105 line_no = Column('line_no', Unicode(10), nullable=True)
1101 f_path = Column('f_path', Unicode(1000), nullable=True)
1106 f_path = Column('f_path', Unicode(1000), nullable=True)
1102 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1107 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1103 text = Column('text', Unicode(25000), nullable=False)
1108 text = Column('text', Unicode(25000), nullable=False)
1104 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1109 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1105
1110
1106 author = relationship('User', lazy='joined')
1111 author = relationship('User', lazy='joined')
1107 repo = relationship('Repository')
1112 repo = relationship('Repository')
1108
1113
1109 @classmethod
1114 @classmethod
1110 def get_users(cls, revision):
1115 def get_users(cls, revision):
1111 """
1116 """
1112 Returns user associated with this changesetComment. ie those
1117 Returns user associated with this changesetComment. ie those
1113 who actually commented
1118 who actually commented
1114
1119
1115 :param cls:
1120 :param cls:
1116 :param revision:
1121 :param revision:
1117 """
1122 """
1118 return Session.query(User)\
1123 return Session.query(User)\
1119 .filter(cls.revision == revision)\
1124 .filter(cls.revision == revision)\
1120 .join(ChangesetComment.author).all()
1125 .join(ChangesetComment.author).all()
1121
1126
1122
1127
1123 class Notification(Base, BaseModel):
1128 class Notification(Base, BaseModel):
1124 __tablename__ = 'notifications'
1129 __tablename__ = 'notifications'
1125 __table_args__ = ({'extend_existing': True},)
1130 __table_args__ = ({'extend_existing': True},)
1126
1131
1127 TYPE_CHANGESET_COMMENT = u'cs_comment'
1132 TYPE_CHANGESET_COMMENT = u'cs_comment'
1128 TYPE_MESSAGE = u'message'
1133 TYPE_MESSAGE = u'message'
1129 TYPE_MENTION = u'mention'
1134 TYPE_MENTION = u'mention'
1130 TYPE_REGISTRATION = u'registration'
1135 TYPE_REGISTRATION = u'registration'
1131
1136
1132 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1137 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1133 subject = Column('subject', Unicode(512), nullable=True)
1138 subject = Column('subject', Unicode(512), nullable=True)
1134 body = Column('body', Unicode(50000), nullable=True)
1139 body = Column('body', Unicode(50000), nullable=True)
1135 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1140 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1136 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1141 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1137 type_ = Column('type', Unicode(256))
1142 type_ = Column('type', Unicode(256))
1138
1143
1139 created_by_user = relationship('User')
1144 created_by_user = relationship('User')
1140 notifications_to_users = relationship('UserNotification', lazy='joined',
1145 notifications_to_users = relationship('UserNotification', lazy='joined',
1141 cascade="all, delete, delete-orphan")
1146 cascade="all, delete, delete-orphan")
1142
1147
1143 @property
1148 @property
1144 def recipients(self):
1149 def recipients(self):
1145 return [x.user for x in UserNotification.query()\
1150 return [x.user for x in UserNotification.query()\
1146 .filter(UserNotification.notification == self).all()]
1151 .filter(UserNotification.notification == self).all()]
1147
1152
1148 @classmethod
1153 @classmethod
1149 def create(cls, created_by, subject, body, recipients, type_=None):
1154 def create(cls, created_by, subject, body, recipients, type_=None):
1150 if type_ is None:
1155 if type_ is None:
1151 type_ = Notification.TYPE_MESSAGE
1156 type_ = Notification.TYPE_MESSAGE
1152
1157
1153 notification = cls()
1158 notification = cls()
1154 notification.created_by_user = created_by
1159 notification.created_by_user = created_by
1155 notification.subject = subject
1160 notification.subject = subject
1156 notification.body = body
1161 notification.body = body
1157 notification.type_ = type_
1162 notification.type_ = type_
1158 notification.created_on = datetime.datetime.now()
1163 notification.created_on = datetime.datetime.now()
1159
1164
1160 for u in recipients:
1165 for u in recipients:
1161 assoc = UserNotification()
1166 assoc = UserNotification()
1162 assoc.notification = notification
1167 assoc.notification = notification
1163 u.notifications.append(assoc)
1168 u.notifications.append(assoc)
1164 Session.add(notification)
1169 Session.add(notification)
1165 return notification
1170 return notification
1166
1171
1167 @property
1172 @property
1168 def description(self):
1173 def description(self):
1169 from rhodecode.model.notification import NotificationModel
1174 from rhodecode.model.notification import NotificationModel
1170 return NotificationModel().make_description(self)
1175 return NotificationModel().make_description(self)
1171
1176
1172
1177
1173 class UserNotification(Base, BaseModel):
1178 class UserNotification(Base, BaseModel):
1174 __tablename__ = 'user_to_notification'
1179 __tablename__ = 'user_to_notification'
1175 __table_args__ = (
1180 __table_args__ = (
1176 UniqueConstraint('user_id', 'notification_id'),
1181 UniqueConstraint('user_id', 'notification_id'),
1177 {'extend_existing': True}
1182 {'extend_existing': True}
1178 )
1183 )
1179 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1184 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1180 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1185 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1181 read = Column('read', Boolean, default=False)
1186 read = Column('read', Boolean, default=False)
1182 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1187 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1183
1188
1184 user = relationship('User', lazy="joined")
1189 user = relationship('User', lazy="joined")
1185 notification = relationship('Notification', lazy="joined",
1190 notification = relationship('Notification', lazy="joined",
1186 order_by=lambda: Notification.created_on.desc(),)
1191 order_by=lambda: Notification.created_on.desc(),)
1187
1192
1188 def mark_as_read(self):
1193 def mark_as_read(self):
1189 self.read = True
1194 self.read = True
1190 Session.add(self)
1195 Session.add(self)
1191
1196
1192
1197
1193 class DbMigrateVersion(Base, BaseModel):
1198 class DbMigrateVersion(Base, BaseModel):
1194 __tablename__ = 'db_migrate_version'
1199 __tablename__ = 'db_migrate_version'
1195 __table_args__ = {'extend_existing': True}
1200 __table_args__ = {'extend_existing': True}
1196 repository_id = Column('repository_id', String(250), primary_key=True)
1201 repository_id = Column('repository_id', String(250), primary_key=True)
1197 repository_path = Column('repository_path', Text)
1202 repository_path = Column('repository_path', Text)
1198 version = Column('version', Integer)
1203 version = Column('version', Integer)
@@ -1,338 +1,338 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.html"/>
2 <%inherit file="root.html"/>
3
3
4 <!-- HEADER -->
4 <!-- HEADER -->
5 <div id="header">
5 <div id="header">
6 <div id="header-inner" class="title hover">
6 <div id="header-inner" class="title hover">
7 <div id="logo">
7 <div id="logo">
8 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
8 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
9 </div>
9 </div>
10 <!-- MENU -->
10 <!-- MENU -->
11 ${self.page_nav()}
11 ${self.page_nav()}
12 <!-- END MENU -->
12 <!-- END MENU -->
13 ${self.body()}
13 ${self.body()}
14 </div>
14 </div>
15 </div>
15 </div>
16 <!-- END HEADER -->
16 <!-- END HEADER -->
17
17
18 <!-- CONTENT -->
18 <!-- CONTENT -->
19 <div id="content">
19 <div id="content">
20 <div class="flash_msg">
20 <div class="flash_msg">
21 <% messages = h.flash.pop_messages() %>
21 <% messages = h.flash.pop_messages() %>
22 % if messages:
22 % if messages:
23 <ul id="flash-messages">
23 <ul id="flash-messages">
24 % for message in messages:
24 % for message in messages:
25 <li class="${message.category}_msg">${message}</li>
25 <li class="${message.category}_msg">${message}</li>
26 % endfor
26 % endfor
27 </ul>
27 </ul>
28 % endif
28 % endif
29 </div>
29 </div>
30 <div id="main">
30 <div id="main">
31 ${next.main()}
31 ${next.main()}
32 </div>
32 </div>
33 </div>
33 </div>
34 <!-- END CONTENT -->
34 <!-- END CONTENT -->
35
35
36 <!-- FOOTER -->
36 <!-- FOOTER -->
37 <div id="footer">
37 <div id="footer">
38 <div id="footer-inner" class="title">
38 <div id="footer-inner" class="title">
39 <div>
39 <div>
40 <p class="footer-link">
40 <p class="footer-link">
41 <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a>
41 <a href="${h.url('bugtracker')}">${_('Submit a bug')}</a>
42 </p>
42 </p>
43 <p class="footer-link-right">
43 <p class="footer-link-right">
44 <a href="${h.url('rhodecode_official')}">RhodeCode</a>
44 <a href="${h.url('rhodecode_official')}">RhodeCode${'-%s' % c.rhodecode_instanceid if c.rhodecode_instanceid else ''}</a>
45 ${c.rhodecode_version} &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski
45 ${c.rhodecode_version} &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski
46 </p>
46 </p>
47 </div>
47 </div>
48 </div>
48 </div>
49 </div>
49 </div>
50 <!-- END FOOTER -->
50 <!-- END FOOTER -->
51
51
52 ### MAKO DEFS ###
52 ### MAKO DEFS ###
53 <%def name="page_nav()">
53 <%def name="page_nav()">
54 ${self.menu()}
54 ${self.menu()}
55 </%def>
55 </%def>
56
56
57 <%def name="breadcrumbs()">
57 <%def name="breadcrumbs()">
58 <div class="breadcrumbs">
58 <div class="breadcrumbs">
59 ${self.breadcrumbs_links()}
59 ${self.breadcrumbs_links()}
60 </div>
60 </div>
61 </%def>
61 </%def>
62
62
63 <%def name="usermenu()">
63 <%def name="usermenu()">
64 <div class="user-menu">
64 <div class="user-menu">
65 <div class="container">
65 <div class="container">
66 <div class="gravatar" id="quick_login_link">
66 <div class="gravatar" id="quick_login_link">
67 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,24)}" />
67 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,24)}" />
68 </div>
68 </div>
69 %if c.rhodecode_user.username != 'default' and c.unread_notifications != 0:
69 %if c.rhodecode_user.username != 'default' and c.unread_notifications != 0:
70 <div class="notifications">
70 <div class="notifications">
71 <a id="notification_counter" href="${h.url('notifications')}">${c.unread_notifications}</a>
71 <a id="notification_counter" href="${h.url('notifications')}">${c.unread_notifications}</a>
72 </div>
72 </div>
73 %endif
73 %endif
74 </div>
74 </div>
75 <div id="quick_login" style="display:none">
75 <div id="quick_login" style="display:none">
76 %if c.rhodecode_user.username == 'default':
76 %if c.rhodecode_user.username == 'default':
77 <h4>${_('Login to your account')}</h4>
77 <h4>${_('Login to your account')}</h4>
78 ${h.form(h.url('login_home',came_from=h.url.current()))}
78 ${h.form(h.url('login_home',came_from=h.url.current()))}
79 <div class="form">
79 <div class="form">
80 <div class="fields">
80 <div class="fields">
81 <div class="field">
81 <div class="field">
82 <div class="label">
82 <div class="label">
83 <label for="username">${_('Username')}:</label>
83 <label for="username">${_('Username')}:</label>
84 </div>
84 </div>
85 <div class="input">
85 <div class="input">
86 ${h.text('username',class_='focus',size=40)}
86 ${h.text('username',class_='focus',size=40)}
87 </div>
87 </div>
88
88
89 </div>
89 </div>
90 <div class="field">
90 <div class="field">
91 <div class="label">
91 <div class="label">
92 <label for="password">${_('Password')}:</label>
92 <label for="password">${_('Password')}:</label>
93 </div>
93 </div>
94 <div class="input">
94 <div class="input">
95 ${h.password('password',class_='focus',size=40)}
95 ${h.password('password',class_='focus',size=40)}
96 </div>
96 </div>
97
97
98 </div>
98 </div>
99 <div class="buttons">
99 <div class="buttons">
100 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>
100 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>
101 <div class="register">
101 <div class="register">
102 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
102 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
103 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
103 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
104 %endif
104 %endif
105 </div>
105 </div>
106 <div class="submit">
106 <div class="submit">
107 ${h.submit('sign_in',_('Log In'),class_="ui-btn xsmall")}
107 ${h.submit('sign_in',_('Log In'),class_="ui-btn xsmall")}
108 </div>
108 </div>
109 </div>
109 </div>
110 </div>
110 </div>
111 </div>
111 </div>
112 ${h.end_form()}
112 ${h.end_form()}
113 %else:
113 %else:
114 <div class="links_left">
114 <div class="links_left">
115 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
115 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
116 <div class="email">${c.rhodecode_user.email}</div>
116 <div class="email">${c.rhodecode_user.email}</div>
117 <div class="big_gravatar"><img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,48)}" /></div>
117 <div class="big_gravatar"><img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,48)}" /></div>
118 <div class="inbox"><a href="${h.url('notifications')}">${_('Inbox')}: ${c.unread_notifications}</a></div>
118 <div class="inbox"><a href="${h.url('notifications')}">${_('Inbox')}: ${c.unread_notifications}</a></div>
119 </div>
119 </div>
120 <div class="links_right">
120 <div class="links_right">
121 <ol class="links">
121 <ol class="links">
122 <li>${h.link_to(_(u'Home'),h.url('home'))}</li>
122 <li>${h.link_to(_(u'Home'),h.url('home'))}</li>
123 <li>${h.link_to(_(u'Journal'),h.url('journal'))}</li>
123 <li>${h.link_to(_(u'Journal'),h.url('journal'))}</li>
124 <li>${h.link_to(_(u'My account'),h.url('admin_settings_my_account'))}</li>
124 <li>${h.link_to(_(u'My account'),h.url('admin_settings_my_account'))}</li>
125 <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li>
125 <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li>
126 </ol>
126 </ol>
127 </div>
127 </div>
128 %endif
128 %endif
129 </div>
129 </div>
130 </div>
130 </div>
131 </%def>
131 </%def>
132
132
133 <%def name="menu(current=None)">
133 <%def name="menu(current=None)">
134 <%
134 <%
135 def is_current(selected):
135 def is_current(selected):
136 if selected == current:
136 if selected == current:
137 return h.literal('class="current"')
137 return h.literal('class="current"')
138 %>
138 %>
139 %if current not in ['home','admin']:
139 %if current not in ['home','admin']:
140 ##REGULAR MENU
140 ##REGULAR MENU
141 <ul id="quick">
141 <ul id="quick">
142 <!-- repo switcher -->
142 <!-- repo switcher -->
143 <li>
143 <li>
144 <a class="menu_link" id="repo_switcher" title="${_('Switch repository')}" href="#">
144 <a class="menu_link" id="repo_switcher" title="${_('Switch repository')}" href="#">
145 <span class="icon">
145 <span class="icon">
146 <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" />
146 <img src="${h.url('/images/icons/database.png')}" alt="${_('Products')}" />
147 </span>
147 </span>
148 <span>&darr;</span>
148 <span>&darr;</span>
149 </a>
149 </a>
150 <ul id="repo_switcher_list" class="repo_switcher">
150 <ul id="repo_switcher_list" class="repo_switcher">
151 <li>
151 <li>
152 <a href="#">${_('loading...')}</a>
152 <a href="#">${_('loading...')}</a>
153 </li>
153 </li>
154 </ul>
154 </ul>
155 </li>
155 </li>
156
156
157 <li ${is_current('summary')}>
157 <li ${is_current('summary')}>
158 <a class="menu_link" title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
158 <a class="menu_link" title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
159 <span class="icon">
159 <span class="icon">
160 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
160 <img src="${h.url('/images/icons/clipboard_16.png')}" alt="${_('Summary')}" />
161 </span>
161 </span>
162 <span>${_('Summary')}</span>
162 <span>${_('Summary')}</span>
163 </a>
163 </a>
164 </li>
164 </li>
165 <li ${is_current('changelog')}>
165 <li ${is_current('changelog')}>
166 <a class="menu_link" title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
166 <a class="menu_link" title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
167 <span class="icon">
167 <span class="icon">
168 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
168 <img src="${h.url('/images/icons/time.png')}" alt="${_('Changelog')}" />
169 </span>
169 </span>
170 <span>${_('Changelog')}</span>
170 <span>${_('Changelog')}</span>
171 </a>
171 </a>
172 </li>
172 </li>
173
173
174 <li ${is_current('switch_to')}>
174 <li ${is_current('switch_to')}>
175 <a class="menu_link" id="branch_tag_switcher" title="${_('Switch to')}" href="#">
175 <a class="menu_link" id="branch_tag_switcher" title="${_('Switch to')}" href="#">
176 <span class="icon">
176 <span class="icon">
177 <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" />
177 <img src="${h.url('/images/icons/arrow_switch.png')}" alt="${_('Switch to')}" />
178 </span>
178 </span>
179 <span>${_('Switch to')}</span>
179 <span>${_('Switch to')}</span>
180 </a>
180 </a>
181 <ul id="switch_to_list" class="switch_to">
181 <ul id="switch_to_list" class="switch_to">
182 <li><a href="#">${_('loading...')}</a></li>
182 <li><a href="#">${_('loading...')}</a></li>
183 </ul>
183 </ul>
184 </li>
184 </li>
185 <li ${is_current('files')}>
185 <li ${is_current('files')}>
186 <a class="menu_link" title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
186 <a class="menu_link" title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
187 <span class="icon">
187 <span class="icon">
188 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
188 <img src="${h.url('/images/icons/file.png')}" alt="${_('Files')}" />
189 </span>
189 </span>
190 <span>${_('Files')}</span>
190 <span>${_('Files')}</span>
191 </a>
191 </a>
192 </li>
192 </li>
193
193
194 <li ${is_current('options')}>
194 <li ${is_current('options')}>
195 <a class="menu_link" title="${_('Options')}" href="#">
195 <a class="menu_link" title="${_('Options')}" href="#">
196 <span class="icon">
196 <span class="icon">
197 <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" />
197 <img src="${h.url('/images/icons/table_gear.png')}" alt="${_('Admin')}" />
198 </span>
198 </span>
199 <span>${_('Options')}</span>
199 <span>${_('Options')}</span>
200 </a>
200 </a>
201 <ul>
201 <ul>
202 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
202 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
203 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
203 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
204 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
204 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
205 %else:
205 %else:
206 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
206 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
207 %endif
207 %endif
208 %endif
208 %endif
209 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
209 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
210 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
210 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
211
211
212 % if h.HasPermissionAll('hg.admin')('access admin main page'):
212 % if h.HasPermissionAll('hg.admin')('access admin main page'):
213 <li>
213 <li>
214 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
214 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
215 <%def name="admin_menu()">
215 <%def name="admin_menu()">
216 <ul>
216 <ul>
217 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
217 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
218 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
218 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
219 <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li>
219 <li>${h.link_to(_('repositories groups'),h.url('repos_groups'),class_='repos_groups')}</li>
220 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
220 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
221 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
221 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
222 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
222 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
223 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
223 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
224 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
224 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
225 </ul>
225 </ul>
226 </%def>
226 </%def>
227
227
228 ${admin_menu()}
228 ${admin_menu()}
229 </li>
229 </li>
230 % endif
230 % endif
231 </ul>
231 </ul>
232 </li>
232 </li>
233
233
234 <li>
234 <li>
235 <a class="menu_link" title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
235 <a class="menu_link" title="${_('Followers')}" href="${h.url('repo_followers_home',repo_name=c.repo_name)}">
236 <span class="icon_short">
236 <span class="icon_short">
237 <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" />
237 <img src="${h.url('/images/icons/heart.png')}" alt="${_('Followers')}" />
238 </span>
238 </span>
239 <span id="current_followers_count" class="short">${c.repository_followers}</span>
239 <span id="current_followers_count" class="short">${c.repository_followers}</span>
240 </a>
240 </a>
241 </li>
241 </li>
242 <li>
242 <li>
243 <a class="menu_link" title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}">
243 <a class="menu_link" title="${_('Forks')}" href="${h.url('repo_forks_home',repo_name=c.repo_name)}">
244 <span class="icon_short">
244 <span class="icon_short">
245 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" />
245 <img src="${h.url('/images/icons/arrow_divide.png')}" alt="${_('Forks')}" />
246 </span>
246 </span>
247 <span class="short">${c.repository_forks}</span>
247 <span class="short">${c.repository_forks}</span>
248 </a>
248 </a>
249 </li>
249 </li>
250 ${usermenu()}
250 ${usermenu()}
251 </ul>
251 </ul>
252 <script type="text/javascript">
252 <script type="text/javascript">
253 YUE.on('repo_switcher','mouseover',function(){
253 YUE.on('repo_switcher','mouseover',function(){
254 function qfilter(){
254 function qfilter(){
255 var nodes = YUQ('ul#repo_switcher_list li a.repo_name');
255 var nodes = YUQ('ul#repo_switcher_list li a.repo_name');
256 var target = 'q_filter_rs';
256 var target = 'q_filter_rs';
257 var func = function(node){
257 var func = function(node){
258 return node.parentNode;
258 return node.parentNode;
259 }
259 }
260 q_filter(target,nodes,func);
260 q_filter(target,nodes,func);
261 }
261 }
262 var loaded = YUD.hasClass('repo_switcher','loaded');
262 var loaded = YUD.hasClass('repo_switcher','loaded');
263 if(!loaded){
263 if(!loaded){
264 YUD.addClass('repo_switcher','loaded');
264 YUD.addClass('repo_switcher','loaded');
265 ypjax("${h.url('repo_switcher')}",'repo_switcher_list',
265 ypjax("${h.url('repo_switcher')}",'repo_switcher_list',
266 function(o){qfilter();},
266 function(o){qfilter();},
267 function(o){YUD.removeClass('repo_switcher','loaded');}
267 function(o){YUD.removeClass('repo_switcher','loaded');}
268 ,null);
268 ,null);
269 }
269 }
270 return false;
270 return false;
271 });
271 });
272
272
273 YUE.on('branch_tag_switcher','mouseover',function(){
273 YUE.on('branch_tag_switcher','mouseover',function(){
274 var loaded = YUD.hasClass('branch_tag_switcher','loaded');
274 var loaded = YUD.hasClass('branch_tag_switcher','loaded');
275 if(!loaded){
275 if(!loaded){
276 YUD.addClass('branch_tag_switcher','loaded');
276 YUD.addClass('branch_tag_switcher','loaded');
277 ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list',
277 ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list',
278 function(o){},
278 function(o){},
279 function(o){YUD.removeClass('branch_tag_switcher','loaded');}
279 function(o){YUD.removeClass('branch_tag_switcher','loaded');}
280 ,null);
280 ,null);
281 }
281 }
282 return false;
282 return false;
283 });
283 });
284 </script>
284 </script>
285 %else:
285 %else:
286 ##ROOT MENU
286 ##ROOT MENU
287 <ul id="quick">
287 <ul id="quick">
288 <li>
288 <li>
289 <a class="menu_link" title="${_('Home')}" href="${h.url('home')}">
289 <a class="menu_link" title="${_('Home')}" href="${h.url('home')}">
290 <span class="icon">
290 <span class="icon">
291 <img src="${h.url('/images/icons/home_16.png')}" alt="${_('Home')}" />
291 <img src="${h.url('/images/icons/home_16.png')}" alt="${_('Home')}" />
292 </span>
292 </span>
293 <span>${_('Home')}</span>
293 <span>${_('Home')}</span>
294 </a>
294 </a>
295 </li>
295 </li>
296 %if c.rhodecode_user.username != 'default':
296 %if c.rhodecode_user.username != 'default':
297 <li>
297 <li>
298 <a class="menu_link" title="${_('Journal')}" href="${h.url('journal')}">
298 <a class="menu_link" title="${_('Journal')}" href="${h.url('journal')}">
299 <span class="icon">
299 <span class="icon">
300 <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" />
300 <img src="${h.url('/images/icons/book.png')}" alt="${_('Journal')}" />
301 </span>
301 </span>
302 <span>${_('Journal')}</span>
302 <span>${_('Journal')}</span>
303 </a>
303 </a>
304 </li>
304 </li>
305 %else:
305 %else:
306 <li>
306 <li>
307 <a class="menu_link" title="${_('Public journal')}" href="${h.url('public_journal')}">
307 <a class="menu_link" title="${_('Public journal')}" href="${h.url('public_journal')}">
308 <span class="icon">
308 <span class="icon">
309 <img src="${h.url('/images/icons/book.png')}" alt="${_('Public journal')}" />
309 <img src="${h.url('/images/icons/book.png')}" alt="${_('Public journal')}" />
310 </span>
310 </span>
311 <span>${_('Public journal')}</span>
311 <span>${_('Public journal')}</span>
312 </a>
312 </a>
313 </li>
313 </li>
314 %endif
314 %endif
315 <li>
315 <li>
316 <a class="menu_link" title="${_('Search')}" href="${h.url('search')}">
316 <a class="menu_link" title="${_('Search')}" href="${h.url('search')}">
317 <span class="icon">
317 <span class="icon">
318 <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" />
318 <img src="${h.url('/images/icons/search_16.png')}" alt="${_('Search')}" />
319 </span>
319 </span>
320 <span>${_('Search')}</span>
320 <span>${_('Search')}</span>
321 </a>
321 </a>
322 </li>
322 </li>
323
323
324 %if h.HasPermissionAll('hg.admin')('access admin main page'):
324 %if h.HasPermissionAll('hg.admin')('access admin main page'):
325 <li ${is_current('admin')}>
325 <li ${is_current('admin')}>
326 <a class="menu_link" title="${_('Admin')}" href="${h.url('admin_home')}">
326 <a class="menu_link" title="${_('Admin')}" href="${h.url('admin_home')}">
327 <span class="icon">
327 <span class="icon">
328 <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" />
328 <img src="${h.url('/images/icons/cog_edit.png')}" alt="${_('Admin')}" />
329 </span>
329 </span>
330 <span>${_('Admin')}</span>
330 <span>${_('Admin')}</span>
331 </a>
331 </a>
332 ${admin_menu()}
332 ${admin_menu()}
333 </li>
333 </li>
334 %endif
334 %endif
335 ${usermenu()}
335 ${usermenu()}
336 </ul>
336 </ul>
337 %endif
337 %endif
338 </%def>
338 </%def>
@@ -1,698 +1,698 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Summary')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <%
20 <%
21 summary = lambda n:{False:'summary-short'}.get(n)
21 summary = lambda n:{False:'summary-short'}.get(n)
22 %>
22 %>
23 %if c.show_stats:
23 %if c.show_stats:
24 <div class="box box-left">
24 <div class="box box-left">
25 %else:
25 %else:
26 <div class="box">
26 <div class="box">
27 %endif
27 %endif
28 <!-- box / title -->
28 <!-- box / title -->
29 <div class="title">
29 <div class="title">
30 ${self.breadcrumbs()}
30 ${self.breadcrumbs()}
31 </div>
31 </div>
32 <!-- end box / title -->
32 <!-- end box / title -->
33 <div class="form">
33 <div class="form">
34 <div id="summary" class="fields">
34 <div id="summary" class="fields">
35
35
36 <div class="field">
36 <div class="field">
37 <div class="label-summary">
37 <div class="label-summary">
38 <label>${_('Name')}:</label>
38 <label>${_('Name')}:</label>
39 </div>
39 </div>
40 <div class="input ${summary(c.show_stats)}">
40 <div class="input ${summary(c.show_stats)}">
41 <div style="float:right;padding:5px 0px 0px 5px">
41 <div style="float:right;padding:5px 0px 0px 5px">
42 %if c.rhodecode_user.username != 'default':
42 %if c.rhodecode_user.username != 'default':
43 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
43 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
44 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
44 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
45 %else:
45 %else:
46 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
46 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
47 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
47 ${h.link_to(_('ATOM'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
48 %endif
48 %endif
49 </div>
49 </div>
50 %if c.rhodecode_user.username != 'default':
50 %if c.rhodecode_user.username != 'default':
51 %if c.following:
51 %if c.following:
52 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
52 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
53 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
53 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
54 </span>
54 </span>
55 %else:
55 %else:
56 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
56 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
57 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
57 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
58 </span>
58 </span>
59 %endif
59 %endif
60 %endif:
60 %endif:
61 ##REPO TYPE
61 ##REPO TYPE
62 %if h.is_hg(c.dbrepo):
62 %if h.is_hg(c.dbrepo):
63 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
63 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url('/images/icons/hgicon.png')}"/>
64 %endif
64 %endif
65 %if h.is_git(c.dbrepo):
65 %if h.is_git(c.dbrepo):
66 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
66 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url('/images/icons/giticon.png')}"/>
67 %endif
67 %endif
68
68
69 ##PUBLIC/PRIVATE
69 ##PUBLIC/PRIVATE
70 %if c.dbrepo.private:
70 %if c.dbrepo.private:
71 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
71 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url('/images/icons/lock.png')}"/>
72 %else:
72 %else:
73 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
73 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url('/images/icons/lock_open.png')}"/>
74 %endif
74 %endif
75
75
76 ##REPO NAME
76 ##REPO NAME
77 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
77 <span class="repo_name" title="${_('Non changable ID %s') % c.dbrepo.repo_id}">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
78
78
79 ##FORK
79 ##FORK
80 %if c.dbrepo.fork:
80 %if c.dbrepo.fork:
81 <div style="margin-top:5px;clear:both"">
81 <div style="margin-top:5px;clear:both"">
82 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
82 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}"><img class="icon" alt="${_('public')}" title="${_('Fork of')} ${c.dbrepo.fork.repo_name}" src="${h.url('/images/icons/arrow_divide.png')}"/>
83 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
83 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
84 </a>
84 </a>
85 </div>
85 </div>
86 %endif
86 %endif
87 ##REMOTE
87 ##REMOTE
88 %if c.dbrepo.clone_uri:
88 %if c.dbrepo.clone_uri:
89 <div style="margin-top:5px;clear:both">
89 <div style="margin-top:5px;clear:both">
90 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
90 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}"><img class="icon" alt="${_('remote clone')}" title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}" src="${h.url('/images/icons/connect.png')}"/>
91 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
91 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
92 </a>
92 </a>
93 </div>
93 </div>
94 %endif
94 %endif
95 </div>
95 </div>
96 </div>
96 </div>
97
97
98 <div class="field">
98 <div class="field">
99 <div class="label-summary">
99 <div class="label-summary">
100 <label>${_('Description')}:</label>
100 <label>${_('Description')}:</label>
101 </div>
101 </div>
102 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
102 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(c.dbrepo.description)}</div>
103 </div>
103 </div>
104
104
105 <div class="field">
105 <div class="field">
106 <div class="label-summary">
106 <div class="label-summary">
107 <label>${_('Contact')}:</label>
107 <label>${_('Contact')}:</label>
108 </div>
108 </div>
109 <div class="input ${summary(c.show_stats)}">
109 <div class="input ${summary(c.show_stats)}">
110 <div class="gravatar">
110 <div class="gravatar">
111 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
111 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
112 </div>
112 </div>
113 ${_('Username')}: ${c.dbrepo.user.username}<br/>
113 ${_('Username')}: ${c.dbrepo.user.username}<br/>
114 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
114 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
115 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
115 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
116 </div>
116 </div>
117 </div>
117 </div>
118
118
119 <div class="field">
119 <div class="field">
120 <div class="label-summary">
120 <div class="label-summary">
121 <label>${_('Clone url')}:</label>
121 <label>${_('Clone url')}:</label>
122 </div>
122 </div>
123 <div class="input ${summary(c.show_stats)}">
123 <div class="input ${summary(c.show_stats)}">
124 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
124 <div style="display:none" id="clone_by_name" class="ui-btn clone">${_('Show by Name')}</div>
125 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
125 <div id="clone_by_id" class="ui-btn clone">${_('Show by ID')}</div>
126 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
126 <input style="width:80%;margin-left:105px" type="text" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
127 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
127 <input style="display:none;width:80%;margin-left:105px" type="text" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}"/>
128 </div>
128 </div>
129 </div>
129 </div>
130
130
131 <div class="field">
131 <div class="field">
132 <div class="label-summary">
132 <div class="label-summary">
133 <label>${_('Trending files')}:</label>
133 <label>${_('Trending files')}:</label>
134 </div>
134 </div>
135 <div class="input ${summary(c.show_stats)}">
135 <div class="input ${summary(c.show_stats)}">
136 %if c.show_stats:
136 %if c.show_stats:
137 <div id="lang_stats"></div>
137 <div id="lang_stats"></div>
138 %else:
138 %else:
139 ${_('Statistics are disabled for this repository')}
139 ${_('Statistics are disabled for this repository')}
140 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
140 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
141 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
141 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
142 %endif
142 %endif
143 %endif
143 %endif
144 </div>
144 </div>
145 </div>
145 </div>
146
146
147 <div class="field">
147 <div class="field">
148 <div class="label-summary">
148 <div class="label-summary">
149 <label>${_('Download')}:</label>
149 <label>${_('Download')}:</label>
150 </div>
150 </div>
151 <div class="input ${summary(c.show_stats)}">
151 <div class="input ${summary(c.show_stats)}">
152 %if len(c.rhodecode_repo.revisions) == 0:
152 %if len(c.rhodecode_repo.revisions) == 0:
153 ${_('There are no downloads yet')}
153 ${_('There are no downloads yet')}
154 %elif c.enable_downloads is False:
154 %elif c.enable_downloads is False:
155 ${_('Downloads are disabled for this repository')}
155 ${_('Downloads are disabled for this repository')}
156 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
156 %if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
157 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
157 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
158 %endif
158 %endif
159 %else:
159 %else:
160 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
160 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
161 <span id="${'zip_link'}">${h.link_to('Download as zip',h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
161 <span id="${'zip_link'}">${h.link_to('Download as zip',h.url('files_archive_home',repo_name=c.dbrepo.repo_name,fname='tip.zip'),class_="archive_icon ui-btn")}</span>
162 <span style="vertical-align: bottom">
162 <span style="vertical-align: bottom">
163 <input id="archive_subrepos" type="checkbox" name="subrepos" />
163 <input id="archive_subrepos" type="checkbox" name="subrepos" />
164 <label for="archive_subrepos" class="tooltip" title="${_('Check this to download archive with subrepos')}" >${_('with subrepos')}</label>
164 <label for="archive_subrepos" class="tooltip" title="${_('Check this to download archive with subrepos')}" >${_('with subrepos')}</label>
165 </span>
165 </span>
166 %endif
166 %endif
167 </div>
167 </div>
168 </div>
168 </div>
169 </div>
169 </div>
170 </div>
170 </div>
171 </div>
171 </div>
172
172
173 %if c.show_stats:
173 %if c.show_stats:
174 <div class="box box-right" style="min-height:455px">
174 <div class="box box-right" style="min-height:455px">
175 <!-- box / title -->
175 <!-- box / title -->
176 <div class="title">
176 <div class="title">
177 <h5>${_('Commit activity by day / author')}</h5>
177 <h5>${_('Commit activity by day / author')}</h5>
178 </div>
178 </div>
179
179
180 <div class="graph">
180 <div class="graph">
181 <div style="padding:0 10px 10px 17px;">
181 <div style="padding:0 10px 10px 17px;">
182 %if c.no_data:
182 %if c.no_data:
183 ${c.no_data_msg}
183 ${c.no_data_msg}
184 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
184 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
185 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
185 ${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name),class_="ui-btn")}
186 %endif
186 %endif
187 %else:
187 %else:
188 ${_('Stats gathered: ')} ${c.stats_percentage}%
188 ${_('Stats gathered: ')} ${c.stats_percentage}%
189 %endif
189 %endif
190 </div>
190 </div>
191 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
191 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
192 <div style="clear: both;height: 10px"></div>
192 <div style="clear: both;height: 10px"></div>
193 <div id="overview" style="width:450px;height:100px;float:left"></div>
193 <div id="overview" style="width:450px;height:100px;float:left"></div>
194
194
195 <div id="legend_data" style="clear:both;margin-top:10px;">
195 <div id="legend_data" style="clear:both;margin-top:10px;">
196 <div id="legend_container"></div>
196 <div id="legend_container"></div>
197 <div id="legend_choices">
197 <div id="legend_choices">
198 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
198 <table id="legend_choices_tables" class="noborder" style="font-size:smaller;color:#545454"></table>
199 </div>
199 </div>
200 </div>
200 </div>
201 </div>
201 </div>
202 </div>
202 </div>
203 %endif
203 %endif
204
204
205 <div class="box">
205 <div class="box">
206 <div class="title">
206 <div class="title">
207 <div class="breadcrumbs">
207 <div class="breadcrumbs">
208 %if c.repo_changesets:
208 %if c.repo_changesets:
209 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
209 ${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
210 %else:
210 %else:
211 ${_('Quick start')}
211 ${_('Quick start')}
212 %endif
212 %endif
213 </div>
213 </div>
214 </div>
214 </div>
215 <div class="table">
215 <div class="table">
216 <div id="shortlog_data">
216 <div id="shortlog_data">
217 <%include file='../shortlog/shortlog_data.html'/>
217 <%include file='../shortlog/shortlog_data.html'/>
218 </div>
218 </div>
219 </div>
219 </div>
220 </div>
220 </div>
221
221
222 %if c.readme_data:
222 %if c.readme_data:
223 <div class="box" style="background-color: #FAFAFA">
223 <div class="box" style="background-color: #FAFAFA">
224 <div class="title">
224 <div id="readme" class="title">
225 <div class="breadcrumbs"><a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a></div>
225 <div class="breadcrumbs"><a href="${h.url('files_home',repo_name=c.repo_name,revision='tip',f_path=c.readme_file)}">${c.readme_file}</a></div>
226 </div>
226 </div>
227 <div class="readme">
227 <div class="readme">
228 <div class="readme_box">
228 <div class="readme_box">
229 ${c.readme_data|n}
229 ${c.readme_data|n}
230 </div>
230 </div>
231 </div>
231 </div>
232 </div>
232 </div>
233 %endif
233 %endif
234
234
235 <script type="text/javascript">
235 <script type="text/javascript">
236 var clone_url = 'clone_url';
236 var clone_url = 'clone_url';
237 YUE.on(clone_url,'click',function(e){
237 YUE.on(clone_url,'click',function(e){
238 if(YUD.hasClass(clone_url,'selected')){
238 if(YUD.hasClass(clone_url,'selected')){
239 return
239 return
240 }
240 }
241 else{
241 else{
242 YUD.addClass(clone_url,'selected');
242 YUD.addClass(clone_url,'selected');
243 YUD.get(clone_url).select();
243 YUD.get(clone_url).select();
244 }
244 }
245 })
245 })
246
246
247 YUE.on('clone_by_name','click',function(e){
247 YUE.on('clone_by_name','click',function(e){
248 // show url by name and hide name button
248 // show url by name and hide name button
249 YUD.setStyle('clone_url','display','');
249 YUD.setStyle('clone_url','display','');
250 YUD.setStyle('clone_by_name','display','none');
250 YUD.setStyle('clone_by_name','display','none');
251
251
252 // hide url by id and show name button
252 // hide url by id and show name button
253 YUD.setStyle('clone_by_id','display','');
253 YUD.setStyle('clone_by_id','display','');
254 YUD.setStyle('clone_url_id','display','none');
254 YUD.setStyle('clone_url_id','display','none');
255
255
256 })
256 })
257 YUE.on('clone_by_id','click',function(e){
257 YUE.on('clone_by_id','click',function(e){
258
258
259 // show url by id and hide id button
259 // show url by id and hide id button
260 YUD.setStyle('clone_by_id','display','none');
260 YUD.setStyle('clone_by_id','display','none');
261 YUD.setStyle('clone_url_id','display','');
261 YUD.setStyle('clone_url_id','display','');
262
262
263 // hide url by name and show id button
263 // hide url by name and show id button
264 YUD.setStyle('clone_by_name','display','');
264 YUD.setStyle('clone_by_name','display','');
265 YUD.setStyle('clone_url','display','none');
265 YUD.setStyle('clone_url','display','none');
266 })
266 })
267
267
268
268
269 var tmpl_links = {};
269 var tmpl_links = {};
270 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
270 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
271 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
271 tmpl_links["${archive['type']}"] = '${h.link_to('__NAME__', h.url('files_archive_home',repo_name=c.dbrepo.repo_name, fname='__CS__'+archive['extension'],subrepos='__SUB__'),class_='archive_icon ui-btn')}';
272 %endfor
272 %endfor
273
273
274 YUE.on(['download_options','archive_subrepos'],'change',function(e){
274 YUE.on(['download_options','archive_subrepos'],'change',function(e){
275 var sm = YUD.get('download_options');
275 var sm = YUD.get('download_options');
276 var new_cs = sm.options[sm.selectedIndex];
276 var new_cs = sm.options[sm.selectedIndex];
277
277
278 for(k in tmpl_links){
278 for(k in tmpl_links){
279 var s = YUD.get(k+'_link');
279 var s = YUD.get(k+'_link');
280 if(s){
280 if(s){
281 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
281 var title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
282 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
282 title_tmpl= title_tmpl.replace('__CS_NAME__',new_cs.text);
283 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
283 title_tmpl = title_tmpl.replace('__CS_EXT__',k);
284
284
285 var url = tmpl_links[k].replace('__CS__',new_cs.value);
285 var url = tmpl_links[k].replace('__CS__',new_cs.value);
286 var subrepos = YUD.get('archive_subrepos').checked;
286 var subrepos = YUD.get('archive_subrepos').checked;
287 url = url.replace('__SUB__',subrepos);
287 url = url.replace('__SUB__',subrepos);
288 url = url.replace('__NAME__',title_tmpl);
288 url = url.replace('__NAME__',title_tmpl);
289 s.innerHTML = url
289 s.innerHTML = url
290 }
290 }
291 }
291 }
292 });
292 });
293 </script>
293 </script>
294 %if c.show_stats:
294 %if c.show_stats:
295 <script type="text/javascript">
295 <script type="text/javascript">
296 var data = ${c.trending_languages|n};
296 var data = ${c.trending_languages|n};
297 var total = 0;
297 var total = 0;
298 var no_data = true;
298 var no_data = true;
299 var tbl = document.createElement('table');
299 var tbl = document.createElement('table');
300 tbl.setAttribute('class','trending_language_tbl');
300 tbl.setAttribute('class','trending_language_tbl');
301 var cnt = 0;
301 var cnt = 0;
302 for (var i=0;i<data.length;i++){
302 for (var i=0;i<data.length;i++){
303 total+= data[i][1].count;
303 total+= data[i][1].count;
304 }
304 }
305 for (var i=0;i<data.length;i++){
305 for (var i=0;i<data.length;i++){
306 cnt += 1;
306 cnt += 1;
307 no_data = false;
307 no_data = false;
308
308
309 var hide = cnt>2;
309 var hide = cnt>2;
310 var tr = document.createElement('tr');
310 var tr = document.createElement('tr');
311 if (hide){
311 if (hide){
312 tr.setAttribute('style','display:none');
312 tr.setAttribute('style','display:none');
313 tr.setAttribute('class','stats_hidden');
313 tr.setAttribute('class','stats_hidden');
314 }
314 }
315 var k = data[i][0];
315 var k = data[i][0];
316 var obj = data[i][1];
316 var obj = data[i][1];
317 var percentage = Math.round((obj.count/total*100),2);
317 var percentage = Math.round((obj.count/total*100),2);
318
318
319 var td1 = document.createElement('td');
319 var td1 = document.createElement('td');
320 td1.width = 150;
320 td1.width = 150;
321 var trending_language_label = document.createElement('div');
321 var trending_language_label = document.createElement('div');
322 trending_language_label.innerHTML = obj.desc+" ("+k+")";
322 trending_language_label.innerHTML = obj.desc+" ("+k+")";
323 td1.appendChild(trending_language_label);
323 td1.appendChild(trending_language_label);
324
324
325 var td2 = document.createElement('td');
325 var td2 = document.createElement('td');
326 td2.setAttribute('style','padding-right:14px !important');
326 td2.setAttribute('style','padding-right:14px !important');
327 var trending_language = document.createElement('div');
327 var trending_language = document.createElement('div');
328 var nr_files = obj.count+" ${_('files')}";
328 var nr_files = obj.count+" ${_('files')}";
329
329
330 trending_language.title = k+" "+nr_files;
330 trending_language.title = k+" "+nr_files;
331
331
332 if (percentage>22){
332 if (percentage>22){
333 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
333 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
334 }
334 }
335 else{
335 else{
336 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
336 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
337 }
337 }
338
338
339 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
339 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
340 trending_language.style.width=percentage+"%";
340 trending_language.style.width=percentage+"%";
341 td2.appendChild(trending_language);
341 td2.appendChild(trending_language);
342
342
343 tr.appendChild(td1);
343 tr.appendChild(td1);
344 tr.appendChild(td2);
344 tr.appendChild(td2);
345 tbl.appendChild(tr);
345 tbl.appendChild(tr);
346 if(cnt == 3){
346 if(cnt == 3){
347 var show_more = document.createElement('tr');
347 var show_more = document.createElement('tr');
348 var td = document.createElement('td');
348 var td = document.createElement('td');
349 lnk = document.createElement('a');
349 lnk = document.createElement('a');
350
350
351 lnk.href='#';
351 lnk.href='#';
352 lnk.innerHTML = "${_('show more')}";
352 lnk.innerHTML = "${_('show more')}";
353 lnk.id='code_stats_show_more';
353 lnk.id='code_stats_show_more';
354 td.appendChild(lnk);
354 td.appendChild(lnk);
355
355
356 show_more.appendChild(td);
356 show_more.appendChild(td);
357 show_more.appendChild(document.createElement('td'));
357 show_more.appendChild(document.createElement('td'));
358 tbl.appendChild(show_more);
358 tbl.appendChild(show_more);
359 }
359 }
360
360
361 }
361 }
362
362
363 YUD.get('lang_stats').appendChild(tbl);
363 YUD.get('lang_stats').appendChild(tbl);
364 YUE.on('code_stats_show_more','click',function(){
364 YUE.on('code_stats_show_more','click',function(){
365 l = YUD.getElementsByClassName('stats_hidden')
365 l = YUD.getElementsByClassName('stats_hidden')
366 for (e in l){
366 for (e in l){
367 YUD.setStyle(l[e],'display','');
367 YUD.setStyle(l[e],'display','');
368 };
368 };
369 YUD.setStyle(YUD.get('code_stats_show_more'),
369 YUD.setStyle(YUD.get('code_stats_show_more'),
370 'display','none');
370 'display','none');
371 });
371 });
372 </script>
372 </script>
373 <script type="text/javascript">
373 <script type="text/javascript">
374 /**
374 /**
375 * Plots summary graph
375 * Plots summary graph
376 *
376 *
377 * @class SummaryPlot
377 * @class SummaryPlot
378 * @param {from} initial from for detailed graph
378 * @param {from} initial from for detailed graph
379 * @param {to} initial to for detailed graph
379 * @param {to} initial to for detailed graph
380 * @param {dataset}
380 * @param {dataset}
381 * @param {overview_dataset}
381 * @param {overview_dataset}
382 */
382 */
383 function SummaryPlot(from,to,dataset,overview_dataset) {
383 function SummaryPlot(from,to,dataset,overview_dataset) {
384 var initial_ranges = {
384 var initial_ranges = {
385 "xaxis":{
385 "xaxis":{
386 "from":from,
386 "from":from,
387 "to":to,
387 "to":to,
388 },
388 },
389 };
389 };
390 var dataset = dataset;
390 var dataset = dataset;
391 var overview_dataset = [overview_dataset];
391 var overview_dataset = [overview_dataset];
392 var choiceContainer = YUD.get("legend_choices");
392 var choiceContainer = YUD.get("legend_choices");
393 var choiceContainerTable = YUD.get("legend_choices_tables");
393 var choiceContainerTable = YUD.get("legend_choices_tables");
394 var plotContainer = YUD.get('commit_history');
394 var plotContainer = YUD.get('commit_history');
395 var overviewContainer = YUD.get('overview');
395 var overviewContainer = YUD.get('overview');
396
396
397 var plot_options = {
397 var plot_options = {
398 bars: {show:true,align:'center',lineWidth:4},
398 bars: {show:true,align:'center',lineWidth:4},
399 legend: {show:true, container:"legend_container"},
399 legend: {show:true, container:"legend_container"},
400 points: {show:true,radius:0,fill:false},
400 points: {show:true,radius:0,fill:false},
401 yaxis: {tickDecimals:0,},
401 yaxis: {tickDecimals:0,},
402 xaxis: {
402 xaxis: {
403 mode: "time",
403 mode: "time",
404 timeformat: "%d/%m",
404 timeformat: "%d/%m",
405 min:from,
405 min:from,
406 max:to,
406 max:to,
407 },
407 },
408 grid: {
408 grid: {
409 hoverable: true,
409 hoverable: true,
410 clickable: true,
410 clickable: true,
411 autoHighlight:true,
411 autoHighlight:true,
412 color: "#999"
412 color: "#999"
413 },
413 },
414 //selection: {mode: "x"}
414 //selection: {mode: "x"}
415 };
415 };
416 var overview_options = {
416 var overview_options = {
417 legend:{show:false},
417 legend:{show:false},
418 bars: {show:true,barWidth: 2,},
418 bars: {show:true,barWidth: 2,},
419 shadowSize: 0,
419 shadowSize: 0,
420 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
420 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
421 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
421 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
422 grid: {color: "#999",},
422 grid: {color: "#999",},
423 selection: {mode: "x"}
423 selection: {mode: "x"}
424 };
424 };
425
425
426 /**
426 /**
427 *get dummy data needed in few places
427 *get dummy data needed in few places
428 */
428 */
429 function getDummyData(label){
429 function getDummyData(label){
430 return {"label":label,
430 return {"label":label,
431 "data":[{"time":0,
431 "data":[{"time":0,
432 "commits":0,
432 "commits":0,
433 "added":0,
433 "added":0,
434 "changed":0,
434 "changed":0,
435 "removed":0,
435 "removed":0,
436 }],
436 }],
437 "schema":["commits"],
437 "schema":["commits"],
438 "color":'#ffffff',
438 "color":'#ffffff',
439 }
439 }
440 }
440 }
441
441
442 /**
442 /**
443 * generate checkboxes accordindly to data
443 * generate checkboxes accordindly to data
444 * @param keys
444 * @param keys
445 * @returns
445 * @returns
446 */
446 */
447 function generateCheckboxes(data) {
447 function generateCheckboxes(data) {
448 //append checkboxes
448 //append checkboxes
449 var i = 0;
449 var i = 0;
450 choiceContainerTable.innerHTML = '';
450 choiceContainerTable.innerHTML = '';
451 for(var pos in data) {
451 for(var pos in data) {
452
452
453 data[pos].color = i;
453 data[pos].color = i;
454 i++;
454 i++;
455 if(data[pos].label != ''){
455 if(data[pos].label != ''){
456 choiceContainerTable.innerHTML +=
456 choiceContainerTable.innerHTML +=
457 '<tr><td><input type="checkbox" id="id_user_{0}" name="{0}" checked="checked" /> \
457 '<tr><td><input type="checkbox" id="id_user_{0}" name="{0}" checked="checked" /> \
458 <label for="id_user_{0}">{0}</label></td></tr>'.format(data[pos].label);
458 <label for="id_user_{0}">{0}</label></td></tr>'.format(data[pos].label);
459 }
459 }
460 }
460 }
461 }
461 }
462
462
463 /**
463 /**
464 * ToolTip show
464 * ToolTip show
465 */
465 */
466 function showTooltip(x, y, contents) {
466 function showTooltip(x, y, contents) {
467 var div=document.getElementById('tooltip');
467 var div=document.getElementById('tooltip');
468 if(!div) {
468 if(!div) {
469 div = document.createElement('div');
469 div = document.createElement('div');
470 div.id="tooltip";
470 div.id="tooltip";
471 div.style.position="absolute";
471 div.style.position="absolute";
472 div.style.border='1px solid #fdd';
472 div.style.border='1px solid #fdd';
473 div.style.padding='2px';
473 div.style.padding='2px';
474 div.style.backgroundColor='#fee';
474 div.style.backgroundColor='#fee';
475 document.body.appendChild(div);
475 document.body.appendChild(div);
476 }
476 }
477 YUD.setStyle(div, 'opacity', 0);
477 YUD.setStyle(div, 'opacity', 0);
478 div.innerHTML = contents;
478 div.innerHTML = contents;
479 div.style.top=(y + 5) + "px";
479 div.style.top=(y + 5) + "px";
480 div.style.left=(x + 5) + "px";
480 div.style.left=(x + 5) + "px";
481
481
482 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
482 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
483 anim.animate();
483 anim.animate();
484 }
484 }
485
485
486 /**
486 /**
487 * This function will detect if selected period has some changesets
487 * This function will detect if selected period has some changesets
488 for this user if it does this data is then pushed for displaying
488 for this user if it does this data is then pushed for displaying
489 Additionally it will only display users that are selected by the checkbox
489 Additionally it will only display users that are selected by the checkbox
490 */
490 */
491 function getDataAccordingToRanges(ranges) {
491 function getDataAccordingToRanges(ranges) {
492
492
493 var data = [];
493 var data = [];
494 var new_dataset = {};
494 var new_dataset = {};
495 var keys = [];
495 var keys = [];
496 var max_commits = 0;
496 var max_commits = 0;
497 for(var key in dataset){
497 for(var key in dataset){
498
498
499 for(var ds in dataset[key].data){
499 for(var ds in dataset[key].data){
500 commit_data = dataset[key].data[ds];
500 commit_data = dataset[key].data[ds];
501 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
501 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
502
502
503 if(new_dataset[key] === undefined){
503 if(new_dataset[key] === undefined){
504 new_dataset[key] = {data:[],schema:["commits"],label:key};
504 new_dataset[key] = {data:[],schema:["commits"],label:key};
505 }
505 }
506 new_dataset[key].data.push(commit_data);
506 new_dataset[key].data.push(commit_data);
507 }
507 }
508 }
508 }
509 if (new_dataset[key] !== undefined){
509 if (new_dataset[key] !== undefined){
510 data.push(new_dataset[key]);
510 data.push(new_dataset[key]);
511 }
511 }
512 }
512 }
513
513
514 if (data.length > 0){
514 if (data.length > 0){
515 return data;
515 return data;
516 }
516 }
517 else{
517 else{
518 //just return dummy data for graph to plot itself
518 //just return dummy data for graph to plot itself
519 return [getDummyData('')];
519 return [getDummyData('')];
520 }
520 }
521 }
521 }
522
522
523 /**
523 /**
524 * redraw using new checkbox data
524 * redraw using new checkbox data
525 */
525 */
526 function plotchoiced(e,args){
526 function plotchoiced(e,args){
527 var cur_data = args[0];
527 var cur_data = args[0];
528 var cur_ranges = args[1];
528 var cur_ranges = args[1];
529
529
530 var new_data = [];
530 var new_data = [];
531 var inputs = choiceContainer.getElementsByTagName("input");
531 var inputs = choiceContainer.getElementsByTagName("input");
532
532
533 //show only checked labels
533 //show only checked labels
534 for(var i=0; i<inputs.length; i++) {
534 for(var i=0; i<inputs.length; i++) {
535 var checkbox_key = inputs[i].name;
535 var checkbox_key = inputs[i].name;
536
536
537 if(inputs[i].checked){
537 if(inputs[i].checked){
538 for(var d in cur_data){
538 for(var d in cur_data){
539 if(cur_data[d].label == checkbox_key){
539 if(cur_data[d].label == checkbox_key){
540 new_data.push(cur_data[d]);
540 new_data.push(cur_data[d]);
541 }
541 }
542 }
542 }
543 }
543 }
544 else{
544 else{
545 //push dummy data to not hide the label
545 //push dummy data to not hide the label
546 new_data.push(getDummyData(checkbox_key));
546 new_data.push(getDummyData(checkbox_key));
547 }
547 }
548 }
548 }
549
549
550 var new_options = YAHOO.lang.merge(plot_options, {
550 var new_options = YAHOO.lang.merge(plot_options, {
551 xaxis: {
551 xaxis: {
552 min: cur_ranges.xaxis.from,
552 min: cur_ranges.xaxis.from,
553 max: cur_ranges.xaxis.to,
553 max: cur_ranges.xaxis.to,
554 mode:"time",
554 mode:"time",
555 timeformat: "%d/%m",
555 timeformat: "%d/%m",
556 },
556 },
557 });
557 });
558 if (!new_data){
558 if (!new_data){
559 new_data = [[0,1]];
559 new_data = [[0,1]];
560 }
560 }
561 // do the zooming
561 // do the zooming
562 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
562 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
563
563
564 plot.subscribe("plotselected", plotselected);
564 plot.subscribe("plotselected", plotselected);
565
565
566 //resubscribe plothover
566 //resubscribe plothover
567 plot.subscribe("plothover", plothover);
567 plot.subscribe("plothover", plothover);
568
568
569 // don't fire event on the overview to prevent eternal loop
569 // don't fire event on the overview to prevent eternal loop
570 overview.setSelection(cur_ranges, true);
570 overview.setSelection(cur_ranges, true);
571
571
572 }
572 }
573
573
574 /**
574 /**
575 * plot only selected items from overview
575 * plot only selected items from overview
576 * @param ranges
576 * @param ranges
577 * @returns
577 * @returns
578 */
578 */
579 function plotselected(ranges,cur_data) {
579 function plotselected(ranges,cur_data) {
580 //updates the data for new plot
580 //updates the data for new plot
581 var data = getDataAccordingToRanges(ranges);
581 var data = getDataAccordingToRanges(ranges);
582 generateCheckboxes(data);
582 generateCheckboxes(data);
583
583
584 var new_options = YAHOO.lang.merge(plot_options, {
584 var new_options = YAHOO.lang.merge(plot_options, {
585 xaxis: {
585 xaxis: {
586 min: ranges.xaxis.from,
586 min: ranges.xaxis.from,
587 max: ranges.xaxis.to,
587 max: ranges.xaxis.to,
588 mode:"time",
588 mode:"time",
589 timeformat: "%d/%m",
589 timeformat: "%d/%m",
590 },
590 },
591 });
591 });
592 // do the zooming
592 // do the zooming
593 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
593 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
594
594
595 plot.subscribe("plotselected", plotselected);
595 plot.subscribe("plotselected", plotselected);
596
596
597 //resubscribe plothover
597 //resubscribe plothover
598 plot.subscribe("plothover", plothover);
598 plot.subscribe("plothover", plothover);
599
599
600 // don't fire event on the overview to prevent eternal loop
600 // don't fire event on the overview to prevent eternal loop
601 overview.setSelection(ranges, true);
601 overview.setSelection(ranges, true);
602
602
603 //resubscribe choiced
603 //resubscribe choiced
604 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
604 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
605 }
605 }
606
606
607 var previousPoint = null;
607 var previousPoint = null;
608
608
609 function plothover(o) {
609 function plothover(o) {
610 var pos = o.pos;
610 var pos = o.pos;
611 var item = o.item;
611 var item = o.item;
612
612
613 //YUD.get("x").innerHTML = pos.x.toFixed(2);
613 //YUD.get("x").innerHTML = pos.x.toFixed(2);
614 //YUD.get("y").innerHTML = pos.y.toFixed(2);
614 //YUD.get("y").innerHTML = pos.y.toFixed(2);
615 if (item) {
615 if (item) {
616 if (previousPoint != item.datapoint) {
616 if (previousPoint != item.datapoint) {
617 previousPoint = item.datapoint;
617 previousPoint = item.datapoint;
618
618
619 var tooltip = YUD.get("tooltip");
619 var tooltip = YUD.get("tooltip");
620 if(tooltip) {
620 if(tooltip) {
621 tooltip.parentNode.removeChild(tooltip);
621 tooltip.parentNode.removeChild(tooltip);
622 }
622 }
623 var x = item.datapoint.x.toFixed(2);
623 var x = item.datapoint.x.toFixed(2);
624 var y = item.datapoint.y.toFixed(2);
624 var y = item.datapoint.y.toFixed(2);
625
625
626 if (!item.series.label){
626 if (!item.series.label){
627 item.series.label = 'commits';
627 item.series.label = 'commits';
628 }
628 }
629 var d = new Date(x*1000);
629 var d = new Date(x*1000);
630 var fd = d.toDateString()
630 var fd = d.toDateString()
631 var nr_commits = parseInt(y);
631 var nr_commits = parseInt(y);
632
632
633 var cur_data = dataset[item.series.label].data[item.dataIndex];
633 var cur_data = dataset[item.series.label].data[item.dataIndex];
634 var added = cur_data.added;
634 var added = cur_data.added;
635 var changed = cur_data.changed;
635 var changed = cur_data.changed;
636 var removed = cur_data.removed;
636 var removed = cur_data.removed;
637
637
638 var nr_commits_suffix = " ${_('commits')} ";
638 var nr_commits_suffix = " ${_('commits')} ";
639 var added_suffix = " ${_('files added')} ";
639 var added_suffix = " ${_('files added')} ";
640 var changed_suffix = " ${_('files changed')} ";
640 var changed_suffix = " ${_('files changed')} ";
641 var removed_suffix = " ${_('files removed')} ";
641 var removed_suffix = " ${_('files removed')} ";
642
642
643
643
644 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
644 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
645 if(added==1){added_suffix=" ${_('file added')} ";}
645 if(added==1){added_suffix=" ${_('file added')} ";}
646 if(changed==1){changed_suffix=" ${_('file changed')} ";}
646 if(changed==1){changed_suffix=" ${_('file changed')} ";}
647 if(removed==1){removed_suffix=" ${_('file removed')} ";}
647 if(removed==1){removed_suffix=" ${_('file removed')} ";}
648
648
649 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
649 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
650 +'<br/>'+
650 +'<br/>'+
651 nr_commits + nr_commits_suffix+'<br/>'+
651 nr_commits + nr_commits_suffix+'<br/>'+
652 added + added_suffix +'<br/>'+
652 added + added_suffix +'<br/>'+
653 changed + changed_suffix + '<br/>'+
653 changed + changed_suffix + '<br/>'+
654 removed + removed_suffix + '<br/>');
654 removed + removed_suffix + '<br/>');
655 }
655 }
656 }
656 }
657 else {
657 else {
658 var tooltip = YUD.get("tooltip");
658 var tooltip = YUD.get("tooltip");
659
659
660 if(tooltip) {
660 if(tooltip) {
661 tooltip.parentNode.removeChild(tooltip);
661 tooltip.parentNode.removeChild(tooltip);
662 }
662 }
663 previousPoint = null;
663 previousPoint = null;
664 }
664 }
665 }
665 }
666
666
667 /**
667 /**
668 * MAIN EXECUTION
668 * MAIN EXECUTION
669 */
669 */
670
670
671 var data = getDataAccordingToRanges(initial_ranges);
671 var data = getDataAccordingToRanges(initial_ranges);
672 generateCheckboxes(data);
672 generateCheckboxes(data);
673
673
674 //main plot
674 //main plot
675 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
675 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
676
676
677 //overview
677 //overview
678 var overview = YAHOO.widget.Flot(overviewContainer,
678 var overview = YAHOO.widget.Flot(overviewContainer,
679 overview_dataset, overview_options);
679 overview_dataset, overview_options);
680
680
681 //show initial selection on overview
681 //show initial selection on overview
682 overview.setSelection(initial_ranges);
682 overview.setSelection(initial_ranges);
683
683
684 plot.subscribe("plotselected", plotselected);
684 plot.subscribe("plotselected", plotselected);
685 plot.subscribe("plothover", plothover)
685 plot.subscribe("plothover", plothover)
686
686
687 overview.subscribe("plotselected", function (ranges) {
687 overview.subscribe("plotselected", function (ranges) {
688 plot.setSelection(ranges);
688 plot.setSelection(ranges);
689 });
689 });
690
690
691 // user choices on overview
691 // user choices on overview
692 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
692 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
693 }
693 }
694 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
694 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
695 </script>
695 </script>
696 %endif
696 %endif
697
697
698 </%def>
698 </%def>
General Comments 0
You need to be logged in to leave comments. Login now